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

Neuvillette Implementation #1727

Merged
merged 78 commits into from
Dec 10, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
2118c43
Initial commit of neuvilette
Charlie-Zheng Sep 28, 2023
155d675
Fixed spiritbreath thorns using the main skill multipliers. Fixed Neu…
Charlie-Zheng Sep 28, 2023
aac0f6f
Added note about the fast CA cancel
Charlie-Zheng Sep 28, 2023
4ff4d05
Fixed NA hitbox
Charlie-Zheng Sep 28, 2023
bbb9140
Adjusted hitbox sizes and ICD
Charlie-Zheng Sep 28, 2023
927da11
Fix burst snapshot delay
Charlie-Zheng Sep 28, 2023
11a5618
Changed Neuvillette E spiritbreath thorn to not be on hit
Charlie-Zheng Sep 30, 2023
c60bc09
changed burst Sourcewater Droplet to spawn 3-2-1 on Q hits
Charlie-Zheng Sep 30, 2023
be01466
Added some TODOs to CA
Charlie-Zheng Sep 30, 2023
59ba631
Added Neuv frames.CA drain frames still needed. CA hitmark frames nee…
Charlie-Zheng Oct 2, 2023
b5e1f51
Added early cancel end lag
Charlie-Zheng Oct 3, 2023
1d5b049
Fixed orb spawn
Charlie-Zheng Oct 3, 2023
514b859
fixed triggering C6 always triggering
Charlie-Zheng Oct 3, 2023
35bfd84
fixed Neuv A4
Charlie-Zheng Oct 3, 2023
03200e1
Run pipeline, added docs, changed sourcewater droplet priority to ran…
Charlie-Zheng Oct 3, 2023
19dee42
added aoe data. fixed sourcewater droplet absorb range to 15
Charlie-Zheng Oct 3, 2023
8c828bf
Differentiated HMC and Neuv sourcewater droplets for gadget limits.
Charlie-Zheng Oct 3, 2023
c8dd01c
Fixed max ticks always giving error. Remove warning because it doesnt…
Charlie-Zheng Oct 3, 2023
bfcb017
Fixed character curve
Charlie-Zheng Oct 3, 2023
d64b2b3
fixed bug with getting 3 random droplets
Charlie-Zheng Oct 3, 2023
4acab4c
Fixed A1 key being wrong. Fixed Spiritbreath thorn being HP scaling i…
Charlie-Zheng Oct 4, 2023
d7dfb3b
Fixed neuv droplet absorb not being random
Charlie-Zheng Oct 10, 2023
2e1d01f
Removed unused function
Charlie-Zheng Oct 10, 2023
2f27adb
Added gadget filter to neuvillette A1. Added english name for Neuvill…
Charlie-Zheng Oct 13, 2023
2cb2deb
Fixed C2 to give crit damage instead of dmg
Charlie-Zheng Oct 14, 2023
eb4f568
Disabled charged attacks at c6 due to incorrect simulated behaviour
Charlie-Zheng Oct 14, 2023
6a34d19
Commented out the c6 functions because they are unsupported
Charlie-Zheng Oct 14, 2023
b7340f2
Fixed c6 unsupported to be in character init. Neuv early cancel flag …
Charlie-Zheng Oct 14, 2023
88646b9
Modified test since Neuv C6 is unsupported
Charlie-Zheng Oct 17, 2023
3b782a7
FIxed documentation for skill[ticks=x]
Charlie-Zheng Oct 17, 2023
4455897
Fixed short charge windup, cleaned up comments, fixed ticks param bei…
Charlie-Zheng Oct 17, 2023
f40747a
Corrected Neuvillette E cd to start 20f after cast instead of 10
Charlie-Zheng Oct 18, 2023
7abd255
Fixed Neuvillette's charge[ticks=X] parameter not working for 7 ticks
Charlie-Zheng Oct 19, 2023
f3f0b91
fix nits
imring Oct 20, 2023
78be636
fix frames for lags
imring Oct 20, 2023
4beadc5
Fixed neuv skill not using the particle CD, fixed neuv skill not chec…
Charlie-Zheng Oct 20, 2023
473410f
Refactored NewSourceWaterDroplet to take a gadget type param
Charlie-Zheng Oct 20, 2023
8e6faf6
Fixed A4 tick rate
Charlie-Zheng Oct 20, 2023
7d48457
Removed .hydrotraveler.droplets and replaced with .gadgets.sourcewate…
Charlie-Zheng Oct 21, 2023
32aa1cc
removed frame caching
Charlie-Zheng Oct 28, 2023
fc9caf7
Refactored Neuvillette CA to support c6. Fixed inaccurate timings for…
Charlie-Zheng Nov 3, 2023
ffd9752
Fixed neuvillette HP drain timing. Refactoring Legal Evaluation code.…
Charlie-Zheng Nov 4, 2023
781ac4b
Fixed typo
Charlie-Zheng Nov 4, 2023
131c0ec
Adjusted healing to be delayed by 8 frames
Charlie-Zheng Nov 4, 2023
62131c6
Fixed incorrect scalings for Neuvillette NA, uncharged CA, and burst
Charlie-Zheng Dec 10, 2023
d5aa86e
Fixed Neuv's CA hitbox in docs
Charlie-Zheng Dec 10, 2023
b645006
Added CharacterEvent debug message when Neuv C6 absorbs a droplet
Charlie-Zheng Dec 10, 2023
29c813d
Change nextTickTime and nextTickTime2 to tickAnimLength and tickAnimL…
Charlie-Zheng Dec 10, 2023
3db8ccf
Fixed the CA hitboxes being wrong
Charlie-Zheng Dec 10, 2023
dc11d3d
Added src_action to droplet spawn logging. Added C4 droplet spawn log…
Charlie-Zheng Dec 10, 2023
5a365ce
Fixed tick timings to match the 1 indexed tick counting convention
Charlie-Zheng Dec 10, 2023
3376996
Fixed c6 occuring after the Charged attack ended
Charlie-Zheng Dec 10, 2023
e7449fe
Fixed uneeded geometry.Point instantation
Charlie-Zheng Dec 10, 2023
7452d0e
Adjusted burst implementation for water droplet spawn location
Charlie-Zheng Dec 10, 2023
32c4aad
Fixed status/keys to use hitlag affect queue
Charlie-Zheng Dec 10, 2023
f46aafb
optimized slice definition for c2
Charlie-Zheng Dec 10, 2023
3dbc958
refactored c4 to use status ICD and fixed c4 droplet spawn location
Charlie-Zheng Dec 10, 2023
c401667
Added more precision to neuv character curves
Charlie-Zheng Dec 10, 2023
63dabfe
fixed misspelled neuv nickname
Charlie-Zheng Dec 10, 2023
9856335
Updated gadgets,sourcewaterdroplet field docs to clarify that charact…
Charlie-Zheng Dec 10, 2023
a3bda70
Refactored skill implementation to use particle ICD status, added dro…
Charlie-Zheng Dec 10, 2023
0acafa8
fixed strike types
Charlie-Zheng Dec 10, 2023
9b176db
Fixed uncharged CA hitmark. Fixed CA/C6 absorb range. Added extra fie…
Charlie-Zheng Dec 10, 2023
0abab43
Fixed hitbox documention
Charlie-Zheng Dec 10, 2023
b189964
Fixed droplet spawn logs to just prepend the src action
Charlie-Zheng Dec 10, 2023
80bb713
Fixed incorrect offfset for BoxHitOnTarget
Charlie-Zheng Dec 10, 2023
1364d6e
Added N0 delays explicitly for neuv
Charlie-Zheng Dec 10, 2023
15d9af4
Added neuv E aligned hitlag to docs
Charlie-Zheng Dec 10, 2023
e093375
Added higher precision to basedef charactercurve
Charlie-Zheng Dec 10, 2023
fdf2788
Fixed grammar on charge[short] param
Charlie-Zheng Dec 10, 2023
1986f44
Removed uneeded c4 if check
Charlie-Zheng Dec 10, 2023
133659a
removed extra comment on sourcewaterdroplets c6
Charlie-Zheng Dec 10, 2023
631fc59
Added hitlag factor to spiritbreath thorn
Charlie-Zheng Dec 10, 2023
0235f20
removed unused earlyCancelEngLag
Charlie-Zheng Dec 10, 2023
4977734
Fixed c6 to be a callback on the CA damage
Charlie-Zheng Dec 10, 2023
19313be
Added translations for Neuv name
Charlie-Zheng Dec 10, 2023
89cb183
Adjusted healing delay to be 5 frames. Added note about drain timing
Charlie-Zheng Dec 10, 2023
ea5129a
Fixed neuv C6 not adding ICD status, and c6 droplets checking when no…
Charlie-Zheng Dec 10, 2023
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
88 changes: 88 additions & 0 deletions internal/characters/neuvillette/asc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package neuvillette

