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
97 changes: 97 additions & 0 deletions internal/characters/jahoda/aimed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package jahoda

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"
"github.com/genshinsim/gcsim/pkg/core/info"
)

var aimedFrames [][]int

var aimedHitmarks = []int{15, 85} // {Aim -> Dash, Aim -> Jump}

func init() {
aimedFrames = make([][]int, 3)

// Aimed Shot
aimedFrames[0] = frames.InitAbilSlice(aimedHitmarks[0])

// Fully-Charged Aimed Shot
aimedFrames[1] = frames.InitAbilSlice(aimedHitmarks[1])
}

func (c *char) Aimed(p map[string]int) (action.Info, error) {
if c.StatusIsActive(shadowPursuitKey) {
c.Core.Tasks.Add(c.drainFlask(c.skillSrc), 0)
return action.Info{
Frames: frames.NewAbilFunc(skillCancelFrames),
AnimationLength: skillCancelFrames[action.InvalidAction],
CanQueueAfter: skillCancelFrames[action.ActionDash], // earliest cancel
State: action.SkillState,
}, nil
}

hold, ok := p["hold"]
if !ok {
hold = attacks.AimParamLv1
}

switch hold {
case attacks.AimParamPhys:
case attacks.AimParamLv1:
default:
return action.Info{}, fmt.Errorf("invalid hold param supplied, got %v", hold)
}
travel, ok := p["travel"]
if !ok {
travel = 10
}
weakspot := p["weakspot"]

ai := info.AttackInfo{
ActorIndex: c.Index(),
Abil: "Fully-Charged Aimed Shot",
AttackTag: attacks.AttackTagExtra,
ICDTag: attacks.ICDTagNone,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypePierce,
Element: attributes.Anemo,
Durability: 25,
Mult: fullaim[c.TalentLvlAttack()],
HitWeakPoint: weakspot == 1,
HitlagHaltFrames: 0.12 * 60,
HitlagFactor: 0.01,
HitlagOnHeadshotOnly: true,
IsDeployable: true,
}
if hold < attacks.AimParamLv1 {
ai.Abil = "Aimed Shot"
ai.Element = attributes.Physical
ai.Mult = aim[c.TalentLvlAttack()]
}

c.Core.QueueAttack(
ai,
combat.NewBoxHit(
c.Core.Combat.Player(),
c.Core.Combat.PrimaryTarget(),
info.Point{Y: -0.5},
0.1,
1,
),
aimedHitmarks[hold],
aimedHitmarks[hold]+travel,
)

return action.Info{
Frames: frames.NewAbilFunc(aimedFrames[hold]),
AnimationLength: aimedFrames[hold][action.InvalidAction],
CanQueueAfter: aimedHitmarks[hold],
State: action.AimState,
}, nil
}
117 changes: 117 additions & 0 deletions internal/characters/jahoda/asc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
package jahoda

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

func (c *char) a1Init() {
eleCountMap := c.countElements()

priority := []attributes.Element{
attributes.Pyro,
attributes.Hydro,
attributes.Electro,
attributes.Cryo,
}

highestEleCount := 0

for _, ele := range priority {
if eleCountMap[ele] > highestEleCount {
highestEleCount = eleCountMap[ele]
c.a1HighestEle = ele
}
}

if highestEleCount == 0 {
c.a1HighestEle = attributes.NoElement
}

if c.Base.Cons >= 2 && c.Core.Player.GetMoonsignLevel() > 2 {
secondHighestEleCount := 0

for _, ele := range priority {
if ele == c.a1HighestEle {
continue
}

if eleCountMap[ele] > secondHighestEleCount {
secondHighestEleCount = eleCountMap[ele]
c.c2NextHighestEle = ele
}
}

if secondHighestEleCount == 0 {
c.c2NextHighestEle = attributes.NoElement
}
}
}

func (c *char) a1() {
if c.Base.Ascension < 1 {
return
}

c.applyA1Buff(c.a1HighestEle)

if c.Base.Cons >= 2 && c.Core.Player.GetMoonsignLevel() > 2 {
c.applyA1Buff(c.c2NextHighestEle)
}
}

