Skip to content

Implements RAW Jump#197

Open
arnaudpourbaix wants to merge 2 commits intoZerdBG3:mainfrom
BG3-RAW-Mods:main
Open

Implements RAW Jump#197
arnaudpourbaix wants to merge 2 commits intoZerdBG3:mainfrom
BG3-RAW-Mods:main

Conversation

@arnaudpourbaix
Copy link

Beside Jump action, there are:

Champion level 7 passive: distance increased by a number of feet equal to your Strength modifier. 5 feet at 20str = 1.5m
It can be improved by using real strength modifier, especially for dexterity champion.

Athlete feat: you can jump after moving 5 feet instead of 10. Currently, it improves jumping distance by 1.5 meters.
Not the best implementation, but considering it increases jump distance by 1.5 when RAW do not. It is already something.

Barbarian Tiger Totem, jump increased by 3m instead of 4.5 (which was in fact a 50% multiplier instead of flat distance bonus)

All distances are now quite low, but since base Jump Distance is lower, it is still a nice improvement.

@arnaudpourbaix
Copy link
Author

I have improved Remarkable Athlete to use real strength modifier.

I have also added Long Jump, which is enabled after moving 3m:

  • Jump is now working as Long Jump for not accessible by players, to avoid breaking AI.
  • Players got 2 new skills: Jump (halved distance), and Long Jump.
  • Athlete feat is now closer to RAW. You can make a long jump after moving 1m. It should be 1.5m but I couldn't make it.

However, I haven't updated my PR because it changes many things. I'm using a new passive to count movement, it is applied to every classes using CF.
And finally, it also changes CommonPlayerActions.

The new passive to track movement inside a turn is also used by my druid shapes.
So, this is not easy to put it there without massive changes. You tell me.

@FelipeRenault
Copy link
Contributor

I'm split regarding the separation between AI and Player jump. Whilst unfair to have the AI not follow the player rules, it wouldn't be the first time my mod does it (see bonus action spells). Also, the AI won't be able to properly use the full jump. With that being said, I think this particular change is benefitial to have it here.

The change to CommonPlayerActions can be done whenever I manage to fix the defaultActions option, which modify it, but it's currently not working. And the part where you add the passive to all players using CF, I can do it using SE without issues.

You can add the passives and common actions to the PR and I'll make the necessary changes to have it working on RAW using SE

@arnaudpourbaix
Copy link
Author

I have been thinking about this, as well as testing.
For now, I have left current jump untouched. Only players will have 2 new spells:

  • jump (which is finally a half-jump)
  • long jump

Long jump is based on a movement tracking which is enabled either by a passive (added for all level 1 classes), either a status (used by wildshapes that need it).
Because of that, it would require to edit characters so they get this new passive. With inheritance largely used by Larian, it might be easy. There are also a few spellSet to edit, to add this new long jump.
But then, I also need to see if AI is able to properly use it. If not (which is probably the case), it will only use a half-jump. And now that distance is reduced, it probably means AI will never use jump again.

Now, we could eventually add a 3m movement cost to AI jump. But if they jump and move, it will unfair.
Let's face it, a 18 strength character can jump 6m. This is not a big deal for enemies.

This change is more to stop any jump abuse that players do. It was already hard with previous change which basically halve jumping distance. But now, it is also halved if didn't move 3m. Suddenly, fly will become more interesting.

@drkekyll
Copy link
Contributor

drkekyll commented Apr 16, 2024

You could give all characters a passive that gives them the boost JumpMaxDistanceMultiplier(2) after moving the requisite distance. That way you don't have to make a separate jump spell and the AI will automatically benefit from having moved first.

Something like...

new entry "RAW_LongJump"
type "PassiveData"
data "Properties" "IsHidden"
data "StatsFunctorContext" "OnTurn"
data "StatsFunctors" "IF(not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3); IF(HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"

new entry "RAW_LongJumpCheck"
type "PassiveData"
data "Properties" "IsHidden"
data "StatsFunctorContext" "OnMovedDistance"
data "Conditions" "StatusDurationMoreThan(context.Source,'RAW_LONG_JUMP_CHECK',0)"
data "StatsFunctors" "SetStatusDuration(SELF,RAW_LONG_JUMP_CHECK,-1,Add)"

new entry "RAW_LONG_JUMP_CHECK"
type "StatusData"
data "StatusType" "BOOST"
data "StackId" "RAW_LONG_JUMP_CHECK"
data "Passives" "RAW_LongJumpCheck"
data "OnRemoveFunctors" "IF(RemoveCause(StatusRemoveCause.External) and HasStatus('RAW_CURRENT_TURN')):ApplyStatus(RAW_LONG_JUMP_READY)"
data "StatusProperties" "FreezeDuration;DisableOverhead;DisableCombatlog;DisablePortraitIndicator"

new entry "RAW_LONG_JUMP_READY"
type "StatusData"
data "StatusType" "BOOST"
data "StackId" "RAW_LONG_JUMP_READY"
data "TickType" "EndTurn"
data "Boosts" "JumpMaxDistanceMultiplier(2)"
data "OnRemoveFunctors" "IF(HasStatus('RAW_CURRENT_TURN') and not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3); IF(HasStatus('RAW_CURRENT_TURN') and HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"
data "RemoveEvents" "OnSpellCast"
data "RemoveConditions" "SpellId('Projectile_Jump')"
data "StatusProperties" "DisableOverhead;DisableCombatlog;DisablePortraitIndicator"

new entry "RAW_CURRENT_TURN"
type "StatusData"
data "StatusType" "BOOST"
using "RAW_CURRENT_TURN"
data "OnRemoveFunctors" "RestoreResource(SneakAttack_Charge,100%,0);RestoreResource(RAW_BonusActionSpellPoint,100%,0);RestoreResource(RAW_NotBonusActionSpellPoint,100%,0);RemoveStatus(RAW_LONG_JUMP_CHECK);ApplyStatus(RAW_DUMMY,100,0)"

new entry "Athlete_StandUp"
type "PassiveData"
using "Athlete_StandUp"
data "Boosts" ""

Could even just have RAW_CURRENT_TURN apply RAW_LONG_JUMP_CHECK when it's applied to cut out a passive assuming NPCs also get the turn tracker passive.

Edit: In retrospect, though, I'm not sure when JumpMaxDistanceMultiplier() is applied in relation to JumpMaxDistanceBonus(), so that might complicate things.

@arnaudpourbaix
Copy link
Author

arnaudpourbaix commented Apr 19, 2024

I think your idea is nice but wouldn't work.
If I set JumpMaxDistanceMultiplier after moving, how does it combine with Enhanced Jump ? The former would set it to 2 and the latter to 3. Would it apply all instances and then gain a multiplier of 6 ? Or would it keep the highest number 3 ?
I know JumpMaxDistanceBonus is additive, I'm pretty sure. But I haven't tested multipliers.

On top of that, if we want to add this multiplier to all AI, we have to add it to very low entries like _Base, _Humanoid or whatever. I don't like it much because when a mod touch a base entry, it breaks compatibility with any other mod that might do the same.
Now, it it still possible to do it but then, we should specify this into compatibility section in readme.

@drkekyll
Copy link
Contributor

drkekyll commented Apr 19, 2024

I mean... you just figure out the order of operations and account for it. If additive bonuses are added first, you half them. If they're added after, then they're wrong anyway because they're improving a normal jump by the same amount they're improving a long jump.

Also, we don't edit character entries to add passives to everyone. We use SE. And, as I suggested at the bottom, it could be done by modifying a status applied by the passive that RAW is already giving every character. This is a non-issue.

@arnaudpourbaix
Copy link
Author

Yes, it could work, it needs some testing. But why spending so much time at this point ? My solution is already working fine.
Projectile_Jump is also used as a base for Fly, so adding to condition to it also need to a make sure it won't disable fly if you didn't move first.
I will update my PR at some point, feel free to improve it if you want to. But I prefer to cover more stuff than trying to reach perfection on one thing, mainly because I have far less time to mod than before and my productivity is close to 0 for several weeks (admittedly, this is also my choice because it was taking all my free time).

@drkekyll
Copy link
Contributor

drkekyll commented Apr 20, 2024

There's no disabling or enabling of any spells involved. That's the point. Everyone just uses the same normal jump and gets the advantage of full long jump distance if they've moved the requisite distance first. This just allows the AI to also benefit.

Either way, though, it occurs to me that the counter should probably reset when you attack or cast since you'd lose your momentum.

@arnaudpourbaix
Copy link
Author

I will give it a try with a new game, Laezel is a perfect fit for these tests, I need to test Psionic Jump and make sure multipliers are correctly applied.
It is about 3m without moving and 6m with. Then, it should be 9m with psionic jump and 18m with psionic jump, movement and dash.

I have thought about reset when not using jump but I can't see any easy way to do it. You can do several things instead of jumping: help someone, use an item, attack (many potential skills here), or cast (same boat).

@drkekyll
Copy link
Contributor

I think the way to do it would be to put remove events/conditions on the _CHECK status and if it's removed by the conditions (instead of Externally by the passive), you reapply it at the base duration.

@arnaudpourbaix
Copy link
Author

arnaudpourbaix commented Apr 22, 2024

I have tested and multipliers are handled nicely, I was able to reach 15m with Psionic and movement (obviously, I couldn't jump 15m because 3m to move, so it's 6 or 12 with dash.
I didn't fully understand what you said last, but since it's your code, you know better for sure ;)

I am using RAW_CURRENT_TURN to apply check status, I think it is convenient :

new entry "RAW_CURRENT_TURN"
type "StatusData"
using "RAW_CURRENT_TURN"
data "OnApplyFunctors" "ApplyStatus(RAW_DUMMY,100,0);IF(not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3); IF(HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"
data "OnRemoveFunctors" "RestoreResource(SneakAttack_Charge,100%,0);RestoreResource(RAW_BonusActionSpellPoint,100%,0);RestoreResource(RAW_NotBonusActionSpellPoint,100%,0);RemoveStatus(RAW_LONG_JUMP_CHECK);ApplyStatus(RAW_DUMMY,100,0)"