import (
"github.com/genshinsim/gcsim/pkg/core/attributes"
"github.com/genshinsim/gcsim/pkg/core/event"
"github.com/genshinsim/gcsim/pkg/core/player/character"
"github.com/genshinsim/gcsim/pkg/gadget"
"github.com/genshinsim/gcsim/pkg/modifier"
)

type NeuvA1Keys struct {
Evt event.Event
Key string
}

var a1Multipliers = [4]float64{1, 1.1, 1.25, 1.6}

func (c *char) a1() {
a1 := []NeuvA1Keys{
{event.OnBloom, "neuvillette-a1-bloom"},
{event.OnCrystallizeHydro, "neuvillette-a1-crystallize-hydro"},
{event.OnElectroCharged, "neuvillette-a1-electro-charged"},
{event.OnFrozen, "neuvillette-a1-frozen"},
{event.OnSwirlHydro, "neuvillette-a1-swirl-hydro"},
{event.OnVaporize, "neuvillette-a1-vaporize"},
}

c.a1Statuses = append(c.a1Statuses,
a1...,
)

for _, val := range a1 {
// need to make a copy of key for the status key
key := val.Key
c.Core.Events.Subscribe(val.Evt, func(args ...interface{}) bool {
if _, ok := args[0].(*gadget.Gadget); ok {
return false
}
c.AddStatus(key, 30*60, true)
return false
}, key)
}
}

func (c *char) countA1() int {
if c.Base.Ascension < 1 {
return 0
}
a1TriggeredReactionsCount := 0
for _, val := range c.a1Statuses {
if c.StatusIsActive(val.Key) {
a1TriggeredReactionsCount += 1
}
if a1TriggeredReactionsCount == 3 {
break
}
}
return a1TriggeredReactionsCount
}

func (c *char) a4() {
c.AddStatMod(character.StatMod{
Base: modifier.NewBase("neuvillette-a4", -1),
AffectedStat: attributes.HydroP,
Extra: true,
Amount: func() ([]float64, bool) {
return c.a4Buff, true
},
})

c.a4Tick()
}

func (c *char) a4Tick() {
hpRatio := c.CurrentHPRatio()
hydroDmgBuff := (hpRatio - 0.3) * 0.6

if hydroDmgBuff < 0 {
hydroDmgBuff = 0
} else if hydroDmgBuff > 0.3 {
hydroDmgBuff = 0.3
}

c.a4Buff[attributes.HydroP] = hydroDmgBuff

// Tick every 2s
c.QueueCharTask(c.a4Tick, 120)
}
73 changes: 73 additions & 0 deletions internal/characters/neuvillette/attack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package neuvillette

import (
"fmt"

"github.com/genshinsim/gcsim/internal/frames"
"github.com/genshinsim/gcsim/pkg/core/action"
"github.com/genshinsim/gcsim/pkg/core/attacks"
"github.com/genshinsim/gcsim/pkg/core/attributes"
"github.com/genshinsim/gcsim/pkg/core/combat"
)

const normalHitNum = 3

var (
attackFrames [][]int
attackHitmarks = []int{19, 16, 32}
attackHitboxes = []float64{1.0, 1.0, 1.5}
)

func init() {
attackFrames = make([][]int, normalHitNum)

attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0], 36)
attackFrames[0][action.ActionAttack] = 29
attackFrames[0][action.ActionCharge] = 20

attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1], 33)
attackFrames[1][action.ActionAttack] = 31
attackFrames[1][action.ActionCharge] = 22

attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2], 62)
attackFrames[2][action.ActionWalk] = 61
attackFrames[2][action.ActionCharge] = 51
}

func (c *char) Attack(p map[string]int) (action.Info, error) {
if c.chargeEarlyCancelled {
return action.Info{}, fmt.Errorf("%v: Cannot early cancel Charged Attack: Equitable Judgement with Normal Attack", c.CharWrapper.Base.Key)
}

ai := combat.AttackInfo{
ActorIndex: c.Index,
Abil: fmt.Sprintf("Normal %v", c.NormalCounter),
AttackTag: attacks.AttackTagNormal,
ICDTag: attacks.ICDTagNormalAttack,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypeDefault,
Element: attributes.Hydro,
Durability: 25,
Mult: attack[c.NormalCounter][c.TalentLvlAttack()],
}

c.Core.QueueAttack(
ai,
combat.NewCircleHitOnTarget(
c.Core.Combat.PrimaryTarget(),
nil,
attackHitboxes[c.NormalCounter],
),
attackHitmarks[c.NormalCounter],
attackHitmarks[c.NormalCounter],
)

defer c.AdvanceNormalIndex()

return action.Info{
Frames: frames.NewAttackFunc(c.Character, attackFrames),
AnimationLength: attackFrames[c.NormalCounter][action.InvalidAction],
CanQueueAfter: attackFrames[c.NormalCounter][action.ActionSwap],
State: action.NormalAttackState,
}, nil
}
145 changes: 145 additions & 0 deletions internal/characters/neuvillette/burst.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
package neuvillette

import (
"fmt"

"github.com/genshinsim/gcsim/internal/common"
"github.com/genshinsim/gcsim/internal/frames"
"github.com/genshinsim/gcsim/pkg/core/action"
"github.com/genshinsim/gcsim/pkg/core/attacks"
"github.com/genshinsim/gcsim/pkg/core/attributes"
"github.com/genshinsim/gcsim/pkg/core/combat"
"github.com/genshinsim/gcsim/pkg/core/geometry"
"github.com/genshinsim/gcsim/pkg/core/glog"
)

var burstFrames []int
var burstHitmarks = [3]int{95, 95 + 40, 95 + 40 + 19}

var dropletPosOffsets = [][][]float64{{{0, 7}, {-1, 7.5}, {0.8, 6.5}}, {{-3.5, 7.5}, {-2.5, 6}}, {{3.3, 6}}}
var dropletRandomRanges = [][]float64{{0.5, 2}, {0.5, 1.2}, {0.5, 1.2}}

var defaultBurstAtkPosOffsets = [][]float64{{-3, 7.5}, {4, 6}}
var burstTickTargetXOffsets = []float64{1.5, -1.5}

func init() {
burstFrames = frames.InitAbilSlice(135)
burstFrames[action.ActionCharge] = 133
burstFrames[action.ActionSkill] = 127
burstFrames[action.ActionDash] = 127
burstFrames[action.ActionJump] = 128
burstFrames[action.ActionWalk] = 134
burstFrames[action.ActionSwap] = 120
}

func (c *char) Burst(p map[string]int) (action.Info, error) {
c.chargeEarlyCancelled = false
player := c.Core.Combat.Player()

aiInitialHit := combat.AttackInfo{
ActorIndex: c.Index,
Abil: "O Tides, I Have Returned: Skill DMG",
AttackTag: attacks.AttackTagElementalBurst,
ICDTag: attacks.ICDTagElementalBurst,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypeDefault,
Element: attributes.Hydro,
Durability: 25,
FlatDmg: burst[c.TalentLvlBurst()] * c.MaxHP(),
}
aiWaterfall := combat.AttackInfo{
ActorIndex: c.Index,
Abil: "O Tides, I Have Returned: Waterfall DMG",
AttackTag: attacks.AttackTagElementalBurst,
ICDTag: attacks.ICDTagElementalBurst,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypeDefault,
Element: attributes.Hydro,
Durability: 25,
FlatDmg: burstWaterfall[c.TalentLvlBurst()] * c.MaxHP(),
}
for i := 0; i < 3; i++ {
ix := i // avoid closure issue

dropletCount := 3 - ix
ai := aiInitialHit
if ix > 0 {
ai = aiWaterfall
}

c.QueueCharTask(func() {
// spawn droplets for current tick using random point from player pos with offset
for j := 0; j < dropletCount; j++ {
common.NewSourcewaterDroplet(
c.Core,
geometry.CalcRandomPointFromCenter(
geometry.CalcOffsetPoint(
player.Pos(),
geometry.Point{X: dropletPosOffsets[ix][j][0], Y: dropletPosOffsets[ix][j][1]},
player.Direction(),
),
dropletRandomRanges[ix][0],
dropletRandomRanges[ix][1],
c.Core.Rand,
),
combat.GadgetTypSourcewaterDropletNeuv,
)
}
c.Core.Combat.Log.NewEvent(fmt.Sprint("Burst: Spawned ", dropletCount, " droplets"), glog.LogCharacterEvent, c.Index)

// determine attack pattern
// initial tick
ap := combat.NewCircleHitOnTarget(player, geometry.Point{Y: 1}, 8)
// 2nd and 3rd tick
if ix > 0 {
// determine attack pattern pos
// default assumption: no target in range -> ticks should spawn at specific offset from player
apPos := geometry.CalcOffsetPoint(
player.Pos(),
geometry.Point{
X: defaultBurstAtkPosOffsets[ix-1][0],
Y: defaultBurstAtkPosOffsets[ix-1][1],
},
player.Direction(),
)

// check if target is within range
target := c.Core.Combat.PrimaryTarget()
if target.IsWithinArea(combat.NewCircleHitOnTarget(player, nil, 10)) {
// target in range -> adjust pos
// pos is a point in random range from target pos + offset
// TODO: offset is not accurate because currently target is always looking in default direction
apPos = geometry.CalcRandomPointFromCenter(
geometry.CalcOffsetPoint(
target.Pos(),
geometry.Point{X: burstTickTargetXOffsets[ix-1]},
target.Direction(),
),
0,
1.5,
c.Core.Rand,
)
}
// create attack pattern for tick after determining pos
ap = combat.NewCircleHitOnTarget(apPos, nil, 5)
}

c.Core.QueueAttack(
ai,
ap,
0,
0,
)
}, burstHitmarks[ix])
}

c.SetCD(action.ActionBurst, 18*60)
c.ConsumeEnergy(4)

return action.Info{
Frames: frames.NewAbilFunc(burstFrames),
AnimationLength: burstFrames[action.InvalidAction],
CanQueueAfter: burstFrames[action.ActionSwap], // earliest cancel
State: action.BurstState,
}, nil
}
Loading
Loading