Jump to content

entPowerLevel, hitOwnerPowerLevel, and FORCE_LEVEL; SP coding help?


Recommended Posts

So uh, I'm trying to understand the SP code, but Raven's lack of commenting makes it very difficult.

 

FORCE_LEVEL_X is some sort of enum deal, with X being 0-5. I'm assuming they're just used like numbers? As in, FORCE_LEVEL_1 would be equivalent to a value of 1? As it pertains to setting things to a function call that returns this value?

 

entPowerLevel, this has to be the attack strength of the person in question, being an NPC or the player, but then what is hitOwnerPowerLevel? Is one used for the attacker, and one for the defender? It's very difficult for me to find any clear indication of which is what...

Link to comment

Ensiform tried to help me on IRC, but I'm still just as lost  :unsure:

 

I think it comes down to these two functions in bg_panimate.cpp. Some of the FORCE_LEVEL numbers I edited to go 1-5 with Fast being 1, Tavion/Duals being 2, Medium being 3, Staff being 4, and Red/Desann being 5.

 

int PM_AnimLevelForSaberAnim( int anim )  // not sure exactly what this is used for, as the next function seems to do basically the same thing

{
if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ )
{
return FORCE_LEVEL_1;
}
if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ )
{
return FORCE_LEVEL_2;
}
if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ )
{
return FORCE_LEVEL_3;
}
if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ )
{//desann
return FORCE_LEVEL_4;
}
if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ )
{//tavion
return FORCE_LEVEL_5;
}
if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ )
{//dual
return SS_DUAL;
}
if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ )
{//staff
return SS_STAFF;
}
return FORCE_LEVEL_0;
}

 

 

 

int PM_PowerLevelForSaberAnim( playerState_t *ps, int saberNum )  

 