func (c *char) countElements() map[attributes.Element]int {
count := map[attributes.Element]int{
attributes.Pyro: 0,
attributes.Hydro: 0,
attributes.Electro: 0,
attributes.Cryo: 0,
}

for _, ch := range c.Core.Player.Chars() {
if ch == nil {
continue
}

switch ch.Base.Element {
case attributes.Pyro,
attributes.Hydro,
attributes.Electro,
attributes.Cryo:
count[ch.Base.Element]++
}
}

return count
}

func (c *char) applyA1Buff(ele attributes.Element) {
switch ele {
case attributes.Pyro:
c.robotAi.FlatDmg *= 1.3
case attributes.Hydro:
c.robotHi.Src *= 1.2
case attributes.Electro:
c.robotCount += 1
case attributes.Cryo:
c.robotHitmarkInterval *= 0.9
}
}

func (c *char) a4() {
if c.Base.Ascension < 4 {
return
}

c.Core.Player.ActiveChar().AddStatMod(character.StatMod{
Base: modifier.NewBase("jahoda-a4", 6*60),
AffectedStat: attributes.EM,
Amount: func() ([]float64, bool) {
return c.a4Buff, true
},
})

c.Core.Log.NewEvent("jahoda a4 triggered", glog.LogCharacterEvent, c.Index()).Write("em snapshot", c.a4Buff[attributes.EM]).Write("expiry", c.Core.F+6*60)
}
85 changes: 85 additions & 0 deletions internal/characters/jahoda/attack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package jahoda

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"
"github.com/genshinsim/gcsim/pkg/core/info"
)

var (
attackFrames [][]int
attackHitmarks = [][]int{{14}, {15, 29}, {40}}
)

const normalHitNum = 3

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

attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 35) // N1 -> Walk
attackFrames[0][action.ActionAttack] = 30
attackFrames[0][action.ActionAim] = 30

attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][1], 52) // N2 -> Walk
attackFrames[1][action.ActionAttack] = 48
attackFrames[1][action.ActionAim] = 47

attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 99) // N3 -> Walk
attackFrames[2][action.ActionAttack] = 88
attackFrames[2][action.ActionAim] = 89
}

func (c *char) Attack(p map[string]int) (action.Info, error) {
if c.StatusIsActive(shadowPursuitKey) {
c.Core.Tasks.Add(c.drainFlask(c.skillSrc), 0)
return action.Info{
Frames: frames.NewAbilFunc(skillCancelFrames),
AnimationLength: skillCancelFrames[action.InvalidAction],
CanQueueAfter: skillCancelFrames[action.ActionDash], // earliest cancel
State: action.SkillState,
}, nil
}

travel, ok := p["travel"]
if !ok {
travel = 10
}

for i, mult := range attack[c.NormalCounter] {
ai := info.AttackInfo{
ActorIndex: c.Index(),
Abil: fmt.Sprintf("Normal %v", c.NormalCounter),
AttackTag: attacks.AttackTagNormal,
ICDTag: attacks.ICDTagNone,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypePierce,
Element: attributes.Physical,
Durability: 25,
Mult: mult[c.TalentLvlAttack()],
HitlagFactor: 0.01,
}

ap := combat.NewBoxHit(c.Core.Combat.Player(), c.Core.Combat.PrimaryTarget(), info.Point{Y: -0.5}, 0.1, 1)
c.Core.QueueAttack(
ai,
ap,
attackHitmarks[c.NormalCounter][i],
attackHitmarks[c.NormalCounter][i]+travel,
)
}

defer c.AdvanceNormalIndex()

return action.Info{
Frames: frames.NewAttackFunc(c.Character, attackFrames),
AnimationLength: attackFrames[c.NormalCounter][action.InvalidAction],
CanQueueAfter: attackHitmarks[c.NormalCounter][len(attackHitmarks[c.NormalCounter])-1],
State: action.NormalAttackState,
}, nil
}
Loading
Loading