Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Document Player Knockback related functions #1601

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

mzxrules
Copy link
Contributor

No description provided.

@mzxrules
Copy link
Contributor Author

Contributions made in this pr are licensed under CC0

Copy link
Contributor

@hensldm hensldm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me, though some nits/questions around adjacent areas.

src/overlays/actors/ovl_player_actor/z_player.c Outdated Show resolved Hide resolved
src/overlays/actors/ovl_player_actor/z_player.c Outdated Show resolved Hide resolved
@zeldaret zeldaret deleted a comment Jan 27, 2024
Copy link
Collaborator

@fig02 fig02 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

apologies for delayed review, got distracted with retail matching

Comment on lines +4076 to 4078
void Player_SetIFrameInvincibilityTimer(Player* this, s32 timer) {
if (this->invincibilityTimer > timer) {
this->invincibilityTimer = timer;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dont really think this name works. "i frame invincibility timer" means "invincibility frame invincibility timer", it doesnt distinguish it from the function above.

Really we need to name this based on the fact that one is visible with flashing, while the other invisible. I dont really have any good ideas for that though, and think the word "invisible" is both clunky and kind of confusing next to the word "invincible"

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

my only suggestion is Player_SetInvincibilityTimer and Player_SetInvincibilityTimerFlashing

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hard disagree. Nothing with these functions creates that flashing effect, that's just a superficial indicator to the underlying state of the player.

With player->invincibilityTimer, remember that negative values are specifically tied with natural invulnerability (what I opted to call IFrames), while positive values are specifically tied to invulnerability after taking damage. We can see this in action for both z_en_ik.c and z_en_wallmas.c.

In z_en_ik.c, the iron knuckle tests that player->invincibilityTimer is less than or equal to 0 in order to override the invincibility from e.g. rolling, allowing it to knock back the player and deal damage, while still maintaining invincibility if the player was already damaged.

In z_en_wallmas.c, the wall master tests that player->invincibilityTimer is greater or equal to 0 to allow the player the ability to dodge being grabbed when rolling.

Now if you follow the logic for func_80837AE0 and func_80837AFC...

  • func_80837AE0 will not behave in a sensible way if passed in a negative value. Firstly, it won't enable the flashing effect you want to attribute to the function, and secondly if player->invincibilityTimer is already negative and another negative value is passed in, the new value won't take unless the result reduces the number of invincibility frames.
  • Similarly func_80837AFC will not function correctly when passed a positive value. player->invincibilityTimer will never be updated to a positive value unless player->invincibilityTimer is already a larger positive value, which would have that same weird effect of reducing the number of invincibility frames.

So that's why I went with damaged/IFrames.

Copy link
Collaborator

@fig02 fig02 Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dont follow your reasoning for why it cant be tied to flashing. As you mention func_80837AE0 only intends to receive positive values, and positive values for invincibilityTimer are what make the player flash (after taking damage).
If you think its more important to emphasize the fact that its for damage, thats fine, but you make it seem like the flashing isnt relevant at all, when it definitely is.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few reasons:

  • There is no "one function sets up flashing, one doesn't", like your suggestion proposed. unk_88F is the value that tracks the flashing effect, and zeroing it simply resets the animation, and again neither function are directly responsible for creating that flashing animation.
  • The state change to player->invincibilityTimer is of far greater importance gameplay wise than the effect of flashing the player.
    • func_80837AE0 is making sure the player isn't under IFrames before setting up a fixed number of damage frames
    • func_80837AFC is either overriding damage frames/neutral with IFrames, or overriding IFrames with a longer IFrame timer
  • The flashing is a visual feedback effect reflecting the state of player->invincibilityTimer and it just doesn't need to be explicitly stated that a flashing effect will trigger, similar to how ChangeRupees does not explicitly state that it will trigger money making sound effects since it updates the rupee accumulator variable instead of the rupee variable directly.

src/overlays/actors/ovl_player_actor/z_player.c Outdated Show resolved Hide resolved
Comment on lines +559 to +565
typedef enum {
/* 0 */ PLAYER_DAMAGE_RESPONSE_NONE,
/* 1 */ PLAYER_DAMAGE_RESPONSE_KNOCKBACK_LARGE,
/* 2 */ PLAYER_DAMAGE_RESPONSE_KNOCKBACK_SMALL,
/* 3 */ PLAYER_DAMAGE_RESPONSE_ICE_TRAP,
/* 4 */ PLAYER_DAMAGE_RESPONSE_ELECTRIC_SHOCK
} PlayerDamageResponseType;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a while ive been meaning to call this "damage reaction". I think both words convey the same thing so its a matter of preference. The term "damage reaction" was used in the gamecube titles to mean something similar, but thats a small point.

Really, I think "damage response/reaction" would be a good term for what we currently call "damage effect" in colliders. So using it here for this player specific thing may hurt if we wanted to use that terminology with colliders. But I wouldnt know what to call this value in that case. they really mean the same thing

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on

"damage response/reaction" would be a good term for what we currently call "damage effect" in colliders

Which fields specifically are you referring to?

Copy link
Collaborator

@fig02 fig02 Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Its easier to explain it from the perspective of a damage table:
/* Fire arrow */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE),
/* Fire magic */ DMG_ENTRY(4, DEKUBABA_DMGEFF_FIRE)
The value currently called "damage effect" describes how an actor should react or respond to the damage source, like by burning up, getting frozen, or dropping certain items, as a few examples. There are a few fields within colliders that would need to change as well, but the important one is collider->elem.toucher/bumper.effect (using masters current names)

These values here in player mean the same thing. But link has his own translation layer on top of the values he receives from a collider, for seemingly no reason. Other than to combine the collider "effects" with knockback types. Here you can see player mapping the values received from a collider to his internal "reaction" values (using code from mzx's branch for readability):

if (this->stateFlags1 & PLAYER_STATE1_27) {
    sp4C = PLAYER_DAMAGE_RESPONSE_NONE;
} else if (this->actor.colChkInfo.acHitEffect == 2) {
    sp4C = PLAYER_DAMAGE_RESPONSE_ICE_TRAP;
} else if (this->actor.colChkInfo.acHitEffect == 3) {
    sp4C = PLAYER_DAMAGE_RESPONSE_ELECTRIC_SHOCK;
} else if (this->actor.colChkInfo.acHitEffect == 4) {
    sp4C = PLAYER_DAMAGE_RESPONSE_KNOCKBACK_LARGE;
} else {
    // (im adding the comment below for clarity, this is not in mzx's branch)
    // Handle fire/burning (acHitEffect == 1) or the different kinds of knockback
    func_80838280(this);
    sp4C = PLAYER_DAMAGE_RESPONSE_NONE;
}

So basically I am suggesting that instead of calling players extra translation layer as "damage response/reaction", the field in the collider/damage system should probably get that name instead. And we would have to call Links something else.

Copy link
Collaborator

@fig02 fig02 Mar 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The value currently called "damage effect" describes how an actor should react or respond to the damage source

To add, of course, these values are actor specific, and do not have global meaning. But this does not change the fact that in general, it does describe the response/reaction to the damage (or sometimes not damage, just getting stunned)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as discussed on discord, I think its best if we revert these name changes for now and consider these names later at a broader scale with the at/acHitEffect fields in colliders.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#1925 (currently) names this->actor.colChkInfo.acHitEffect -> this->actor.colChkInfo.playerACHitReaction / PlayerACHitReaction / PLAYER_AC_HIT_REACTION_

func_808382DC as highlighted by fig above maps PLAYER_AC_HIT_REACTION_ values to this PR's current PLAYER_DAMAGE_RESPONSE_

include/z64player.h Outdated Show resolved Hide resolved
Comment on lines +559 to +565
typedef enum {
/* 0 */ PLAYER_DAMAGE_RESPONSE_NONE,
/* 1 */ PLAYER_DAMAGE_RESPONSE_KNOCKBACK_LARGE,
/* 2 */ PLAYER_DAMAGE_RESPONSE_KNOCKBACK_SMALL,
/* 3 */ PLAYER_DAMAGE_RESPONSE_ICE_TRAP,
/* 4 */ PLAYER_DAMAGE_RESPONSE_ELECTRIC_SHOCK
} PlayerDamageResponseType;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you elaborate on

"damage response/reaction" would be a good term for what we currently call "damage effect" in colliders

Which fields specifically are you referring to?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants