Skip to content

Commit 03eae84

Browse files
committed
Merge branch 'gcsim_main' into pyromc
# Conflicts: # internal/services/assets/avatars_gen.go # pkg/simulation/imports_char_gen.go # ui/packages/docs/src/components/AoE/character_data.json
2 parents 587653f + 829ff36 commit 03eae84

43 files changed

Lines changed: 2288 additions & 28 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

internal/characters/chasca/attack.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,11 @@ func (c *char) attackSkillTap(_ map[string]int) action.Info {
129129
}, windup+attackSkillTapHitmark)
130130

131131
defer c.AdvanceNormalIndex()
132+
132133
return action.Info{
133-
Frames: c.skillNextFrames(frames.NewAttackFunc(c.Character, attackFrames), 0),
134+
Frames: c.skillNextFrames(func(next action.Action) int {
135+
return frames.AtkSpdAdjust(attackSkillTapFrames[next], c.Stat(attributes.AtkSpd))
136+
}, 0),
134137
AnimationLength: attackSkillTapFrames[action.InvalidAction],
135138
CanQueueAfter: 1, // can run out of nightsoul and start falling earlier
136139
State: action.NormalAttackState,

internal/characters/raiden/burst.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"github.com/genshinsim/gcsim/pkg/core/event"
1010
"github.com/genshinsim/gcsim/pkg/core/geometry"
1111
"github.com/genshinsim/gcsim/pkg/core/glog"
12+
"github.com/genshinsim/gcsim/pkg/core/player/character"
1213
"github.com/genshinsim/gcsim/pkg/core/targets"
1314
)
1415

@@ -126,13 +127,14 @@ func (c *char) onSwapClearBurst() {
126127

127128
func (c *char) onBurstStackCount() {
128129
// TODO: this used to be on PostBurst; need to check if it works correctly still
129-
c.Core.Events.Subscribe(event.OnBurst, func(_ ...interface{}) bool {
130+
c.Core.Events.Subscribe(event.OnEnergyBurst, func(args ...interface{}) bool {
130131
if c.Core.Player.Active() == c.Index {
131132
return false
132133
}
133-
char := c.Core.Player.ActiveChar()
134+
char := args[0].(*character.CharWrapper)
135+
amount := args[2].(float64)
134136
// add stacks based on char max energy
135-
stacks := resolveStackGain[c.TalentLvlBurst()] * char.EnergyMax
137+
stacks := resolveStackGain[c.TalentLvlBurst()] * amount
136138
if c.Base.Cons > 0 {
137139
if char.Base.Element == attributes.Electro {
138140
stacks *= 1.8

internal/characters/skirk/asc.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ func (c *char) createVoidRift() {
7979
c.onVoidAbsorb(1)
8080
c.burstVoids = min(c.burstVoids+1, 3)
8181
}
82-
c.voidRifts.Push(c.Core.F)
82+
c.voidRifts.PushOverwrite(c.Core.F)
8383
}
8484

8585
func (c *char) a4Init() {

internal/characters/skirk/config.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ documentation:
6868
fields_data:
6969
- fields: [.skirk.serpents_subtlety]
7070
desc: Amount of Serpent's Subtlety.
71+
- fields: [.skirk.void_rifts]
72+
desc: Number of Void Rifts on the field.
7173
- fields: [.skirk.a4_stacks]
7274
desc: Number of A4 stacks.
7375
- fields: [.skirk.c6_stacks]

internal/characters/skirk/skirk.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,11 @@ func (c *char) Condition(fields []string) (any, error) {
100100
switch fields[0] {
101101
case "serpents_subtlety":
102102
return c.serpentsSubtlety, nil
103+
case "void_rifts":
104+
filter := func(src int) bool {
105+
return src+a1Dur >= c.Core.F
106+
}
107+
return c.voidRifts.Count(filter), nil
103108
case "a4_stacks":
104109
return c.getA4Stacks(), nil
105110
case "c6_stacks":

internal/characters/skirk/utils.go

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,19 +34,21 @@ func (r *RingQueue[T]) Push(elem T) error {
3434
if r.isFull {
3535
return fmt.Errorf("out of bounds push, container is full")
3636
}
37-
r.data[r.end] = elem // place the new element on the available space
38-
r.end = (r.end + 1) % len(r.data) // move the end forward by modulo of capacity
39-
r.isFull = r.end == r.start // check if we're full now
37+
r.pushUnchecked(elem)
4038

4139
return nil
4240
}
4341

4442
func (r *RingQueue[T]) PushOverwrite(elem T) {
4543
if r.isFull {
4644
r.Pop()
47-
r.Push(elem)
45+
r.pushUnchecked(elem)
4846
return
4947
}
48+
r.pushUnchecked(elem)
49+
}
50+
51+
func (r *RingQueue[T]) pushUnchecked(elem T) {
5052
r.data[r.end] = elem // place the new element on the available space
5153
r.end = (r.end + 1) % len(r.data) // move the end forward by modulo of capacity
5254
r.isFull = r.end == r.start // check if we're full now

internal/characters/varesa/asc.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package varesa
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/event"
7+
"github.com/genshinsim/gcsim/pkg/core/player/character"
8+
"github.com/genshinsim/gcsim/pkg/core/stacks"
9+
"github.com/genshinsim/gcsim/pkg/core/targets"
10+
"github.com/genshinsim/gcsim/pkg/modifier"
11+
)
12+
13+
const a1Status = "rainbow-crash"
14+
15+
func (c *char) a1() {
16+
if c.Base.Ascension < 1 {
17+
return
18+
}
19+
c.AddStatus(a1Status, 5*60, true)
20+
}
21+
22+
func (c *char) a1PlungeBuff() float64 {
23+
if c.Base.Ascension < 1 {
24+
return 0.0
25+
}
26+
if !c.StatusIsActive(a1Status) {
27+
return 0.0
28+
}
29+
if c.Base.Cons >= 1 || c.nightsoulState.HasBlessing() {
30+
return 1.8
31+
}
32+
return 0.5
33+
}
34+
35+
func (c *char) a1Cancel(a combat.AttackCB) {
36+
if a.Target.Type() != targets.TargettableEnemy {
37+
return
38+
}
39+
if c.Base.Ascension < 1 {
40+
return
41+
}
42+
c.DeleteStatus(a1Status)
43+
}
44+
45+
func (c *char) a4() {
46+
if c.Base.Ascension < 4 {
47+
return
48+
}
49+
50+
m := make([]float64, attributes.EndStatType)
51+
c.AddStatMod(character.StatMod{
52+
Base: modifier.NewBase("varesa-a4", -1),
53+
Amount: func() ([]float64, bool) {
54+
m[attributes.ATKP] = 0.35 * float64(c.a4Stacks.Count())
55+
return m, true
56+
},
57+
})
58+
59+
c.a4Stacks = stacks.NewMultipleRefreshNoRemove(2, c.QueueCharTask, &c.Core.F)
60+
c.Core.Events.Subscribe(event.OnNightsoulBurst, func(args ...interface{}) bool {
61+
c.a4Stacks.Add(12 * 60)
62+
return false
63+
}, "varesa-a4")
64+
}
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
package varesa
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{23, 7, 33}
17+
attackHitlagHaltFrame = []float64{0, 0, 0.03}
18+
attackHitlagFactor = []float64{0, 0, 0.01}
19+
attackHitboxes = [][]float64{{2.5, 2.5}, {2.8, 3.5}, {2.5}}
20+
attackOffsets = []float64{-0.2, -0.2, 0}
21+
22+
fieryAttackFrames [][]int
23+
fieryAttackHitmarks = []int{17, 29, 37}
24+
fieryAttackHitboxes = [][]float64{{2.5}, {4, 4}, {4, 4.5}}
25+
fieryAttackOffsets = []float64{1, -0.5, -0.5}
26+
)
27+
28+
const normalHitNum = 3
29+
30+
func init() {
31+
attackFrames = make([][]int, normalHitNum)
32+
33+
attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0], 49) // N1 -> Walk
34+
attackFrames[0][action.ActionAttack] = 33
35+
attackFrames[0][action.ActionCharge] = 23
36+
37+
attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1], 30) // N2 -> N3
38+
attackFrames[1][action.ActionCharge] = 17
39+
attackFrames[1][action.ActionWalk] = 28
40+
41+
attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2], 59) // N3 -> Walk
42+
attackFrames[2][action.ActionAttack] = 45
43+
attackFrames[2][action.ActionCharge] = 32
44+
45+
fieryAttackFrames = make([][]int, normalHitNum)
46+
47+
fieryAttackFrames[0] = frames.InitNormalCancelSlice(fieryAttackHitmarks[0], 39) // N1 -> Walk
48+
fieryAttackFrames[0][action.ActionAttack] = 29
49+
fieryAttackFrames[0][action.ActionCharge] = 31
50+
51+
fieryAttackFrames[1] = frames.InitNormalCancelSlice(fieryAttackHitmarks[1], 47) // N2 -> Walk
52+
fieryAttackFrames[1][action.ActionAttack] = 39
53+
fieryAttackFrames[1][action.ActionCharge] = 25
54+
55+
fieryAttackFrames[2] = frames.InitNormalCancelSlice(fieryAttackHitmarks[2], 63) // N3 -> N4/Walk
56+
fieryAttackFrames[2][action.ActionCharge] = 37
57+
}
58+
59+
func (c *char) Attack(p map[string]int) (action.Info, error) {
60+
// OnRemoved is sometimes called after the next action is executed. so we need to exit nightsoul here too
61+
c.clearNightsoulCB(action.NormalAttackState)
62+
63+
if c.StatusIsActive(skillStatus) {
64+
// TODO: or c.Core.Player.Exec(action.ActionCharge, c.Base.Key, nil)
65+
return c.ChargeAttack(p)
66+
}
67+
if c.nightsoulState.HasBlessing() {
68+
return c.fieryAttack(), nil
69+
}
70+
71+
windup := 0
72+
if c.NormalCounter == 0 {
73+
if c.Core.Player.CurrentState() == action.BurstState && c.usedShortBurst {
74+
windup = 4
75+
} else {
76+
windup = 6
77+
}
78+
}
79+
80+
ai := combat.AttackInfo{
81+
ActorIndex: c.Index,
82+
Abil: fmt.Sprintf("Normal %v", c.NormalCounter),
83+
AttackTag: attacks.AttackTagNormal,
84+
ICDTag: attacks.ICDTagNormalAttack,
85+
ICDGroup: attacks.ICDGroupDefault,
86+
StrikeType: attacks.StrikeTypeDefault,
87+
Element: attributes.Electro,
88+
Durability: 25,
89+
Mult: attack[c.NormalCounter][c.TalentLvlAttack()],
90+
HitlagFactor: attackHitlagFactor[c.NormalCounter],
91+
HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter] * 60,
92+
CanBeDefenseHalted: true,
93+
}
94+
var ap combat.AttackPattern
95+
switch len(attackHitboxes[c.NormalCounter]) {
96+
case 1: // circle
97+
ap = combat.NewCircleHitOnTarget(
98+
c.Core.Combat.Player(),
99+
geometry.Point{Y: attackOffsets[c.NormalCounter]},
100+
attackHitboxes[c.NormalCounter][0],
101+
)
102+
case 2: // box
103+
ap = combat.NewBoxHitOnTarget(
104+
c.Core.Combat.Player(),
105+
geometry.Point{Y: attackOffsets[c.NormalCounter]},
106+
attackHitboxes[c.NormalCounter][0],
107+
attackHitboxes[c.NormalCounter][1],
108+
)
109+
}
110+
c.Core.QueueAttack(ai, ap, attackHitmarks[c.NormalCounter]-windup, attackHitmarks[c.NormalCounter]-windup)
111+
112+
defer c.AdvanceNormalIndex()
113+
114+
return action.Info{
115+
Frames: func(next action.Action) int { return attackFrames[c.NormalCounter][next] - windup },
116+
AnimationLength: attackFrames[c.NormalCounter][action.InvalidAction] - windup,
117+
CanQueueAfter: attackHitmarks[c.NormalCounter] - windup,
118+
State: action.NormalAttackState,
119+
}, nil
120+
}
121+
122+
func (c *char) fieryAttack() action.Info {
123+
ai := combat.AttackInfo{
124+
ActorIndex: c.Index,
125+
Abil: fmt.Sprintf("Fiery Passion %v", c.NormalCounter),
126+
AttackTag: attacks.AttackTagNormal,
127+
ICDTag: attacks.ICDTagNormalAttack,
128+
ICDGroup: attacks.ICDGroupDefault,
129+
StrikeType: attacks.StrikeTypeDefault,
130+
Element: attributes.Electro,
131+
Durability: 25,
132+
Mult: fieryAttack[c.NormalCounter][c.TalentLvlAttack()],
133+
HitlagFactor: 0.01,
134+
HitlagHaltFrames: 0.03 * 60,
135+
CanBeDefenseHalted: true,
136+
}
137+
138+
windup := 6
139+
switch c.Core.Player.CurrentState() {
140+
case action.NormalAttackState:
141+
windup = 0
142+
case action.BurstState:
143+
if c.usedShortBurst {
144+
windup = 0
145+
} else {
146+
windup = 4
147+
}
148+
}
149+
150+
var ap combat.AttackPattern
151+
switch len(fieryAttackHitboxes[c.NormalCounter]) {
152+
case 1: // circle
153+
ap = combat.NewCircleHitOnTarget(
154+
c.Core.Combat.Player(),
155+
geometry.Point{Y: fieryAttackOffsets[c.NormalCounter]},
156+
fieryAttackHitboxes[c.NormalCounter][0],
157+
)
158+
case 2: // box
159+
ap = combat.NewBoxHitOnTarget(
160+
c.Core.Combat.Player(),
161+
geometry.Point{Y: fieryAttackOffsets[c.NormalCounter]},
162+
fieryAttackHitboxes[c.NormalCounter][0],
163+
fieryAttackHitboxes[c.NormalCounter][1],
164+
)
165+
}
166+
c.Core.QueueAttack(ai, ap, fieryAttackHitmarks[c.NormalCounter]-windup, fieryAttackHitmarks[c.NormalCounter]-windup)
167+
168+
defer c.AdvanceNormalIndex()
169+
170+
return action.Info{
171+
Frames: func(next action.Action) int { return fieryAttackFrames[c.NormalCounter][next] - windup },
172+
AnimationLength: fieryAttackFrames[c.NormalCounter][action.InvalidAction] - windup,
173+
CanQueueAfter: fieryAttackHitmarks[c.NormalCounter] - windup,
174+
State: action.NormalAttackState,
175+
}
176+
}

0 commit comments

Comments
 (0)