{
int anim = ps->torsoAnim;
int animTimeElapsed = PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, (animNumber_t)anim ) - ps->torsoAnimTimer;
if ( anim >= BOTH_A1_T__B_ && anim <= BOTH_D1_B____ )
{
//FIXME: these two need their own style
if ( ps->saber[0].type == SABER_LANCE )
{
return FORCE_LEVEL_4;
}
else if ( ps->saber[0].type == SABER_TRIDENT )
{
return FORCE_LEVEL_3;
}
return FORCE_LEVEL_1;
}
if ( anim >= BOTH_A2_T__B_ && anim <= BOTH_D2_B____ )
{
return FORCE_LEVEL_3;
}
if ( anim >= BOTH_A3_T__B_ && anim <= BOTH_D3_B____ )
{
return FORCE_LEVEL_5;
}
if ( anim >= BOTH_A4_T__B_ && anim <= BOTH_D4_B____ )
{//desann
return FORCE_LEVEL_5;
}
if ( anim >= BOTH_A5_T__B_ && anim <= BOTH_D5_B____ )
{//tavion
return FORCE_LEVEL_2;
}
if ( anim >= BOTH_A6_T__B_ && anim <= BOTH_D6_B____ )
{//dual
return FORCE_LEVEL_2;
}
if ( anim >= BOTH_A7_T__B_ && anim <= BOTH_D7_B____ )
{//staff
return FORCE_LEVEL_4;
}
if ( ( anim >= BOTH_P1_S1_T_ && anim <= BOTH_P1_S1_BR )
|| ( anim >= BOTH_P6_S6_T_ && anim <= BOTH_P6_S6_BR ) 
|| ( anim >= BOTH_P7_S7_T_ && anim <= BOTH_P7_S7_BR ) )
{//parries
switch ( ps->saberAnimLevel )
{
case SS_STRONG:
case SS_DESANN:
return FORCE_LEVEL_5;
break;
case SS_STAFF:
return FORCE_LEVEL_4;
break;
case SS_TAVION:
case SS_DUAL:
return FORCE_LEVEL_2;
break;
case SS_MEDIUM:
return FORCE_LEVEL_3;
break;
case SS_FAST:
return FORCE_LEVEL_1;
break;
default:
return FORCE_LEVEL_0;
break;
}
}
if ( ( anim >= BOTH_K1_S1_T_ && anim <= BOTH_K1_S1_BR )
|| ( anim >= BOTH_K6_S6_T_ && anim <= BOTH_K6_S6_BR ) 
|| ( anim >= BOTH_K7_S7_T_ && anim <= BOTH_K7_S7_BR ) )
{//knockaways
return FORCE_LEVEL_3;
}
if ( ( anim >= BOTH_V1_BR_S1 && anim <= BOTH_V1_B__S1 )
|| ( anim >= BOTH_V6_BR_S6 && anim <= BOTH_V6_B__S6 ) 
|| ( anim >= BOTH_V7_BR_S7 && anim <= BOTH_V7_B__S7 ) )
{//knocked-away attacks
return FORCE_LEVEL_1;
}
if ( ( anim >= BOTH_H1_S1_T_ && anim <= BOTH_H1_S1_BR )
|| ( anim >= BOTH_H6_S6_T_ && anim <= BOTH_H6_S6_BR ) 
|| ( anim >= BOTH_H7_S7_T_ && anim <= BOTH_H7_S7_BR ) )
{//broken parries
return FORCE_LEVEL_0;
}
switch ( anim )
{
case BOTH_A2_STABBACK1:
if ( ps->torsoAnimTimer < 450 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 400 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_ATTACK_BACK:
if ( ps->torsoAnimTimer < 500 )
{//end of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_CROUCHATTACKBACK1:
if ( ps->torsoAnimTimer < 800 )
{//end of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_BUTTERFLY_LEFT:
case BOTH_BUTTERFLY_RIGHT:
case BOTH_BUTTERFLY_FL1:
case BOTH_BUTTERFLY_FR1:
//FIXME: break up?
return FORCE_LEVEL_3;
break;
case BOTH_FJSS_TR_BL:
case BOTH_FJSS_TL_BR:
//FIXME: break up?
return FORCE_LEVEL_3;
break;
case BOTH_K1_S1_T_: //# knockaway saber top
case BOTH_K1_S1_TR: //# knockaway saber top right
case BOTH_K1_S1_TL: //# knockaway saber top left
case BOTH_K1_S1_BL: //# knockaway saber bottom left
case BOTH_K1_S1_B_: //# knockaway saber bottom
case BOTH_K1_S1_BR: //# knockaway saber bottom right
//FIXME: break up?
return FORCE_LEVEL_3;
break;
case BOTH_LUNGE2_B__T_:
if ( ps->torsoAnimTimer < 400 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 150 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_FORCELEAP2_T__B_:
if ( ps->torsoAnimTimer < 400 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 550 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_VS_ATR_S:
case BOTH_VS_ATL_S:
case BOTH_VT_ATR_S:
case BOTH_VT_ATL_S:
return FORCE_LEVEL_3;//???
break;
case BOTH_JUMPFLIPSLASHDOWN1:
if ( ps->torsoAnimTimer <= 900 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 550 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_JUMPFLIPSTABDOWN:
if ( ps->torsoAnimTimer <= 1200 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed <= 250 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_JUMPATTACK6:
/*
if (pm->ps)
{
if ( ( pm->ps->legsAnimTimer >= 1450
&& PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 400 ) 
||(pm->ps->legsAnimTimer >= 400
&& PM_AnimLength( g_entities[ps->clientNum].client->clientInfo.animFileIndex, BOTH_JUMPATTACK6 ) - pm->ps->legsAnimTimer >= 1100 ) )
{//pretty much sideways
return FORCE_LEVEL_3;
}
}
*/
if ( ( ps->torsoAnimTimer >= 1450
&& animTimeElapsed >= 400 )
||(ps->torsoAnimTimer >= 400
&& animTimeElapsed >= 1100 ) )
{//pretty much sideways
return FORCE_LEVEL_3;
}
return FORCE_LEVEL_0;
break;
case BOTH_JUMPATTACK7:
if ( ps->torsoAnimTimer <= 1200 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 200 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_SPINATTACK6:
if ( animTimeElapsed <= 200 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_SPINATTACK7:
if ( ps->torsoAnimTimer <= 500 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 500 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_FORCELONGLEAP_ATTACK:
if ( animTimeElapsed <= 200 )
{//1st four frames of anim
return FORCE_LEVEL_3;
}
break;
/*


case BOTH_A7_KICK_B:
case BOTH_A7_KICK_R:
case BOTH_A7_KICK_L:
//FIXME: break up
return FORCE_LEVEL_3;
break;
*/
case BOTH_STABDOWN:
if ( ps->torsoAnimTimer <= 900 )
{//end of anim
return FORCE_LEVEL_3;
}
break;
case BOTH_STABDOWN_STAFF:
if ( ps->torsoAnimTimer <= 850 )
{//end of anim
return FORCE_LEVEL_3;
}
break;
case BOTH_STABDOWN_DUAL:
if ( ps->torsoAnimTimer <= 900 )
{//end of anim
return FORCE_LEVEL_3;
}
break;
case BOTH_A6_SABERPROTECT:
if ( ps->torsoAnimTimer < 650 )
{//end of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A7_SOULCAL:
if ( ps->torsoAnimTimer < 650 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 600 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A1_SPECIAL:
if ( ps->torsoAnimTimer < 600 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 200 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A2_SPECIAL:
if ( ps->torsoAnimTimer < 300 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 200 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A3_SPECIAL:
if ( ps->torsoAnimTimer < 700 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 200 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_FLIP_ATTACK7:
return FORCE_LEVEL_3;
break;
case BOTH_PULL_IMPALE_STAB:
if ( ps->torsoAnimTimer < 1000 )
{//end of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_PULL_IMPALE_SWING:
if ( ps->torsoAnimTimer < 500 )//750 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 650 )//600 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_ALORA_SPIN_SLASH:
if ( ps->torsoAnimTimer < 900 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 250 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A6_FB:
if ( ps->torsoAnimTimer < 250 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 250 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A6_LR: 
if ( ps->torsoAnimTimer < 250 )
{//end of anim
return FORCE_LEVEL_0;
}
else if ( animTimeElapsed < 250 )
{//beginning of anim
return FORCE_LEVEL_0;
}
return FORCE_LEVEL_3;
break;
case BOTH_A7_HILT:
return FORCE_LEVEL_0;
break;
--cut it off here because it starts doing saber lock stuff here I'm not worried about yet

 

 

 

Problem is, I know almost exactly how observed SP gameplay works, but trying to add up the code is proving near impossible to me with only minimal coding experience :(

Link to comment

Here's how SP saber system works to my knowledge: (I don't know if there are teeny tiny exceptions, but these are my observations)

 

Defense Power level is only modified by twoHanded (which is +1), and parrybonus (which is + whatever the amount). Other than that, the same for every style, and every level of Saber Defense. i.e. Saber Defense 3 on any saber style can't block Medium style any more or any less than Defense 1. Only thing that changes is whether you can do plain blocks or knockaways. Defense power level is 1 without any mods on your .sab file.

 

Offense power level is dependent on saber style (Level 1 for fast, 2 for medium/staff/duals/tavion, 3 for strong, 4 for desann), +1 for twoHanded, + X amount for breakparrybonus. Force Speed makes attacks have a flat power level of strong style, Dark rage adds +1, 2, or 3 based on level of the power.

 

 

EDIT: Saber collisions and calculations are in wp_saber.cpp, and these two functions above are in bg_panimate.cpp

Link to comment

^Good point I guess, but I'm not just talking about little point edits here. The saber combat code has lots of layers, so I need to be more careful if I want to get anywhere with editing it, at least that's my opinion right now  ;)

 

And I know what I want to do. I can give you a huge list. I even have redsaurus who's a more experienced coder helping me who will probably do the heavy lifting, or at least, I'm betting on that.

 

//explanation of concept

The concept is simple enough. Your saber offense level would give you a base power level, and then your saber style depending on which one it is, modifies it by -1, -0.5, 0, +.5, +1 etc. Saber Defense would give you a base defense level. If a slash is too powerful for you to block normally, you block it but it adds to a counter. Once you hit the max counter, you start staggering and having your guard crushed, and the counter slowly decreases back down over time.

 

^That's basically the concept in a nutshell. I could go into a lot more detail, but I feel that should get the point across.

 

//example of a difficulty I'm having

I understand the code more or less on the whole, but when I try and dissect the details I get very confused at times. For example, if you scroll down in pm_PowerLevelforSaberAnim or whatever it's called, there's a section where it has a switch case structure that talks about BOTH_P animations, which are standard blocks, but then it also returns a FORCE_LEVEL for these blocks based on saber style. 

 

This honestly makes zero sense to me, unless that piece isn't actually used by the game. It has Desann style return a force level of 4, strong of 3, medium 2, etc. as if it was talking about attacks, however those are block animations. Just flat blocks, and they should all have the same power/defense level or whatever. Just after that, it looks at knockaways vs. blocks vs. having your guard crushed, where a knockaway by the defender is a Force Level of 3, and standard block is a Force Level of 1, and a crush is 0 IIRC. This makes sense, as it's probably just looking at the strength of the defender and returning values for that. The function takes two entity arguments IIRC, and the defender (hitOwner it seems in wp_saber.cpp) can be first, or the attacker (ent in wp_saber.cpp), depending on what information you want I suppose.

 

 

Also, I'm updating the first post to use the code tags correctly  :P

Link to comment

Maybe what would help me, is understanding more of the in between pieces I skirt over, like the "ent" and "hitOwner" bits I talked about. I'm going to try and learn the difference between little things like ps->, npc->, so I know what is referring to what.

 

So, quick question, anything with an arrow (the ->) just means that what comes after the arrow is just a property of the thing that came before. For example (I just made this up, not real!), player->attacking, with "attacking" being a variable (would it have to be local to "player" which I guess would be... I don't even know lol, an object? But is this C or C++ we're talking about?) just is looking at the value of player's attacking property/variable etc. etc.?

 

I know that's kind of confusing, but can anyone tell me if I have the right idea? 

Link to comment

Check a model with modelview. yu can see the saber animation. For what i see,...

seven style:

A1 fast

A2 medium

A3 strong

A4 Desann

A5 Tavion

A6 Dual

A7 Staff.

The name like A1_T_B__ is the descriprion of the swing attack: in that case from top, to bottom. if yu see A2_L_R is from left, to rigth. BR_TL is from bottom right to top left. etc.

THe animation with P enum are the parries. (for deflect blatser and also for parry enemy i think)

The animation with T are hte trasnistaion animation by the standing pose of the saber style and the swing atk.

The looong list of LK animation are the lock animation. every one have also a Win animation and a Lose animation. the Lose animation making if enemy crush your defense, and the Win animation if the player crash the defense of the enemy.

at the end, the R animation, are the return, by swing to sword stand poses.

The kata animation have the animation yu can see in the sabers file descriptrion. remember that something like LS_BUTTEFLY_LEFT it's like BOTH_BUTTEFLY_LEFT into the sab file commands.

 

that's is the explanation of the name of the animation of lightsabers attacks. :)

they are... a thousand of animation, i hope yu can undersytand all the code. i see everyt style have 7 swing attack or something like that. every swing have a transitrion and a return animation. understood?

and so 7 styles x 7 swings x 3 (a swing have 3 animation, the T, transistion, the Swing, with name style and desdfritpion and the return: ) total : 147

:

Stoiss likes this
Link to comment

Lol asgarath I know all the animations, that's why I get confused, I don't always understand how it fits with the code. Well, I guess I would be even more confused.

 

K is for knockaway,

V is for being a knockawayee,

H is for having your guard crushed I believe.

Link to comment

Maybe what would help me, is understanding more of the in between pieces I skirt over, like the "ent" and "hitOwner" bits I talked about. I'm going to try and learn the difference between little things like ps->, npc->, so I know what is referring to what.

 

So, quick question, anything with an arrow (the ->) just means that what comes after the arrow is just a property of the thing that came before. For example (I just made this up, not real!), player->attacking, with "attacking" being a variable (would it have to be local to "player" which I guess would be... I don't even know lol, an object? But is this C or C++ we're talking about?) just is looking at the value of player's attacking property/variable etc. etc.?

 

I know that's kind of confusing, but can anyone tell me if I have the right idea? 

Well, basically. It has to do with pointers, which are one of the trickiest subjects in C/C++.

 

Basically, a pointer can be considered to be a variable which "points" to something else. Actually, a pointer is a number - since everything is stored in memory, the number refers to the address (which is usually in hexadecimal, like 0x6FAE1100 for instance). Pointers are sometimes constant, but we aren't going to go into nitty-gritty hardware/low level OS stuff at present. Just think of a pointer as a variable which points to something else.

 

Naturally, all of these things you're talking about are actually structs. I suggest poking around the g_local.h file, it has a lot of structs defined. Each struct can be considered a block of data with various properties, like ints, strings, more pointers, etc. In Java, you have classes which are basically the same thing as a struct. In C++, there are also classes, which are almost exactly the same thing as a struct. There's only very minor differences with a class (you can actually do everything you could with a class, with a struct. The only difference is that class uses private by default, instead of public). I'm rambling of course, so forgive me.

 

So...what are all the different structs in the game? Well...

  • gentity_t / "ent" is the core entity structure. It has everything that any entity would have.
  • entityState_t / "s" contains networked information for each entity. This information is relayed to all clients in the game. (MP only)
  • gclient_t / "client" contains information specific to players and NPCs. All other entities (doors, etc) will have a NULL pointer to this.
  • playerState_t / "ps" contains networked information for each player. Unlike entityState_t, your playerState_t is only networked to you. (This exists in SP but it isn't needed)
  • NPC_t / "npc" contains information (not networked) for each NPC. Also there are some global variables too.
  • cg_t / "cg" contains tons of global variables, which are all stored in cg. Generally, this is only accessed on the clientside / cgame.

 

When you have an object which is a pointer to something else (e.g, a gentity_t* vs a gentity_t), you use -> to dereference the pointer, and in turn access one of the properties in that struct. Conversely, when something is not a pointer, like just a simple gentity_t, you use a . to access the properties (so for instance, in mp: ent->s.number). The reason why most things you see use pointers (like...playerState_t* ps for instance) vs nonpointer is because:

  1. It's faster. A whole struct can take up more than 4 bytes, while gentity_t (in SP) is ~2000 bytes long (around 2 kilobytes). So instead of copying all of the information when you do a function call, you're only copying 4 bytes (the address of that variable). Conversely, the playerState_t struct is either 600 or 700 bytes long (I forget the exact number, I checked when I was doing ASM SP hacks way back in the day), so it's at least semi-manageable to have as a non-pointer.
  2. You're actually modifying the data. If you just passed in a gentity_t instead of a gentity_t*, you would be modifying a copy of it, instead of modifying the data stored in that pointer. Remember: when you don't pass a pointer (or in C++, a reference), you're actually copying the values of that struct into another variable.
  3. It offers more flexibility. In addition to not needing a return value (since you don't need to return something), you also have the ability to have multiple things being modified at once (so in essence, you can use this method to have multiple return values of a function!)
  4. Some operations (ie, memcpy, memset, other function calls...) require pointers.
  5. It's more secure (TO SOME EXTENT). If you have bad data being passed into the function, all you need to do is check if that variable being passed in is NULL.
  6. It's cleaner-looking code. (in my opinion)

So yeah, that should summarize that for you. Sorry if that was long and rambly, I just tried to squeeze as much info as possible. :)

Link to comment

^Lot to digest, sorry I haven't posted yet! I really appreciate all the info though and you taking the time to explain things!

 

I kind of knew pointers boosted variable value retrieval speed, but that was about it. The rest I still need to read in more detail.

Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...