@drkekyll
Copy link
Contributor

drkekyll commented Apr 22, 2024

I was saying add some remove conditions to RAW_LONG_JUMP_CHECK like

new entry "RAW_LONG_JUMP_CHECK"
type "StatusData"
data "StatusType" "BOOST"
data "StackId" "RAW_LONG_JUMP_CHECK"
data "Passives" "RAW_LongJumpCheck"
data "RemoveEvents" "OnSpellCast;OnAttack;OnEntityPickUp;OnEntityDrag;OnLockpickingFinished"
data "RemoveConditions" "not HasSpellFlag(SpellFlags.CastWhileMoving)"
data "OnRemoveFunctors" "IF(RemoveCause(StatusRemoveCause.External) and HasStatus('RAW_CURRENT_TURN')):ApplyStatus(RAW_LONG_JUMP_READY);IF(RemoveCause(StatusRemoveCause.Condition) and HasStatus('RAW_CURRENT_TURN') and not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3));IF(RemoveCause(StatusRemoveCause.Condition) and HasStatus('RAW_CURRENT_TURN') and HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"
data "StatusProperties" "FreezeDuration;DisableOverhead;DisableCombatlog;DisablePortraitIndicator"

Unfortunately, the spell flags for HasSpellFlag() don't correspond exactly to what they're called for stats entries so I'm not sure how to exclude spells flagged to be usable while moving. I'm just guessing.

@arnaudpourbaix
Copy link
Author

arnaudpourbaix commented Apr 23, 2024

When you put RemoveConditions along with RemoveEvents, the former defines actual conditions for removal and events what will trigger a condition check. If you will only specify the latter, any of these events will remove status. I assume you already know this.

So in your case, it doesn't work very well. I have updated with this:

new entry "RAW_LONG_JUMP_CHECK"
type "StatusData"
data "StatusType" "BOOST"
data "Passives" "RAW_LongJumpCheck"
data "RemoveEvents" "OnSpellCast;OnAttack;OnEntityPickUp;OnEntityDrag;OnLockpickingFinished"
data "OnRemoveFunctors" "IF(RemoveCause(StatusRemoveCause.External) and HasStatus('RAW_CURRENT_TURN')):ApplyStatus(RAW_LONG_JUMP_READY);IF(RemoveCause(StatusRemoveCause.Condition) and HasStatus('RAW_CURRENT_TURN') and not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3));IF(RemoveCause(StatusRemoveCause.Condition) and HasStatus('RAW_CURRENT_TURN') and HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"
data "StatusProperties" "FreezeDuration;DisableOverhead;DisableCombatlog;DisablePortraitIndicator"

new entry "RAW_LONG_JUMP_READY"
type "StatusData"
data "StatusType" "BOOST"
data "TickType" "EndTurn"
data "Boosts" "JumpMaxDistanceMultiplier(2)"
data "RemoveEvents" "OnSpellCast;OnAttack;OnEntityPickUp;OnEntityDrag;OnLockpickingFinished"
data "OnRemoveFunctors" "IF(HasStatus('RAW_CURRENT_TURN') and not HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,3); IF(HasStatus('RAW_CURRENT_TURN') and HasPassive('Athlete_StandUp')):ApplyStatus(RAW_LONG_JUMP_CHECK,100,1)"

Both status need to be removed if you do anything but moving. Even Dash will remove them, so you need to dash first. This is a small drawback but everything else seems good.
It seems rather complicated to test everything that lead to removal within RemoveConditions. Maybe with some functions to see more clearly what is going on, but is it really necessary ?

@drkekyll
Copy link
Contributor

Yeah, the point of the condition was to make it so casting a spell that you can cast while moving doesn't reset them since AllowMoveAndCast is a spell flag. I checked CastWhileMoving up there, though, so that almost certainly wouldn't have worked. But it's also possible that HasSpellFlag() is only set up for certain flags. Either that or they set it up with alternate references to many of the flags (they definitely did this, I just don't know that they set up all the flags). You could manually exclude dashes from the reset, but I was trying to avoid having to check all the spells manually.

@drkekyll
Copy link
Contributor

arnaud ended up putting a check for the dash spell category in the remove conditions for RAW_LONG_JUMP_CHECK and RAW_LONG_JUMP_READY in add-on. It's working well so far, but, as I mentioned in discord, it's a bit of a pain to have to switch to turn-based mode to make long jumps outside combat. I would add a passive that just gives the character RAW_LONG_JUMP_READY when they're not in combat (or not in turn-based whichever is easier since they should work the same) if/when you get around to merging this.

@arnaudpourbaix
Copy link
Author

You are right, it needs to go when not in combat. When I'll resume bg3 modding, I'll do it 🙂

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants