Skip to content

Commit 4319a4d

Browse files
Add Escoffier (#2389)
Adding Escoffier
1 parent 451130e commit 4319a4d

29 files changed

Lines changed: 1737 additions & 6 deletions

File tree

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
package escoffier
2+
3+
import (
4+
"github.com/genshinsim/gcsim/pkg/core/attributes"
5+
"github.com/genshinsim/gcsim/pkg/core/combat"
6+
"github.com/genshinsim/gcsim/pkg/core/info"
7+
"github.com/genshinsim/gcsim/pkg/core/targets"
8+
"github.com/genshinsim/gcsim/pkg/enemy"
9+
"github.com/genshinsim/gcsim/pkg/modifier"
10+
)
11+
12+
const a1Scaling = 1.3824
13+
const a1key = "escoffier-a1"
14+
const a1Count = 9
15+
const a1FirstTick = 157
16+
const a1Interval = 58.5
17+
const a4Dur = 12 * 60
18+
19+
var a4Shred = []float64{0.0, 0.05, 0.10, 0.15, 0.55}
20+
21+
func (c *char) a1() {
22+
if c.Base.Ascension < 1 {
23+
return
24+
}
25+
c.a1Src = c.Core.F
26+
ticks := a1Count + c.c4ExtraCount()
27+
for i := 0; i < ticks; i++ {
28+
c.QueueCharTask(c.a1Tick(c.a1Src), a1FirstTick+ceil(a1Interval*float64(i)))
29+
}
30+
// this status is purely cosmetic and doesn't do anything right now
31+
c.AddStatus(a1key, a1FirstTick+ceil(float64(ticks-1)*a1Interval), true)
32+
}
33+
34+
func (c *char) a1Tick(src int) func() {
35+
return func() {
36+
if src != c.a1Src {
37+
return
38+
}
39+
scale := a1Scaling + c.c4ExtraHeal()
40+
heal := scale * c.TotalAtk()
41+
c.Core.Player.Heal(info.HealInfo{
42+
Caller: c.Index,
43+
Target: c.Core.Player.Active(),
44+
Message: "Rehab Diet",
45+
Src: heal,
46+
Bonus: c.Stat(attributes.Heal),
47+
})
48+
}
49+
}
50+
51+
func (c *char) a4Init() {
52+
c.a4HydroCryoCount = 0
53+
for _, char := range c.Core.Player.Chars() {
54+
switch char.Base.Element {
55+
case attributes.Hydro, attributes.Cryo:
56+
c.a4HydroCryoCount++
57+
default:
58+
}
59+
}
60+
}
61+
62+
func (c *char) makeA4CB() combat.AttackCBFunc {
63+
if c.Base.Ascension < 4 {
64+
return nil
65+
}
66+
return func(a combat.AttackCB) {
67+
if a.Target.Type() != targets.TargettableEnemy {
68+
return
69+
}
70+
e, ok := a.Target.(*enemy.Enemy)
71+
if !ok {
72+
return
73+
}
74+
shred := a4Shred[c.a4HydroCryoCount]
75+
e.AddResistMod(combat.ResistMod{
76+
Base: modifier.NewBaseWithHitlag("escoffier-a4-shred-cryo", a4Dur),
77+
Ele: attributes.Cryo,
78+
Value: -shred,
79+
})
80+
e.AddResistMod(combat.ResistMod{
81+
Base: modifier.NewBaseWithHitlag("escoffier-a4-shred-hydro", a4Dur),
82+
Ele: attributes.Hydro,
83+
Value: -shred,
84+
})
85+
}
86+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
package escoffier
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/genshinsim/gcsim/internal/frames"
7+
"github.com/genshinsim/gcsim/pkg/core/action"
8+
"github.com/genshinsim/gcsim/pkg/core/attacks"
9+
"github.com/genshinsim/gcsim/pkg/core/attributes"
10+
"github.com/genshinsim/gcsim/pkg/core/combat"
11+
"github.com/genshinsim/gcsim/pkg/core/geometry"
12+
)
13+
14+
var (
15+
attackFrames [][]int
16+
attackHitmarks = [][]int{{7}, {16}, {27, 40}}
17+
attackHitlagHaltFrame = [][]float64{{0.06}, {0.06}, {0.06, 0.06}}
18+
attackDefHalt = [][]bool{{true}, {true}, {true, true}}
19+
attackHitboxes = [][][]float64{{{1.6, 2}}, {{2}}, {{2.5}, {2.5}}}
20+
attackOffsets = [][]float64{{1}, {-0.2}, {-0.2, -0.2}}
21+
)
22+
23+
const normalHitNum = 3
24+
25+
func init() {
26+
attackFrames = make([][]int, normalHitNum)
27+
28+
attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 17)
29+
attackFrames[0][action.ActionCharge] = 20
30+
attackFrames[0][action.ActionWalk] = 22
31+
32+
attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 21)
33+
attackFrames[1][action.ActionAttack] = 29
34+
attackFrames[1][action.ActionWalk] = 30
35+
36+
attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][1], 62)
37+
attackFrames[2][action.ActionWalk] = 61
38+
attackFrames[2][action.ActionCharge] = 500 //TODO: this action is illegal; need better way to handle it
39+
}
40+
41+
// Normal attack damage queue generator
42+
// relatively standard with no major differences versus other characters
43+
func (c *char) Attack(p map[string]int) (action.Info, error) {
44+
for i, mult := range attack[c.NormalCounter] {
45+
ai := combat.AttackInfo{
46+
ActorIndex: c.Index,
47+
Abil: fmt.Sprintf("Normal %v", c.NormalCounter),
48+
AttackTag: attacks.AttackTagNormal,
49+
ICDTag: attacks.ICDTagNormalAttack,
50+
ICDGroup: attacks.ICDGroupDefault,
51+
StrikeType: attacks.StrikeTypeSlash,
52+
Element: attributes.Physical,
53+
Durability: 25,
54+
Mult: mult[c.TalentLvlAttack()],
55+
HitlagFactor: 0.01,
56+
HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter][i] * 60,
57+
CanBeDefenseHalted: attackDefHalt[c.NormalCounter][i],
58+
}
59+
60+
ap := combat.NewCircleHitOnTarget(
61+
c.Core.Combat.Player(),
62+
geometry.Point{Y: attackOffsets[c.NormalCounter][i]},
63+
attackHitboxes[c.NormalCounter][i][0],
64+
)
65+
if c.NormalCounter == 0 {
66+
ai.StrikeType = attacks.StrikeTypeSpear
67+
ap = combat.NewBoxHitOnTarget(
68+
c.Core.Combat.Player(),
69+
geometry.Point{Y: attackOffsets[c.NormalCounter][i]},
70+
attackHitboxes[c.NormalCounter][i][0],
71+
attackHitboxes[c.NormalCounter][i][1],
72+
)
73+
}
74+
c.QueueCharTask(func() {
75+
c.Core.QueueAttack(ai, ap, 0, 0)
76+
}, attackHitmarks[c.NormalCounter][i])
77+
}
78+
79+
defer c.AdvanceNormalIndex()
80+
81+
return action.Info{
82+
Frames: frames.NewAttackFunc(c.Character, attackFrames),
83+
AnimationLength: attackFrames[c.NormalCounter][action.InvalidAction],
84+
CanQueueAfter: attackHitmarks[c.NormalCounter][len(attackHitmarks[c.NormalCounter])-1],
85+
State: action.NormalAttackState,
86+
}, nil
87+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
package escoffier
2+
3+
import (
4+
"github.com/genshinsim/gcsim/internal/frames"
5+
"github.com/genshinsim/gcsim/pkg/core/action"
6+
"github.com/genshinsim/gcsim/pkg/core/attacks"
7+
"github.com/genshinsim/gcsim/pkg/core/attributes"
8+
"github.com/genshinsim/gcsim/pkg/core/combat"
9+
"github.com/genshinsim/gcsim/pkg/core/geometry"
10+
"github.com/genshinsim/gcsim/pkg/core/info"
11+
)
12+
13+
var burstFrames []int
14+
15+
const (
16+
initialHeal = 97 // depends on ping
17+
hitmark = 92
18+
)
19+
20+
func init() {
21+
burstFrames = frames.InitAbilSlice(110)
22+
burstFrames[action.ActionSkill] = 109
23+
burstFrames[action.ActionSwap] = 108
24+
}
25+
26+
func (c *char) Burst(p map[string]int) (action.Info, error) {
27+
ai := combat.AttackInfo{
28+
ActorIndex: c.Index,
29+
Abil: "Scoring Cuts",
30+
AttackTag: attacks.AttackTagElementalBurst,
31+
ICDTag: attacks.ICDTagNone,
32+
ICDGroup: attacks.ICDGroupDefault,
33+
StrikeType: attacks.StrikeTypeDefault,
34+
Element: attributes.Cryo,
35+
Durability: 50,
36+
Mult: burst[c.TalentLvlBurst()],
37+
}
38+
c.QueueCharTask(func() {
39+
c.Core.QueueAttack(ai, combat.NewCircleHitOnTarget(c.Core.Combat.Player(), geometry.Point{Y: -1.5}, 7), 0, 0, c.makeA4CB())
40+
}, hitmark)
41+
42+
// initial heal
43+
c.QueueCharTask(func() {
44+
heal := burstHealFlat[c.TalentLvlBurst()] + burstHealPer[c.TalentLvlBurst()]*c.TotalAtk()
45+
c.Core.Player.Heal(info.HealInfo{
46+
Caller: c.Index,
47+
Target: -1,
48+
Message: "Scoring Cuts (Healing)",
49+
Src: heal,
50+
Bonus: c.Stat(attributes.Heal),
51+
})
52+
}, initialHeal)
53+
54+
c.a1()
55+
56+
c.SetCD(action.ActionBurst, int(burstCD[c.TalentLvlBurst()])*60)
57+
c.ConsumeEnergy(5)
58+
59+
c.c1()
60+
return action.Info{
61+
Frames: frames.NewAbilFunc(burstFrames),
62+
AnimationLength: burstFrames[action.InvalidAction],
63+
CanQueueAfter: burstFrames[action.ActionSwap], // earliest cancel
64+
State: action.BurstState,
65+
}, nil
66+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
package escoffier
2+
3+
import (
4+
"github.com/genshinsim/gcsim/internal/frames"
5+
"github.com/genshinsim/gcsim/pkg/core/action"
6+
"github.com/genshinsim/gcsim/pkg/core/attacks"
7+
"github.com/genshinsim/gcsim/pkg/core/attributes"
8+
"github.com/genshinsim/gcsim/pkg/core/combat"
9+
"github.com/genshinsim/gcsim/pkg/core/geometry"
10+
)
11+
12+
var chargeFrames []int
13+
14+
const chargeHitmark = 20
15+
16+
func init() {
17+
chargeFrames = frames.InitAbilSlice(65)
18+
chargeFrames[action.ActionAttack] = 53
19+
chargeFrames[action.ActionSkill] = 52
20+
chargeFrames[action.ActionBurst] = 52
21+
chargeFrames[action.ActionDash] = chargeHitmark
22+
chargeFrames[action.ActionJump] = chargeHitmark
23+
chargeFrames[action.ActionSwap] = 52
24+
}
25+
26+
// Charge attack damage queue generator
27+
// Very standard - consistent with other characters like Xiangling
28+
func (c *char) ChargeAttack(p map[string]int) (action.Info, error) {
29+
ai := combat.AttackInfo{
30+
ActorIndex: c.Index,
31+
Abil: "Charge",
32+
AttackTag: attacks.AttackTagExtra,
33+
ICDTag: attacks.ICDTagNone,
34+
ICDGroup: attacks.ICDGroupPoleExtraAttack,
35+
StrikeType: attacks.StrikeTypeSlash,
36+
Element: attributes.Physical,
37+
Durability: 25,
38+
Mult: charge[c.TalentLvlAttack()],
39+
HitlagHaltFrames: 0.10,
40+
HitlagFactor: 0.01,
41+
CanBeDefenseHalted: true,
42+
}
43+
44+
c.Core.QueueAttack(
45+
ai,
46+
combat.NewBoxHit(
47+
c.Core.Combat.Player(),
48+
c.Core.Combat.PrimaryTarget(),
49+
geometry.Point{Y: 1.5},
50+
3.3,
51+
3,
52+
),
53+
chargeHitmark,
54+
chargeHitmark,
55+
)
56+
57+
return action.Info{
58+
Frames: frames.NewAbilFunc(chargeFrames),
59+
AnimationLength: chargeFrames[action.InvalidAction],
60+
CanQueueAfter: chargeHitmark,
61+
State: action.ChargeAttackState,
62+
}, nil
63+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
package_name: escoffier
2+
genshin_id: 10000112
3+
key: escoffier
4+
action_param_keys:
5+
skill:
6+
- param: "travel"
7+
low_plunge:
8+
- param: "collision"
9+
high_plunge:
10+
- param: "collision"
11+
icd_groups:
12+
- group_name: ICDGroupEscoffierSkill
13+
reset_timer: 90
14+
ele_app_sequence: [1, 0, 0, 0, 0, 0]
15+
damage_sequence: [1, 1, 1, 1, 1]
16+
skill_data_mapping:
17+
attack: # Kitchen Skills
18+
attack_1:
19+
- 0 # 1-Hit DMG|{param0:F1P}
20+
attack_2:
21+
- 1 # 2-Hit DMG|{param1:F1P}
22+
attack_3:
23+
- 2 # 3-Hit DMG|{param2:F1P}+{param3:F1P}
24+
- 3 # 3-Hit DMG|{param2:F1P}+{param3:F1P}
25+
charge:
26+
- 4 # Charged Attack DMG|{param4:F1P}
27+
collision:
28+
- 6 # Plunge DMG|{param6:F1P}
29+
lowPlunge:
30+
- 7 # Low/High Plunge DMG|{param7:P}/{param8:P}
31+
highPlunge:
32+
- 8 # Low/High Plunge DMG|{param7:P}/{param8:P}
33+
skill: # Low-Temperature Cooking
34+
skillInital:
35+
- 0 # Skill DMG|{param0:F1P}
36+
skillDot:
37+
- 1 # Frozen Parfait Attack DMG|{param1:F1P}
38+
skillDur:
39+
- 2 # Cooking Mek: Cold Storage Mode Duration|{param2:F1}s
40+
arkhe:
41+
- 3 # Surging Blade DMG|{param3:F1P}
42+
burst: # Scoring Cuts
43+
burst:
44+
- 0 # Skill DMG|{param0:F1P}
45+
burstHealPer:
46+
- 1 # Healing|{param1:F1P} ATK+{param2:I}
47+
burstHealFlat:
48+
- 2 # Healing|{param1:F1P} ATK+{param2:I}
49+
burstCD:
50+
- 3 # CD|{param3:F1}s
51+
burstEnergy:
52+
- 4 # Energy Cost|{param4:I}

0 commit comments

Comments
 (0)