Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 19 additions & 4 deletions internal/characters/ororon/asc.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,8 @@ func (c *char) a1NightSoulAttack(atk *combat.AttackEvent) {
return
}
c.AddStatus(a1DamageIcdKey, 1.8*60, true)
if !c.nightsoulState.HasBlessing() {
c.a1EnterBlessing()
}
c.a1EnterBlessing()

c.nightsoulState.ConsumePoints(10)
c.hypersense(1.6, a1Abil, atk.Pattern.Shape.Pos())
}
Expand Down Expand Up @@ -137,9 +136,25 @@ func (c *char) hypersense(mult float64, abil string, initialTargetPos geometry.P
c.c6onHypersense()
}

// When Ororon has the jump blessing, do nothing. Blessing will exit when jump is done.
func (c *char) a1ExitBlessing() {
c.inA1Blessing = false
if !c.inTransmissionBlessing {
c.nightsoulState.ExitBlessing()
}
}

func (c *char) a1EnterBlessing() {
c.nightsoulState.EnterBlessing(c.nightsoulState.Points())
c.QueueCharTask(c.nightsoulState.ExitBlessing, 6*60)
c.inA1Blessing = true
c.a1Src = c.Core.F
src := c.a1Src
c.QueueCharTask(func() {
if src != c.a1Src {
return
}
c.a1ExitBlessing()
}, 6*60)
}

func (c *char) a1OnSkill() {
Expand Down
4 changes: 4 additions & 0 deletions internal/characters/ororon/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ action_param_keys:
- param: "weakspot"
skill:
- param: "travel"
jump:
- param: "hold"
high_plunge:
- param: "fall"
skill_data_mapping:
attack:
aim:
Expand Down
105 changes: 105 additions & 0 deletions internal/characters/ororon/jump.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package ororon

import (
"github.com/genshinsim/gcsim/internal/frames"
"github.com/genshinsim/gcsim/pkg/core/action"
"github.com/genshinsim/gcsim/pkg/core/event"
"github.com/genshinsim/gcsim/pkg/core/player"
)

var jumpHoldFrames [][]int

func init() {
// Hold Jump
jumpHoldFrames = make([][]int, 2)
// Hold Jump -> X
jumpHoldFrames[0] = frames.InitAbilSlice(60 * 10) // set to very high number for most abilities
jumpHoldFrames[0][action.ActionHighPlunge] = plungeCancelFrames

// Fall -> X
jumpHoldFrames[1] = frames.InitAbilSlice(47)
jumpHoldFrames[1][action.ActionAttack] = 45
jumpHoldFrames[1][action.ActionAim] = 46
jumpHoldFrames[1][action.ActionSkill] = 45
jumpHoldFrames[1][action.ActionBurst] = 46
jumpHoldFrames[1][action.ActionDash] = 46
jumpHoldFrames[1][action.ActionJump] = 45
jumpHoldFrames[1][action.ActionWalk] = 47
jumpHoldFrames[1][action.ActionSwap] = 44
}

func (c *char) exitJumpBlessing() {
c.inTransmissionBlessing = false
if !c.inA1Blessing {
c.nightsoulState.ExitBlessing()
}
}

// Add NS status.
// Set a CB to cancel high jump if max duration exceeded.
// Grant airborne status.
// Consume stamina.
// Hold defines when fall action will automatically be called.
func (c *char) highJump(hold int, p map[string]int) (action.Info, error) {
if (hold > maxJumpFrames-fallCancelFrames) || (hold < 0) {
hold = maxJumpFrames - fallCancelFrames
}

jumpDur := fallCancelFrames + hold
c.jmpSrc = c.Core.F
src := c.jmpSrc
c.Core.Player.SetAirborne(player.AirborneOroron)

// Don't add NS if jump is cancelled before NS would be added.
c.QueueCharTask(func() {
if src != c.jmpSrc {
return
}
c.nightsoulState.EnterBlessing(c.nightsoulState.Points())
c.inTransmissionBlessing = true
}, jumpNsDelay)

// Consume stamina.
c.QueueCharTask(func() {
h := c.Core.Player
// Apply stamina reduction mods.
stamDrain := h.AbilStamCost(c.Index, action.ActionJump, p)
h.Stam -= stamDrain
if h.Stam < 0 {
h.Stam = 0
}
// While in high jump, ororon cannot start resuming stamina regen until after landing.
h.LastStamUse = *h.F + jumpDur + fallFrames
h.Events.Emit(event.OnStamUse, action.ActionJump)
}, jumpStamDrainDelay)

act := action.Info{
Frames: frames.NewAbilFunc(jumpHoldFrames[0]),
AnimationLength: jumpDur + jumpHoldFrames[1][action.ActionWalk],
CanQueueAfter: plungeCancelFrames, // earliest cancel
State: action.JumpState,
}

// Trigger a fall after max jump duration
// TODO: Is this hitlag extended? Does this skip if the action is canceled?
act.QueueAction(func() {
if src != c.jmpSrc {
return
}

fallParam := map[string]int{"fall": 1}
// Ideally this would inject the action into the queue of actions to take from the config file, rather than calling exec directly
c.Core.Player.Exec(action.ActionHighPlunge, c.Base.Key, fallParam)
}, jumpDur)
// c.QueueCharTask(fallCb, jumpDur)
return act, nil
}

// TODO: How does it work if xinyuan airborne buff is active and hold jump is used?
func (c *char) Jump(p map[string]int) (action.Info, error) {
hold := p["hold"]
if hold == 0 {
return c.Character.Jump(p)
}
return c.highJump(hold-1, p)
}
47 changes: 42 additions & 5 deletions internal/characters/ororon/ororon.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,44 @@ import (
tmpl "github.com/genshinsim/gcsim/internal/template/character"
"github.com/genshinsim/gcsim/internal/template/nightsoul"
"github.com/genshinsim/gcsim/pkg/core"
"github.com/genshinsim/gcsim/pkg/core/action"
"github.com/genshinsim/gcsim/pkg/core/info"
"github.com/genshinsim/gcsim/pkg/core/keys"
"github.com/genshinsim/gcsim/pkg/core/player/character"
"github.com/genshinsim/gcsim/pkg/core/stacks"
"github.com/genshinsim/gcsim/pkg/model"
)

const (
superJumpBeginFrames = 15 // Jump->SuperJump Frames

jumpNsDelay = 15 // From swap ui gray to nightsoul state outline appears
jumpStamDrainDelay = 5
jumpStamDrainAmt = 75
jumpStamReqAmt = 1

maxJumpFrames = 162 // From swap ui gray to glider wings appear
plungeCancelFrames = superJumpBeginFrames + 18 // From start of jump animation to plunge animation start. Earliest possible plunge cancel.
fallCancelFrames = superJumpBeginFrames + 46 // From From start of jump animation to UI changes from gliding to standard UI. Earliest possible cancel.

fallFrames = 44 // From fall animation start to swap icon un-gray.
)

func init() {
core.RegisterCharFunc(keys.Ororon, NewChar)
}

type char struct {
*tmpl.Character
nightsoulState *nightsoul.State
particlesGenerated bool
c2Bonus []float64
c6stacks *stacks.MultipleRefreshNoRemove
c6bonus []float64
nightsoulState *nightsoul.State
particlesGenerated bool
c2Bonus []float64
c6stacks *stacks.MultipleRefreshNoRemove
c6bonus []float64
a1Src int
jmpSrc int
inA1Blessing bool
inTransmissionBlessing bool
}

func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error {
Expand Down Expand Up @@ -55,3 +75,20 @@ func (c *char) AnimationStartDelay(k model.AnimationDelayKey) int {
}
return c.Character.AnimationStartDelay(k)
}

func (c *char) ActionStam(a action.Action, p map[string]int) float64 {
if a == action.ActionJump && p["hold"] != 0 {
return 75
}
return c.Character.ActionStam(a, p)
}

func (c *char) ActionReady(a action.Action, p map[string]int) (bool, action.Failure) {
// check if a1 window is active is on-field
if a == action.ActionJump && p["hold"] != 0 {
if c.Core.Player.Stam < jumpStamReqAmt {
return false, action.InsufficientStamina
}
}
return c.Character.ActionReady(a, p)
}
28 changes: 28 additions & 0 deletions internal/characters/ororon/ororon_gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading