You do have a good point regarding the verb names. What was going through my head was two things:
- having the verb name clarify how many arguments it wanted (especially since IntNeg and IntAbs only want 1 arg)
- having the verb name mimic the layout of the operator usage (e.g. IntSubInt(5, 4) mimics the layout of 5 - 4)
I don't think I'll change them, but I also don't love them. I am considering changing most of the "input" verbs to remove the "jkString" prefix. I hate changing verb names (making API changes) after releasing them to the wild, but it may not be too painful in this case given it's surely only you using them.
I haven't touched GetThingHeadLVec. It's pretty flawed and would need a complete rewrite; I'm more likely to leave it and make a new verb, but that's low on my list of stuff I want to do.
A suggestion for the syntax when using already pushed stack vars is to put placeholders so you remember how many incoming stack entries you're going to use. Some characters are completely ignored by the COG parser, and so have no impact on the resulting bytecode, but in my opinion, help the programmer keep track of where they're using stack entries. An example:
Note my use of @ as a placeholder for a stack entry, and ` as a placeholder for ,. It's not perfect, but it lets me stay on top of jkPlayPOVKey wanting 4 arguments, and where the existing stack entries are used versus the two additional ones I'm specifying "normally" in the parenthesis. To clarify, @ and ` do nothing and are ignored during COG parsing time and don't affect COG runtime performance; they're just there for my benefit.
This lets me search a COG for @ to see anywhere I used this crazy syntax to make sure I'm not messing it up when making later changes to the COG.
Unfortunately, I'm pretty sure what you posted doesn't work.
So, what you're wanting is:
SetThingVel(source, VectorAdd(@` VectorAdd(@` @)));
But what's actually happening is:
SetThingVel(@` VectorAdd(@` VectorAdd(@` source)));
You'd need to take "source" out of the SetThingVel() line, and put it up above all 3 VectorScale's.
To reiterate:
SetThingVel(thing, VectorAdd(vec1, VectorAdd(vec2, vec3)));
Decomposes as:
And:
SetThingVel(thing, VectorAdd(VectorAdd(vec1, vec2), vec3));
Decomposes as:
...I think. I'm not checking these by looking at the compiled bytecode to be sure, just going based on when I have done that.... jeez, I dunno how many years ago.
Doing stuff like this can easily lead to mistakes. Assignment (=) is probably the most common gotcha, because the variable to be assigned to normally would have had to have been pushed onto the stack first (in my example, at the top before player;). Nested verbs can also (as seen) lead to mistakes.
I'm all for this though; syntax parsers are made to be abused, and the more you do stuff like this both the better you know the tool(s) you're using, and in the case of COG, you can squeeze more performance out of it. Also, it tends to be fun. This is all for fun; pretensions like maintainable code can go screw.
QM
Edit: Thinking on it, this could be a situation to use StackExchange():
...should work. Makes the code clear as mud, but eh.
Note that StackExchange() does not need to contain the two stack entries it's exchanging as arguments. This was so it could all be a (semi)logical one-liner.
Edit 2: JK 2023 rev212, cogext 2023 rev020:
https://drive.google.com/file/d/1pw35pXi3MIiOvWZhUcRMsrWc-4n4pXfO/view
https://drive.google.com/file/d/1wJn6j6Lnw1sx6pqvkKRGL2zlYt7p54O7/view
JK patch is just to support a new cogext feature. Adding hierarchy nodes to a 3do after it's in use blows the game up. Or it did, not anymore.
New cogext verbs:
AppendThingMaterial(thing, mat); returns index of new mat, clones model if not already a clone
AppendModelMaterial(model, mat); returns index of new mat, can't add a dynamic mat to a static model
AppendThingEmptyMesh(thing, mesh_name_string); returns index of new mesh, clones model if not already a clone
AppendModelEmptyMesh(model, mesh_name_string); returns index of new mesh, use this with SetModelMesh (etc.)
AppendModelNode(model, int_flags, int_type, mesh_index_or_name_string, parent_node_index_or_name_string, vector_xyz, vector_pyr, vector_pivot, node_name_string); returns index of new node, 9 arguments!
No AppendThingNode yet. Appending a hierarchy node is apparently not something that the game was designed to accommodate; was tough enough getting all the kinks worked out without automatic cloning. Also, I've found that it's pretty useful for modifying models that aren't clones (e.g. adding a right hand to vanilla player models at runtime).
The two AppendMaterial verbs are borderline useless, but I made them first to ease myself into the much more difficult AppendMesh and especially AppendNode verbs.
I intend to do a bunch of support verbs for manipulating nodes, but needed a break after I got the core feature working.
Here's some example COG:
- having the verb name clarify how many arguments it wanted (especially since IntNeg and IntAbs only want 1 arg)
- having the verb name mimic the layout of the operator usage (e.g. IntSubInt(5, 4) mimics the layout of 5 - 4)
I don't think I'll change them, but I also don't love them. I am considering changing most of the "input" verbs to remove the "jkString" prefix. I hate changing verb names (making API changes) after releasing them to the wild, but it may not be too painful in this case given it's surely only you using them.
I haven't touched GetThingHeadLVec. It's pretty flawed and would need a complete rewrite; I'm more likely to leave it and make a new verb, but that's low on my list of stuff I want to do.
A suggestion for the syntax when using already pushed stack vars is to put placeholders so you remember how many incoming stack entries you're going to use. Some characters are completely ignored by the COG parser, and so have no impact on the resulting bytecode, but in my opinion, help the programmer keep track of where they're using stack entries. An example:
Code:
player; if(mode == 3) { holdAnim; } else if(mode == 4) { povPreBlockAnim; } else if(mode == 5) { holdAnim; } else if(mode == 6) { povPreBlockAnim; } else if(mode == 1) { povFireAnimR1; } else // if(mode == 2) { povPreFireAnim; } jkPlayPOVKey(@` @` 0, 0x14); mountAnimID = StackExchange();
Note my use of @ as a placeholder for a stack entry, and ` as a placeholder for ,. It's not perfect, but it lets me stay on top of jkPlayPOVKey wanting 4 arguments, and where the existing stack entries are used versus the two additional ones I'm specifying "normally" in the parenthesis. To clarify, @ and ` do nothing and are ignored during COG parsing time and don't affect COG runtime performance; they're just there for my benefit.
This lets me search a COG for @ to see anywhere I used this crazy syntax to make sure I'm not messing it up when making later changes to the COG.
Unfortunately, I'm pretty sure what you posted doesn't work.
So, what you're wanting is:
SetThingVel(source, VectorAdd(@` VectorAdd(@` @)));
But what's actually happening is:
SetThingVel(@` VectorAdd(@` VectorAdd(@` source)));
Code:
ScaledRVec; ScaledLVec; ScaledUVec; source; VectorAdd(); // inner, consumes source and ScaledUVec, which doesn't make sense VectorAdd(); // outer, consumes return from inner VectorAdd and ScaledLVec SetThingVel(); // consumes return from outer VectorAdd and ScaledRVec, which doesn't make sense
You'd need to take "source" out of the SetThingVel() line, and put it up above all 3 VectorScale's.
To reiterate:
SetThingVel(thing, VectorAdd(vec1, VectorAdd(vec2, vec3)));
Decomposes as:
Code:
thing; vec1; vec2; vec3; VectorAdd(); // inner VectorAdd(); // outer SetThingVel();
And:
SetThingVel(thing, VectorAdd(VectorAdd(vec1, vec2), vec3));
Decomposes as:
Code:
thing; vec1; vec2; VectorAdd(); // inner vec3; VectorAdd(); // outer SetThingVel();
...I think. I'm not checking these by looking at the compiled bytecode to be sure, just going based on when I have done that.... jeez, I dunno how many years ago.
Doing stuff like this can easily lead to mistakes. Assignment (=) is probably the most common gotcha, because the variable to be assigned to normally would have had to have been pushed onto the stack first (in my example, at the top before player;). Nested verbs can also (as seen) lead to mistakes.
I'm all for this though; syntax parsers are made to be abused, and the more you do stuff like this both the better you know the tool(s) you're using, and in the case of COG, you can squeeze more performance out of it. Also, it tends to be fun. This is all for fun; pretensions like maintainable code can go screw.
QM
Edit: Thinking on it, this could be a situation to use StackExchange():
Code:
ScaledRVec; ScaledLVec; ScaledUVec; SetThingVel(StackExchange(VectorAdd(@` VectorAdd(@` @)), source));
...should work. Makes the code clear as mud, but eh.
Note that StackExchange() does not need to contain the two stack entries it's exchanging as arguments. This was so it could all be a (semi)logical one-liner.
Edit 2: JK 2023 rev212, cogext 2023 rev020:
https://drive.google.com/file/d/1pw35pXi3MIiOvWZhUcRMsrWc-4n4pXfO/view
https://drive.google.com/file/d/1wJn6j6Lnw1sx6pqvkKRGL2zlYt7p54O7/view
JK patch is just to support a new cogext feature. Adding hierarchy nodes to a 3do after it's in use blows the game up. Or it did, not anymore.
New cogext verbs:
AppendThingMaterial(thing, mat); returns index of new mat, clones model if not already a clone
AppendModelMaterial(model, mat); returns index of new mat, can't add a dynamic mat to a static model
AppendThingEmptyMesh(thing, mesh_name_string); returns index of new mesh, clones model if not already a clone
AppendModelEmptyMesh(model, mesh_name_string); returns index of new mesh, use this with SetModelMesh (etc.)
AppendModelNode(model, int_flags, int_type, mesh_index_or_name_string, parent_node_index_or_name_string, vector_xyz, vector_pyr, vector_pivot, node_name_string); returns index of new node, 9 arguments!
No AppendThingNode yet. Appending a hierarchy node is apparently not something that the game was designed to accommodate; was tough enough getting all the kinks worked out without automatic cloning. Also, I've found that it's pretty useful for modifying models that aren't clones (e.g. adding a right hand to vanilla player models at runtime).
The two AppendMaterial verbs are borderline useless, but I made them first to ease myself into the much more difficult AppendMesh and especially AppendNode verbs.
I intend to do a bunch of support verbs for manipulating nodes, but needed a break after I got the core feature working.
Here's some example COG:
Code:
loading: player = GetLocalPlayerThing(); Sleep(1e-45); // wait for multiplayer model assignment m = GetThingModel(player); if(GetModelNodeCount(m) == 18) { AppendModelNode(m, 0x0000, 0x00001, -1, "k_torso", '0 0 0', '', '', "Object01"); i = AppendModelEmptyMesh(m, "k_rhand_"); SetModelMesh(m, i, LoadModel("fistg.3do"), 0); AppendModelNode(m, 0x0000, 0x00004, i, "k_rhand", '0 0 0', '', '-0.002488 0.009120 -0.005542', "k_rhand_"); # "fistg.3do";call _deglove; "bryg.3do"; call _deglove; "strg.3do"; call _deglove; "detg.3do"; call _deglove; "bowg.3do"; call _deglove; "rptg.3do"; call _deglove; "rldg.3do"; call _deglove; "seqg.3do"; call _deglove; "cong.3do"; call _deglove; "sabg.3do"; call _deglove; } Return; _deglove: LoadModel(@); m = StackExchange(); ReplaceModelMaterial(m, LoadMaterial("kyhand.mat"), -1); ReplaceModelMaterial(m, LoadMaterial("kyhandf.mat"), -1); Return;