From 1b556314f115fb9ae5df114fff90426fad2e1bd7 Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 10 Nov 2023 20:49:30 +1100 Subject: [PATCH 01/36] Navia Initial Commit. Todo, fix SurgingBlade() and SkillCB() not being registered, and add skill buffs, and cons. Also fix Burst hitbox and targetting. Also Frames --- internal/characters/navia/asc.go | 82 +++++++++++ internal/characters/navia/attack.go | 77 +++++++++++ internal/characters/navia/burst.go | 84 +++++++++++ internal/characters/navia/config.yaml | 3 + internal/characters/navia/cons.go | 69 +++++++++ internal/characters/navia/navia.go | 36 +++++ internal/characters/navia/skill.go | 173 +++++++++++++++++++++++ internal/characters/navia/stats.go | 192 ++++++++++++++++++++++++++ pkg/core/curves/charactercurves.go | 65 +++++++++ pkg/shortcut/characters.go | 1 + 10 files changed, 782 insertions(+) create mode 100644 internal/characters/navia/asc.go create mode 100644 internal/characters/navia/attack.go create mode 100644 internal/characters/navia/burst.go create mode 100644 internal/characters/navia/config.yaml create mode 100644 internal/characters/navia/cons.go create mode 100644 internal/characters/navia/navia.go create mode 100644 internal/characters/navia/skill.go create mode 100644 internal/characters/navia/stats.go diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go new file mode 100644 index 0000000000..35c3fc0894 --- /dev/null +++ b/internal/characters/navia/asc.go @@ -0,0 +1,82 @@ +package navia + +import ( + "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/player/character" + "github.com/genshinsim/gcsim/pkg/modifier" +) + +var a4ATKP = 0.2 + +func init() { + +} + +// For 4s after using Ceremonial Crystalshot, the DMG dealt by Navia's Normal Attacks, +// Charged Attacks, and Plunging Attacks will be converted into Geo DMG which cannot +// be overridden by other Elemental infusions, and the DMG dealt by Navia's Normal Attacks, +// Charged Attacks, and Plunging Attacks will be increased by 40%. +func (c *char) a1() { + if c.Base.Ascension < 1 { + return + } + + // add Geo infusion + c.Core.Player.AddWeaponInfuse( + c.Index, + "navia-a1-infusion", + attributes.Geo, + 60*4, + false, + attacks.AttackTagNormal, + ) + + // add Damage Bonus + m := make([]float64, attributes.EndStatType) + c.AddAttackMod(character.AttackMod{ + Base: modifier.NewBaseWithHitlag("navia-a1-dmg", 60*4), // 5s + Amount: func(atk *combat.AttackEvent, _ combat.Target) ([]float64, bool) { + // skip if not normal/charged/plunge + if atk.Info.AttackTag != attacks.AttackTagNormal && + atk.Info.AttackTag != attacks.AttackTagExtra && + atk.Info.AttackTag != attacks.AttackTagPlunge { + return nil, false + } + // apply buff + m[attributes.DmgP] = 0.4 + return m, true + }, + }) +} + +// For each Pyro/Electro/Cryo/Hydro party member, Navia gains 20% increased ATK. +// This effect can stack up to 2 times. +func (c *char) a4() { + if c.Base.Ascension < 4 { + return + } + + ele := 0 + for _, char := range c.Core.Player.Chars() { + if char.Base.Element != attributes.Geo && char.Base. + Element != attributes.Anemo && char.Base.Element != attributes.Dendro { + ele = ele + 1 + } + } + if ele > 2 { + ele = 2 + } + + m := make([]float64, attributes.EndStatType) + m[attributes.ATKP] = a4ATKP * float64(ele) + c.AddStatMod(character.StatMod{ + Base: modifier.NewBase("navia-a4", -1), + AffectedStat: attributes.ATKP, + Amount: func() ([]float64, bool) { + return m, true + }, + }) + +} diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go new file mode 100644 index 0000000000..e0c3f9fb86 --- /dev/null +++ b/internal/characters/navia/attack.go @@ -0,0 +1,77 @@ +package navia + +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/geometry" +) + +var ( + attackFrames [][]int + attackHitmarks = [][]int{{30}, {19}, {25, 42, 59}, {17}} + attackHitlagHaltFrame = [][]float64{{0.09}, {.12}, {0, 0}, {.09}} + attackDefHalt = [][]bool{{true}, {true}, {false, false, false}, {true}} + attackHitboxes = [][]float64{{2}, {2}, {2}, {2}} + attackOffsets = []float64{0.5, 0.5, 0.5, 0} + attackFanAngles = []float64{270, 270, 360, 270} +) + +const normalHitNum = 4 + +func init() { + attackFrames = make([][]int, normalHitNum) + + attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 38) + attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 46) + attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 31) + attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 107) +} + +func (c *char) Attack(p map[string]int) (action.Info, error) { + for i, mult := range auto[c.NormalCounter] { + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: fmt.Sprintf("Normal %v", c.NormalCounter), + AttackTag: attacks.AttackTagNormal, + ICDTag: attacks.ICDTagNormalAttack, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Physical, + Durability: 25, + Mult: mult[c.TalentLvlAttack()], + HitlagFactor: 0.01, + HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter][i] * 60, + CanBeDefenseHalted: attackDefHalt[c.NormalCounter][i], + } + ap := combat.NewCircleHitOnTargetFanAngle( + c.Core.Combat.Player(), + geometry.Point{Y: attackOffsets[c.NormalCounter]}, + attackHitboxes[c.NormalCounter][0], + attackFanAngles[c.NormalCounter], + ) + if c.NormalCounter == 4 { + ap = combat.NewBoxHitOnTarget( + c.Core.Combat.Player(), + geometry.Point{Y: attackOffsets[c.NormalCounter]}, + attackHitboxes[c.NormalCounter][0], + attackHitboxes[c.NormalCounter][1], + ) + } + c.QueueCharTask(func() { + c.Core.QueueAttack(ai, ap, 0, 0) + }, attackHitmarks[c.NormalCounter][i]) + } + + 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 +} diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go new file mode 100644 index 0000000000..cf67bb3b56 --- /dev/null +++ b/internal/characters/navia/burst.go @@ -0,0 +1,84 @@ +package navia + +import ( + "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/targets" +) + +var burstFrames []int + +const burstHitmark = 100 + +func init() { + burstFrames = frames.InitAbilSlice(114) + burstFrames[action.ActionSkill] = 114 +} + +func (c *char) Burst(p map[string]int) (action.Info, error) { + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "As the Sunlit Sky's Singing Salute", + AttackTag: attacks.AttackTagElementalBurst, + ICDTag: attacks.ICDTagNone, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 50, + Mult: burst[0][c.TalentLvlBurst()], + } + + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 6), + burstHitmark, + burstHitmark, + ) + + c.ConsumeEnergy(5) + c.SetCD(action.ActionBurst, 15*60) + + ai.Abil = "Fire Support" + ai.ICDTag = attacks.ICDTagElementalBurst + ai.Durability = 25 + ai.Mult = burst[1][c.TalentLvlBurst()] + + for i := 45; i <= 12*60; i = i + 45 { + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), nil, 3), + burstHitmark, + burstHitmark+i, + c.BurstCB(), + ) + } + + return action.Info{ + Frames: frames.NewAbilFunc(burstFrames), + AnimationLength: burstFrames[action.InvalidAction], + CanQueueAfter: burstFrames[action.ActionSwap], // earliest cancel + State: action.BurstState, + }, nil +} + +func (c *char) BurstCB() combat.AttackCBFunc { + if c.StatusIsActive("navia-q-shrapnel-icd") { + return nil + } + + return func(a combat.AttackCB) { + if a.Target.Type() != targets.TargettableEnemy { + return + } + + if c.shrapnel < 6 { + c.shrapnel++ + } + + c.AddStatus("navia-q-shrapnel-icd", 2.4*60, false) + } + +} diff --git a/internal/characters/navia/config.yaml b/internal/characters/navia/config.yaml new file mode 100644 index 0000000000..3434958a16 --- /dev/null +++ b/internal/characters/navia/config.yaml @@ -0,0 +1,3 @@ +package_name: "navia" +genshin_id: 10000091 +key: "navia" \ No newline at end of file diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go new file mode 100644 index 0000000000..631b8b347f --- /dev/null +++ b/internal/characters/navia/cons.go @@ -0,0 +1,69 @@ +package navia + +import ( + "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/targets" + "github.com/genshinsim/gcsim/pkg/enemy" + "github.com/genshinsim/gcsim/pkg/modifier" + "math" +) + +// Each charge of Crystal Shrapnel consumed when Navia uses Ceremonial Crystalshot will +// restore 2 Energy to her and decrease the CD of As the Sunlit Sky's Singing Salute by 1s. +// Up to 6 Energy can be gained this way, and the CD of Ceremonial Crystalshot can be +// decreased by up to 3s. +func (c *char) c1(shrapnel int) { + count := math.Min(float64(shrapnel), 3) + c.ReduceActionCooldown(action.ActionBurst, int(count*60)) + c.AddEnergy("navia-c1-energy", count*2) + return +} + +// The CRIT Rate of Ceremonial Crystalshot is increased by 8% for each charge of Crystal +// Shrapnel consumed. CRIT Rate can be increased by up to 24% in this way. +// In addition, when Ceremonial Crystalshot hits an opponent, one shot of Fire Support +// from As the Sunlit Sky's Singing Salute will strike near the location of the hit. +// Up to one instance of Fire Support can be triggered each time Ceremonial Crystalshot is used, +// and DMG dealt by Fire Support in this way is considered Elemental Burst DMG. +func (c *char) c2() combat.AttackCBFunc { + if c.Base.Cons < 2 { + return nil + } + return func(a combat.AttackCB) { + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "The President's Pursuit of Victory", + AttackTag: attacks.AttackTagElementalBurst, + ICDTag: attacks.ICDTagElementalBurst, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 25, + Mult: burst[1][c.TalentLvlSkill()], + } + //currently not snapshotting + c.Core.QueueAttack(ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), nil, 3), 0, 0) + } +} + +// When As the Sunlit Sky's Singing Salute hits an opponent, +// that opponent's Geo RES will be decreased by 20% for 8s. +func (c *char) c4() combat.AttackCBFunc { + if c.Base.Cons < 4 { + return nil + } + return func(a combat.AttackCB) { + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return + } + e.AddResistMod(combat.ResistMod{ + Base: modifier.NewBaseWithHitlag("navia-c4-shred", 8*60), + Ele: attributes.Geo, + Value: -0.2, + }) + } +} diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go new file mode 100644 index 0000000000..6c7129e29d --- /dev/null +++ b/internal/characters/navia/navia.go @@ -0,0 +1,36 @@ +package navia + +import ( + tmpl "github.com/genshinsim/gcsim/internal/template/character" + "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" +) + +func init() { + core.RegisterCharFunc(keys.Navia, NewChar) +} + +type char struct { + *tmpl.Character + shrapnel int +} + +func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error { + c := char{} + c.Character = tmpl.NewWithWrapper(s, w) + + c.EnergyMax = 60 + c.NormalHitNum = 4 + c.SkillCon = 3 + c.BurstCon = 5 + c.shrapnel = 0 + c.SetNumCharges(action.ActionSkill, 2) + c.shrapnelGain() + + w.Character = &c + + return nil +} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go new file mode 100644 index 0000000000..1676258474 --- /dev/null +++ b/internal/characters/navia/skill.go @@ -0,0 +1,173 @@ +package navia + +import ( + "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/event" + "github.com/genshinsim/gcsim/pkg/core/geometry" + "github.com/genshinsim/gcsim/pkg/core/targets" + "github.com/genshinsim/gcsim/pkg/enemy" + "math" +) + +var skillPressFrames []int +var skillHoldFrames []int +var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, + event.OnCrystallizePyro} + +const ( + skillPressCDStart = 16 + skillPressHitmark = 17 + + skillHoldCDStart = 11 + skillHoldHitmark = 12 +) + +func init() { + skillPressFrames = frames.InitAbilSlice(39) // E -> N1/Q + skillPressFrames[action.ActionDash] = 34 + skillPressFrames[action.ActionJump] = 35 + skillPressFrames[action.ActionSwap] = 37 + + // skill (hold) -> x + skillHoldFrames = frames.InitAbilSlice(46) // E -> Swap + skillHoldFrames[action.ActionAttack] = 38 + skillHoldFrames[action.ActionBurst] = 37 + skillHoldFrames[action.ActionDash] = 30 + skillHoldFrames[action.ActionJump] = 30 +} + +func (c *char) Skill(p map[string]int) (action.Info, error) { + hold := p["hold"] + c.SurgingBlade() + hitmark := 0 + if hold > 0 { + hitmark += skillHoldHitmark + hold + } else { + hitmark += skillPressHitmark + hold + } + + if p["shrapnel"] != 0 { + c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) + } + travel := 5 + if p["travel"] != 0 { + travel = p["travel"] + } + shots := 1.0 + switch c.shrapnel { + case 0: + shots = 1.2 + case 1: + shots = 1.4 + case 2: + shots = 1.66 + default: + shots = 2.0 + } + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "Rosula Shardshot", + AttackTag: attacks.AttackTagElementalArt, + ICDTag: attacks.ICDTagNone, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 25, + Mult: skillshotgun[c.TalentLvlSkill()] * shots, + } + // When firing, attack with the Surging Blade + c.SurgingBlade(hitmark) + + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 25), + hitmark, + travel+hitmark, + c.SkillCB(), + ) + // C1 Energy Restoration and CD reduction + if c.Base.Cons >= 1 { + c.QueueCharTask(func() { + c.c1(c.shrapnel) + }, hitmark) + } + // remove the shrapnel after firing + c.QueueCharTask( + func() { + if c.Base.Cons < 6 { + c.shrapnel = 0 + } else { + c.shrapnel = c.shrapnel - 3 // C6 keeps any more than the three + } + return + }, + hitmark, + ) + + return action.Info{}, nil +} + +func (c *char) SkillCB() combat.AttackCBFunc { + done := false + return func(a combat.AttackCB) { + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return + } + + c.c4() + + if done { + return + } + c.Core.QueueParticle(c.Base.Key.String(), 4, attributes.Geo, c.ParticleDelay) + + c.c2() + + done = true + + } +} + +func (c *char) SurgingBlade(delay int) { + if c.StatusIsActive("surging-blade-cd") { + return + } + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "Surging Blade", + AttackTag: attacks.AttackTagElementalArt, + ICDTag: attacks.ICDTagNone, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 0, + Mult: skillblade[c.TalentLvlSkill()], + } + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), + 3+delay, + 3+delay, + nil, + ) + c.AddStatus("surging-blade-cd", 7*60, true) + return +} + +func (c *char) ShrapnelGain() { + + for _, crystal := range crystallise { + c.Core.Events.Subscribe(crystal, func(args ...interface{}) bool { + if c.shrapnel < 6 { + c.shrapnel++ + } + return false + }, "shrapnel-gain") + } + +} diff --git a/internal/characters/navia/stats.go b/internal/characters/navia/stats.go new file mode 100644 index 0000000000..6586b40e62 --- /dev/null +++ b/internal/characters/navia/stats.go @@ -0,0 +1,192 @@ +package navia + +var ( + auto = [][][]float64{ + //1 + { + { + 93.5 / 100.0, + 101.1 / 100.0, + 108.7 / 100.0, + 119.6 / 100.0, + 127.2 / 100.0, + 135.9 / 100.0, + 147.9 / 100.0, + 159.9 / 100.0, + 171.8 / 100.0, + 184.9 / 100.0, + 197.9 / 100.0, + 211.0 / 100.0, + 224.0 / 100.0, + 237.1 / 100.0, + 250.1 / 100.0, + }, + }, + //2 + { + { + 86.5 / 100.0, + 93.5 / 100.0, + 100.6 / 100.0, + 110.6 / 100.0, + 117.7 / 100.0, + 125.7 / 100.0, + 136.8 / 100.0, + 147.9 / 100.0, + 158.9 / 100.0, + 171.0 / 100.0, + 183.1 / 100.0, + 195.1 / 100.0, + 207.2 / 100.0, + 219.3 / 100.0, + 231.4 / 100.0, + }, + }, + //3 + { + { + 34.9 / 100.0, + 37.7 / 100.0, + 40.6 / 100.0, + 44.6 / 100.0, + 47.5 / 100.0, + 50.7 / 100.0, + 55.2 / 100.0, + 59.6 / 100.0, + 64.1 / 100.0, + 69.0 / 100.0, + 73.8 / 100.0, + 78.7 / 100.0, + 83.6 / 100.0, + 88.4 / 100.0, + 93.3 / 100.0, + }, + { + 34.9 / 100.0, + 37.7 / 100.0, + 40.6 / 100.0, + 44.6 / 100.0, + 47.5 / 100.0, + 50.7 / 100.0, + 55.2 / 100.0, + 59.6 / 100.0, + 64.1 / 100.0, + 69.0 / 100.0, + 73.8 / 100.0, + 78.7 / 100.0, + 83.6 / 100.0, + 88.4 / 100.0, + 93.3 / 100.0, + }, + { + 34.9 / 100.0, + 37.7 / 100.0, + 40.6 / 100.0, + 44.6 / 100.0, + 47.5 / 100.0, + 50.7 / 100.0, + 55.2 / 100.0, + 59.6 / 100.0, + 64.1 / 100.0, + 69.0 / 100.0, + 73.8 / 100.0, + 78.7 / 100.0, + 83.6 / 100.0, + 88.4 / 100.0, + 93.3 / 100.0, + }, + }, + //4 + { + { + 133.4 / 100.0, + 144.3 / 100.0, + 155.2 / 100.0, + 170.7 / 100.0, + 181.5 / 100.0, + 193.9 / 100.0, + 211.0 / 100.0, + 228.1 / 100.0, + 245.1 / 100.0, + 263.8 / 100.0, + 282.4 / 100.0, + 301.0 / 100.0, + 319.6 / 100.0, + 338.2 / 100.0, + 356.9 / 100.0, + }, + }, + } + skillshotgun = []float64{ + 307.2 / 500.0, + 330.2 / 500.0, + 353.3 / 500.0, + 384.0 / 500.0, + 407.0 / 500.0, + 430.1 / 500.0, + 460.8 / 500.0, + 491.5 / 500.0, + 522.2 / 500.0, + 553.0 / 500.0, + 583.7 / 500.0, + 614.4 / 500.0, + 652.8 / 500.0, + 691.2 / 500.0, + 729.6 / 500.0, + } + skillblade = []float64{ + 33.6 / 100.0, + 36.1 / 100.0, + 38.6 / 100.0, + 42.0 / 100.0, + 44.5 / 100.0, + 47.0 / 100.0, + 50.4 / 100.0, + 53.8 / 100.0, + 57.1 / 100.0, + 60.5 / 100.0, + 63.8 / 100.0, + 67.2 / 100.0, + 71.4 / 100.0, + 75.6 / 100.0, + 79.8 / 100.0, + } + burst = [][]float64{ + // skill cast + { + 76.0 / 100.0, + 81.7 / 100.0, + 87.4 / 100.0, + 95.0 / 100.0, + 100.7 / 100.0, + 106.4 / 100.0, + 114.0 / 100.0, + 121.6 / 100.0, + 129.2 / 100.0, + 136.8 / 100.0, + 144.4 / 100.0, + 152.0 / 100.0, + 161.5 / 100.0, //not copying the rest + 161.5 / 100.0, + 161.5 / 100.0, + }, + // artillery + { + 48.4 / 100.0, + 52.0 / 100.0, + 55.7 / 100.0, + 60.5 / 100.0, + 60.5 / 100.0, + 67.8 / 100.0, + 72.6 / 100.0, + 77.4 / 100.0, + 82.3 / 100.0, + 87.1 / 100.0, + 92.0 / 100.0, + 96.8 / 100.0, + 102.9 / 100.0, //too lazy to copy the rest + 102.9 / 100.0, + 102.9 / 100.0, + }, + } +) diff --git a/pkg/core/curves/charactercurves.go b/pkg/core/curves/charactercurves.go index 42d10fe43b..1c63b659df 100644 --- a/pkg/core/curves/charactercurves.go +++ b/pkg/core/curves/charactercurves.go @@ -2867,6 +2867,71 @@ var CharBaseMap = map[keys.Char]CharBase{ }, }, }, + keys.Navia: { + Rarity: 5, + Body: info.BodyLady, + Element: attributes.Geo, + Region: info.ZoneFontaine, + WeaponClass: info.WeaponClassClaymore, + HPCurve: GROW_CURVE_HP_S5, + AtkCurve: GROW_CURVE_ATTACK_S5, + DefCurve: GROW_CURVE_HP_S5, + BaseHP: 985, + BaseAtk: 27, + BaseDef: 62, + Specialized: attributes.CD, + PromotionBonus: []PromoData{ + { + MaxLevel: 20, + HP: 0, + Atk: 0, + Def: 0, + Special: 0, + }, + { + MaxLevel: 40, + HP: 3399 - 2555, + Atk: 94 - 71, + Def: 160 - 134, + Special: 0, + }, + { + MaxLevel: 50, + HP: 5686 - 5086, + Atk: 158 - 141, + Def: 356 - 319, + Special: 0.096, + }, + { + MaxLevel: 60, + HP: 7341 - 6542, + Atk: 204 - 182, + Def: 460 - 410, + Special: 0.692 - 0.596, + }, + { + MaxLevel: 70, + HP: 8806 - 8206, + Atk: 245 - 228, + Def: 552 - 515, + Special: 0, + }, + { + MaxLevel: 80, + HP: 10278 - 9679, + Atk: 286 - 269, + Def: 644 - 607, + Special: 0.096, + }, + { + MaxLevel: 90, + HP: 11761 - 11161, + Atk: 17, + Def: 37, + Special: 0.096, + }, + }, + }, keys.Ningguang: { Rarity: 4, Body: info.BodyLady, diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index fd2d0d9ea0..ff62871b8d 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -77,6 +77,7 @@ var CharNameToKey = map[string]keys.Char{ "sara": keys.Sara, "lisa": keys.Lisa, "mona": keys.Mona, + "navia": keys.Navia, "ningguang": keys.Ningguang, "ning": keys.Ningguang, "noelle": keys.Noelle, From 37eeb1c2043f072d4bca916ed78868ad1e89bed1 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 11 Nov 2023 00:27:28 +1100 Subject: [PATCH 02/36] Fixed Stackoverflow from lack of Init, added CDs for Skill, along with DMG%, CR and CD buffs. Burst adjusted firerate and hitboxes --- internal/characters/navia/asc.go | 9 ++-- internal/characters/navia/attack.go | 2 +- internal/characters/navia/burst.go | 7 ++- internal/characters/navia/navia.go | 11 ++-- internal/characters/navia/skill.go | 78 ++++++++++++++++++++--------- 5 files changed, 72 insertions(+), 35 deletions(-) diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index 35c3fc0894..d318471adb 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -8,8 +8,6 @@ import ( "github.com/genshinsim/gcsim/pkg/modifier" ) -var a4ATKP = 0.2 - func init() { } @@ -31,12 +29,14 @@ func (c *char) a1() { 60*4, false, attacks.AttackTagNormal, + attacks.AttackTagExtra, + attacks.AttackTagPlunge, ) // add Damage Bonus m := make([]float64, attributes.EndStatType) c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBaseWithHitlag("navia-a1-dmg", 60*4), // 5s + Base: modifier.NewBaseWithHitlag("navia-a1-dmg", 60*4), // 4s Amount: func(atk *combat.AttackEvent, _ combat.Target) ([]float64, bool) { // skip if not normal/charged/plunge if atk.Info.AttackTag != attacks.AttackTagNormal && @@ -70,7 +70,7 @@ func (c *char) a4() { } m := make([]float64, attributes.EndStatType) - m[attributes.ATKP] = a4ATKP * float64(ele) + m[attributes.ATKP] = 0.2 * float64(ele) c.AddStatMod(character.StatMod{ Base: modifier.NewBase("navia-a4", -1), AffectedStat: attributes.ATKP, @@ -78,5 +78,4 @@ func (c *char) a4() { return m, true }, }) - } diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index e0c3f9fb86..d834a58571 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -13,7 +13,7 @@ import ( var ( attackFrames [][]int attackHitmarks = [][]int{{30}, {19}, {25, 42, 59}, {17}} - attackHitlagHaltFrame = [][]float64{{0.09}, {.12}, {0, 0}, {.09}} + attackHitlagHaltFrame = [][]float64{{0.09}, {.12}, {0, 0, 0}, {.09}} attackDefHalt = [][]bool{{true}, {true}, {false, false, false}, {true}} attackHitboxes = [][]float64{{2}, {2}, {2}, {2}} attackOffsets = []float64{0.5, 0.5, 0.5, 0} diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index cf67bb3b56..3388e70997 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -6,6 +6,8 @@ import ( "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" "github.com/genshinsim/gcsim/pkg/core/targets" ) @@ -33,7 +35,7 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { c.Core.QueueAttack( ai, - combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 6), + combat.NewBoxHit(c.Core.Combat.Player(), c.Core.Combat.Player(), geometry.Point{Y: 0}, 12, 6), burstHitmark, burstHitmark, ) @@ -46,6 +48,7 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { ai.Durability = 25 ai.Mult = burst[1][c.TalentLvlBurst()] + //TODO: Random Targetting for i := 45; i <= 12*60; i = i + 45 { c.Core.QueueAttack( ai, @@ -76,6 +79,8 @@ func (c *char) BurstCB() combat.AttackCBFunc { if c.shrapnel < 6 { c.shrapnel++ + c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index) + } c.AddStatus("navia-q-shrapnel-icd", 2.4*60, false) diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index 6c7129e29d..8e134a16b0 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -21,16 +21,19 @@ type char struct { func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error { c := char{} c.Character = tmpl.NewWithWrapper(s, w) - c.EnergyMax = 60 c.NormalHitNum = 4 c.SkillCon = 3 c.BurstCon = 5 - c.shrapnel = 0 c.SetNumCharges(action.ActionSkill, 2) - c.shrapnelGain() - w.Character = &c return nil } + +func (c *char) Init() error { + c.a4() + c.ShrapnelGain() + c.shrapnel = 0 + return nil +} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 1676258474..bc49bf5839 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -8,8 +8,11 @@ import ( "github.com/genshinsim/gcsim/pkg/core/combat" "github.com/genshinsim/gcsim/pkg/core/event" "github.com/genshinsim/gcsim/pkg/core/geometry" + "github.com/genshinsim/gcsim/pkg/core/glog" + "github.com/genshinsim/gcsim/pkg/core/player/character" "github.com/genshinsim/gcsim/pkg/core/targets" "github.com/genshinsim/gcsim/pkg/enemy" + "github.com/genshinsim/gcsim/pkg/modifier" "math" ) @@ -22,7 +25,6 @@ const ( skillPressCDStart = 16 skillPressHitmark = 17 - skillHoldCDStart = 11 skillHoldHitmark = 12 ) @@ -42,21 +44,19 @@ func init() { func (c *char) Skill(p map[string]int) (action.Info, error) { hold := p["hold"] - c.SurgingBlade() hitmark := 0 if hold > 0 { hitmark += skillHoldHitmark + hold + c.SetCDWithDelay(action.ActionSkill, 9*60, hitmark) } else { hitmark += skillPressHitmark + hold + c.SetCDWithDelay(action.ActionSkill, 9*60, skillPressCDStart) } if p["shrapnel"] != 0 { c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) } - travel := 5 - if p["travel"] != 0 { - travel = p["travel"] - } + shots := 1.0 switch c.shrapnel { case 0: @@ -82,11 +82,30 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { // When firing, attack with the Surging Blade c.SurgingBlade(hitmark) + excess := math.Max(float64(c.shrapnel-3), 0) + c.AddAttackMod(character.AttackMod{ + Base: modifier.NewBase("navia-skill-dmgup", 8*60), + Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { + if atk.Info.AttackTag != attacks.AttackTagExtra { + return nil, false + } + m := make([]float64, attributes.EndStatType) + m[attributes.DmgP] = 0.15 * excess + if c.Base.Cons >= 2 { + m[attributes.CR] = 0.08 * excess + } + if c.Base.Cons >= 6 { + m[attributes.CD] = 0.35 * excess + } + return m, true + }, + }) + c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 25), hitmark, - travel+hitmark, + 5+hitmark, c.SkillCB(), ) // C1 Energy Restoration and CD reduction @@ -107,8 +126,18 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { }, hitmark, ) + // Add Geo Infusion + c.QueueCharTask( + c.a1, + hitmark+30, + ) - return action.Info{}, nil + return action.Info{ + Frames: frames.NewAbilFunc(skillHoldFrames), + AnimationLength: skillHoldFrames[action.ActionDash], + CanQueueAfter: skillHoldFrames[action.ActionDash], + State: action.SkillState, + }, nil } func (c *char) SkillCB() combat.AttackCBFunc { @@ -133,7 +162,21 @@ func (c *char) SkillCB() combat.AttackCBFunc { } } -func (c *char) SurgingBlade(delay int) { +func (c *char) ShrapnelGain() { + + for _, crystal := range crystallise { + c.Core.Events.Subscribe(crystal, func(args ...interface{}) bool { + if c.shrapnel < 6 { + c.shrapnel++ + c.Core.Log.NewEvent("Crystal Shrapnel gained from Crystallise", glog.LogCharacterEvent, c.Index) + } + return false + }, "shrapnel-gain") + } + +} + +func (c *char) SurgingBlade(hitmark int) { if c.StatusIsActive("surging-blade-cd") { return } @@ -151,23 +194,10 @@ func (c *char) SurgingBlade(delay int) { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - 3+delay, - 3+delay, + 3+hitmark, + 3+hitmark, nil, ) c.AddStatus("surging-blade-cd", 7*60, true) return } - -func (c *char) ShrapnelGain() { - - for _, crystal := range crystallise { - c.Core.Events.Subscribe(crystal, func(args ...interface{}) bool { - if c.shrapnel < 6 { - c.shrapnel++ - } - return false - }, "shrapnel-gain") - } - -} From 9573b2871c14a0e9477500f1eef9504ab462e4b9 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 11 Nov 2023 01:33:39 +1100 Subject: [PATCH 03/36] Fixed All Burst hits granting Shrapnel Shotgun Scaling (was divided by 5) Core ordering Minor Skill Stuff --- internal/characters/navia/burst.go | 8 ++++---- internal/characters/navia/skill.go | 21 +++++++++++++++------ internal/characters/navia/stats.go | 30 +++++++++++++++--------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 3388e70997..6b76a65508 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -68,11 +68,11 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { } func (c *char) BurstCB() combat.AttackCBFunc { - if c.StatusIsActive("navia-q-shrapnel-icd") { - return nil - } return func(a combat.AttackCB) { + if c.StatusIsActive("navia-q-shrapnel-icd") { + return + } if a.Target.Type() != targets.TargettableEnemy { return } @@ -83,7 +83,7 @@ func (c *char) BurstCB() combat.AttackCBFunc { } - c.AddStatus("navia-q-shrapnel-icd", 2.4*60, false) + c.AddStatus("navia-q-shrapnel-icd", 2.4*60-1, false) } } diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index bc49bf5839..918b0b232b 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -1,6 +1,7 @@ package navia import ( + "fmt" "github.com/genshinsim/gcsim/internal/frames" "github.com/genshinsim/gcsim/pkg/core/action" "github.com/genshinsim/gcsim/pkg/core/attacks" @@ -57,6 +58,8 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) } + c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) + shots := 1.0 switch c.shrapnel { case 0: @@ -83,13 +86,13 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.SurgingBlade(hitmark) excess := math.Max(float64(c.shrapnel-3), 0) + m := make([]float64, attributes.EndStatType) c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBase("navia-skill-dmgup", 8*60), + Base: modifier.NewBase("navia-skill-dmgup", hitmark+6), Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { - if atk.Info.AttackTag != attacks.AttackTagExtra { + if atk.Info.AttackTag != attacks.AttackTagElementalArt { return nil, false } - m := make([]float64, attributes.EndStatType) m[attributes.DmgP] = 0.15 * excess if c.Base.Cons >= 2 { m[attributes.CR] = 0.08 * excess @@ -194,10 +197,16 @@ func (c *char) SurgingBlade(hitmark int) { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - 3+hitmark, - 3+hitmark, + 10+hitmark, + 10+hitmark, nil, ) - c.AddStatus("surging-blade-cd", 7*60, true) + c.QueueCharTask( + func() { + c.AddStatus("surging-blade-cd", 7*60, true) + }, + hitmark, + ) + return } diff --git a/internal/characters/navia/stats.go b/internal/characters/navia/stats.go index 6586b40e62..962dfc80fa 100644 --- a/internal/characters/navia/stats.go +++ b/internal/characters/navia/stats.go @@ -118,21 +118,21 @@ var ( }, } skillshotgun = []float64{ - 307.2 / 500.0, - 330.2 / 500.0, - 353.3 / 500.0, - 384.0 / 500.0, - 407.0 / 500.0, - 430.1 / 500.0, - 460.8 / 500.0, - 491.5 / 500.0, - 522.2 / 500.0, - 553.0 / 500.0, - 583.7 / 500.0, - 614.4 / 500.0, - 652.8 / 500.0, - 691.2 / 500.0, - 729.6 / 500.0, + 307.2 / 100.0, + 330.2 / 100.0, + 353.3 / 100.0, + 384.0 / 100.0, + 407.0 / 100.0, + 430.1 / 100.0, + 460.8 / 100.0, + 491.5 / 100.0, + 522.2 / 100.0, + 553.0 / 100.0, + 583.7 / 100.0, + 614.4 / 100.0, + 652.8 / 100.0, + 691.2 / 100.0, + 729.6 / 100.0, } skillblade = []float64{ 33.6 / 100.0, From 70e58996bd0a520fada83f4d01045e9c7b5a23e0 Mon Sep 17 00:00:00 2001 From: David Ji Date: Tue, 14 Nov 2023 00:26:17 +1100 Subject: [PATCH 04/36] Added: - Burst Status - Conditionals Fixed: - Burst Targetting - Burst Snapshot - C2 - C4 - Particle Gen - Skill ICDs --- internal/characters/navia/burst.go | 66 +++++++++++++++++++++++++++--- internal/characters/navia/cons.go | 63 +++++++++++++++------------- internal/characters/navia/navia.go | 20 ++++++++- internal/characters/navia/skill.go | 31 +++++++++----- 4 files changed, 133 insertions(+), 47 deletions(-) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 6b76a65508..10c59457d6 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -13,7 +13,12 @@ import ( var burstFrames []int -const burstHitmark = 100 +const ( + burstHitmark = 100 + burstKey = "navia-artillery" + burstDuration = 720 + targetRadius = 10 +) func init() { burstFrames = frames.InitAbilSlice(114) @@ -40,6 +45,20 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { burstHitmark, ) + c.QueueCharTask( + func() { + c.AddStatus(burstKey, burstDuration, false) + c.naviaburst = true + }, + burstHitmark, + ) + c.QueueCharTask( + func() { + c.naviaburst = false + }, + burstHitmark+burstDuration, + ) + c.ConsumeEnergy(5) c.SetCD(action.ActionBurst, 15*60) @@ -48,12 +67,22 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { ai.Durability = 25 ai.Mult = burst[1][c.TalentLvlBurst()] - //TODO: Random Targetting - for i := 45; i <= 12*60; i = i + 45 { - c.Core.QueueAttack( + snap := c.Snapshot(&ai) + c.artillerySnapshot = combat.AttackEvent{ + Info: ai, + Snapshot: snap, + SourceFrame: c.Core.F, + } + + for i := 45; i <= burstDuration; i = i + 45 { + + c.Core.QueueAttackWithSnap( ai, - combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), nil, 3), - burstHitmark, + c.artillerySnapshot.Snapshot, + combat.NewCircleHitOnTarget( + c.location(targetRadius), + nil, + 3), burstHitmark+i, c.BurstCB(), ) @@ -87,3 +116,28 @@ func (c *char) BurstCB() combat.AttackCBFunc { } } + +// random location +func (c *char) location(r float64) geometry.Point { + enemy := c.Core.Combat.RandomEnemyWithinArea( + combat.NewCircleHitOnTarget(c.Core.Combat.Player().Pos(), nil, 10), + nil, + ) + var pos geometry.Point + if enemy != nil { + pos = enemy.Pos() + } else { + x := c.Core.Rand.Float64()*r*2 - r + y := c.Core.Rand.Float64()*r*2 - r + for x*x+y*y > r*r { + x = c.Core.Rand.Float64()*r*2 - r + y = c.Core.Rand.Float64()*r*2 - r + + } + pos = geometry.Point{ + X: x, + Y: y, + } + } + return pos +} diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 631b8b347f..1f928e0c29 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -28,42 +28,49 @@ func (c *char) c1(shrapnel int) { // from As the Sunlit Sky's Singing Salute will strike near the location of the hit. // Up to one instance of Fire Support can be triggered each time Ceremonial Crystalshot is used, // and DMG dealt by Fire Support in this way is considered Elemental Burst DMG. -func (c *char) c2() combat.AttackCBFunc { +func (c *char) c2(a combat.AttackCB) { if c.Base.Cons < 2 { - return nil + return } - return func(a combat.AttackCB) { - ai := combat.AttackInfo{ - ActorIndex: c.Index, - Abil: "The President's Pursuit of Victory", - AttackTag: attacks.AttackTagElementalBurst, - ICDTag: attacks.ICDTagElementalBurst, - ICDGroup: attacks.ICDGroupDefault, - StrikeType: attacks.StrikeTypeBlunt, - Element: attributes.Geo, - Durability: 25, - Mult: burst[1][c.TalentLvlSkill()], - } - //currently not snapshotting - c.Core.QueueAttack(ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), nil, 3), 0, 0) + if !c.naviaburst { + return } + + // Function doesn't check for enemy type or limit as assumes that the CB will check or it. + + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "The President's Pursuit of Victory", + AttackTag: attacks.AttackTagElementalBurst, + ICDTag: attacks.ICDTagElementalBurst, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 25, + Mult: burst[1][c.TalentLvlSkill()], + } + c.Core.QueueAttackWithSnap( + ai, + c.artillerySnapshot.Snapshot, + combat.NewCircleHitOnTarget(a.Target.Pos(), nil, 3), + 0, + nil, + ) } // When As the Sunlit Sky's Singing Salute hits an opponent, // that opponent's Geo RES will be decreased by 20% for 8s. -func (c *char) c4() combat.AttackCBFunc { +func (c *char) c4(a combat.AttackCB) { if c.Base.Cons < 4 { - return nil + return } - return func(a combat.AttackCB) { - e := a.Target.(*enemy.Enemy) - if e.Type() != targets.TargettableEnemy { - return - } - e.AddResistMod(combat.ResistMod{ - Base: modifier.NewBaseWithHitlag("navia-c4-shred", 8*60), - Ele: attributes.Geo, - Value: -0.2, - }) + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return } + e.AddResistMod(combat.ResistMod{ + Base: modifier.NewBaseWithHitlag("navia-c4-shred", 8*60), + Ele: attributes.Geo, + Value: -0.2, + }) } diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index 8e134a16b0..e068207726 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -4,6 +4,7 @@ import ( tmpl "github.com/genshinsim/gcsim/internal/template/character" "github.com/genshinsim/gcsim/pkg/core" "github.com/genshinsim/gcsim/pkg/core/action" + "github.com/genshinsim/gcsim/pkg/core/combat" "github.com/genshinsim/gcsim/pkg/core/info" "github.com/genshinsim/gcsim/pkg/core/keys" "github.com/genshinsim/gcsim/pkg/core/player/character" @@ -15,7 +16,9 @@ func init() { type char struct { *tmpl.Character - shrapnel int + shrapnel int + artillerySnapshot combat.AttackEvent + naviaburst bool } func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error { @@ -33,7 +36,20 @@ func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) er func (c *char) Init() error { c.a4() - c.ShrapnelGain() c.shrapnel = 0 + c.ShrapnelGain() return nil } + +func (c *char) Condition(fields []string) (any, error) { + switch fields[0] { + case "shrapnel": + return c.shrapnel, nil + case "navia-burst": + return c.naviaburst, nil + case "artillery": + return c.naviaburst, nil + default: + return c.Character.Condition(fields) + } +} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 918b0b232b..8654268c23 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -25,8 +25,10 @@ var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeC const ( skillPressCDStart = 16 skillPressHitmark = 17 + skillHoldHitmark = 12 - skillHoldHitmark = 12 + particleICDKey = "navia-particle-icd" + arkheICDKey = "navia-arkhe-icd" ) func init() { @@ -82,8 +84,6 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { Durability: 25, Mult: skillshotgun[c.TalentLvlSkill()] * shots, } - // When firing, attack with the Surging Blade - c.SurgingBlade(hitmark) excess := math.Max(float64(c.shrapnel-3), 0) m := make([]float64, attributes.EndStatType) @@ -109,7 +109,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 25), hitmark, 5+hitmark, - c.SkillCB(), + c.SkillCB(hitmark), ) // C1 Energy Restoration and CD reduction if c.Base.Cons >= 1 { @@ -143,7 +143,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { }, nil } -func (c *char) SkillCB() combat.AttackCBFunc { +func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { done := false return func(a combat.AttackCB) { e := a.Target.(*enemy.Enemy) @@ -151,14 +151,23 @@ func (c *char) SkillCB() combat.AttackCBFunc { return } - c.c4() + // When firing, attack with the Surging Blade + c.SurgingBlade(hitmark) + + c.c4(a) if done { return } - c.Core.QueueParticle(c.Base.Key.String(), 4, attributes.Geo, c.ParticleDelay) - - c.c2() + c.c2(a) + if !c.StatusIsActive(particleICDKey) { + count := 3.0 + if c.Core.Rand.Float64() < 0.5 { + count = 4 + } + c.Core.QueueParticle(c.Base.Key.String(), count, attributes.Geo, c.ParticleDelay) + c.AddStatus(particleICDKey, 0.2*60, true) + } done = true @@ -180,7 +189,7 @@ func (c *char) ShrapnelGain() { } func (c *char) SurgingBlade(hitmark int) { - if c.StatusIsActive("surging-blade-cd") { + if c.StatusIsActive(arkheICDKey) { return } ai := combat.AttackInfo{ @@ -203,7 +212,7 @@ func (c *char) SurgingBlade(hitmark int) { ) c.QueueCharTask( func() { - c.AddStatus("surging-blade-cd", 7*60, true) + c.AddStatus(arkheICDKey, 7*60, true) }, hitmark, ) From 67de85254af12f206bb8a68857def219883f7028 Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 17 Nov 2023 16:04:42 +1100 Subject: [PATCH 05/36] Added: - Basic Frames Fixed: - Skill AoE (still rudimentary) --- internal/characters/navia/attack.go | 10 +-- internal/characters/navia/cons.go | 5 +- internal/characters/navia/navia.go | 1 + internal/characters/navia/skill.go | 101 +++++++++++++++++----------- 4 files changed, 71 insertions(+), 46 deletions(-) diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index d834a58571..0f6066b406 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -12,7 +12,7 @@ import ( var ( attackFrames [][]int - attackHitmarks = [][]int{{30}, {19}, {25, 42, 59}, {17}} + attackHitmarks = [][]int{{29}, {40}, {59, 65, 71}, {82}} attackHitlagHaltFrame = [][]float64{{0.09}, {.12}, {0, 0, 0}, {.09}} attackDefHalt = [][]bool{{true}, {true}, {false, false, false}, {true}} attackHitboxes = [][]float64{{2}, {2}, {2}, {2}} @@ -25,10 +25,10 @@ const normalHitNum = 4 func init() { attackFrames = make([][]int, normalHitNum) - attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 38) - attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 46) - attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 31) - attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 107) + attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 32) + attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 42) + attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 43) + attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 82) } func (c *char) Attack(p map[string]int) (action.Info, error) { diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 1f928e0c29..aeb6cb9386 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -35,7 +35,10 @@ func (c *char) c2(a combat.AttackCB) { if !c.naviaburst { return } - + if !c.c2ready { + return + } + c.c2ready = false // Function doesn't check for enemy type or limit as assumes that the CB will check or it. ai := combat.AttackInfo{ diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index e068207726..ed6efc8d7f 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -19,6 +19,7 @@ type char struct { shrapnel int artillerySnapshot combat.AttackEvent naviaburst bool + c2ready bool } func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error { diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 8654268c23..958de5cece 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -21,31 +21,33 @@ var skillPressFrames []int var skillHoldFrames []int var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, event.OnCrystallizePyro} +var skillMultiplier = []float64{0, 1, 1.05, 1.1, 1.2, 1.36, 1.4, 1.6, 1.666, 1.9, 2} const ( - skillPressCDStart = 16 - skillPressHitmark = 17 - skillHoldHitmark = 12 - - particleICDKey = "navia-particle-icd" - arkheICDKey = "navia-arkhe-icd" + skillPressCDStart = 30 + skillPressHitmark = 30 + skillHoldHitmark = 48 + arkeDelay = 12 + particleICDKey = "navia-particle-icd" + arkheICDKey = "navia-arkhe-icd" ) func init() { - skillPressFrames = frames.InitAbilSlice(39) // E -> N1/Q - skillPressFrames[action.ActionDash] = 34 - skillPressFrames[action.ActionJump] = 35 - skillPressFrames[action.ActionSwap] = 37 + skillPressFrames = frames.InitAbilSlice(38) // E -> N1/Q + skillPressFrames[action.ActionDash] = 38 + skillPressFrames[action.ActionJump] = 38 + skillPressFrames[action.ActionSwap] = 38 // skill (hold) -> x - skillHoldFrames = frames.InitAbilSlice(46) // E -> Swap - skillHoldFrames[action.ActionAttack] = 38 - skillHoldFrames[action.ActionBurst] = 37 - skillHoldFrames[action.ActionDash] = 30 - skillHoldFrames[action.ActionJump] = 30 + skillHoldFrames = frames.InitAbilSlice(51) // E -> Swap + skillHoldFrames[action.ActionAttack] = 51 + skillHoldFrames[action.ActionBurst] = 51 + skillHoldFrames[action.ActionDash] = 51 + skillHoldFrames[action.ActionJump] = 51 } func (c *char) Skill(p map[string]int) (action.Info, error) { + c.c2ready = true hold := p["hold"] hitmark := 0 if hold > 0 { @@ -62,17 +64,8 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) - shots := 1.0 - switch c.shrapnel { - case 0: - shots = 1.2 - case 1: - shots = 1.4 - case 2: - shots = 1.66 - default: - shots = 2.0 - } + shots := 5 + int(math.Max(float64(c.shrapnel-3), 0))*2 + ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "Rosula Shardshot", @@ -82,7 +75,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { StrikeType: attacks.StrikeTypeBlunt, Element: attributes.Geo, Durability: 25, - Mult: skillshotgun[c.TalentLvlSkill()] * shots, + Mult: skillshotgun[c.TalentLvlSkill()], } excess := math.Max(float64(c.shrapnel-3), 0) @@ -104,13 +97,27 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { }, }) - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 25), - hitmark, - 5+hitmark, - c.SkillCB(hitmark), - ) + for _, t := range c.Core.Combat.EnemiesWithinArea( + combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 25, 15), + nil, + ) { + hits := 0 + for i := 0; i < shots; i++ { + if ok, _ := t.AttackWillLand(combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), + geometry.Point{Y: 0}, + 25, 15)); ok { + hits++ + } + } + ai.Mult = skillshotgun[c.TalentLvlSkill()] * skillMultiplier[hits] + c.Core.QueueAttack( + ai, + combat.NewSingleTargetHit(t.Key()), + hitmark, + hitmark+5, + c.SkillCB(hitmark), + ) + } // C1 Energy Restoration and CD reduction if c.Base.Cons >= 1 { c.QueueCharTask(func() { @@ -134,13 +141,22 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.a1, hitmark+30, ) - + c.c2ready = false + if hold > 1 { + return action.Info{ + Frames: frames.NewAbilFunc(skillHoldFrames), + AnimationLength: skillHoldFrames[action.ActionDash] + hold, + CanQueueAfter: skillHoldFrames[action.ActionDash] + hold, + State: action.SkillState, + }, nil + } return action.Info{ - Frames: frames.NewAbilFunc(skillHoldFrames), - AnimationLength: skillHoldFrames[action.ActionDash], - CanQueueAfter: skillHoldFrames[action.ActionDash], + Frames: frames.NewAbilFunc(skillPressFrames), + AnimationLength: skillPressFrames[action.ActionDash], + CanQueueAfter: skillPressFrames[action.ActionDash], State: action.SkillState, }, nil + } func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { @@ -160,6 +176,7 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { return } c.c2(a) + c.c2ready = false if !c.StatusIsActive(particleICDKey) { count := 3.0 if c.Core.Rand.Float64() < 0.5 { @@ -174,6 +191,10 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { } } +// ShrapnelGain adds Shrapnel Stacks when crystallise occurs. Stacks should last 300s but this is way to long to bother +// When a character in the party obtains an Elemental Shard created from the Crystallize reaction, +// Navia will gain 1 Crystal Shrapnel charge. Navia can hold up to 6 charges of Crystal Shrapnel at once. +// Each time Crystal Shrapnel gain is triggered, the duration of the Shards you have already will be reset. func (c *char) ShrapnelGain() { for _, crystal := range crystallise { @@ -206,8 +227,8 @@ func (c *char) SurgingBlade(hitmark int) { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - 10+hitmark, - 10+hitmark, + arkeDelay+hitmark, + arkeDelay+hitmark, nil, ) c.QueueCharTask( From df7dfc9eb1af067216e43ab169e81ecad0ecfe6d Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 08:55:36 +1100 Subject: [PATCH 06/36] Fixed: - Skill Array length --- internal/characters/navia/skill.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 958de5cece..cde0f4a6bc 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -21,7 +21,7 @@ var skillPressFrames []int var skillHoldFrames []int var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, event.OnCrystallizePyro} -var skillMultiplier = []float64{0, 1, 1.05, 1.1, 1.2, 1.36, 1.4, 1.6, 1.666, 1.9, 2} +var skillMultiplier = []float64{0, 1, 1.05, 1.1, 1.15, 1.2, 1.36, 1.4, 1.6, 1.666, 1.9, 2} const ( skillPressCDStart = 30 From fa94a75393f107dd438eff76bcfe2db968291aab Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 09:25:56 +1100 Subject: [PATCH 07/36] Fixed: - Character Curve --- pkg/core/curves/charactercurves.go | 50 +++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/pkg/core/curves/charactercurves.go b/pkg/core/curves/charactercurves.go index 1c63b659df..f2693a8dfe 100644 --- a/pkg/core/curves/charactercurves.go +++ b/pkg/core/curves/charactercurves.go @@ -2876,9 +2876,9 @@ var CharBaseMap = map[keys.Char]CharBase{ HPCurve: GROW_CURVE_HP_S5, AtkCurve: GROW_CURVE_ATTACK_S5, DefCurve: GROW_CURVE_HP_S5, - BaseHP: 985, - BaseAtk: 27, - BaseDef: 62, + BaseHP: 984.77954, + BaseAtk: 27.37140, + BaseDef: 61.74456, Specialized: attributes.CD, PromotionBonus: []PromoData{ { @@ -2890,45 +2890,45 @@ var CharBaseMap = map[keys.Char]CharBase{ }, { MaxLevel: 40, - HP: 3399 - 2555, - Atk: 94 - 71, - Def: 160 - 134, + HP: 844.35944, + Atk: 23.46679, + Def: 52.9416, Special: 0, }, { MaxLevel: 50, - HP: 5686 - 5086, - Atk: 158 - 141, - Def: 356 - 319, + HP: 1444.29907, + Atk: 40.14056, + Def: 90.558, Special: 0.096, }, { MaxLevel: 60, - HP: 7341 - 6542, - Atk: 204 - 182, - Def: 460 - 410, - Special: 0.692 - 0.596, + HP: 2244.21851, + Atk: 62.37225, + Def: 140.7132, + Special: 0.192, }, { MaxLevel: 70, - HP: 8806 - 8206, - Atk: 245 - 228, - Def: 552 - 515, - Special: 0, + HP: 2844.15796, + Atk: 79.04601, + Def: 178.32961, + Special: 0.192, }, { MaxLevel: 80, - HP: 10278 - 9679, - Atk: 286 - 269, - Def: 644 - 607, - Special: 0.096, + HP: 3444.09766, + Atk: 95.71979, + Def: 215.946, + Special: 0.288, }, { MaxLevel: 90, - HP: 11761 - 11161, - Atk: 17, - Def: 37, - Special: 0.096, + HP: 4044.03735, + Atk: 112.39355, + Def: 253.56239, + Special: 0.384, }, }, }, From f7050a2a8c9160a4e1bef6c1df19020535bdf7be Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 10:02:37 +1100 Subject: [PATCH 08/36] Added: - Nighttime Whispers of the Echoing Woods TODO: - Fix how the Crystallise Bonus works --- .../artifacts/nighttimewhispers/config.yaml | 2 + .../artifacts/nighttimewhispers/whispers.go | 92 ++++++++ pkg/core/keys/sets.go | 2 + pkg/shortcut/artifacts.go | 215 +++++++++--------- pkg/simulation/imports.go | 1 + 5 files changed, 207 insertions(+), 105 deletions(-) create mode 100644 internal/artifacts/nighttimewhispers/config.yaml create mode 100644 internal/artifacts/nighttimewhispers/whispers.go diff --git a/internal/artifacts/nighttimewhispers/config.yaml b/internal/artifacts/nighttimewhispers/config.yaml new file mode 100644 index 0000000000..109acf9a92 --- /dev/null +++ b/internal/artifacts/nighttimewhispers/config.yaml @@ -0,0 +1,2 @@ +key: "nighttimewhispers" +text_map_id: 15034 \ No newline at end of file diff --git a/internal/artifacts/nighttimewhispers/whispers.go b/internal/artifacts/nighttimewhispers/whispers.go new file mode 100644 index 0000000000..b37c7d7951 --- /dev/null +++ b/internal/artifacts/nighttimewhispers/whispers.go @@ -0,0 +1,92 @@ +package nighttimewhispers + +import ( + "fmt" + "github.com/genshinsim/gcsim/pkg/core" + "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/event" + "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/player/shield" + "github.com/genshinsim/gcsim/pkg/modifier" +) + +const ( + buffKey = "nighttime-whispers-buff" + shieldMulti = 2.5 + buffVal = 0.16 +) + +func init() { + core.RegisterSetFunc(keys.NighttimeWhispersInTheEchoingWoods, NewSet) +} + +type Set struct { + char *character.CharWrapper + count int + Index int +} + +func (s *Set) SetIndex(idx int) { s.Index = idx } + +func (s *Set) Init() error { + return nil +} + +func NewSet(c *core.Core, char *character.CharWrapper, count int, param map[string]int) (info.Set, error) { + s := Set{ + char: char, + count: count, + } + + if count >= 2 { + m := make([]float64, attributes.EndStatType) + m[attributes.ATKP] = 0.18 + char.AddStatMod(character.StatMod{ + Base: modifier.NewBase("nighttime-whispers-2pc", -1), + AffectedStat: attributes.ATKP, + Amount: func() ([]float64, bool) { + return m, true + }, + }) + } + + if count >= 4 { + f := func(args ...interface{}) bool { + atk := args[1].(*combat.AttackEvent) + if atk.Info.ActorIndex != char.Index { + return false + } + bonus := 1.0 + // TODO: Need to make the Crystallise bonus not tied to the base buff + m := make([]float64, attributes.EndStatType) + char.AddStatMod(character.StatMod{ + Base: modifier.NewBaseWithHitlag(buffKey, 60*10), + AffectedStat: attributes.DmgP, + Amount: func() ([]float64, bool) { + if atk.Info.AttackTag != attacks.AttackTagElementalArt { + return nil, false + } + if char.Index == c.Player.Active() && c.Player.Shields.PlayerIsShielded() { + s := c.Player.Shields.List() + for _, t := range s { + if t.Type() == shield.Crystallize { + bonus = shieldMulti + break + } + } + } + m[attributes.DmgP] = bonus * buffVal + return m, true + }, + }) + return false + } + c.Events.Subscribe(event.OnSkill, f, fmt.Sprintf("nighttime-whispers-4pc-%v", char.Base.Key.String())) + } + + return &s, nil +} diff --git a/pkg/core/keys/sets.go b/pkg/core/keys/sets.go index 86d45ef68b..9cf01a33ce 100644 --- a/pkg/core/keys/sets.go +++ b/pkg/core/keys/sets.go @@ -59,6 +59,7 @@ var setNames = []string{ "maidenbeloved", "marechausseehunter", "martialartist", + "nighttimewhispers", "noblesseoblige", "nymphsdream", "oceanhuedclam", @@ -110,6 +111,7 @@ const ( MaidenBeloved MarechausseeHunter MartialArtist + NighttimeWhispersInTheEchoingWoods NoblesseOblige NymphsDream OceanHuedClam diff --git a/pkg/shortcut/artifacts.go b/pkg/shortcut/artifacts.go index 44c6084049..25afea6f1e 100644 --- a/pkg/shortcut/artifacts.go +++ b/pkg/shortcut/artifacts.go @@ -3,109 +3,114 @@ package shortcut import "github.com/genshinsim/gcsim/pkg/core/keys" var SetNameToKey = map[string]keys.Set{ - "adventurer": keys.Adventurer, - "archaicpetra": keys.ArchaicPetra, - "ap": keys.ArchaicPetra, - "berserker": keys.Berserker, - "blizzardstrayer": keys.BlizzardStrayer, - "blizzard": keys.BlizzardStrayer, - "bs": keys.BlizzardStrayer, - "bloodstainedchivalry": keys.BloodstainedChivalry, - "bloodstained": keys.BloodstainedChivalry, - "bsc": keys.BloodstainedChivalry, - "braveheart": keys.BraveHeart, - "brave": keys.BraveHeart, - "crimsonwitchofflames": keys.CrimsonWitchOfFlames, - "cwof": keys.CrimsonWitchOfFlames, - "cw": keys.CrimsonWitchOfFlames, - "crimson": keys.CrimsonWitchOfFlames, - "witch": keys.CrimsonWitchOfFlames, - "deepwoodmemories": keys.DeepwoodMemories, - "deepwood": keys.DeepwoodMemories, - "defenderswill": keys.DefendersWill, - "defenders": keys.DefendersWill, - "defender": keys.DefendersWill, - "desertpavilionchronicle": keys.DesertPavilionChronicle, - "desertpavilion": keys.DesertPavilionChronicle, - "dpc": keys.DesertPavilionChronicle, - "vourukashasglow": keys.VourukashasGlow, - "echoesofanoffering": keys.EchoesOfAnOffering, - "echoes": keys.EchoesOfAnOffering, - "emblemofseveredfate": keys.EmblemOfSeveredFate, - "emblem": keys.EmblemOfSeveredFate, - "eosf": keys.EmblemOfSeveredFate, - "esf": keys.EmblemOfSeveredFate, - "flowerofparadiselost": keys.FlowerOfParadiseLost, - "paradiselost": keys.FlowerOfParadiseLost, - "fopl": keys.FlowerOfParadiseLost, - "gambler": keys.Gambler, - "gladiatorsfinale": keys.GladiatorsFinale, - "glad": keys.GladiatorsFinale, - "gladiators": keys.GladiatorsFinale, - "gildeddreams": keys.GildedDreams, - "gilded": keys.GildedDreams, - "gd": keys.GildedDreams, - "goldentroupe": keys.GoldenTroupe, - "gt": keys.GoldenTroupe, - "heartofdepth": keys.HeartOfDepth, - "hod": keys.HeartOfDepth, - "huskofopulentdreams": keys.HuskOfOpulentDreams, - "husk": keys.HuskOfOpulentDreams, - "hood": keys.HuskOfOpulentDreams, - "instructor": keys.Instructor, - "ins": keys.Instructor, - "lavawalker": keys.Lavawalker, - "lw": keys.Lavawalker, - "luckydog": keys.LuckyDog, - "maidenbeloved": keys.MaidenBeloved, - "maiden": keys.MaidenBeloved, - "mb": keys.MaidenBeloved, - "marechausseehunter": keys.MarechausseeHunter, - "mh": keys.MarechausseeHunter, - "martialartist": keys.MartialArtist, - "noblesseoblige": keys.NoblesseOblige, - "noblesse": keys.NoblesseOblige, - "no": keys.NoblesseOblige, - "nymphsdream": keys.NymphsDream, - "oceanhuedclam": keys.OceanHuedClam, - "ohc": keys.OceanHuedClam, - "clam": keys.OceanHuedClam, - "paleflame": keys.PaleFlame, - "pf": keys.PaleFlame, - "prayersfordestiny": keys.PrayersForDestiny, - "prayersforillumination": keys.PrayersForIllumination, - "prayersforwisdom": keys.PrayersForWisdom, - "prayerstospringtime": keys.PrayersToSpringtime, - "resolutionofsojourner": keys.ResolutionOfSojourner, - "sojourner": keys.ResolutionOfSojourner, - "retracingbolide": keys.RetracingBolide, - "bolide": keys.RetracingBolide, - "scholar": keys.Scholar, - "shimenawasreminiscence": keys.ShimenawasReminiscence, - "shimenawa": keys.ShimenawasReminiscence, - "shime": keys.ShimenawasReminiscence, - "shim": keys.ShimenawasReminiscence, - "sr": keys.ShimenawasReminiscence, - "tenacityofthemillelith": keys.TenacityOfTheMillelith, - "tom": keys.TenacityOfTheMillelith, - "totm": keys.TenacityOfTheMillelith, - "tenacity": keys.TenacityOfTheMillelith, - "millelith": keys.TenacityOfTheMillelith, - "theexile": keys.TheExile, - "exile": keys.TheExile, - "thunderingfury": keys.ThunderingFury, - "tf": keys.ThunderingFury, - "thundersoother": keys.Thundersoother, - "ts": keys.Thundersoother, - "tinymiracle": keys.TinyMiracle, - "travelingdoctor": keys.TravelingDoctor, - "vermillionhereafter": keys.VermillionHereafter, - "vermillion": keys.VermillionHereafter, - "vh": keys.VermillionHereafter, - "viridescentvenerer": keys.ViridescentVenerer, - "viridescent": keys.ViridescentVenerer, - "vv": keys.ViridescentVenerer, - "wandererstroupe": keys.WanderersTroupe, - "wanderers": keys.WanderersTroupe, - "wt": keys.WanderersTroupe, + "adventurer": keys.Adventurer, + "archaicpetra": keys.ArchaicPetra, + "ap": keys.ArchaicPetra, + "berserker": keys.Berserker, + "blizzardstrayer": keys.BlizzardStrayer, + "blizzard": keys.BlizzardStrayer, + "bs": keys.BlizzardStrayer, + "bloodstainedchivalry": keys.BloodstainedChivalry, + "bloodstained": keys.BloodstainedChivalry, + "bsc": keys.BloodstainedChivalry, + "braveheart": keys.BraveHeart, + "brave": keys.BraveHeart, + "crimsonwitchofflames": keys.CrimsonWitchOfFlames, + "cwof": keys.CrimsonWitchOfFlames, + "cw": keys.CrimsonWitchOfFlames, + "crimson": keys.CrimsonWitchOfFlames, + "witch": keys.CrimsonWitchOfFlames, + "deepwoodmemories": keys.DeepwoodMemories, + "deepwood": keys.DeepwoodMemories, + "defenderswill": keys.DefendersWill, + "defenders": keys.DefendersWill, + "defender": keys.DefendersWill, + "desertpavilionchronicle": keys.DesertPavilionChronicle, + "desertpavilion": keys.DesertPavilionChronicle, + "dpc": keys.DesertPavilionChronicle, + "vourukashasglow": keys.VourukashasGlow, + "echoesofanoffering": keys.EchoesOfAnOffering, + "echoes": keys.EchoesOfAnOffering, + "emblemofseveredfate": keys.EmblemOfSeveredFate, + "emblem": keys.EmblemOfSeveredFate, + "eosf": keys.EmblemOfSeveredFate, + "esf": keys.EmblemOfSeveredFate, + "flowerofparadiselost": keys.FlowerOfParadiseLost, + "paradiselost": keys.FlowerOfParadiseLost, + "fopl": keys.FlowerOfParadiseLost, + "gambler": keys.Gambler, + "gladiatorsfinale": keys.GladiatorsFinale, + "glad": keys.GladiatorsFinale, + "gladiators": keys.GladiatorsFinale, + "gildeddreams": keys.GildedDreams, + "gilded": keys.GildedDreams, + "gd": keys.GildedDreams, + "goldentroupe": keys.GoldenTroupe, + "gt": keys.GoldenTroupe, + "heartofdepth": keys.HeartOfDepth, + "hod": keys.HeartOfDepth, + "huskofopulentdreams": keys.HuskOfOpulentDreams, + "husk": keys.HuskOfOpulentDreams, + "hood": keys.HuskOfOpulentDreams, + "instructor": keys.Instructor, + "ins": keys.Instructor, + "lavawalker": keys.Lavawalker, + "lw": keys.Lavawalker, + "luckydog": keys.LuckyDog, + "maidenbeloved": keys.MaidenBeloved, + "maiden": keys.MaidenBeloved, + "mb": keys.MaidenBeloved, + "marechausseehunter": keys.MarechausseeHunter, + "mh": keys.MarechausseeHunter, + "martialartist": keys.MartialArtist, + "nighttimewhispers": keys.NighttimeWhispersInTheEchoingWoods, + "nighttimewhispersintheechoingwoods": keys.NighttimeWhispersInTheEchoingWoods, + "echoingwoods": keys.NighttimeWhispersInTheEchoingWoods, + "nwew": keys.NighttimeWhispersInTheEchoingWoods, + "nwitew": keys.NighttimeWhispersInTheEchoingWoods, + "noblesseoblige": keys.NoblesseOblige, + "noblesse": keys.NoblesseOblige, + "no": keys.NoblesseOblige, + "nymphsdream": keys.NymphsDream, + "oceanhuedclam": keys.OceanHuedClam, + "ohc": keys.OceanHuedClam, + "clam": keys.OceanHuedClam, + "paleflame": keys.PaleFlame, + "pf": keys.PaleFlame, + "prayersfordestiny": keys.PrayersForDestiny, + "prayersforillumination": keys.PrayersForIllumination, + "prayersforwisdom": keys.PrayersForWisdom, + "prayerstospringtime": keys.PrayersToSpringtime, + "resolutionofsojourner": keys.ResolutionOfSojourner, + "sojourner": keys.ResolutionOfSojourner, + "retracingbolide": keys.RetracingBolide, + "bolide": keys.RetracingBolide, + "scholar": keys.Scholar, + "shimenawasreminiscence": keys.ShimenawasReminiscence, + "shimenawa": keys.ShimenawasReminiscence, + "shime": keys.ShimenawasReminiscence, + "shim": keys.ShimenawasReminiscence, + "sr": keys.ShimenawasReminiscence, + "tenacityofthemillelith": keys.TenacityOfTheMillelith, + "tom": keys.TenacityOfTheMillelith, + "totm": keys.TenacityOfTheMillelith, + "tenacity": keys.TenacityOfTheMillelith, + "millelith": keys.TenacityOfTheMillelith, + "theexile": keys.TheExile, + "exile": keys.TheExile, + "thunderingfury": keys.ThunderingFury, + "tf": keys.ThunderingFury, + "thundersoother": keys.Thundersoother, + "ts": keys.Thundersoother, + "tinymiracle": keys.TinyMiracle, + "travelingdoctor": keys.TravelingDoctor, + "vermillionhereafter": keys.VermillionHereafter, + "vermillion": keys.VermillionHereafter, + "vh": keys.VermillionHereafter, + "viridescentvenerer": keys.ViridescentVenerer, + "viridescent": keys.ViridescentVenerer, + "vv": keys.ViridescentVenerer, + "wandererstroupe": keys.WanderersTroupe, + "wanderers": keys.WanderersTroupe, + "wt": keys.WanderersTroupe, } diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index 17c40aa2ba..cf36eea6b3 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -38,6 +38,7 @@ import ( _ "github.com/genshinsim/gcsim/internal/artifacts/maiden" _ "github.com/genshinsim/gcsim/internal/artifacts/marechausseehunter" _ "github.com/genshinsim/gcsim/internal/artifacts/martialartist" + _ "github.com/genshinsim/gcsim/internal/artifacts/nighttimewhispers" _ "github.com/genshinsim/gcsim/internal/artifacts/noblesse" _ "github.com/genshinsim/gcsim/internal/artifacts/nymphsdream" _ "github.com/genshinsim/gcsim/internal/artifacts/oceanhuedclam" From 8b5fee0672904fd77adbbab3fb883d8da748b07b Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 10:14:13 +1100 Subject: [PATCH 09/36] Fixed: - Nighttime Whispers array error and converted to AttackMod instead of Stat mod --- internal/artifacts/nighttimewhispers/whispers.go | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/internal/artifacts/nighttimewhispers/whispers.go b/internal/artifacts/nighttimewhispers/whispers.go index b37c7d7951..516e73833c 100644 --- a/internal/artifacts/nighttimewhispers/whispers.go +++ b/internal/artifacts/nighttimewhispers/whispers.go @@ -56,17 +56,15 @@ func NewSet(c *core.Core, char *character.CharWrapper, count int, param map[stri if count >= 4 { f := func(args ...interface{}) bool { - atk := args[1].(*combat.AttackEvent) - if atk.Info.ActorIndex != char.Index { + if c.Player.Active() != char.Index { return false } bonus := 1.0 // TODO: Need to make the Crystallise bonus not tied to the base buff m := make([]float64, attributes.EndStatType) - char.AddStatMod(character.StatMod{ - Base: modifier.NewBaseWithHitlag(buffKey, 60*10), - AffectedStat: attributes.DmgP, - Amount: func() ([]float64, bool) { + char.AddAttackMod(character.AttackMod{ + Base: modifier.NewBaseWithHitlag(buffKey, 60*10), + Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { if atk.Info.AttackTag != attacks.AttackTagElementalArt { return nil, false } From e7b85179021be38753f2495b5d8e508c5207228f Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 12:41:39 +1100 Subject: [PATCH 10/36] Fixed: - Burst and C2 Snapshotting - C2 only working with Burst up - Nighttime Whispers granting Damage% instead of Geo% - Renamed nighttimewhispers.go --- .../{whispers.go => nighttimewhispers.go} | 13 ++++--------- internal/characters/navia/burst.go | 12 ++++-------- internal/characters/navia/cons.go | 9 +++------ 3 files changed, 11 insertions(+), 23 deletions(-) rename internal/artifacts/nighttimewhispers/{whispers.go => nighttimewhispers.go} (82%) diff --git a/internal/artifacts/nighttimewhispers/whispers.go b/internal/artifacts/nighttimewhispers/nighttimewhispers.go similarity index 82% rename from internal/artifacts/nighttimewhispers/whispers.go rename to internal/artifacts/nighttimewhispers/nighttimewhispers.go index 516e73833c..04b9c340ae 100644 --- a/internal/artifacts/nighttimewhispers/whispers.go +++ b/internal/artifacts/nighttimewhispers/nighttimewhispers.go @@ -3,9 +3,7 @@ package nighttimewhispers import ( "fmt" "github.com/genshinsim/gcsim/pkg/core" - "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/event" "github.com/genshinsim/gcsim/pkg/core/info" "github.com/genshinsim/gcsim/pkg/core/keys" @@ -36,7 +34,7 @@ func (s *Set) Init() error { return nil } -func NewSet(c *core.Core, char *character.CharWrapper, count int, param map[string]int) (info.Set, error) { +func NewSet(c *core.Core, char *character.CharWrapper, count int, _ map[string]int) (info.Set, error) { s := Set{ char: char, count: count, @@ -62,12 +60,9 @@ func NewSet(c *core.Core, char *character.CharWrapper, count int, param map[stri bonus := 1.0 // TODO: Need to make the Crystallise bonus not tied to the base buff m := make([]float64, attributes.EndStatType) - char.AddAttackMod(character.AttackMod{ + char.AddStatMod(character.StatMod{ Base: modifier.NewBaseWithHitlag(buffKey, 60*10), - Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { - if atk.Info.AttackTag != attacks.AttackTagElementalArt { - return nil, false - } + Amount: func() ([]float64, bool) { if char.Index == c.Player.Active() && c.Player.Shields.PlayerIsShielded() { s := c.Player.Shields.List() for _, t := range s { @@ -77,7 +72,7 @@ func NewSet(c *core.Core, char *character.CharWrapper, count int, param map[stri } } } - m[attributes.DmgP] = bonus * buffVal + m[attributes.GeoP] = bonus * buffVal return m, true }, }) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 10c59457d6..68a2805eac 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -25,7 +25,7 @@ func init() { burstFrames[action.ActionSkill] = 114 } -func (c *char) Burst(p map[string]int) (action.Info, error) { +func (c *char) Burst(_ map[string]int) (action.Info, error) { ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "As the Sunlit Sky's Singing Salute", @@ -75,14 +75,10 @@ func (c *char) Burst(p map[string]int) (action.Info, error) { } for i := 45; i <= burstDuration; i = i + 45 { - - c.Core.QueueAttackWithSnap( + c.Core.QueueAttack( ai, - c.artillerySnapshot.Snapshot, - combat.NewCircleHitOnTarget( - c.location(targetRadius), - nil, - 3), + combat.NewCircleHitOnTarget(c.location(targetRadius), nil, 3), + burstHitmark+i, burstHitmark+i, c.BurstCB(), ) diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index aeb6cb9386..dbaba5f080 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -32,9 +32,6 @@ func (c *char) c2(a combat.AttackCB) { if c.Base.Cons < 2 { return } - if !c.naviaburst { - return - } if !c.c2ready { return } @@ -52,12 +49,12 @@ func (c *char) c2(a combat.AttackCB) { Durability: 25, Mult: burst[1][c.TalentLvlSkill()], } - c.Core.QueueAttackWithSnap( + c.Core.QueueAttack( ai, - c.artillerySnapshot.Snapshot, combat.NewCircleHitOnTarget(a.Target.Pos(), nil, 3), 0, - nil, + 0, + c.BurstCB(), ) } From bb8decf0b9b69b4835cb2cc4f749c06a6a8d4f30 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 15:29:55 +1100 Subject: [PATCH 11/36] Fixed: - Nighttime Whispers' 2nd Buff - Added comments --- .../nighttimewhispers/nighttimewhispers.go | 59 ++++++++++++++----- internal/characters/navia/burst.go | 21 +++---- internal/characters/navia/cons.go | 1 - internal/characters/navia/skill.go | 24 +++++--- 4 files changed, 68 insertions(+), 37 deletions(-) diff --git a/internal/artifacts/nighttimewhispers/nighttimewhispers.go b/internal/artifacts/nighttimewhispers/nighttimewhispers.go index 04b9c340ae..4810f34ae6 100644 --- a/internal/artifacts/nighttimewhispers/nighttimewhispers.go +++ b/internal/artifacts/nighttimewhispers/nighttimewhispers.go @@ -13,9 +13,9 @@ import ( ) const ( - buffKey = "nighttime-whispers-buff" - shieldMulti = 2.5 - buffVal = 0.16 + buffKey = "nighttime-whispers-buff" + buffVal = 0.16 + secondBuffKey = "nighttime-whispers-second-buff" ) func init() { @@ -40,6 +40,7 @@ func NewSet(c *core.Core, char *character.CharWrapper, count int, _ map[string]i count: count, } + // 2pc - ATK +18%. if count >= 2 { m := make([]float64, attributes.EndStatType) m[attributes.ATKP] = 0.18 @@ -52,32 +53,60 @@ func NewSet(c *core.Core, char *character.CharWrapper, count int, _ map[string]i }) } + // 4pc - After using an Elemental Skill, gain a 16% Geo DMG Bonus for 10s if count >= 4 { f := func(args ...interface{}) bool { if c.Player.Active() != char.Index { return false } - bonus := 1.0 - // TODO: Need to make the Crystallise bonus not tied to the base buff m := make([]float64, attributes.EndStatType) char.AddStatMod(character.StatMod{ Base: modifier.NewBaseWithHitlag(buffKey, 60*10), Amount: func() ([]float64, bool) { - if char.Index == c.Player.Active() && c.Player.Shields.PlayerIsShielded() { - s := c.Player.Shields.List() - for _, t := range s { - if t.Type() == shield.Crystallize { - bonus = shieldMulti - break - } - } - } - m[attributes.GeoP] = bonus * buffVal + m[attributes.GeoP] = buffVal return m, true }, }) + + // While under a shield granted by the Crystallize reaction, the above + // effect will be increased by 150%, and this additional increase disappears + // 1s after that shield is lost. + for i := 0; i < 11*60; i += 30 { // An extra second to account for possible hitlag extension + char.QueueCharTask( + func() { + // Checks that base buff is active + if !char.StatusIsActive(buffKey) { + if char.StatusIsActive(secondBuffKey) { + char.RemoveTag(secondBuffKey) + } + return + } + + // Checks for a Crystallise Shield. + if char.Index == c.Player.Active() && c.Player.Shields.PlayerIsShielded() { + s := c.Player.Shields.List() + for _, t := range s { + if t.Type() == shield.Crystallize { + n := make([]float64, attributes.EndStatType) + char.AddStatMod(character.StatMod{ + Base: modifier.NewBaseWithHitlag(secondBuffKey, 60), + Amount: func() ([]float64, bool) { + n[attributes.GeoP] = 1.5 * buffVal + return n, true + }, + }) + break + } + } + } + return + }, + i, + ) + } return false } + c.Events.Subscribe(event.OnSkill, f, fmt.Sprintf("nighttime-whispers-4pc-%v", char.Base.Key.String())) } diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 68a2805eac..b507a04b29 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -25,6 +25,10 @@ func init() { burstFrames[action.ActionSkill] = 114 } +// On the orders of the President of the Spina di Rosula, call for a magnificent +// Golden Rose Salute. Unleashes a massive bombardment on opponents in front of her, +// dealing Aoe Geo DMG and providing Fire Support for a duration afterward, periodically +// dealing Geo DMG. func (c *char) Burst(_ map[string]int) (action.Info, error) { ai := combat.AttackInfo{ ActorIndex: c.Index, @@ -92,6 +96,9 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { }, nil } +// When attacks from Golden Rose's Salute hit opponents, Navia will gain 1 charge +// of Crystal Shrapnel. +// This effect can be triggered up to once every 2.4s. func (c *char) BurstCB() combat.AttackCBFunc { return func(a combat.AttackCB) { @@ -113,7 +120,7 @@ func (c *char) BurstCB() combat.AttackCBFunc { } -// random location +// Targets a random enemy if there is an enemy present, if not, it targets a random spot func (c *char) location(r float64) geometry.Point { enemy := c.Core.Combat.RandomEnemyWithinArea( combat.NewCircleHitOnTarget(c.Core.Combat.Player().Pos(), nil, 10), @@ -123,17 +130,7 @@ func (c *char) location(r float64) geometry.Point { if enemy != nil { pos = enemy.Pos() } else { - x := c.Core.Rand.Float64()*r*2 - r - y := c.Core.Rand.Float64()*r*2 - r - for x*x+y*y > r*r { - x = c.Core.Rand.Float64()*r*2 - r - y = c.Core.Rand.Float64()*r*2 - r - - } - pos = geometry.Point{ - X: x, - Y: y, - } + pos = geometry.CalcRandomPointFromCenter(c.Core.Combat.Player().Pos(), 0, r, c.Core.Rand) } return pos } diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index dbaba5f080..0a105a34ac 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -37,7 +37,6 @@ func (c *char) c2(a combat.AttackCB) { } c.c2ready = false // Function doesn't check for enemy type or limit as assumes that the CB will check or it. - ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "The President's Pursuit of Victory", diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index cde0f4a6bc..9b1ebea34d 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -27,7 +27,7 @@ const ( skillPressCDStart = 30 skillPressHitmark = 30 skillHoldHitmark = 48 - arkeDelay = 12 + arkheDelay = 12 particleICDKey = "navia-particle-icd" arkheICDKey = "navia-arkhe-icd" ) @@ -97,10 +97,13 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { }, }) + // Looks for enemies in the path of each bullet + // Initially trims enemies to check by scanning only the hit zone for _, t := range c.Core.Combat.EnemiesWithinArea( combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 25, 15), nil, ) { + // Tallies up the hits hits := 0 for i := 0; i < shots; i++ { if ok, _ := t.AttackWillLand(combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), @@ -109,6 +112,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { hits++ } } + // Applies damage based on the hits ai.Mult = skillshotgun[c.TalentLvlSkill()] * skillMultiplier[hits] c.Core.QueueAttack( ai, @@ -118,12 +122,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.SkillCB(hitmark), ) } - // C1 Energy Restoration and CD reduction - if c.Base.Cons >= 1 { - c.QueueCharTask(func() { - c.c1(c.shrapnel) - }, hitmark) - } + // remove the shrapnel after firing c.QueueCharTask( func() { @@ -170,6 +169,13 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { // When firing, attack with the Surging Blade c.SurgingBlade(hitmark) + // C1 Energy Restoration and CD reduction + if c.Base.Cons >= 1 { + c.QueueCharTask(func() { + c.c1(c.shrapnel) + }, hitmark) + } + c.c4(a) if done { @@ -227,8 +233,8 @@ func (c *char) SurgingBlade(hitmark int) { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - arkeDelay+hitmark, - arkeDelay+hitmark, + arkheDelay+hitmark, + arkheDelay+hitmark, nil, ) c.QueueCharTask( From 7d36056e4f9882f719c89805559efd5422378047 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 19 Nov 2023 15:40:54 +1100 Subject: [PATCH 12/36] Fixed: - c4 Applying on skill instead of Burst --- internal/characters/navia/burst.go | 1 + internal/characters/navia/cons.go | 24 ++++++++++++++---------- internal/characters/navia/skill.go | 2 -- 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index b507a04b29..77aafafaa3 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -85,6 +85,7 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { burstHitmark+i, burstHitmark+i, c.BurstCB(), + c.c4(), ) } diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 0a105a34ac..1e44d7d80e 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -54,22 +54,26 @@ func (c *char) c2(a combat.AttackCB) { 0, 0, c.BurstCB(), + c.c4(), ) } // When As the Sunlit Sky's Singing Salute hits an opponent, // that opponent's Geo RES will be decreased by 20% for 8s. -func (c *char) c4(a combat.AttackCB) { +func (c *char) c4() combat.AttackCBFunc { if c.Base.Cons < 4 { - return + return nil } - e := a.Target.(*enemy.Enemy) - if e.Type() != targets.TargettableEnemy { - return + return func(a combat.AttackCB) { + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return + } + e.AddResistMod(combat.ResistMod{ + Base: modifier.NewBaseWithHitlag("navia-c4-shred", 8*60), + Ele: attributes.Geo, + Value: -0.2, + }) } - e.AddResistMod(combat.ResistMod{ - Base: modifier.NewBaseWithHitlag("navia-c4-shred", 8*60), - Ele: attributes.Geo, - Value: -0.2, - }) + } diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 9b1ebea34d..33494d94e9 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -176,8 +176,6 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { }, hitmark) } - c.c4(a) - if done { return } From 679ee4b5c0803587ce0dbda97b3eeac96c45e4cd Mon Sep 17 00:00:00 2001 From: David Ji Date: Mon, 20 Nov 2023 22:01:11 +1100 Subject: [PATCH 13/36] Update for v2 --- .../nighttimewhispers/nighttimewhispers.go | 6 +- internal/characters/navia/cons.go | 2 +- internal/characters/navia/skill.go | 4 +- internal/characters/navia/stats.go | 120 +++++++++--------- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/internal/artifacts/nighttimewhispers/nighttimewhispers.go b/internal/artifacts/nighttimewhispers/nighttimewhispers.go index 4810f34ae6..420997e28e 100644 --- a/internal/artifacts/nighttimewhispers/nighttimewhispers.go +++ b/internal/artifacts/nighttimewhispers/nighttimewhispers.go @@ -13,9 +13,9 @@ import ( ) const ( - buffKey = "nighttime-whispers-buff" - buffVal = 0.16 - secondBuffKey = "nighttime-whispers-second-buff" + buffKey = "nighttime-whispers-skill-buff" + buffVal = 0.2 + secondBuffKey = "nighttime-whispers-shield-buff" ) func init() { diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 1e44d7d80e..da76371f67 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -18,7 +18,7 @@ import ( func (c *char) c1(shrapnel int) { count := math.Min(float64(shrapnel), 3) c.ReduceActionCooldown(action.ActionBurst, int(count*60)) - c.AddEnergy("navia-c1-energy", count*2) + c.AddEnergy("navia-c1-energy", count*3) return } diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 33494d94e9..dc00d7e0f8 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -88,10 +88,10 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { } m[attributes.DmgP] = 0.15 * excess if c.Base.Cons >= 2 { - m[attributes.CR] = 0.08 * excess + m[attributes.CR] = 0.12 * excess } if c.Base.Cons >= 6 { - m[attributes.CD] = 0.35 * excess + m[attributes.CD] = 0.45 * excess } return m, true }, diff --git a/internal/characters/navia/stats.go b/internal/characters/navia/stats.go index 962dfc80fa..282b3ea39d 100644 --- a/internal/characters/navia/stats.go +++ b/internal/characters/navia/stats.go @@ -118,75 +118,75 @@ var ( }, } skillshotgun = []float64{ - 307.2 / 100.0, - 330.2 / 100.0, - 353.3 / 100.0, - 384.0 / 100.0, - 407.0 / 100.0, - 430.1 / 100.0, - 460.8 / 100.0, - 491.5 / 100.0, - 522.2 / 100.0, - 553.0 / 100.0, - 583.7 / 100.0, - 614.4 / 100.0, - 652.8 / 100.0, - 691.2 / 100.0, - 729.6 / 100.0, + 3.948, + 4.244, + 4.540, + 4.935, + 5.23, + 5.527, + 5.922, + 6.317, + 6.712, + 7.106, + 7.501, + 7.896, + 8.389, + 8.883, + 9.377, } skillblade = []float64{ - 33.6 / 100.0, - 36.1 / 100.0, - 38.6 / 100.0, - 42.0 / 100.0, - 44.5 / 100.0, - 47.0 / 100.0, - 50.4 / 100.0, - 53.8 / 100.0, - 57.1 / 100.0, - 60.5 / 100.0, - 63.8 / 100.0, - 67.2 / 100.0, - 71.4 / 100.0, - 75.6 / 100.0, - 79.8 / 100.0, + 0.36, + 0.387, + 0.414, + 0.45, + 0.477, + 0.504, + 0.54, + 0.576, + 0.612, + 0.648, + 0.684, + 0.720, + 0.765, + 0.81, + 0.855, } burst = [][]float64{ // skill cast { - 76.0 / 100.0, - 81.7 / 100.0, - 87.4 / 100.0, - 95.0 / 100.0, - 100.7 / 100.0, - 106.4 / 100.0, - 114.0 / 100.0, - 121.6 / 100.0, - 129.2 / 100.0, - 136.8 / 100.0, - 144.4 / 100.0, - 152.0 / 100.0, - 161.5 / 100.0, //not copying the rest - 161.5 / 100.0, - 161.5 / 100.0, + 0.752, + 0.808, + 0.865, + 0.94, + 0.996, + 1.053, + 1.128, + 1.203, + 1.278, + 1.354, + 1.429, + 1.504, + 1.598, + 1.692, + 1.786, }, // artillery { - 48.4 / 100.0, - 52.0 / 100.0, - 55.7 / 100.0, - 60.5 / 100.0, - 60.5 / 100.0, - 67.8 / 100.0, - 72.6 / 100.0, - 77.4 / 100.0, - 82.3 / 100.0, - 87.1 / 100.0, - 92.0 / 100.0, - 96.8 / 100.0, - 102.9 / 100.0, //too lazy to copy the rest - 102.9 / 100.0, - 102.9 / 100.0, + 0.431, + 0.464, + 0.496, + 0.539, + 0.572, + 0.604, + 0.647, + 0.69, + 0.734, + 0.777, + 0.82, + 0.863, + 0.917, + 0.971, + 1.025, }, } ) From 63381573a8e052a3c5a1fc66b1c7d009bda1b02a Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 25 Nov 2023 07:41:46 +1100 Subject: [PATCH 14/36] Update for v2 --- internal/characters/navia/cons.go | 3 +++ internal/characters/navia/skill.go | 10 ++-------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index da76371f67..e38a41e90d 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -16,6 +16,9 @@ import ( // Up to 6 Energy can be gained this way, and the CD of Ceremonial Crystalshot can be // decreased by up to 3s. func (c *char) c1(shrapnel int) { + if c.Base.Cons < 1 { + return + } count := math.Min(float64(shrapnel), 3) c.ReduceActionCooldown(action.ActionBurst, int(count*60)) c.AddEnergy("navia-c1-energy", count*3) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index dc00d7e0f8..b64431aa00 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -123,9 +123,10 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { ) } - // remove the shrapnel after firing + // remove the shrapnel after firing and action C1 c.QueueCharTask( func() { + c.c1(c.shrapnel) if c.Base.Cons < 6 { c.shrapnel = 0 } else { @@ -169,13 +170,6 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { // When firing, attack with the Surging Blade c.SurgingBlade(hitmark) - // C1 Energy Restoration and CD reduction - if c.Base.Cons >= 1 { - c.QueueCharTask(func() { - c.c1(c.shrapnel) - }, hitmark) - } - if done { return } From 0171c65bd7127dc2511a8b780502c84f1b8453de Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 15 Dec 2023 16:11:00 +1100 Subject: [PATCH 15/36] Refactored some callbacks --- internal/characters/navia/cons.go | 61 +++++++++++++++------------ internal/characters/navia/skill.go | 66 ++++++++++++++---------------- pkg/shortcut/characters.go | 3 ++ 3 files changed, 67 insertions(+), 63 deletions(-) diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index e38a41e90d..f166b9189f 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -31,34 +31,41 @@ func (c *char) c1(shrapnel int) { // from As the Sunlit Sky's Singing Salute will strike near the location of the hit. // Up to one instance of Fire Support can be triggered each time Ceremonial Crystalshot is used, // and DMG dealt by Fire Support in this way is considered Elemental Burst DMG. -func (c *char) c2(a combat.AttackCB) { - if c.Base.Cons < 2 { - return - } - if !c.c2ready { - return - } - c.c2ready = false - // Function doesn't check for enemy type or limit as assumes that the CB will check or it. - ai := combat.AttackInfo{ - ActorIndex: c.Index, - Abil: "The President's Pursuit of Victory", - AttackTag: attacks.AttackTagElementalBurst, - ICDTag: attacks.ICDTagElementalBurst, - ICDGroup: attacks.ICDGroupDefault, - StrikeType: attacks.StrikeTypeBlunt, - Element: attributes.Geo, - Durability: 25, - Mult: burst[1][c.TalentLvlSkill()], +func (c *char) c2() combat.AttackCBFunc { + return func(a combat.AttackCB) { + if c.Base.Cons < 2 { + return + } + if !c.c2ready { + return + } + c.c2ready = false + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return + } + + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "The President's Pursuit of Victory", + AttackTag: attacks.AttackTagElementalBurst, + ICDTag: attacks.ICDTagElementalBurst, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 25, + Mult: burst[1][c.TalentLvlSkill()], + } + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(e.Pos(), nil, 3), + 0, + 0, + c.BurstCB(), + c.c4(), + ) } - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(a.Target.Pos(), nil, 3), - 0, - 0, - c.BurstCB(), - c.c4(), - ) + } // When As the Sunlit Sky's Singing Salute hits an opponent, diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index b64431aa00..6b3aa56e0a 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -46,6 +46,7 @@ func init() { skillHoldFrames[action.ActionJump] = 51 } +// TODO: Refactor so that Shrapnel is calculated when fired func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = true hold := p["hold"] @@ -119,7 +120,9 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { combat.NewSingleTargetHit(t.Key()), hitmark, hitmark+5, - c.SkillCB(hitmark), + c.particleCB(), + c.SurgingBlade(), + c.c2(), ) } @@ -159,7 +162,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { } -func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { +func (c *char) particleCB() combat.AttackCBFunc { done := false return func(a combat.AttackCB) { e := a.Target.(*enemy.Enemy) @@ -167,14 +170,9 @@ func (c *char) SkillCB(hitmark int) combat.AttackCBFunc { return } - // When firing, attack with the Surging Blade - c.SurgingBlade(hitmark) - if done { return } - c.c2(a) - c.c2ready = false if !c.StatusIsActive(particleICDKey) { count := 3.0 if c.Core.Rand.Float64() < 0.5 { @@ -207,34 +205,30 @@ func (c *char) ShrapnelGain() { } -func (c *char) SurgingBlade(hitmark int) { - if c.StatusIsActive(arkheICDKey) { - return - } - ai := combat.AttackInfo{ - ActorIndex: c.Index, - Abil: "Surging Blade", - AttackTag: attacks.AttackTagElementalArt, - ICDTag: attacks.ICDTagNone, - ICDGroup: attacks.ICDGroupDefault, - StrikeType: attacks.StrikeTypeBlunt, - Element: attributes.Geo, - Durability: 0, - Mult: skillblade[c.TalentLvlSkill()], - } - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - arkheDelay+hitmark, - arkheDelay+hitmark, - nil, - ) - c.QueueCharTask( - func() { - c.AddStatus(arkheICDKey, 7*60, true) - }, - hitmark, - ) +func (c *char) SurgingBlade() combat.AttackCBFunc { + return func(a combat.AttackCB) { + if c.StatusIsActive(arkheICDKey) { + return + } + c.AddStatus(arkheICDKey, 7*60, false) + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "Surging Blade", + AttackTag: attacks.AttackTagElementalArt, + ICDTag: attacks.ICDTagNone, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 0, + Mult: skillblade[c.TalentLvlSkill()], + } + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), + arkheDelay, + arkheDelay, + nil, + ) - return + } } diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index ff62871b8d..e79a413057 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -78,6 +78,9 @@ var CharNameToKey = map[string]keys.Char{ "lisa": keys.Lisa, "mona": keys.Mona, "navia": keys.Navia, + "taylorswift": keys.Navia, + "crybaby2": keys.Navia, + "spinadirosula": keys.Navia, "ningguang": keys.Ningguang, "ning": keys.Ningguang, "noelle": keys.Noelle, From 75d7ada428d07524e57c8e46e5b3b07acc4d8021 Mon Sep 17 00:00:00 2001 From: Charlie-Zheng Date: Sun, 10 Dec 2023 15:26:04 -0700 Subject: [PATCH 16/36] Neuvillette Implementation (#1727) --------- Co-authored-by: imring --- ui/packages/docs/src/components/Actions/character_data.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/packages/docs/src/components/Actions/character_data.json b/ui/packages/docs/src/components/Actions/character_data.json index 35c628761e..d974b68bc9 100644 --- a/ui/packages/docs/src/components/Actions/character_data.json +++ b/ui/packages/docs/src/components/Actions/character_data.json @@ -2652,4 +2652,4 @@ "legal": true } ] -} +} \ No newline at end of file From b858236ef54d7ccaea0066bb9c908dd901dd14a4 Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 10 Nov 2023 20:49:30 +1100 Subject: [PATCH 17/36] Navia Initial Commit. Todo, fix SurgingBlade() and SkillCB() not being registered, and add skill buffs, and cons. Also fix Burst hitbox and targetting. Also Frames --- internal/characters/navia/asc.go | 5 ++++- internal/characters/navia/attack.go | 8 ++++---- internal/characters/navia/burst.go | 2 ++ 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index d318471adb..e1c5b5e18b 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -8,6 +8,8 @@ import ( "github.com/genshinsim/gcsim/pkg/modifier" ) +var a4ATKP = 0.2 + func init() { } @@ -70,7 +72,7 @@ func (c *char) a4() { } m := make([]float64, attributes.EndStatType) - m[attributes.ATKP] = 0.2 * float64(ele) + m[attributes.ATKP] = a4ATKP * float64(ele) c.AddStatMod(character.StatMod{ Base: modifier.NewBase("navia-a4", -1), AffectedStat: attributes.ATKP, @@ -78,4 +80,5 @@ func (c *char) a4() { return m, true }, }) + } diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index 0f6066b406..da238d247f 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -25,10 +25,10 @@ const normalHitNum = 4 func init() { attackFrames = make([][]int, normalHitNum) - attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 32) - attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 42) - attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 43) - attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 82) + attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 38) + attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 46) + attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 31) + attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 107) } func (c *char) Attack(p map[string]int) (action.Info, error) { diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 77aafafaa3..707c911eeb 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -63,6 +63,8 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { burstHitmark+burstDuration, ) +======= +>>>>>>> 9997d67d6 (Navia Initial Commit. Todo, fix SurgingBlade() and SkillCB() not being registered, and add skill buffs, and cons. Also fix Burst hitbox and targetting. Also Frames) c.ConsumeEnergy(5) c.SetCD(action.ActionBurst, 15*60) From ff96f0951849b36ed3e90b4c1a098c4736c56b98 Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 15 Dec 2023 16:34:08 +1100 Subject: [PATCH 18/36] Reverted some things I missed when rebasing --- internal/characters/navia/asc.go | 5 +---- internal/characters/navia/attack.go | 10 +++++----- internal/characters/navia/burst.go | 2 -- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index e1c5b5e18b..d318471adb 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -8,8 +8,6 @@ import ( "github.com/genshinsim/gcsim/pkg/modifier" ) -var a4ATKP = 0.2 - func init() { } @@ -72,7 +70,7 @@ func (c *char) a4() { } m := make([]float64, attributes.EndStatType) - m[attributes.ATKP] = a4ATKP * float64(ele) + m[attributes.ATKP] = 0.2 * float64(ele) c.AddStatMod(character.StatMod{ Base: modifier.NewBase("navia-a4", -1), AffectedStat: attributes.ATKP, @@ -80,5 +78,4 @@ func (c *char) a4() { return m, true }, }) - } diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index da238d247f..37cf92ac5a 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -25,13 +25,13 @@ const normalHitNum = 4 func init() { attackFrames = make([][]int, normalHitNum) - attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 38) - attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 46) - attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 31) - attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 107) + attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 32) + attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 42) + attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 43) + attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 82) } -func (c *char) Attack(p map[string]int) (action.Info, error) { +func (c *char) Attack(_ map[string]int) (action.Info, error) { for i, mult := range auto[c.NormalCounter] { ai := combat.AttackInfo{ ActorIndex: c.Index, diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 707c911eeb..77aafafaa3 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -63,8 +63,6 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { burstHitmark+burstDuration, ) -======= ->>>>>>> 9997d67d6 (Navia Initial Commit. Todo, fix SurgingBlade() and SkillCB() not being registered, and add skill buffs, and cons. Also fix Burst hitbox and targetting. Also Frames) c.ConsumeEnergy(5) c.SetCD(action.ActionBurst, 15*60) From 18f8252f088fbad8c8c220ecccb2ced5beae3376 Mon Sep 17 00:00:00 2001 From: David Ji Date: Fri, 15 Dec 2023 16:45:10 +1100 Subject: [PATCH 19/36] Fixed Keys --- pkg/core/keys/char.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/core/keys/char.go b/pkg/core/keys/char.go index c086203bf9..ade944d678 100644 --- a/pkg/core/keys/char.go +++ b/pkg/core/keys/char.go @@ -130,6 +130,7 @@ const ( Neuvillette Freminet Furina + Navia TestCharDoNotUse EndCharKeys ) @@ -225,6 +226,7 @@ var charNames = []string{ "neuvillette", "freminet", "furina", + "navia", "test_char_do_not_use", } @@ -319,6 +321,7 @@ var charPrettyName = []string{ "Neuvillette", "Freminet", "Furina", + "Navia", "!!!TEST CHAR DO NOT USE!!!", } @@ -409,5 +412,6 @@ var CharKeyToEle = map[Char]attributes.Element{ Neuvillette: attributes.Hydro, Freminet: attributes.Cryo, Furina: attributes.Hydro, + Navia: attributes.Geo, TestCharDoNotUse: attributes.Geo, } From 62a6b0d6693857b19d21535c88aaf71d14bc2955 Mon Sep 17 00:00:00 2001 From: David Ji Date: Wed, 20 Dec 2023 15:56:17 +1100 Subject: [PATCH 20/36] Removed nighttimewhispers.go --- .../artifacts/nighttimewhispers/config.yaml | 2 - .../nighttimewhispers/nighttimewhispers.go | 114 ---------- internal/characters/navia/skill.go | 27 ++- pkg/core/keys/sets.go | 1 - pkg/shortcut/artifacts.go | 215 +++++++++--------- pkg/simulation/imports.go | 1 - 6 files changed, 127 insertions(+), 233 deletions(-) delete mode 100644 internal/artifacts/nighttimewhispers/config.yaml delete mode 100644 internal/artifacts/nighttimewhispers/nighttimewhispers.go diff --git a/internal/artifacts/nighttimewhispers/config.yaml b/internal/artifacts/nighttimewhispers/config.yaml deleted file mode 100644 index 109acf9a92..0000000000 --- a/internal/artifacts/nighttimewhispers/config.yaml +++ /dev/null @@ -1,2 +0,0 @@ -key: "nighttimewhispers" -text_map_id: 15034 \ No newline at end of file diff --git a/internal/artifacts/nighttimewhispers/nighttimewhispers.go b/internal/artifacts/nighttimewhispers/nighttimewhispers.go deleted file mode 100644 index 420997e28e..0000000000 --- a/internal/artifacts/nighttimewhispers/nighttimewhispers.go +++ /dev/null @@ -1,114 +0,0 @@ -package nighttimewhispers - -import ( - "fmt" - "github.com/genshinsim/gcsim/pkg/core" - "github.com/genshinsim/gcsim/pkg/core/attributes" - "github.com/genshinsim/gcsim/pkg/core/event" - "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/player/shield" - "github.com/genshinsim/gcsim/pkg/modifier" -) - -const ( - buffKey = "nighttime-whispers-skill-buff" - buffVal = 0.2 - secondBuffKey = "nighttime-whispers-shield-buff" -) - -func init() { - core.RegisterSetFunc(keys.NighttimeWhispersInTheEchoingWoods, NewSet) -} - -type Set struct { - char *character.CharWrapper - count int - Index int -} - -func (s *Set) SetIndex(idx int) { s.Index = idx } - -func (s *Set) Init() error { - return nil -} - -func NewSet(c *core.Core, char *character.CharWrapper, count int, _ map[string]int) (info.Set, error) { - s := Set{ - char: char, - count: count, - } - - // 2pc - ATK +18%. - if count >= 2 { - m := make([]float64, attributes.EndStatType) - m[attributes.ATKP] = 0.18 - char.AddStatMod(character.StatMod{ - Base: modifier.NewBase("nighttime-whispers-2pc", -1), - AffectedStat: attributes.ATKP, - Amount: func() ([]float64, bool) { - return m, true - }, - }) - } - - // 4pc - After using an Elemental Skill, gain a 16% Geo DMG Bonus for 10s - if count >= 4 { - f := func(args ...interface{}) bool { - if c.Player.Active() != char.Index { - return false - } - m := make([]float64, attributes.EndStatType) - char.AddStatMod(character.StatMod{ - Base: modifier.NewBaseWithHitlag(buffKey, 60*10), - Amount: func() ([]float64, bool) { - m[attributes.GeoP] = buffVal - return m, true - }, - }) - - // While under a shield granted by the Crystallize reaction, the above - // effect will be increased by 150%, and this additional increase disappears - // 1s after that shield is lost. - for i := 0; i < 11*60; i += 30 { // An extra second to account for possible hitlag extension - char.QueueCharTask( - func() { - // Checks that base buff is active - if !char.StatusIsActive(buffKey) { - if char.StatusIsActive(secondBuffKey) { - char.RemoveTag(secondBuffKey) - } - return - } - - // Checks for a Crystallise Shield. - if char.Index == c.Player.Active() && c.Player.Shields.PlayerIsShielded() { - s := c.Player.Shields.List() - for _, t := range s { - if t.Type() == shield.Crystallize { - n := make([]float64, attributes.EndStatType) - char.AddStatMod(character.StatMod{ - Base: modifier.NewBaseWithHitlag(secondBuffKey, 60), - Amount: func() ([]float64, bool) { - n[attributes.GeoP] = 1.5 * buffVal - return n, true - }, - }) - break - } - } - } - return - }, - i, - ) - } - return false - } - - c.Events.Subscribe(event.OnSkill, f, fmt.Sprintf("nighttime-whispers-4pc-%v", char.Base.Key.String())) - } - - return &s, nil -} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 6b3aa56e0a..167a247811 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -21,7 +21,20 @@ var skillPressFrames []int var skillHoldFrames []int var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, event.OnCrystallizePyro} -var skillMultiplier = []float64{0, 1, 1.05, 1.1, 1.15, 1.2, 1.36, 1.4, 1.6, 1.666, 1.9, 2} +var skillMultiplier = []float64{ + 0, //0 hits + 1, //1 hit + 1.05000000074505806, //2 hit + 1.10000000149011612, //3 hit etc + 1.15000000596046448, + 1.20000000298023224, + 1.36000001430511475, + 1.4000000059604645, + 1.6000000238418579, + 1.6660000085830688, + 1.8999999761581421, + 2, +} const ( skillPressCDStart = 30 @@ -46,7 +59,6 @@ func init() { skillHoldFrames[action.ActionJump] = 51 } -// TODO: Refactor so that Shrapnel is calculated when fired func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = true hold := p["hold"] @@ -58,14 +70,19 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { hitmark += skillPressHitmark + hold c.SetCDWithDelay(action.ActionSkill, 9*60, skillPressCDStart) } - + shots := 5 if p["shrapnel"] != 0 { c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) } - c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) + c.QueueCharTask( + func() { + c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) - shots := 5 + int(math.Max(float64(c.shrapnel-3), 0))*2 + shots = 5 + int(math.Max(float64(c.shrapnel-3), 0))*2 + }, + hitmark-1, + ) ai := combat.AttackInfo{ ActorIndex: c.Index, diff --git a/pkg/core/keys/sets.go b/pkg/core/keys/sets.go index 9cf01a33ce..140b9481cc 100644 --- a/pkg/core/keys/sets.go +++ b/pkg/core/keys/sets.go @@ -111,7 +111,6 @@ const ( MaidenBeloved MarechausseeHunter MartialArtist - NighttimeWhispersInTheEchoingWoods NoblesseOblige NymphsDream OceanHuedClam diff --git a/pkg/shortcut/artifacts.go b/pkg/shortcut/artifacts.go index 25afea6f1e..44c6084049 100644 --- a/pkg/shortcut/artifacts.go +++ b/pkg/shortcut/artifacts.go @@ -3,114 +3,109 @@ package shortcut import "github.com/genshinsim/gcsim/pkg/core/keys" var SetNameToKey = map[string]keys.Set{ - "adventurer": keys.Adventurer, - "archaicpetra": keys.ArchaicPetra, - "ap": keys.ArchaicPetra, - "berserker": keys.Berserker, - "blizzardstrayer": keys.BlizzardStrayer, - "blizzard": keys.BlizzardStrayer, - "bs": keys.BlizzardStrayer, - "bloodstainedchivalry": keys.BloodstainedChivalry, - "bloodstained": keys.BloodstainedChivalry, - "bsc": keys.BloodstainedChivalry, - "braveheart": keys.BraveHeart, - "brave": keys.BraveHeart, - "crimsonwitchofflames": keys.CrimsonWitchOfFlames, - "cwof": keys.CrimsonWitchOfFlames, - "cw": keys.CrimsonWitchOfFlames, - "crimson": keys.CrimsonWitchOfFlames, - "witch": keys.CrimsonWitchOfFlames, - "deepwoodmemories": keys.DeepwoodMemories, - "deepwood": keys.DeepwoodMemories, - "defenderswill": keys.DefendersWill, - "defenders": keys.DefendersWill, - "defender": keys.DefendersWill, - "desertpavilionchronicle": keys.DesertPavilionChronicle, - "desertpavilion": keys.DesertPavilionChronicle, - "dpc": keys.DesertPavilionChronicle, - "vourukashasglow": keys.VourukashasGlow, - "echoesofanoffering": keys.EchoesOfAnOffering, - "echoes": keys.EchoesOfAnOffering, - "emblemofseveredfate": keys.EmblemOfSeveredFate, - "emblem": keys.EmblemOfSeveredFate, - "eosf": keys.EmblemOfSeveredFate, - "esf": keys.EmblemOfSeveredFate, - "flowerofparadiselost": keys.FlowerOfParadiseLost, - "paradiselost": keys.FlowerOfParadiseLost, - "fopl": keys.FlowerOfParadiseLost, - "gambler": keys.Gambler, - "gladiatorsfinale": keys.GladiatorsFinale, - "glad": keys.GladiatorsFinale, - "gladiators": keys.GladiatorsFinale, - "gildeddreams": keys.GildedDreams, - "gilded": keys.GildedDreams, - "gd": keys.GildedDreams, - "goldentroupe": keys.GoldenTroupe, - "gt": keys.GoldenTroupe, - "heartofdepth": keys.HeartOfDepth, - "hod": keys.HeartOfDepth, - "huskofopulentdreams": keys.HuskOfOpulentDreams, - "husk": keys.HuskOfOpulentDreams, - "hood": keys.HuskOfOpulentDreams, - "instructor": keys.Instructor, - "ins": keys.Instructor, - "lavawalker": keys.Lavawalker, - "lw": keys.Lavawalker, - "luckydog": keys.LuckyDog, - "maidenbeloved": keys.MaidenBeloved, - "maiden": keys.MaidenBeloved, - "mb": keys.MaidenBeloved, - "marechausseehunter": keys.MarechausseeHunter, - "mh": keys.MarechausseeHunter, - "martialartist": keys.MartialArtist, - "nighttimewhispers": keys.NighttimeWhispersInTheEchoingWoods, - "nighttimewhispersintheechoingwoods": keys.NighttimeWhispersInTheEchoingWoods, - "echoingwoods": keys.NighttimeWhispersInTheEchoingWoods, - "nwew": keys.NighttimeWhispersInTheEchoingWoods, - "nwitew": keys.NighttimeWhispersInTheEchoingWoods, - "noblesseoblige": keys.NoblesseOblige, - "noblesse": keys.NoblesseOblige, - "no": keys.NoblesseOblige, - "nymphsdream": keys.NymphsDream, - "oceanhuedclam": keys.OceanHuedClam, - "ohc": keys.OceanHuedClam, - "clam": keys.OceanHuedClam, - "paleflame": keys.PaleFlame, - "pf": keys.PaleFlame, - "prayersfordestiny": keys.PrayersForDestiny, - "prayersforillumination": keys.PrayersForIllumination, - "prayersforwisdom": keys.PrayersForWisdom, - "prayerstospringtime": keys.PrayersToSpringtime, - "resolutionofsojourner": keys.ResolutionOfSojourner, - "sojourner": keys.ResolutionOfSojourner, - "retracingbolide": keys.RetracingBolide, - "bolide": keys.RetracingBolide, - "scholar": keys.Scholar, - "shimenawasreminiscence": keys.ShimenawasReminiscence, - "shimenawa": keys.ShimenawasReminiscence, - "shime": keys.ShimenawasReminiscence, - "shim": keys.ShimenawasReminiscence, - "sr": keys.ShimenawasReminiscence, - "tenacityofthemillelith": keys.TenacityOfTheMillelith, - "tom": keys.TenacityOfTheMillelith, - "totm": keys.TenacityOfTheMillelith, - "tenacity": keys.TenacityOfTheMillelith, - "millelith": keys.TenacityOfTheMillelith, - "theexile": keys.TheExile, - "exile": keys.TheExile, - "thunderingfury": keys.ThunderingFury, - "tf": keys.ThunderingFury, - "thundersoother": keys.Thundersoother, - "ts": keys.Thundersoother, - "tinymiracle": keys.TinyMiracle, - "travelingdoctor": keys.TravelingDoctor, - "vermillionhereafter": keys.VermillionHereafter, - "vermillion": keys.VermillionHereafter, - "vh": keys.VermillionHereafter, - "viridescentvenerer": keys.ViridescentVenerer, - "viridescent": keys.ViridescentVenerer, - "vv": keys.ViridescentVenerer, - "wandererstroupe": keys.WanderersTroupe, - "wanderers": keys.WanderersTroupe, - "wt": keys.WanderersTroupe, + "adventurer": keys.Adventurer, + "archaicpetra": keys.ArchaicPetra, + "ap": keys.ArchaicPetra, + "berserker": keys.Berserker, + "blizzardstrayer": keys.BlizzardStrayer, + "blizzard": keys.BlizzardStrayer, + "bs": keys.BlizzardStrayer, + "bloodstainedchivalry": keys.BloodstainedChivalry, + "bloodstained": keys.BloodstainedChivalry, + "bsc": keys.BloodstainedChivalry, + "braveheart": keys.BraveHeart, + "brave": keys.BraveHeart, + "crimsonwitchofflames": keys.CrimsonWitchOfFlames, + "cwof": keys.CrimsonWitchOfFlames, + "cw": keys.CrimsonWitchOfFlames, + "crimson": keys.CrimsonWitchOfFlames, + "witch": keys.CrimsonWitchOfFlames, + "deepwoodmemories": keys.DeepwoodMemories, + "deepwood": keys.DeepwoodMemories, + "defenderswill": keys.DefendersWill, + "defenders": keys.DefendersWill, + "defender": keys.DefendersWill, + "desertpavilionchronicle": keys.DesertPavilionChronicle, + "desertpavilion": keys.DesertPavilionChronicle, + "dpc": keys.DesertPavilionChronicle, + "vourukashasglow": keys.VourukashasGlow, + "echoesofanoffering": keys.EchoesOfAnOffering, + "echoes": keys.EchoesOfAnOffering, + "emblemofseveredfate": keys.EmblemOfSeveredFate, + "emblem": keys.EmblemOfSeveredFate, + "eosf": keys.EmblemOfSeveredFate, + "esf": keys.EmblemOfSeveredFate, + "flowerofparadiselost": keys.FlowerOfParadiseLost, + "paradiselost": keys.FlowerOfParadiseLost, + "fopl": keys.FlowerOfParadiseLost, + "gambler": keys.Gambler, + "gladiatorsfinale": keys.GladiatorsFinale, + "glad": keys.GladiatorsFinale, + "gladiators": keys.GladiatorsFinale, + "gildeddreams": keys.GildedDreams, + "gilded": keys.GildedDreams, + "gd": keys.GildedDreams, + "goldentroupe": keys.GoldenTroupe, + "gt": keys.GoldenTroupe, + "heartofdepth": keys.HeartOfDepth, + "hod": keys.HeartOfDepth, + "huskofopulentdreams": keys.HuskOfOpulentDreams, + "husk": keys.HuskOfOpulentDreams, + "hood": keys.HuskOfOpulentDreams, + "instructor": keys.Instructor, + "ins": keys.Instructor, + "lavawalker": keys.Lavawalker, + "lw": keys.Lavawalker, + "luckydog": keys.LuckyDog, + "maidenbeloved": keys.MaidenBeloved, + "maiden": keys.MaidenBeloved, + "mb": keys.MaidenBeloved, + "marechausseehunter": keys.MarechausseeHunter, + "mh": keys.MarechausseeHunter, + "martialartist": keys.MartialArtist, + "noblesseoblige": keys.NoblesseOblige, + "noblesse": keys.NoblesseOblige, + "no": keys.NoblesseOblige, + "nymphsdream": keys.NymphsDream, + "oceanhuedclam": keys.OceanHuedClam, + "ohc": keys.OceanHuedClam, + "clam": keys.OceanHuedClam, + "paleflame": keys.PaleFlame, + "pf": keys.PaleFlame, + "prayersfordestiny": keys.PrayersForDestiny, + "prayersforillumination": keys.PrayersForIllumination, + "prayersforwisdom": keys.PrayersForWisdom, + "prayerstospringtime": keys.PrayersToSpringtime, + "resolutionofsojourner": keys.ResolutionOfSojourner, + "sojourner": keys.ResolutionOfSojourner, + "retracingbolide": keys.RetracingBolide, + "bolide": keys.RetracingBolide, + "scholar": keys.Scholar, + "shimenawasreminiscence": keys.ShimenawasReminiscence, + "shimenawa": keys.ShimenawasReminiscence, + "shime": keys.ShimenawasReminiscence, + "shim": keys.ShimenawasReminiscence, + "sr": keys.ShimenawasReminiscence, + "tenacityofthemillelith": keys.TenacityOfTheMillelith, + "tom": keys.TenacityOfTheMillelith, + "totm": keys.TenacityOfTheMillelith, + "tenacity": keys.TenacityOfTheMillelith, + "millelith": keys.TenacityOfTheMillelith, + "theexile": keys.TheExile, + "exile": keys.TheExile, + "thunderingfury": keys.ThunderingFury, + "tf": keys.ThunderingFury, + "thundersoother": keys.Thundersoother, + "ts": keys.Thundersoother, + "tinymiracle": keys.TinyMiracle, + "travelingdoctor": keys.TravelingDoctor, + "vermillionhereafter": keys.VermillionHereafter, + "vermillion": keys.VermillionHereafter, + "vh": keys.VermillionHereafter, + "viridescentvenerer": keys.ViridescentVenerer, + "viridescent": keys.ViridescentVenerer, + "vv": keys.ViridescentVenerer, + "wandererstroupe": keys.WanderersTroupe, + "wanderers": keys.WanderersTroupe, + "wt": keys.WanderersTroupe, } diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index cf36eea6b3..17c40aa2ba 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -38,7 +38,6 @@ import ( _ "github.com/genshinsim/gcsim/internal/artifacts/maiden" _ "github.com/genshinsim/gcsim/internal/artifacts/marechausseehunter" _ "github.com/genshinsim/gcsim/internal/artifacts/martialartist" - _ "github.com/genshinsim/gcsim/internal/artifacts/nighttimewhispers" _ "github.com/genshinsim/gcsim/internal/artifacts/noblesse" _ "github.com/genshinsim/gcsim/internal/artifacts/nymphsdream" _ "github.com/genshinsim/gcsim/internal/artifacts/oceanhuedclam" From 53aee55b3ebe241db52e22c7728765ae6836a830 Mon Sep 17 00:00:00 2001 From: David Ji Date: Thu, 21 Dec 2023 11:13:14 +1100 Subject: [PATCH 21/36] Fixed missing import, and infusion timing out --- internal/characters/navia/asc.go | 16 +++------------- internal/characters/navia/burst.go | 2 +- internal/characters/navia/navia.go | 17 +++++++++++++++++ internal/characters/navia/skill.go | 12 ++++++------ pkg/shortcut/characters.go | 3 +-- pkg/simulation/imports.go | 1 + 6 files changed, 29 insertions(+), 22 deletions(-) diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index d318471adb..57939f42c8 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -4,6 +4,7 @@ import ( "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/glog" "github.com/genshinsim/gcsim/pkg/core/player/character" "github.com/genshinsim/gcsim/pkg/modifier" ) @@ -20,18 +21,7 @@ func (c *char) a1() { if c.Base.Ascension < 1 { return } - - // add Geo infusion - c.Core.Player.AddWeaponInfuse( - c.Index, - "navia-a1-infusion", - attributes.Geo, - 60*4, - false, - attacks.AttackTagNormal, - attacks.AttackTagExtra, - attacks.AttackTagPlunge, - ) + c.Core.Log.NewEvent("infusion added", glog.LogCharacterEvent, c.Index) // add Damage Bonus m := make([]float64, attributes.EndStatType) @@ -62,7 +52,7 @@ func (c *char) a4() { for _, char := range c.Core.Player.Chars() { if char.Base.Element != attributes.Geo && char.Base. Element != attributes.Anemo && char.Base.Element != attributes.Dendro { - ele = ele + 1 + ele++ } } if ele > 2 { diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 77aafafaa3..5f2fdc7b3a 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -78,7 +78,7 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { SourceFrame: c.Core.F, } - for i := 45; i <= burstDuration; i = i + 45 { + for i := 45; i <= burstDuration; i += 45 { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.location(targetRadius), nil, 3), diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index ed6efc8d7f..75a98d8a5d 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -4,6 +4,8 @@ import ( tmpl "github.com/genshinsim/gcsim/internal/template/character" "github.com/genshinsim/gcsim/pkg/core" "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" "github.com/genshinsim/gcsim/pkg/core/keys" @@ -54,3 +56,18 @@ func (c *char) Condition(fields []string) (any, error) { return c.Character.Condition(fields) } } +func (c *char) Snapshot(ai *combat.AttackInfo) combat.Snapshot { + ds := c.Character.Snapshot(ai) + + if c.Character.StatusIsActive("navia-a1-dmg") { // weapon infusion can't be overriden for navia + switch ai.AttackTag { + case attacks.AttackTagNormal: + case attacks.AttackTagPlunge: + case attacks.AttackTagExtra: + default: + return ds + } + ai.Element = attributes.Geo + } + return ds +} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 167a247811..a905ea3aeb 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -22,10 +22,10 @@ var skillHoldFrames []int var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, event.OnCrystallizePyro} var skillMultiplier = []float64{ - 0, //0 hits - 1, //1 hit - 1.05000000074505806, //2 hit - 1.10000000149011612, //3 hit etc + 0, // 0 hits + 1, // 1 hit + 1.05000000074505806, // 2 hit + 1.10000000149011612, // 3 hit etc 1.15000000596046448, 1.20000000298023224, 1.36000001430511475, @@ -150,7 +150,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { if c.Base.Cons < 6 { c.shrapnel = 0 } else { - c.shrapnel = c.shrapnel - 3 // C6 keeps any more than the three + c.shrapnel -= 3 // C6 keeps any more than the three } return }, @@ -159,7 +159,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { // Add Geo Infusion c.QueueCharTask( c.a1, - hitmark+30, + hitmark, ) c.c2ready = false if hold > 1 { diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index b1845118d1..1a86816aa9 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -76,9 +76,8 @@ var CharNameToKey = map[string]keys.Char{ "lisa": keys.Lisa, "mona": keys.Mona, "navia": keys.Navia, - "taylorswift": keys.Navia, - "crybaby2": keys.Navia, "spinadirosula": keys.Navia, + "demoisselle": keys.Navia, "ningguang": keys.Ningguang, "ning": keys.Ningguang, "noelle": keys.Noelle, diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index 6fe2b0fdf4..d3f1509141 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -98,6 +98,7 @@ import ( _ "github.com/genshinsim/gcsim/internal/characters/mika" _ "github.com/genshinsim/gcsim/internal/characters/mona" _ "github.com/genshinsim/gcsim/internal/characters/nahida" + _ "github.com/genshinsim/gcsim/internal/characters/navia" _ "github.com/genshinsim/gcsim/internal/characters/neuvillette" _ "github.com/genshinsim/gcsim/internal/characters/nilou" _ "github.com/genshinsim/gcsim/internal/characters/ningguang" From b662940519c4428aa53c13937b31ad76f4c6c6e8 Mon Sep 17 00:00:00 2001 From: David Ji Date: Thu, 21 Dec 2023 11:20:55 +1100 Subject: [PATCH 22/36] Fixed shot calculation --- internal/characters/navia/skill.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index a905ea3aeb..24bff9f4bc 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -79,7 +79,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { func() { c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) - shots = 5 + int(math.Max(float64(c.shrapnel-3), 0))*2 + shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 }, hitmark-1, ) From 0b29bda7bc06ea00d0de2a44d1c4b5cf20f2a1f9 Mon Sep 17 00:00:00 2001 From: David Ji Date: Thu, 21 Dec 2023 11:38:27 +1100 Subject: [PATCH 23/36] Fixed shot calculation again --- internal/characters/navia/skill.go | 135 ++++++++++++++--------------- pkg/core/keys/sets.go | 1 - 2 files changed, 65 insertions(+), 71 deletions(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 24bff9f4bc..49223a6263 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -74,16 +74,6 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { if p["shrapnel"] != 0 { c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) } - - c.QueueCharTask( - func() { - c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) - - shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 - }, - hitmark-1, - ) - ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "Rosula Shardshot", @@ -96,71 +86,76 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { Mult: skillshotgun[c.TalentLvlSkill()], } - excess := math.Max(float64(c.shrapnel-3), 0) - m := make([]float64, attributes.EndStatType) - c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBase("navia-skill-dmgup", hitmark+6), - Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { - if atk.Info.AttackTag != attacks.AttackTagElementalArt { - return nil, false - } - m[attributes.DmgP] = 0.15 * excess - if c.Base.Cons >= 2 { - m[attributes.CR] = 0.12 * excess - } - if c.Base.Cons >= 6 { - m[attributes.CD] = 0.45 * excess - } - return m, true - }, - }) - - // Looks for enemies in the path of each bullet - // Initially trims enemies to check by scanning only the hit zone - for _, t := range c.Core.Combat.EnemiesWithinArea( - combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 25, 15), - nil, - ) { - // Tallies up the hits - hits := 0 - for i := 0; i < shots; i++ { - if ok, _ := t.AttackWillLand(combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), - geometry.Point{Y: 0}, - 25, 15)); ok { - hits++ - } - } - // Applies damage based on the hits - ai.Mult = skillshotgun[c.TalentLvlSkill()] * skillMultiplier[hits] - c.Core.QueueAttack( - ai, - combat.NewSingleTargetHit(t.Key()), - hitmark, - hitmark+5, - c.particleCB(), - c.SurgingBlade(), - c.c2(), - ) - } - - // remove the shrapnel after firing and action C1 c.QueueCharTask( func() { - c.c1(c.shrapnel) - if c.Base.Cons < 6 { - c.shrapnel = 0 - } else { - c.shrapnel -= 3 // C6 keeps any more than the three + c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) + shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 + + // Calculate buffs based on excess shrapnel + excess := math.Max(float64(c.shrapnel-3), 0) + m := make([]float64, attributes.EndStatType) + c.AddAttackMod(character.AttackMod{ + Base: modifier.NewBase("navia-skill-dmgup", hitmark+6), + Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { + if atk.Info.AttackTag != attacks.AttackTagElementalArt { + return nil, false + } + m[attributes.DmgP] = 0.15 * excess + if c.Base.Cons >= 2 { + m[attributes.CR] = 0.12 * excess + } + if c.Base.Cons >= 6 { + m[attributes.CD] = 0.45 * excess + } + return m, true + }, + }) + + // Looks for enemies in the path of each bullet + // Initially trims enemies to check by scanning only the hit zone + for _, t := range c.Core.Combat.EnemiesWithinArea( + combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 25, 15), + nil, + ) { + // Tallies up the hits + hits := 0 + for i := 0; i < shots; i++ { + if ok, _ := t.AttackWillLand(combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), + geometry.Point{Y: 0}, + 25, 15)); ok { + hits++ + } + } + // Applies damage based on the hits + ai.Mult = skillshotgun[c.TalentLvlSkill()] * skillMultiplier[hits] + c.Core.QueueAttack( + ai, + combat.NewSingleTargetHit(t.Key()), + 1, + 6, + c.particleCB(), + c.SurgingBlade(), + c.c2(), + ) + // remove the shrapnel after firing and action C1 and A1 + c.QueueCharTask( + func() { + c.c1(c.shrapnel) + if c.Base.Cons < 6 { + c.shrapnel = 0 + } else { + c.shrapnel -= 3 // C6 keeps any more than the three + } + c.a1() + return + }, + 1, + ) } - return }, - hitmark, - ) - // Add Geo Infusion - c.QueueCharTask( - c.a1, - hitmark, + hitmark-1, ) + c.c2ready = false if hold > 1 { return action.Info{ diff --git a/pkg/core/keys/sets.go b/pkg/core/keys/sets.go index 140b9481cc..86d45ef68b 100644 --- a/pkg/core/keys/sets.go +++ b/pkg/core/keys/sets.go @@ -59,7 +59,6 @@ var setNames = []string{ "maidenbeloved", "marechausseehunter", "martialartist", - "nighttimewhispers", "noblesseoblige", "nymphsdream", "oceanhuedclam", From 753dd9a024ad13e3c3020c1d909d1e8d6cf58057 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 14:37:40 +1100 Subject: [PATCH 24/36] Correct Frames based on footage Fixed Normal Attack Hitboxes Fixed Burst Artillery Timings Fixed Surging blade being a callback Adjusted for New Crystallise mechanism --- internal/characters/navia/attack.go | 24 ++- internal/characters/navia/burst.go | 39 +++-- internal/characters/navia/cons.go | 2 +- internal/characters/navia/skill.go | 249 ++++++++++++++++++++-------- 4 files changed, 215 insertions(+), 99 deletions(-) diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index 37cf92ac5a..da64618af5 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -12,12 +12,11 @@ import ( var ( attackFrames [][]int - attackHitmarks = [][]int{{29}, {40}, {59, 65, 71}, {82}} - attackHitlagHaltFrame = [][]float64{{0.09}, {.12}, {0, 0, 0}, {.09}} + attackHitmarks = [][]int{{23}, {22}, {31, 39, 38}, {41}} + attackHitlagHaltFrame = [][]float64{{0.06}, {0.06}, {0.01, 0.01, 0.01}, {.06}} attackDefHalt = [][]bool{{true}, {true}, {false, false, false}, {true}} - attackHitboxes = [][]float64{{2}, {2}, {2}, {2}} - attackOffsets = []float64{0.5, 0.5, 0.5, 0} - attackFanAngles = []float64{270, 270, 360, 270} + attackHitboxes = [][]float64{{2}, {4.3, 2}, {4.5, 3}, {4.7, 2}} + attackOffsets = []float64{0.5, -1.5, 1, -1.85} ) const normalHitNum = 4 @@ -25,10 +24,10 @@ const normalHitNum = 4 func init() { attackFrames = make([][]int, normalHitNum) - attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 32) + attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 28) attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 42) - attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 43) - attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 82) + attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 48) + attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 93) } func (c *char) Attack(_ map[string]int) (action.Info, error) { @@ -47,18 +46,17 @@ func (c *char) Attack(_ map[string]int) (action.Info, error) { HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter][i] * 60, CanBeDefenseHalted: attackDefHalt[c.NormalCounter][i], } - ap := combat.NewCircleHitOnTargetFanAngle( + ap := combat.NewBoxHitOnTarget( c.Core.Combat.Player(), geometry.Point{Y: attackOffsets[c.NormalCounter]}, attackHitboxes[c.NormalCounter][0], - attackFanAngles[c.NormalCounter], + attackHitboxes[c.NormalCounter][1], ) - if c.NormalCounter == 4 { - ap = combat.NewBoxHitOnTarget( + if c.NormalCounter == 1 { + ap = combat.NewCircleHitOnTarget( c.Core.Combat.Player(), geometry.Point{Y: attackOffsets[c.NormalCounter]}, attackHitboxes[c.NormalCounter][0], - attackHitboxes[c.NormalCounter][1], ) } c.QueueCharTask(func() { diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 5f2fdc7b3a..f008d55d3d 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -11,18 +11,22 @@ import ( "github.com/genshinsim/gcsim/pkg/core/targets" ) -var burstFrames []int +var ( + burstFrames []int +) const ( - burstHitmark = 100 + burstHitmark = 104 burstKey = "navia-artillery" burstDuration = 720 targetRadius = 10 + burstDelay = 154 ) func init() { - burstFrames = frames.InitAbilSlice(114) - burstFrames[action.ActionSkill] = 114 + burstFrames = frames.InitAbilSlice(102) + burstFrames[action.ActionSwap] = 93 + burstFrames[action.ActionWalk] = 127 } // On the orders of the President of the Spina di Rosula, call for a magnificent @@ -51,19 +55,19 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { c.QueueCharTask( func() { - c.AddStatus(burstKey, burstDuration, false) + c.AddStatus(burstKey, burstDuration+burstDelay, false) c.naviaburst = true }, - burstHitmark, + burstDelay, ) c.QueueCharTask( func() { c.naviaburst = false }, - burstHitmark+burstDuration, + burstDuration+burstDelay, ) - c.ConsumeEnergy(5) + c.ConsumeEnergy(12) c.SetCD(action.ActionBurst, 15*60) ai.Abil = "Fire Support" @@ -77,13 +81,12 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { Snapshot: snap, SourceFrame: c.Core.F, } - - for i := 45; i <= burstDuration; i += 45 { + for i, j := 3, 0; i <= burstDuration; i += BurstInterval(j) { c.Core.QueueAttack( ai, combat.NewCircleHitOnTarget(c.location(targetRadius), nil, 3), - burstHitmark+i, - burstHitmark+i, + burstDelay+i, + burstDelay+i+9, c.BurstCB(), c.c4(), ) @@ -97,6 +100,16 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { }, nil } +func BurstInterval(j int) int { + if j%3 == 1 { + j++ + return 48 + } else { + j++ + return 42 + } +} + // When attacks from Golden Rose's Salute hit opponents, Navia will gain 1 charge // of Crystal Shrapnel. // This effect can be triggered up to once every 2.4s. @@ -116,7 +129,7 @@ func (c *char) BurstCB() combat.AttackCBFunc { } - c.AddStatus("navia-q-shrapnel-icd", 2.4*60-1, false) + c.AddStatus("navia-q-shrapnel-icd", 2.4*60, false) } } diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index f166b9189f..15b7f11d25 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -60,7 +60,7 @@ func (c *char) c2() combat.AttackCBFunc { ai, combat.NewCircleHitOnTarget(e.Pos(), nil, 3), 0, - 0, + 9, c.BurstCB(), c.c4(), ) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 49223a6263..984b9b76ff 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -11,16 +11,19 @@ import ( "github.com/genshinsim/gcsim/pkg/core/geometry" "github.com/genshinsim/gcsim/pkg/core/glog" "github.com/genshinsim/gcsim/pkg/core/player/character" + "github.com/genshinsim/gcsim/pkg/core/player/shield" "github.com/genshinsim/gcsim/pkg/core/targets" "github.com/genshinsim/gcsim/pkg/enemy" "github.com/genshinsim/gcsim/pkg/modifier" + "github.com/genshinsim/gcsim/pkg/reactable" "math" ) var skillPressFrames []int +var skillPowerPressFrames []int var skillHoldFrames []int -var crystallise = []event.Event{event.OnCrystallizeElectro, event.OnCrystallizeCryo, event.OnCrystallizeHydro, - event.OnCrystallizePyro} +var skillPowerHoldFrames []int + var skillMultiplier = []float64{ 0, // 0 hits 1, // 1 hit @@ -36,38 +39,115 @@ var skillMultiplier = []float64{ 2, } +var hitscans = [][]float64{ + {0.9, 0, 0, 9}, + {0.25, 7.911, 0, 0}, + {0.25, -1.826, 0, 0}, + {0.25, -4.325, 0, 0}, + {0.25, 0.773, 0, 0}, + {0.25, 6.209, 0, 0}, + {0.25, -2.752, 0, 0}, + {0.25, 7.845, 0.01, 0.01}, + {0.25, -7.933, -0.01, -0.01}, + {0.25, 2.626, 0, 0}, + {0.25, -5.43724, 0, 0}, +} + const ( - skillPressCDStart = 30 - skillPressHitmark = 30 - skillHoldHitmark = 48 - arkheDelay = 12 - particleICDKey = "navia-particle-icd" - arkheICDKey = "navia-arkhe-icd" + skillPressCDStart = 11 + + travelDelay = 9 + + arkheDelay = 65 + + skillHoldCDStart = 41 + skillHoldDuration = 241 //an additional 1f to account for hold being set to 1 to activate + + particleICDKey = "navia-particle-icd" + arkheICDKey = "navia-arkhe-icd" ) func init() { - skillPressFrames = frames.InitAbilSlice(38) // E -> N1/Q - skillPressFrames[action.ActionDash] = 38 - skillPressFrames[action.ActionJump] = 38 + skillPressFrames = frames.InitAbilSlice(40) // E -> E/Q + skillPressFrames[action.ActionDash] = 24 + skillPressFrames[action.ActionJump] = 24 skillPressFrames[action.ActionSwap] = 38 + skillPressFrames[action.ActionWalk] = 35 + skillPressFrames[action.ActionAttack] = 38 + + // skill with >=3 shrapnel -> x + skillPowerPressFrames = frames.InitAbilSlice(41) // E -> E/Q + skillPowerPressFrames[action.ActionDash] = 26 + skillPowerPressFrames[action.ActionJump] = 24 + skillPowerPressFrames[action.ActionSwap] = 40 + skillPowerPressFrames[action.ActionWalk] = 39 + skillPowerPressFrames[action.ActionAttack] = 40 // skill (hold) -> x - skillHoldFrames = frames.InitAbilSlice(51) // E -> Swap - skillHoldFrames[action.ActionAttack] = 51 - skillHoldFrames[action.ActionBurst] = 51 - skillHoldFrames[action.ActionDash] = 51 - skillHoldFrames[action.ActionJump] = 51 + skillHoldFrames = frames.InitAbilSlice(71) // E -> E/Q + skillHoldFrames[action.ActionDash] = 54 + skillHoldFrames[action.ActionJump] = 54 + skillHoldFrames[action.ActionSwap] = 69 + skillHoldFrames[action.ActionWalk] = 65 + skillHoldFrames[action.ActionAttack] = 69 + + // skill (hold) with >=3 shrapnel -> x + skillPowerHoldFrames = frames.InitAbilSlice(73) // E -> E/Q + skillPowerHoldFrames[action.ActionDash] = 56 + skillPowerHoldFrames[action.ActionJump] = 56 + skillPowerHoldFrames[action.ActionSwap] = 71 + skillPowerHoldFrames[action.ActionWalk] = 70 + skillPowerHoldFrames[action.ActionAttack] = 72 } func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = true hold := p["hold"] - hitmark := 0 + firingTime := 0 if hold > 0 { - hitmark += skillHoldHitmark + hold - c.SetCDWithDelay(action.ActionSkill, 9*60, hitmark) + if hold > skillHoldDuration { + hold = skillHoldDuration + } + firingTime += skillHoldCDStart + hold - 1 + + // At 0.25, tap is converted to hold, and the suction begins + // TODO: Confirm suction begins at 15f + for i := 15; i < firingTime; i += 30 { + for j, k := 0, 0; j < c.Core.Combat.GadgetCount(); j++ { + cs, ok := c.Core.Combat.Gadget(j).(*reactable.CrystallizeShard) + // skip if not a shard + if !ok { + continue + } + + // If shard is out of 12m range, skip + if !cs.IsWithinArea(combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 12)) { + continue + } + + // approximate sucking in as 0.4m per frame (~8m distance took 20f to arrive at gorou) + distance := cs.Pos().Distance(c.Core.Combat.Player().Pos()) + travel := int(math.Ceil(distance / 0.4)) + // if the crystal won't arrive before the shot is fired, skip + if firingTime-i < travel { + continue + } + // special check to account for edge case if shard just spawned and will arrive before it can be picked up + if c.Core.F+travel < cs.EarliestPickup { + continue + } + c.Core.Tasks.Add(func() { + cs.AddShieldKillShard() + }, travel) + k++ + if k >= 3 { + break + } + } + } + c.SetCDWithDelay(action.ActionSkill, 9*60, skillHoldCDStart+hold) } else { - hitmark += skillPressHitmark + hold + firingTime += skillPressCDStart c.SetCDWithDelay(action.ActionSkill, 9*60, skillPressCDStart) } shots := 5 @@ -95,7 +175,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { excess := math.Max(float64(c.shrapnel-3), 0) m := make([]float64, attributes.EndStatType) c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBase("navia-skill-dmgup", hitmark+6), + Base: modifier.NewBase("navia-skill-dmgup", travelDelay), Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { if atk.Info.AttackTag != attacks.AttackTagElementalArt { return nil, false @@ -120,9 +200,10 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { // Tallies up the hits hits := 0 for i := 0; i < shots; i++ { - if ok, _ := t.AttackWillLand(combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), - geometry.Point{Y: 0}, - 25, 15)); ok { + if ok, _ := t.AttackWillLand(combat.NewBoxHitOnTarget(c.Core.Combat.Player(), geometry.Point{X: hitscans[i][2], + Y: hitscans[i][3]}.Rotate(geometry.Point{X: math.Sin(hitscans[i][0]), + Y: math.Cos(hitscans[i][0])}), + 11.5, hitscans[i][1])); ok { hits++ } } @@ -131,33 +212,36 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.Core.QueueAttack( ai, combat.NewSingleTargetHit(t.Key()), - 1, - 6, + 0, + travelDelay, c.particleCB(), - c.SurgingBlade(), c.c2(), ) - // remove the shrapnel after firing and action C1 and A1 - c.QueueCharTask( - func() { - c.c1(c.shrapnel) - if c.Base.Cons < 6 { - c.shrapnel = 0 - } else { - c.shrapnel -= 3 // C6 keeps any more than the three - } - c.a1() - return - }, - 1, - ) } + // remove the shrapnel after firing and action C1 and A1 + c.c1(c.shrapnel) + if c.Base.Cons < 6 { + c.shrapnel = 0 + } else { + c.shrapnel -= 3 // C6 keeps any more than the three + } + c.a1() }, - hitmark-1, + firingTime, ) + c.Core.Tasks.Add(c.SurgingBlade, firingTime) + c.c2ready = false if hold > 1 { + if shots >= 11 { + return action.Info{ + Frames: frames.NewAbilFunc(skillPowerHoldFrames), + AnimationLength: skillPowerHoldFrames[action.ActionDash] + hold, + CanQueueAfter: skillPowerHoldFrames[action.ActionDash] + hold, + State: action.SkillState, + }, nil + } return action.Info{ Frames: frames.NewAbilFunc(skillHoldFrames), AnimationLength: skillHoldFrames[action.ActionDash] + hold, @@ -165,6 +249,14 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { State: action.SkillState, }, nil } + if shots >= 11 { + return action.Info{ + Frames: frames.NewAbilFunc(skillPowerPressFrames), + AnimationLength: skillPowerPressFrames[action.ActionDash], + CanQueueAfter: skillPowerPressFrames[action.ActionDash], + State: action.SkillState, + }, nil + } return action.Info{ Frames: frames.NewAbilFunc(skillPressFrames), AnimationLength: skillPressFrames[action.ActionDash], @@ -199,48 +291,61 @@ func (c *char) particleCB() combat.AttackCBFunc { } } -// ShrapnelGain adds Shrapnel Stacks when crystallise occurs. Stacks should last 300s but this is way to long to bother +// ShrapnelGain adds Shrapnel Stacks when a Crystallise Shield is picked up. +// Stacks should last 300s but this is way to long to bother // When a character in the party obtains an Elemental Shard created from the Crystallize reaction, // Navia will gain 1 Crystal Shrapnel charge. Navia can hold up to 6 charges of Crystal Shrapnel at once. // Each time Crystal Shrapnel gain is triggered, the duration of the Shards you have already will be reset. func (c *char) ShrapnelGain() { + c.Core.Events.Subscribe(event.OnShielded, func(args ...interface{}) bool { + // Check shield + shd := args[0].(shield.Shield) - for _, crystal := range crystallise { - c.Core.Events.Subscribe(crystal, func(args ...interface{}) bool { - if c.shrapnel < 6 { - c.shrapnel++ - c.Core.Log.NewEvent("Crystal Shrapnel gained from Crystallise", glog.LogCharacterEvent, c.Index) - } + if shd.Type() != shield.Crystallize { return false - }, "shrapnel-gain") - } - -} - -func (c *char) SurgingBlade() combat.AttackCBFunc { - return func(a combat.AttackCB) { - if c.StatusIsActive(arkheICDKey) { - return } - c.AddStatus(arkheICDKey, 7*60, false) - ai := combat.AttackInfo{ - ActorIndex: c.Index, - Abil: "Surging Blade", - AttackTag: attacks.AttackTagElementalArt, - ICDTag: attacks.ICDTagNone, - ICDGroup: attacks.ICDGroupDefault, - StrikeType: attacks.StrikeTypeBlunt, - Element: attributes.Geo, - Durability: 0, - Mult: skillblade[c.TalentLvlSkill()], + if c.shrapnel < 6 { + c.shrapnel++ + c.Core.Log.NewEvent("Crystal Shrapnel gained from Crystallise", glog.LogCharacterEvent, c.Index) } + return false + }, "shrapnel-gain") +} + +func (c *char) SurgingBlade() { + if c.StatusIsActive(arkheICDKey) { + return + } + c.AddStatus(arkheICDKey, 7*60, false) + e := c.Core.Combat.ClosestEnemyWithinArea(combat.NewCircleHitFanAngle(c.Core.Combat.Player(), + geometry.Point{Y: 0}.Rotate(geometry.Point{Y: 0}), geometry.Point{Y: 0}, 11.5, 7.933+7.911), nil) + ai := combat.AttackInfo{ + ActorIndex: c.Index, + Abil: "Surging Blade", + AttackTag: attacks.AttackTagElementalArt, + ICDTag: attacks.ICDTagNone, + ICDGroup: attacks.ICDGroupDefault, + StrikeType: attacks.StrikeTypeBlunt, + Element: attributes.Geo, + Durability: 0, + Mult: skillblade[c.TalentLvlSkill()], + } + if e != nil { c.Core.QueueAttack( ai, - combat.NewCircleHitOnTarget(c.Core.Combat.PrimaryTarget(), geometry.Point{Y: 0}, 3), - arkheDelay, + combat.NewCircleHitOnTarget(e, geometry.Point{Y: 0}, 3), + 0, arkheDelay, nil, ) - + } else { + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.Core.Combat.Player(), geometry.Point{Y: 3}, 3), + 0, + 0, + nil, + ) } + return } From 3a2f755c4c61c975a7114a89275f6786d73d744e Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 17:59:01 +1100 Subject: [PATCH 25/36] Added N0 timings and removed incorrect import --- .vscode/settings.json | 2 +- internal/characters/navia/asc.go | 2 +- internal/characters/navia/attack.go | 2 +- internal/characters/navia/burst.go | 2 +- internal/characters/navia/cons.go | 2 +- internal/characters/navia/navia.go | 2 +- internal/characters/navia/skill.go | 2 +- internal/characters/navia/stats.go | 2 +- internal/common/normalattackstate.go | 4 +++- pkg/core/keys/char.go | 2 +- pkg/core/keys/sets.go | 2 +- pkg/core/keys/weapon.go | 2 +- pkg/shortcut/artifacts.go | 2 +- pkg/shortcut/characters.go | 2 +- pkg/shortcut/weapons.go | 2 +- pkg/simulation/imports.go | 3 +-- 16 files changed, 18 insertions(+), 17 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 93527bab56..3b017dacb5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,7 @@ { "editor.formatOnSave": true, "editor.codeActionsOnSave": { - "source.fixAll.eslint": true + "source.fixAll.eslint": "explicit" }, "[javascript]": { diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index 57939f42c8..9de4f0b278 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -68,4 +68,4 @@ func (c *char) a4() { return m, true }, }) -} +} \ No newline at end of file diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index da64618af5..b4ce6116dc 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -72,4 +72,4 @@ func (c *char) Attack(_ map[string]int) (action.Info, error) { CanQueueAfter: attackHitmarks[c.NormalCounter][len(attackHitmarks[c.NormalCounter])-1], State: action.NormalAttackState, }, nil -} +} \ No newline at end of file diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index f008d55d3d..db958e9049 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -147,4 +147,4 @@ func (c *char) location(r float64) geometry.Point { pos = geometry.CalcRandomPointFromCenter(c.Core.Combat.Player().Pos(), 0, r, c.Core.Rand) } return pos -} +} \ No newline at end of file diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 15b7f11d25..20b84fbf59 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -86,4 +86,4 @@ func (c *char) c4() combat.AttackCBFunc { }) } -} +} \ No newline at end of file diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index 75a98d8a5d..5f1d385b37 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -70,4 +70,4 @@ func (c *char) Snapshot(ai *combat.AttackInfo) combat.Snapshot { ai.Element = attributes.Geo } return ds -} +} \ No newline at end of file diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 984b9b76ff..9ad47a90d1 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -348,4 +348,4 @@ func (c *char) SurgingBlade() { ) } return -} +} \ No newline at end of file diff --git a/internal/characters/navia/stats.go b/internal/characters/navia/stats.go index 282b3ea39d..9399970093 100644 --- a/internal/characters/navia/stats.go +++ b/internal/characters/navia/stats.go @@ -189,4 +189,4 @@ var ( 1.025, }, } -) +) \ No newline at end of file diff --git a/internal/common/normalattackstate.go b/internal/common/normalattackstate.go index 56c052aa57..f383175866 100644 --- a/internal/common/normalattackstate.go +++ b/internal/common/normalattackstate.go @@ -55,7 +55,7 @@ func init() { percentDelay5[keys.LumineGeo] = 7 percentDelay5[keys.LumineHydro] = 7 percentDelay5[keys.LuminePyro] = 7 - + percentDelay5[keys.Navia] = 19 percentDelay5[keys.Nilou] = 11 // I didn't test Nilou E stance, assuming it's the same values for now @@ -162,6 +162,8 @@ func Get0PercentN0Delay(activeChar *character.CharWrapper) int { return 5 case keys.Neuvillette: return 0 + case keys.Navia: + return 19 } return 0 } diff --git a/pkg/core/keys/char.go b/pkg/core/keys/char.go index ade944d678..f5155ab4f2 100644 --- a/pkg/core/keys/char.go +++ b/pkg/core/keys/char.go @@ -414,4 +414,4 @@ var CharKeyToEle = map[Char]attributes.Element{ Furina: attributes.Hydro, Navia: attributes.Geo, TestCharDoNotUse: attributes.Geo, -} +} \ No newline at end of file diff --git a/pkg/core/keys/sets.go b/pkg/core/keys/sets.go index 4f082bd4d1..7c87cb3aa4 100644 --- a/pkg/core/keys/sets.go +++ b/pkg/core/keys/sets.go @@ -135,4 +135,4 @@ const ( VermillionHereafter ViridescentVenerer WanderersTroupe -) +) \ No newline at end of file diff --git a/pkg/core/keys/weapon.go b/pkg/core/keys/weapon.go index 3945e67ec5..5c5a3454a4 100644 --- a/pkg/core/keys/weapon.go +++ b/pkg/core/keys/weapon.go @@ -393,4 +393,4 @@ const ( WolfFang WolfsGravestone XiphosMoonlight -) +) \ No newline at end of file diff --git a/pkg/shortcut/artifacts.go b/pkg/shortcut/artifacts.go index 0fc159a8b0..2506eaddc1 100644 --- a/pkg/shortcut/artifacts.go +++ b/pkg/shortcut/artifacts.go @@ -114,4 +114,4 @@ var SetNameToKey = map[string]keys.Set{ "wandererstroupe": keys.WanderersTroupe, "wanderers": keys.WanderersTroupe, "wt": keys.WanderersTroupe, -} +} \ No newline at end of file diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index 1a86816aa9..abd7e728d6 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -155,4 +155,4 @@ var CharNameToKey = map[string]keys.Char{ "freminet": keys.Freminet, "furina": keys.Furina, "furinadefontaine": keys.Furina, -} +} \ No newline at end of file diff --git a/pkg/shortcut/weapons.go b/pkg/shortcut/weapons.go index 4694bc2ec5..f062381b08 100644 --- a/pkg/shortcut/weapons.go +++ b/pkg/shortcut/weapons.go @@ -322,4 +322,4 @@ var WeaponNameToKey = map[string]keys.Weapon{ "wgs": keys.WolfsGravestone, "xiphosmoonlight": keys.XiphosMoonlight, "xiphos": keys.XiphosMoonlight, -} +} \ No newline at end of file diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index 80c12165af..2b7f2d2c02 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -38,7 +38,6 @@ import ( _ "github.com/genshinsim/gcsim/internal/artifacts/maiden" _ "github.com/genshinsim/gcsim/internal/artifacts/marechausseehunter" _ "github.com/genshinsim/gcsim/internal/artifacts/martialartist" - _ "github.com/genshinsim/gcsim/internal/artifacts/nighttimewhispersintheechoingwoods" _ "github.com/genshinsim/gcsim/internal/artifacts/noblesse" _ "github.com/genshinsim/gcsim/internal/artifacts/nymphsdream" _ "github.com/genshinsim/gcsim/internal/artifacts/oceanhuedclam" @@ -321,4 +320,4 @@ import ( _ "github.com/genshinsim/gcsim/internal/weapons/sword/travelershandysword" _ "github.com/genshinsim/gcsim/internal/weapons/sword/wolffang" _ "github.com/genshinsim/gcsim/internal/weapons/sword/xiphos" -) +) \ No newline at end of file From 637a753f8a7c4ca9de16eac4f88474358cbc3f8a Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 19:08:26 +1100 Subject: [PATCH 26/36] Fixed NA out of range error, and incorrect Skill hitboxes --- internal/characters/navia/attack.go | 8 ++++---- internal/characters/navia/skill.go | 10 +++++----- pkg/simulation/imports.go | 1 + 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index b4ce6116dc..3ba7f1bed6 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -46,17 +46,17 @@ func (c *char) Attack(_ map[string]int) (action.Info, error) { HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter][i] * 60, CanBeDefenseHalted: attackDefHalt[c.NormalCounter][i], } - ap := combat.NewBoxHitOnTarget( + ap := combat.NewCircleHitOnTarget( c.Core.Combat.Player(), geometry.Point{Y: attackOffsets[c.NormalCounter]}, attackHitboxes[c.NormalCounter][0], - attackHitboxes[c.NormalCounter][1], ) - if c.NormalCounter == 1 { - ap = combat.NewCircleHitOnTarget( + if c.NormalCounter != 0 { + ap = combat.NewBoxHitOnTarget( c.Core.Combat.Player(), geometry.Point{Y: attackOffsets[c.NormalCounter]}, attackHitboxes[c.NormalCounter][0], + attackHitboxes[c.NormalCounter][1], ) } c.QueueCharTask(func() { diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 9ad47a90d1..784b3e7510 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -40,7 +40,7 @@ var skillMultiplier = []float64{ } var hitscans = [][]float64{ - {0.9, 0, 0, 9}, + {0.9, 0, 0, 0}, {0.25, 7.911, 0, 0}, {0.25, -1.826, 0, 0}, {0.25, -4.325, 0, 0}, @@ -51,6 +51,7 @@ var hitscans = [][]float64{ {0.25, -7.933, -0.01, -0.01}, {0.25, 2.626, 0, 0}, {0.25, -5.43724, 0, 0}, + //width, angle, x offset, y offset } const ( @@ -194,16 +195,15 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { // Looks for enemies in the path of each bullet // Initially trims enemies to check by scanning only the hit zone for _, t := range c.Core.Combat.EnemiesWithinArea( - combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 25, 15), + combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 11.5, 15), nil, ) { // Tallies up the hits hits := 0 for i := 0; i < shots; i++ { if ok, _ := t.AttackWillLand(combat.NewBoxHitOnTarget(c.Core.Combat.Player(), geometry.Point{X: hitscans[i][2], - Y: hitscans[i][3]}.Rotate(geometry.Point{X: math.Sin(hitscans[i][0]), - Y: math.Cos(hitscans[i][0])}), - 11.5, hitscans[i][1])); ok { + Y: hitscans[i][3]}.Rotate(geometry.DegreesToDirection(hitscans[i][1])), + hitscans[i][0], 11.5)); ok { hits++ } } diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index 2b7f2d2c02..6375b57170 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -38,6 +38,7 @@ import ( _ "github.com/genshinsim/gcsim/internal/artifacts/maiden" _ "github.com/genshinsim/gcsim/internal/artifacts/marechausseehunter" _ "github.com/genshinsim/gcsim/internal/artifacts/martialartist" + _ "github.com/genshinsim/gcsim/internal/artifacts/nighttimewhispersintheechoingwoods" _ "github.com/genshinsim/gcsim/internal/artifacts/noblesse" _ "github.com/genshinsim/gcsim/internal/artifacts/nymphsdream" _ "github.com/genshinsim/gcsim/internal/artifacts/oceanhuedclam" From 011f953c08bf85508332ec5f03ebc7620597d26a Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 19:26:14 +1100 Subject: [PATCH 27/36] Cleaned up Pull Crystal Code Slightly --- internal/characters/navia/skill.go | 69 ++++++++++++++++-------------- 1 file changed, 38 insertions(+), 31 deletions(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 784b3e7510..0b5bcc0e79 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -114,37 +114,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { // At 0.25, tap is converted to hold, and the suction begins // TODO: Confirm suction begins at 15f for i := 15; i < firingTime; i += 30 { - for j, k := 0, 0; j < c.Core.Combat.GadgetCount(); j++ { - cs, ok := c.Core.Combat.Gadget(j).(*reactable.CrystallizeShard) - // skip if not a shard - if !ok { - continue - } - - // If shard is out of 12m range, skip - if !cs.IsWithinArea(combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 12)) { - continue - } - - // approximate sucking in as 0.4m per frame (~8m distance took 20f to arrive at gorou) - distance := cs.Pos().Distance(c.Core.Combat.Player().Pos()) - travel := int(math.Ceil(distance / 0.4)) - // if the crystal won't arrive before the shot is fired, skip - if firingTime-i < travel { - continue - } - // special check to account for edge case if shard just spawned and will arrive before it can be picked up - if c.Core.F+travel < cs.EarliestPickup { - continue - } - c.Core.Tasks.Add(func() { - cs.AddShieldKillShard() - }, travel) - k++ - if k >= 3 { - break - } - } + c.PullCrystals(firingTime, i) } c.SetCDWithDelay(action.ActionSkill, 9*60, skillHoldCDStart+hold) } else { @@ -348,4 +318,41 @@ func (c *char) SurgingBlade() { ) } return +} + +// PullCrystals to Navia. This has a range of 12m from datamine, so +// check every every 30f. +func (c *char) PullCrystals(firingTime, i int) { + for j, k := 0, 0; j < c.Core.Combat.GadgetCount(); j++ { + cs, ok := c.Core.Combat.Gadget(j).(*reactable.CrystallizeShard) + // skip if not a shard + if !ok { + continue + } + + // If shard is out of 12m range, skip + if !cs.IsWithinArea(combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 12)) { + continue + } + + // approximate sucking in as 0.4m per frame (~8m distance took 20f to arrive at gorou) + distance := cs.Pos().Distance(c.Core.Combat.Player().Pos()) + travel := int(math.Ceil(distance / 0.4)) + // if the crystal won't arrive before the shot is fired, skip + if firingTime-i < travel { + continue + } + // special check to account for edge case if shard just spawned and will arrive before it can be picked up + if c.Core.F+travel < cs.EarliestPickup { + continue + } + c.Core.Tasks.Add(func() { + cs.AddShieldKillShard() + }, travel) + // max three crystals + k++ + if k >= 3 { + break + } + } } \ No newline at end of file From c81a2d2a0c093c5555bebd16b2f2c12ee46e4e1b Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 21:18:28 +1100 Subject: [PATCH 28/36] Fixed Navia's Skill buffs not applying to her shotgun due to timeout --- internal/characters/navia/skill.go | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 0b5bcc0e79..6c06f4be14 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -122,9 +122,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.SetCDWithDelay(action.ActionSkill, 9*60, skillPressCDStart) } shots := 5 - if p["shrapnel"] != 0 { - c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) - } + ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "Rosula Shardshot", @@ -139,6 +137,9 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.QueueCharTask( func() { + if p["shrapnel"] != 0 { + c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) + } c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 @@ -146,7 +147,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { excess := math.Max(float64(c.shrapnel-3), 0) m := make([]float64, attributes.EndStatType) c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBase("navia-skill-dmgup", travelDelay), + Base: modifier.NewBase("navia-skill-dmgup", travelDelay+1), Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { if atk.Info.AttackTag != attacks.AttackTagElementalArt { return nil, false From d3ef43a5c36f4ece88ed80bb3364c17f87c7ab34 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 21:35:26 +1100 Subject: [PATCH 29/36] Fixed NA starting during Hold Skill --- internal/characters/navia/skill.go | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 6c06f4be14..8702d06c91 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -103,11 +103,12 @@ func init() { func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = true + shrapnel := 0 hold := p["hold"] firingTime := 0 if hold > 0 { if hold > skillHoldDuration { - hold = skillHoldDuration + hold = skillHoldDuration - 1 } firingTime += skillHoldCDStart + hold - 1 @@ -142,7 +143,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { } c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 - + shrapnel = c.shrapnel // Calculate buffs based on excess shrapnel excess := math.Max(float64(c.shrapnel-3), 0) m := make([]float64, attributes.EndStatType) @@ -205,32 +206,32 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = false if hold > 1 { - if shots >= 11 { + if shrapnel >= 3 { return action.Info{ - Frames: frames.NewAbilFunc(skillPowerHoldFrames), - AnimationLength: skillPowerHoldFrames[action.ActionDash] + hold, + Frames: func(next action.Action) int { return hold + skillPowerHoldFrames[next] }, + AnimationLength: skillPowerHoldFrames[action.InvalidAction] + hold, CanQueueAfter: skillPowerHoldFrames[action.ActionDash] + hold, State: action.SkillState, }, nil } return action.Info{ - Frames: frames.NewAbilFunc(skillHoldFrames), - AnimationLength: skillHoldFrames[action.ActionDash] + hold, + Frames: func(next action.Action) int { return hold + skillHoldFrames[next] }, + AnimationLength: skillHoldFrames[action.InvalidAction] + hold, CanQueueAfter: skillHoldFrames[action.ActionDash] + hold, State: action.SkillState, }, nil } - if shots >= 11 { + if shrapnel >= 3 { return action.Info{ Frames: frames.NewAbilFunc(skillPowerPressFrames), - AnimationLength: skillPowerPressFrames[action.ActionDash], + AnimationLength: skillPowerPressFrames[action.InvalidAction], CanQueueAfter: skillPowerPressFrames[action.ActionDash], State: action.SkillState, }, nil } return action.Info{ Frames: frames.NewAbilFunc(skillPressFrames), - AnimationLength: skillPressFrames[action.ActionDash], + AnimationLength: skillPressFrames[action.InvalidAction], CanQueueAfter: skillPressFrames[action.ActionDash], State: action.SkillState, }, nil From 17b91fe36158c79751de206dc5ca83d9c2c8a58f Mon Sep 17 00:00:00 2001 From: David Ji Date: Sat, 23 Dec 2023 21:36:33 +1100 Subject: [PATCH 30/36] Fixed NA starting during Hold Skill --- internal/characters/navia/skill.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 8702d06c91..c1b2ac84dc 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -206,7 +206,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.c2ready = false if hold > 1 { - if shrapnel >= 3 { + if shrapnel >= 3 { //when does this get evaluated. It needs to look ahead to determine how long the lock out is? return action.Info{ Frames: func(next action.Action) int { return hold + skillPowerHoldFrames[next] }, AnimationLength: skillPowerHoldFrames[action.InvalidAction] + hold, From bc3e60167e5925a764ed2267362acd49c121a011 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 15:01:16 +1100 Subject: [PATCH 31/36] Fixed comments, updated locale, ran pipeline, added documentation, fixed pruning hitbox for skill --- internal/characters/navia/asc.go | 16 +- internal/characters/navia/attack.go | 24 +- internal/characters/navia/burst.go | 136 +++--- .../navia/{config.yaml => config.yml} | 0 internal/characters/navia/cons.go | 52 +- internal/characters/navia/navia.go | 18 +- internal/characters/navia/skill.go | 462 +++++++++--------- internal/characters/navia/stats.go | 280 +++++------ pkg/core/attacks/icd.go | 5 + pkg/core/curves/charactercurves.go | 52 +- pkg/shortcut/characters.go | 7 +- .../db/src/Data/char_data.generated.json | 15 + .../docs/docs/reference/characters/navia.md | 44 ++ .../components/Actions/character_data.json | 34 ++ .../src/components/AoE/character_data.json | 146 ++++++ .../src/components/Fields/character_data.json | 8 + .../src/components/Frames/character_data.json | 8 + .../src/components/Hitlag/character_data.json | 46 ++ .../src/components/Names/character_data.json | 4 + .../src/components/Params/character_data.json | 12 + .../ui/src/Data/char_data.generated.json | 15 + .../ui/src/Data/weapon_data.generated.json | 6 +- .../src/Translation/locales/IngameNames.json | 6 + 23 files changed, 868 insertions(+), 528 deletions(-) rename internal/characters/navia/{config.yaml => config.yml} (100%) create mode 100644 ui/packages/docs/docs/reference/characters/navia.md diff --git a/internal/characters/navia/asc.go b/internal/characters/navia/asc.go index 9de4f0b278..4ad34a60bf 100644 --- a/internal/characters/navia/asc.go +++ b/internal/characters/navia/asc.go @@ -9,9 +9,9 @@ import ( "github.com/genshinsim/gcsim/pkg/modifier" ) -func init() { - -} +const ( + a1Key = "navia-a1-dmg" +) // For 4s after using Ceremonial Crystalshot, the DMG dealt by Navia's Normal Attacks, // Charged Attacks, and Plunging Attacks will be converted into Geo DMG which cannot @@ -21,12 +21,12 @@ func (c *char) a1() { if c.Base.Ascension < 1 { return } - c.Core.Log.NewEvent("infusion added", glog.LogCharacterEvent, c.Index) + c.Core.Log.NewEvent("a1 infusion added", glog.LogCharacterEvent, c.Index) // add Damage Bonus m := make([]float64, attributes.EndStatType) c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBaseWithHitlag("navia-a1-dmg", 60*4), // 4s + Base: modifier.NewBaseWithHitlag(a1Key, 60*4), // 4s Amount: func(atk *combat.AttackEvent, _ combat.Target) ([]float64, bool) { // skip if not normal/charged/plunge if atk.Info.AttackTag != attacks.AttackTagNormal && @@ -50,8 +50,8 @@ func (c *char) a4() { ele := 0 for _, char := range c.Core.Player.Chars() { - if char.Base.Element != attributes.Geo && char.Base. - Element != attributes.Anemo && char.Base.Element != attributes.Dendro { + switch char.Base.Element { + case attributes.Pyro, attributes.Electro, attributes.Cryo, attributes.Hydro: ele++ } } @@ -68,4 +68,4 @@ func (c *char) a4() { return m, true }, }) -} \ No newline at end of file +} diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index 3ba7f1bed6..59537f7946 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -12,11 +12,12 @@ import ( var ( attackFrames [][]int - attackHitmarks = [][]int{{23}, {22}, {31, 39, 38}, {41}} - attackHitlagHaltFrame = [][]float64{{0.06}, {0.06}, {0.01, 0.01, 0.01}, {.06}} + attackHitmarks = [][]int{{23}, {22}, {31, 39, 48}, {41}} + attackHitlagHaltFrame = [][]float64{{0.06}, {0.06}, {0.01, 0.01, 0.01}, {0.06}} attackDefHalt = [][]bool{{true}, {true}, {false, false, false}, {true}} - attackHitboxes = [][]float64{{2}, {4.3, 2}, {4.5, 3}, {4.7, 2}} - attackOffsets = []float64{0.5, -1.5, 1, -1.85} + attackHitboxes = [][]float64{{2}, {2, 4.3}, {3, 4.5}, {2, 4.7}} + attackOffsets = []float64{0.5, -1.5, 0.3, -1.85} + attackEarliestCancel = []int{23, 22, 29, 41} ) const normalHitNum = 4 @@ -24,10 +25,12 @@ const normalHitNum = 4 func init() { attackFrames = make([][]int, normalHitNum) - attackFrames[0] = frames.InitNormalCancelSlice(attackHitmarks[0][0], 28) - attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1][0], 42) - attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2][0], 48) - attackFrames[3] = frames.InitNormalCancelSlice(attackHitmarks[3][0], 93) + attackFrames[0] = frames.InitNormalCancelSlice(attackEarliestCancel[0], 28) + attackFrames[1] = frames.InitNormalCancelSlice(attackEarliestCancel[1], 42) + attackFrames[2] = frames.InitNormalCancelSlice(attackEarliestCancel[2], 48) + attackFrames[2][action.ActionSkill] = 30 + attackFrames[3] = frames.InitNormalCancelSlice(attackEarliestCancel[3], 93) + } func (c *char) Attack(_ map[string]int) (action.Info, error) { @@ -45,6 +48,7 @@ func (c *char) Attack(_ map[string]int) (action.Info, error) { HitlagFactor: 0.01, HitlagHaltFrames: attackHitlagHaltFrame[c.NormalCounter][i] * 60, CanBeDefenseHalted: attackDefHalt[c.NormalCounter][i], + IsDeployable: c.NormalCounter == 2, } ap := combat.NewCircleHitOnTarget( c.Core.Combat.Player(), @@ -69,7 +73,7 @@ func (c *char) Attack(_ map[string]int) (action.Info, error) { return action.Info{ Frames: frames.NewAttackFunc(c.Character, attackFrames), AnimationLength: attackFrames[c.NormalCounter][action.InvalidAction], - CanQueueAfter: attackHitmarks[c.NormalCounter][len(attackHitmarks[c.NormalCounter])-1], + CanQueueAfter: attackEarliestCancel[c.NormalCounter], State: action.NormalAttackState, }, nil -} \ No newline at end of file +} diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index db958e9049..f02a7ef442 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -19,20 +19,22 @@ const ( burstHitmark = 104 burstKey = "navia-artillery" burstDuration = 720 - targetRadius = 10 burstDelay = 154 + burstICDKey = "navia-q-shrapnel-icd" ) func init() { - burstFrames = frames.InitAbilSlice(102) + burstFrames = frames.InitAbilSlice(127) + burstFrames[action.ActionAttack] = 102 + burstFrames[action.ActionSkill] = 102 + burstFrames[action.ActionDash] = 103 + burstFrames[action.ActionJump] = 103 burstFrames[action.ActionSwap] = 93 - burstFrames[action.ActionWalk] = 127 } -// On the orders of the President of the Spina di Rosula, call for a magnificent -// Golden Rose Salute. Unleashes a massive bombardment on opponents in front of her, -// dealing Aoe Geo DMG and providing Fire Support for a duration afterward, periodically -// dealing Geo DMG. +// On the orders of the President of the Spina di Rosula, call for a magnificent Rosula Dorata Salute. +// Unleashes a massive cannon bombardment on opponents in front of her, dealing AoE Geo DMG and +// providing Cannon Fire Support for a duration afterward, periodically dealing Geo DMG to nearby opponents. func (c *char) Burst(_ map[string]int) (action.Info, error) { ai := combat.AttackInfo{ ActorIndex: c.Index, @@ -48,50 +50,48 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { c.Core.QueueAttack( ai, - combat.NewBoxHit(c.Core.Combat.Player(), c.Core.Combat.Player(), geometry.Point{Y: 0}, 12, 6), + combat.NewBoxHitOnTarget(c.Core.Combat.Player(), nil, 5, 12), burstHitmark, burstHitmark, + c.burstCB(), + c.c4(), ) - c.QueueCharTask( - func() { - c.AddStatus(burstKey, burstDuration+burstDelay, false) - c.naviaburst = true - }, - burstDelay, - ) - c.QueueCharTask( - func() { - c.naviaburst = false - }, - burstDuration+burstDelay, - ) + c.QueueCharTask(func() { + c.AddStatus(burstKey, burstDuration, false) + + ai.Abil = "Cannon Fire Support" + ai.ICDTag = attacks.ICDTagElementalBurst + ai.ICDGroup = attacks.ICDGroupNaviaBurst + ai.Durability = 25 + ai.Mult = burst[1][c.TalentLvlBurst()] + + tick := 0 + var nextTick int + for i := 0; i <= burstDuration; i += nextTick { + tick++ + c.Core.Tasks.Add(func() { + // queue attack + c.Core.QueueAttack( + ai, + combat.NewCircleHitOnTarget(c.calcCannonPos(), nil, 3), + 0, + 9, + c.burstCB(), + c.c4(), + ) + }, i) + // if tick 2, 5, 8, 11, 14 was queued then the next tick is in 48f instead of 42f + if tick%3 == 2 { + nextTick = 48 + } else { + nextTick = 42 + } + } + }, burstDelay) c.ConsumeEnergy(12) c.SetCD(action.ActionBurst, 15*60) - - ai.Abil = "Fire Support" - ai.ICDTag = attacks.ICDTagElementalBurst - ai.Durability = 25 - ai.Mult = burst[1][c.TalentLvlBurst()] - - snap := c.Snapshot(&ai) - c.artillerySnapshot = combat.AttackEvent{ - Info: ai, - Snapshot: snap, - SourceFrame: c.Core.F, - } - for i, j := 3, 0; i <= burstDuration; i += BurstInterval(j) { - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(c.location(targetRadius), nil, 3), - burstDelay+i, - burstDelay+i+9, - c.BurstCB(), - c.c4(), - ) - } - return action.Info{ Frames: frames.NewAbilFunc(burstFrames), AnimationLength: burstFrames[action.InvalidAction], @@ -100,51 +100,53 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { }, nil } -func BurstInterval(j int) int { - if j%3 == 1 { - j++ - return 48 - } else { - j++ - return 42 - } -} - -// When attacks from Golden Rose's Salute hit opponents, Navia will gain 1 charge -// of Crystal Shrapnel. +// When cannon attacks hit opponents, Navia will gain 1 stack of Crystal Shrapnel. // This effect can be triggered up to once every 2.4s. -func (c *char) BurstCB() combat.AttackCBFunc { +func (c *char) burstCB() combat.AttackCBFunc { return func(a combat.AttackCB) { - if c.StatusIsActive("navia-q-shrapnel-icd") { + if c.StatusIsActive(burstICDKey) { return } + c.AddStatus(burstICDKey, 2.4*60, true) if a.Target.Type() != targets.TargettableEnemy { return } if c.shrapnel < 6 { c.shrapnel++ - c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index) + c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index).Write("shrapnel", c.shrapnel) } - c.AddStatus("navia-q-shrapnel-icd", 2.4*60, false) } } // Targets a random enemy if there is an enemy present, if not, it targets a random spot -func (c *char) location(r float64) geometry.Point { +func (c *char) calcCannonPos() geometry.Point { + player := c.Core.Combat.Player() // gadget is attached to player + + // look for random enemy within 10m radius from player pos enemy := c.Core.Combat.RandomEnemyWithinArea( combat.NewCircleHitOnTarget(c.Core.Combat.Player().Pos(), nil, 10), nil, ) - var pos geometry.Point + + // enemy found: choose random point between 0 and 1.2m from their pos if enemy != nil { - pos = enemy.Pos() - } else { - pos = geometry.CalcRandomPointFromCenter(c.Core.Combat.Player().Pos(), 0, r, c.Core.Rand) + return geometry.CalcRandomPointFromCenter(enemy.Pos(), 0, 1.2, c.Core.Rand) } - return pos -} \ No newline at end of file + + // no enemy: targeting is randomly between 1m and 6m from player pos + Y: 4 + return geometry.CalcRandomPointFromCenter( + geometry.CalcOffsetPoint( + player.Pos(), + geometry.Point{Y: 4}, + player.Direction(), + ), + 1, + 6, + c.Core.Rand, + ) +} diff --git a/internal/characters/navia/config.yaml b/internal/characters/navia/config.yml similarity index 100% rename from internal/characters/navia/config.yaml rename to internal/characters/navia/config.yml diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 20b84fbf59..da47e4520b 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -5,41 +5,43 @@ import ( "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/targets" "github.com/genshinsim/gcsim/pkg/enemy" "github.com/genshinsim/gcsim/pkg/modifier" - "math" ) -// Each charge of Crystal Shrapnel consumed when Navia uses Ceremonial Crystalshot will -// restore 2 Energy to her and decrease the CD of As the Sunlit Sky's Singing Salute by 1s. -// Up to 6 Energy can be gained this way, and the CD of Ceremonial Crystalshot can be -// decreased by up to 3s. +const c2IcdKey = "navia-c2-icd" + +// Each stack of Crystal Shrapnel consumed when Navia uses Ceremonial Crystalshot will +// restore 3 Energy to her and decrease the CD of As the Sunlit Sky's Singing Salute by 1s. +// Up to 9 Energy can be gained this way, and the CD of "As the Sunlit Sky's Singing Salute" +// can be decreased by up to 3s. func (c *char) c1(shrapnel int) { if c.Base.Cons < 1 { return } - count := math.Min(float64(shrapnel), 3) - c.ReduceActionCooldown(action.ActionBurst, int(count*60)) - c.AddEnergy("navia-c1-energy", count*3) + count := min(shrapnel, 3) + c.ReduceActionCooldown(action.ActionBurst, count*60) + c.AddEnergy("navia-c1-energy", float64(count*3)) return } -// The CRIT Rate of Ceremonial Crystalshot is increased by 8% for each charge of Crystal -// Shrapnel consumed. CRIT Rate can be increased by up to 24% in this way. -// In addition, when Ceremonial Crystalshot hits an opponent, one shot of Fire Support -// from As the Sunlit Sky's Singing Salute will strike near the location of the hit. -// Up to one instance of Fire Support can be triggered each time Ceremonial Crystalshot is used, -// and DMG dealt by Fire Support in this way is considered Elemental Burst DMG. +// Each stack of Crystal Shrapnel consumed will increase the CRIT Rate of this +// Ceremonial Crystalshot instance by 12%. CRIT Rate can be increased by up to 36% in this way. +// In addition, when Ceremonial Crystalshot hits an opponent, one Cannon Fire Support shot from +// As the Sunlit Sky's Singing Salute will strike near the location of the hit. +// Up to one instance of Cannon Fire Support can be triggered each time Ceremonial Crystalshot is used, +// and DMG dealt by said Cannon Fire Support this way is considered Elemental Burst DMG. func (c *char) c2() combat.AttackCBFunc { + if c.Base.Cons < 2 { + return nil + } return func(a combat.AttackCB) { - if c.Base.Cons < 2 { + if c.StatusIsActive(c2IcdKey) { return } - if !c.c2ready { - return - } - c.c2ready = false + c.AddStatus(c2IcdKey, 0.25*60, true) e := a.Target.(*enemy.Enemy) if e.Type() != targets.TargettableEnemy { return @@ -50,18 +52,18 @@ func (c *char) c2() combat.AttackCBFunc { Abil: "The President's Pursuit of Victory", AttackTag: attacks.AttackTagElementalBurst, ICDTag: attacks.ICDTagElementalBurst, - ICDGroup: attacks.ICDGroupDefault, + ICDGroup: attacks.ICDGroupNaviaBurst, StrikeType: attacks.StrikeTypeBlunt, Element: attributes.Geo, Durability: 25, - Mult: burst[1][c.TalentLvlSkill()], + Mult: burst[1][c.TalentLvlBurst()], } c.Core.QueueAttack( ai, - combat.NewCircleHitOnTarget(e.Pos(), nil, 3), + combat.NewCircleHitOnTarget(geometry.CalcRandomPointFromCenter(e.Pos(), 0, 1.2, c.Core.Rand), nil, 3), 0, - 9, - c.BurstCB(), + 23, + c.burstCB(), c.c4(), ) } @@ -86,4 +88,4 @@ func (c *char) c4() combat.AttackCBFunc { }) } -} \ No newline at end of file +} diff --git a/internal/characters/navia/navia.go b/internal/characters/navia/navia.go index 5f1d385b37..b6e067a34e 100644 --- a/internal/characters/navia/navia.go +++ b/internal/characters/navia/navia.go @@ -18,17 +18,14 @@ func init() { type char struct { *tmpl.Character - shrapnel int - artillerySnapshot combat.AttackEvent - naviaburst bool - c2ready bool + shrapnel int } func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) error { c := char{} c.Character = tmpl.NewWithWrapper(s, w) c.EnergyMax = 60 - c.NormalHitNum = 4 + c.NormalHitNum = normalHitNum c.SkillCon = 3 c.BurstCon = 5 c.SetNumCharges(action.ActionSkill, 2) @@ -39,8 +36,7 @@ func NewChar(s *core.Core, w *character.CharWrapper, _ info.CharacterProfile) er func (c *char) Init() error { c.a4() - c.shrapnel = 0 - c.ShrapnelGain() + c.shrapnelGain() return nil } @@ -48,10 +44,6 @@ func (c *char) Condition(fields []string) (any, error) { switch fields[0] { case "shrapnel": return c.shrapnel, nil - case "navia-burst": - return c.naviaburst, nil - case "artillery": - return c.naviaburst, nil default: return c.Character.Condition(fields) } @@ -59,7 +51,7 @@ func (c *char) Condition(fields []string) (any, error) { func (c *char) Snapshot(ai *combat.AttackInfo) combat.Snapshot { ds := c.Character.Snapshot(ai) - if c.Character.StatusIsActive("navia-a1-dmg") { // weapon infusion can't be overriden for navia + if c.Character.StatusIsActive(a1Key) { // weapon infusion can't be overriden for navia switch ai.AttackTag { case attacks.AttackTagNormal: case attacks.AttackTagPlunge: @@ -70,4 +62,4 @@ func (c *char) Snapshot(ai *combat.AttackInfo) combat.Snapshot { ai.Element = attributes.Geo } return ds -} \ No newline at end of file +} diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index c1b2ac84dc..edd85f65b8 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -2,6 +2,8 @@ package navia import ( "fmt" + "math" + "github.com/genshinsim/gcsim/internal/frames" "github.com/genshinsim/gcsim/pkg/core/action" "github.com/genshinsim/gcsim/pkg/core/attacks" @@ -10,120 +12,129 @@ import ( "github.com/genshinsim/gcsim/pkg/core/event" "github.com/genshinsim/gcsim/pkg/core/geometry" "github.com/genshinsim/gcsim/pkg/core/glog" - "github.com/genshinsim/gcsim/pkg/core/player/character" "github.com/genshinsim/gcsim/pkg/core/player/shield" "github.com/genshinsim/gcsim/pkg/core/targets" "github.com/genshinsim/gcsim/pkg/enemy" - "github.com/genshinsim/gcsim/pkg/modifier" "github.com/genshinsim/gcsim/pkg/reactable" - "math" ) -var skillPressFrames []int -var skillPowerPressFrames []int -var skillHoldFrames []int -var skillPowerHoldFrames []int - -var skillMultiplier = []float64{ - 0, // 0 hits - 1, // 1 hit - 1.05000000074505806, // 2 hit - 1.10000000149011612, // 3 hit etc - 1.15000000596046448, - 1.20000000298023224, - 1.36000001430511475, - 1.4000000059604645, - 1.6000000238418579, - 1.6660000085830688, - 1.8999999761581421, - 2, -} - -var hitscans = [][]float64{ - {0.9, 0, 0, 0}, - {0.25, 7.911, 0, 0}, - {0.25, -1.826, 0, 0}, - {0.25, -4.325, 0, 0}, - {0.25, 0.773, 0, 0}, - {0.25, 6.209, 0, 0}, - {0.25, -2.752, 0, 0}, - {0.25, 7.845, 0.01, 0.01}, - {0.25, -7.933, -0.01, -0.01}, - {0.25, 2.626, 0, 0}, - {0.25, -5.43724, 0, 0}, - //width, angle, x offset, y offset -} +var ( + skillFrames [][][]int + skillMultiplier = []float64{ + 0, // 0 hits + 1, // 1 hits + 1.05000000074505806, // 2 hits + 1.10000000149011612, // 3 hits etc + 1.15000000596046448, + 1.20000000298023224, + 1.36000001430511475, + 1.4000000059604645, + 1.6000000238418579, + 1.6660000085830688, + 1.8999999761581421, + 2, + } + hitscans = [][]float64{ + // width, angle, x offset, y offset + {0.9, 0, 0, 0}, + {0.25, 7.911, 0, 0}, + {0.25, -1.826, 0, 0}, + {0.25, -4.325, 0, 0}, + {0.25, 0.773, 0, 0}, + {0.25, 6.209, 0, 0}, + {0.25, -2.752, 0, 0}, + {0.25, 7.845, 0.01, 0.01}, + {0.25, -7.933, -0.01, -0.01}, + {0.25, 2.626, 0, 0}, + {0.25, -9.993, 0, 0}, + } +) const ( - skillPressCDStart = 11 - travelDelay = 9 - arkheDelay = 65 + skillPressCDStart = 11 + + skillHoldCDStart = 41 + skillMaxHoldDuration = 241 // add 1f to account for hold being set to 1 for activation - skillHoldCDStart = 41 - skillHoldDuration = 241 //an additional 1f to account for hold being set to 1 to activate + bulletBoxWidth = 11.5 particleICDKey = "navia-particle-icd" arkheICDKey = "navia-arkhe-icd" ) func init() { - skillPressFrames = frames.InitAbilSlice(40) // E -> E/Q - skillPressFrames[action.ActionDash] = 24 - skillPressFrames[action.ActionJump] = 24 - skillPressFrames[action.ActionSwap] = 38 - skillPressFrames[action.ActionWalk] = 35 - skillPressFrames[action.ActionAttack] = 38 - - // skill with >=3 shrapnel -> x - skillPowerPressFrames = frames.InitAbilSlice(41) // E -> E/Q - skillPowerPressFrames[action.ActionDash] = 26 - skillPowerPressFrames[action.ActionJump] = 24 - skillPowerPressFrames[action.ActionSwap] = 40 - skillPowerPressFrames[action.ActionWalk] = 39 - skillPowerPressFrames[action.ActionAttack] = 40 - - // skill (hold) -> x - skillHoldFrames = frames.InitAbilSlice(71) // E -> E/Q - skillHoldFrames[action.ActionDash] = 54 - skillHoldFrames[action.ActionJump] = 54 - skillHoldFrames[action.ActionSwap] = 69 - skillHoldFrames[action.ActionWalk] = 65 - skillHoldFrames[action.ActionAttack] = 69 - - // skill (hold) with >=3 shrapnel -> x - skillPowerHoldFrames = frames.InitAbilSlice(73) // E -> E/Q - skillPowerHoldFrames[action.ActionDash] = 56 - skillPowerHoldFrames[action.ActionJump] = 56 - skillPowerHoldFrames[action.ActionSwap] = 71 - skillPowerHoldFrames[action.ActionWalk] = 70 - skillPowerHoldFrames[action.ActionAttack] = 72 + skillFrames = make([][][]int, 2) + + // press + skillFrames[0] = make([][]int, 2) + + // normal + skillFrames[0][0] = frames.InitAbilSlice(40) // E -> E/Q + skillFrames[0][0][action.ActionAttack] = 38 + skillFrames[0][0][action.ActionDash] = 24 + skillFrames[0][0][action.ActionJump] = 24 + skillFrames[0][0][action.ActionWalk] = 35 + skillFrames[0][0][action.ActionSwap] = 38 + + // >= 3 shrapnel + skillFrames[0][1] = frames.InitAbilSlice(41) // E -> E/Q + skillFrames[0][1][action.ActionAttack] = 40 + skillFrames[0][1][action.ActionDash] = 26 + skillFrames[0][1][action.ActionJump] = 24 + skillFrames[0][1][action.ActionWalk] = 39 + skillFrames[0][1][action.ActionSwap] = 40 + + // hold + skillFrames[1] = make([][]int, 2) + + // normal + skillFrames[1][0] = frames.InitAbilSlice(71) // E -> E/Q + skillFrames[1][0][action.ActionAttack] = 70 + skillFrames[1][0][action.ActionDash] = 54 + skillFrames[1][0][action.ActionJump] = 54 + skillFrames[1][0][action.ActionWalk] = 65 + skillFrames[1][0][action.ActionSwap] = 69 + + // >= 3 shrapnel + skillFrames[1][1] = frames.InitAbilSlice(73) // E -> E/Q + skillFrames[1][1][action.ActionDash] = 56 + skillFrames[1][1][action.ActionJump] = 56 + skillFrames[1][1][action.ActionWalk] = 70 + skillFrames[1][1][action.ActionSwap] = 71 } func (c *char) Skill(p map[string]int) (action.Info, error) { - c.c2ready = true - shrapnel := 0 - hold := p["hold"] - firingTime := 0 + // frame related setup + holdIndex := 0 + shrapnelIndex := 0 + firingTime := skillPressCDStart // assume tap as default + + // check hold + // hold is added to minimum hold length + hold := max(p["hold"], 0) if hold > 0 { - if hold > skillHoldDuration { - hold = skillHoldDuration - 1 + // use hold skill frames + holdIndex = 1 + // cap hold to max hold duration over minimum hold + if hold > skillMaxHoldDuration { + hold = skillMaxHoldDuration } - firingTime += skillHoldCDStart + hold - 1 + // subtract 1 to account for needing to supply > 0 to indicate hold + hold -= 1 + // calc firingTime + firingTime = skillHoldCDStart + hold - // At 0.25, tap is converted to hold, and the suction begins - // TODO: Confirm suction begins at 15f - for i := 15; i < firingTime; i += 30 { - c.PullCrystals(firingTime, i) + // crystal pulling related + // At 0.2, tap is converted to hold, and the suction begins + firingTimeF := c.Core.F + firingTime + for i := 12; i < firingTime; i += 30 { + c.pullCrystals(firingTimeF, i) } - c.SetCDWithDelay(action.ActionSkill, 9*60, skillHoldCDStart+hold) - } else { - firingTime += skillPressCDStart - c.SetCDWithDelay(action.ActionSkill, 9*60, skillPressCDStart) } - shots := 5 - + c.SetCDWithDelay(action.ActionSkill, 9*60, firingTime) + ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "Rosula Shardshot", @@ -133,228 +144,215 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { StrikeType: attacks.StrikeTypeBlunt, Element: attributes.Geo, Durability: 25, - Mult: skillshotgun[c.TalentLvlSkill()], } c.QueueCharTask( func() { - if p["shrapnel"] != 0 { - c.shrapnel = int(math.Min(float64(p["shrapnel"]), 6)) + if c.shrapnel >= 3 { + shrapnelIndex = 1 } - c.Core.Log.NewEvent(fmt.Sprintf("%v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) - shots = 5 + int(math.Min(float64(c.shrapnel), 3))*2 - shrapnel = c.shrapnel + c.Core.Log.NewEvent(fmt.Sprintf("firing %v crystal shrapnel", c.shrapnel), glog.LogCharacterEvent, c.Index) + // Calculate buffs based on excess shrapnel - excess := math.Max(float64(c.shrapnel-3), 0) - m := make([]float64, attributes.EndStatType) - c.AddAttackMod(character.AttackMod{ - Base: modifier.NewBase("navia-skill-dmgup", travelDelay+1), - Amount: func(atk *combat.AttackEvent, t combat.Target) ([]float64, bool) { - if atk.Info.AttackTag != attacks.AttackTagElementalArt { - return nil, false - } - m[attributes.DmgP] = 0.15 * excess - if c.Base.Cons >= 2 { - m[attributes.CR] = 0.12 * excess - } - if c.Base.Cons >= 6 { - m[attributes.CD] = 0.45 * excess - } - return m, true - }, - }) + excess := float64(max(c.shrapnel-3, 0)) + + // snap and add buffs + snap := c.Snapshot(&ai) + c.addShrapnelBuffs(&snap, excess) // Looks for enemies in the path of each bullet // Initially trims enemies to check by scanning only the hit zone + shots := 5 + min(c.shrapnel, 3)*2 for _, t := range c.Core.Combat.EnemiesWithinArea( - combat.NewCircleHitOnTargetFanAngle(c.Core.Combat.Player(), geometry.Point{Y: 0}, 11.5, 15), + combat.NewBoxHitOnTarget(c.Core.Combat.Player(), geometry.Point{X: -0.20568, Y: -0.043841}, 4.0722, 11.5461), nil, ) { // Tallies up the hits hits := 0 for i := 0; i < shots; i++ { - if ok, _ := t.AttackWillLand(combat.NewBoxHitOnTarget(c.Core.Combat.Player(), geometry.Point{X: hitscans[i][2], - Y: hitscans[i][3]}.Rotate(geometry.DegreesToDirection(hitscans[i][1])), - hitscans[i][0], 11.5)); ok { + if ok, _ := t.AttackWillLand( + combat.NewBoxHitOnTarget( + c.Core.Combat.Player(), + geometry.Point{X: hitscans[i][2], Y: hitscans[i][3]}.Rotate(geometry.DegreesToDirection(hitscans[i][1])), + hitscans[i][0], + bulletBoxWidth, + )); ok { hits++ } } + c.Core.Log.NewEvent(fmt.Sprintf("target %v hit %v times", t.Key(), hits), glog.LogCharacterEvent, c.Index) // Applies damage based on the hits ai.Mult = skillshotgun[c.TalentLvlSkill()] * skillMultiplier[hits] - c.Core.QueueAttack( + c.Core.QueueAttackWithSnap( ai, + snap, combat.NewSingleTargetHit(t.Key()), - 0, travelDelay, - c.particleCB(), + c.particleCB, c.c2(), ) } - // remove the shrapnel after firing and action C1 and A1 + c.surgingBlade(excess) + + // trigger A1 and C1 on firing + c.a1() c.c1(c.shrapnel) + + // remove the shrapnel after firing if c.Base.Cons < 6 { c.shrapnel = 0 } else { - c.shrapnel -= 3 // C6 keeps any more than the three + c.shrapnel = min(c.shrapnel-3, 0) // C6 keeps any more than the three } - c.a1() }, firingTime, ) - c.Core.Tasks.Add(c.SurgingBlade, firingTime) - - c.c2ready = false - if hold > 1 { - if shrapnel >= 3 { //when does this get evaluated. It needs to look ahead to determine how long the lock out is? - return action.Info{ - Frames: func(next action.Action) int { return hold + skillPowerHoldFrames[next] }, - AnimationLength: skillPowerHoldFrames[action.InvalidAction] + hold, - CanQueueAfter: skillPowerHoldFrames[action.ActionDash] + hold, - State: action.SkillState, - }, nil - } - return action.Info{ - Frames: func(next action.Action) int { return hold + skillHoldFrames[next] }, - AnimationLength: skillHoldFrames[action.InvalidAction] + hold, - CanQueueAfter: skillHoldFrames[action.ActionDash] + hold, - State: action.SkillState, - }, nil - } - if shrapnel >= 3 { - return action.Info{ - Frames: frames.NewAbilFunc(skillPowerPressFrames), - AnimationLength: skillPowerPressFrames[action.InvalidAction], - CanQueueAfter: skillPowerPressFrames[action.ActionDash], - State: action.SkillState, - }, nil - } return action.Info{ - Frames: frames.NewAbilFunc(skillPressFrames), - AnimationLength: skillPressFrames[action.InvalidAction], - CanQueueAfter: skillPressFrames[action.ActionDash], + Frames: func(next action.Action) int { return skillFrames[holdIndex][shrapnelIndex][next] + hold }, + AnimationLength: skillFrames[holdIndex][shrapnelIndex][action.InvalidAction] + hold, + CanQueueAfter: skillFrames[holdIndex][shrapnelIndex][action.ActionJump] + hold, State: action.SkillState, }, nil - } -func (c *char) particleCB() combat.AttackCBFunc { - done := false - return func(a combat.AttackCB) { - e := a.Target.(*enemy.Enemy) - if e.Type() != targets.TargettableEnemy { - return - } +func (c *char) particleCB(a combat.AttackCB) { + e := a.Target.(*enemy.Enemy) + if e.Type() != targets.TargettableEnemy { + return + } - if done { - return - } - if !c.StatusIsActive(particleICDKey) { - count := 3.0 - if c.Core.Rand.Float64() < 0.5 { - count = 4 - } - c.Core.QueueParticle(c.Base.Key.String(), count, attributes.Geo, c.ParticleDelay) - c.AddStatus(particleICDKey, 0.2*60, true) - } + if c.StatusIsActive(particleICDKey) { + return + } + c.AddStatus(particleICDKey, 0.2*60, true) - done = true + count := 3.0 + if c.Core.Rand.Float64() < 0.5 { + count = 4 + } + c.Core.QueueParticle(c.Base.Key.String(), count, attributes.Geo, c.ParticleDelay) +} +// add the buffs by modifying snap +// needs to be done this way so that excess calculated during firing is also used for that firing's surgingBlade +func (c *char) addShrapnelBuffs(snap *combat.Snapshot, excess float64) { + dmg := 0.15 * excess + cr := 0.0 + cd := 0.0 + if c.Base.Cons >= 2 { + cr = 0.12 * excess + } + if c.Base.Cons >= 6 { + cr = 0.45 * excess } + snap.Stats[attributes.DmgP] += dmg + snap.Stats[attributes.CR] += cr + snap.Stats[attributes.CD] += cd + c.Core.Log.NewEvent("adding shrapnel buffs", glog.LogCharacterEvent, c.Index).Write("dmg%", dmg).Write("cr", cr).Write("cd", cd) } -// ShrapnelGain adds Shrapnel Stacks when a Crystallise Shield is picked up. -// Stacks should last 300s but this is way to long to bother +// shrapnelGain adds Shrapnel Stacks when a Crystallise Shield is picked up. +// Stacks should last 300s but this is way too long to bother // When a character in the party obtains an Elemental Shard created from the Crystallize reaction, // Navia will gain 1 Crystal Shrapnel charge. Navia can hold up to 6 charges of Crystal Shrapnel at once. // Each time Crystal Shrapnel gain is triggered, the duration of the Shards you have already will be reset. -func (c *char) ShrapnelGain() { +func (c *char) shrapnelGain() { c.Core.Events.Subscribe(event.OnShielded, func(args ...interface{}) bool { // Check shield shd := args[0].(shield.Shield) - if shd.Type() != shield.Crystallize { return false } + if c.shrapnel < 6 { c.shrapnel++ - c.Core.Log.NewEvent("Crystal Shrapnel gained from Crystallise", glog.LogCharacterEvent, c.Index) + c.Core.Log.NewEvent("Crystal Shrapnel gained from Crystallise", glog.LogCharacterEvent, c.Index).Write("shrapnel", c.shrapnel) } return false }, "shrapnel-gain") } -func (c *char) SurgingBlade() { +func (c *char) surgingBlade(excess float64) { if c.StatusIsActive(arkheICDKey) { return } - c.AddStatus(arkheICDKey, 7*60, false) - e := c.Core.Combat.ClosestEnemyWithinArea(combat.NewCircleHitFanAngle(c.Core.Combat.Player(), - geometry.Point{Y: 0}.Rotate(geometry.Point{Y: 0}), geometry.Point{Y: 0}, 11.5, 7.933+7.911), nil) + ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "Surging Blade", AttackTag: attacks.AttackTagElementalArt, ICDTag: attacks.ICDTagNone, ICDGroup: attacks.ICDGroupDefault, - StrikeType: attacks.StrikeTypeBlunt, + StrikeType: attacks.StrikeTypePierce, Element: attributes.Geo, Durability: 0, Mult: skillblade[c.TalentLvlSkill()], } + + // determine attack pos + player := c.Core.Combat.Player() + // shotgun area + e := c.Core.Combat.ClosestEnemyWithinArea(combat.NewBoxHitOnTarget(c.Core.Combat.Player(), geometry.Point{X: -0.20568, Y: -0.043841}, 4.0722, 11.5461), nil) + // pos is at player + Y: 3.6 by default + pos := geometry.CalcOffsetPoint(player.Pos(), geometry.Point{Y: 3.6}, player.Direction()) if e != nil { - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(e, geometry.Point{Y: 0}, 3), - 0, - arkheDelay, - nil, - ) - } else { - c.Core.QueueAttack( - ai, - combat.NewCircleHitOnTarget(c.Core.Combat.Player(), geometry.Point{Y: 3}, 3), - 0, - 0, - nil, - ) + // enemy in shotgun area: use their pos + pos = e.Pos() } - return + + // aligned cd trigger is delayed and only once it's triggered the aligned attack task should be queued + c.QueueCharTask(func() { + c.AddStatus(arkheICDKey, 7*60, true) + c.QueueCharTask(func() { + snap := c.Snapshot(&ai) + c.addShrapnelBuffs(&snap, excess) + c.Core.QueueAttackWithSnap( + ai, + snap, + combat.NewCircleHitOnTarget(pos, nil, 3), + 0, + ) + }, 36) + }, 28) } -// PullCrystals to Navia. This has a range of 12m from datamine, so -// check every every 30f. -func (c *char) PullCrystals(firingTime, i int) { - for j, k := 0, 0; j < c.Core.Combat.GadgetCount(); j++ { - cs, ok := c.Core.Combat.Gadget(j).(*reactable.CrystallizeShard) - // skip if not a shard - if !ok { - continue - } +// pull crystals to Navia. This has a range of 12m from datamine, so +// check every every 30f. +func (c *char) pullCrystals(firingTimeF, i int) { + c.Core.Tasks.Add(func() { + count := 0 + for j := 0; j < c.Core.Combat.GadgetCount(); j++ { + cs, ok := c.Core.Combat.Gadget(j).(*reactable.CrystallizeShard) + // skip if not a shard + if !ok { + continue + } + // If shard is out of 12m range, skip + if !cs.IsWithinArea(combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 12)) { + continue + } - // If shard is out of 12m range, skip - if !cs.IsWithinArea(combat.NewCircleHitOnTarget(c.Core.Combat.Player(), nil, 12)) { - continue - } + // approximate sucking in as 0.4m per frame (~8m distance took 20f to arrive at gorou) + distance := cs.Pos().Distance(c.Core.Combat.Player().Pos()) + travel := int(math.Ceil(distance / 0.4)) + // if the crystal won't arrive before the shot is fired, skip + if c.Core.F+travel >= firingTimeF { + continue + } + // special check to account for edge case if shard just spawned and will arrive before it can be picked up + if c.Core.F+travel < cs.EarliestPickup { + continue + } + c.Core.Tasks.Add(func() { + cs.AddShieldKillShard() + }, travel) - // approximate sucking in as 0.4m per frame (~8m distance took 20f to arrive at gorou) - distance := cs.Pos().Distance(c.Core.Combat.Player().Pos()) - travel := int(math.Ceil(distance / 0.4)) - // if the crystal won't arrive before the shot is fired, skip - if firingTime-i < travel { - continue - } - // special check to account for edge case if shard just spawned and will arrive before it can be picked up - if c.Core.F+travel < cs.EarliestPickup { - continue - } - c.Core.Tasks.Add(func() { - cs.AddShieldKillShard() - }, travel) - // max three crystals - k++ - if k >= 3 { - break + // max three crystals + count++ + if count == 3 { + break + } } - } -} \ No newline at end of file + }, i) +} diff --git a/internal/characters/navia/stats.go b/internal/characters/navia/stats.go index 9399970093..0b159e7586 100644 --- a/internal/characters/navia/stats.go +++ b/internal/characters/navia/stats.go @@ -5,188 +5,188 @@ var ( //1 { { - 93.5 / 100.0, - 101.1 / 100.0, - 108.7 / 100.0, - 119.6 / 100.0, - 127.2 / 100.0, - 135.9 / 100.0, - 147.9 / 100.0, - 159.9 / 100.0, - 171.8 / 100.0, - 184.9 / 100.0, - 197.9 / 100.0, - 211.0 / 100.0, - 224.0 / 100.0, - 237.1 / 100.0, - 250.1 / 100.0, + 0.9352, + 1.0113, + 1.0874, + 1.1962, + 1.2723, + 1.3593, + 1.4789, + 1.5985, + 1.7181, + 1.8486, + 1.9791, + 2.1096, + 2.2401, + 2.3706, + 2.5011, }, }, //2 { { - 86.5 / 100.0, - 93.5 / 100.0, - 100.6 / 100.0, - 110.6 / 100.0, - 117.7 / 100.0, - 125.7 / 100.0, - 136.8 / 100.0, - 147.9 / 100.0, - 158.9 / 100.0, - 171.0 / 100.0, - 183.1 / 100.0, - 195.1 / 100.0, - 207.2 / 100.0, - 219.3 / 100.0, - 231.4 / 100.0, + 0.8651, + 0.9355, + 1.0059, + 1.1065, + 1.1769, + 1.2574, + 1.3680, + 1.4787, + 1.5893, + 1.7100, + 1.8307, + 1.9514, + 2.0721, + 2.1928, + 2.3135, }, }, //3 { { - 34.9 / 100.0, - 37.7 / 100.0, - 40.6 / 100.0, - 44.6 / 100.0, - 47.5 / 100.0, - 50.7 / 100.0, - 55.2 / 100.0, - 59.6 / 100.0, - 64.1 / 100.0, - 69.0 / 100.0, - 73.8 / 100.0, - 78.7 / 100.0, - 83.6 / 100.0, - 88.4 / 100.0, - 93.3 / 100.0, + 0.3489, + 0.3773, + 0.4056, + 0.4462, + 0.4746, + 0.5071, + 0.5517, + 0.5963, + 0.6409, + 0.6896, + 0.7383, + 0.7870, + 0.8356, + 0.8843, + 0.9330, }, { - 34.9 / 100.0, - 37.7 / 100.0, - 40.6 / 100.0, - 44.6 / 100.0, - 47.5 / 100.0, - 50.7 / 100.0, - 55.2 / 100.0, - 59.6 / 100.0, - 64.1 / 100.0, - 69.0 / 100.0, - 73.8 / 100.0, - 78.7 / 100.0, - 83.6 / 100.0, - 88.4 / 100.0, - 93.3 / 100.0, + 0.3489, + 0.3773, + 0.4056, + 0.4462, + 0.4746, + 0.5071, + 0.5517, + 0.5963, + 0.6409, + 0.6896, + 0.7383, + 0.7870, + 0.8356, + 0.8843, + 0.9330, }, { - 34.9 / 100.0, - 37.7 / 100.0, - 40.6 / 100.0, - 44.6 / 100.0, - 47.5 / 100.0, - 50.7 / 100.0, - 55.2 / 100.0, - 59.6 / 100.0, - 64.1 / 100.0, - 69.0 / 100.0, - 73.8 / 100.0, - 78.7 / 100.0, - 83.6 / 100.0, - 88.4 / 100.0, - 93.3 / 100.0, + 0.3489, + 0.3773, + 0.4056, + 0.4462, + 0.4746, + 0.5071, + 0.5517, + 0.5963, + 0.6409, + 0.6896, + 0.7383, + 0.7870, + 0.8356, + 0.8843, + 0.9330, }, }, //4 { { - 133.4 / 100.0, - 144.3 / 100.0, - 155.2 / 100.0, - 170.7 / 100.0, - 181.5 / 100.0, - 193.9 / 100.0, - 211.0 / 100.0, - 228.1 / 100.0, - 245.1 / 100.0, - 263.8 / 100.0, - 282.4 / 100.0, - 301.0 / 100.0, - 319.6 / 100.0, - 338.2 / 100.0, - 356.9 / 100.0, + 1.3343, + 1.4429, + 1.5515, + 1.7067, + 1.8153, + 1.9394, + 2.1101, + 2.2807, + 2.4514, + 2.6376, + 2.8238, + 3.0100, + 3.1962, + 3.3823, + 3.5685, }, }, } skillshotgun = []float64{ - 3.948, - 4.244, - 4.540, - 4.935, - 5.23, - 5.527, - 5.922, - 6.317, - 6.712, - 7.106, - 7.501, - 7.896, - 8.389, - 8.883, - 9.377, + 3.9480, + 4.2441, + 4.5402, + 4.9350, + 5.2311, + 5.5272, + 5.9220, + 6.3168, + 6.7116, + 7.1064, + 7.5012, + 7.8960, + 8.3895, + 8.8830, + 9.3765, } skillblade = []float64{ - 0.36, + 0.360, 0.387, 0.414, - 0.45, + 0.450, 0.477, 0.504, - 0.54, + 0.540, 0.576, 0.612, 0.648, 0.684, 0.720, 0.765, - 0.81, + 0.810, 0.855, } burst = [][]float64{ // skill cast { - 0.752, - 0.808, - 0.865, - 0.94, - 0.996, - 1.053, - 1.128, - 1.203, - 1.278, - 1.354, - 1.429, - 1.504, - 1.598, - 1.692, - 1.786, + 0.7520, + 0.8084, + 0.8648, + 0.9400, + 0.9964, + 1.0528, + 1.1280, + 1.2032, + 1.2784, + 1.3536, + 1.4288, + 1.5040, + 1.5980, + 1.6920, + 1.7860, }, // artillery { - 0.431, - 0.464, - 0.496, - 0.539, - 0.572, - 0.604, - 0.647, - 0.69, - 0.734, - 0.777, - 0.82, - 0.863, - 0.917, - 0.971, - 1.025, + 0.4315, + 0.4639, + 0.4962, + 0.5394, + 0.5717, + 0.6041, + 0.6472, + 0.6904, + 0.7336, + 0.7767, + 0.8199, + 0.8630, + 0.9169, + 0.9709, + 1.0248, }, } -) \ No newline at end of file +) diff --git a/pkg/core/attacks/icd.go b/pkg/core/attacks/icd.go index f51e0bd77c..730c1a7b9a 100644 --- a/pkg/core/attacks/icd.go +++ b/pkg/core/attacks/icd.go @@ -56,6 +56,7 @@ const ( ICDTagNeuvilletteC6 ICDTagFurinaChevalmarin ICDTagFurinaUsher + ICDTagNaviaBurst ICDTagLength ) @@ -95,6 +96,7 @@ const ( ICDGroupTravelerDewdrop ICDGroupTravelerBurst ICDGroupFurinaSalonSolitaire + ICDGroupNaviaBurst ICDGroupLength ) @@ -136,6 +138,7 @@ func init() { ICDGroupResetTimer[ICDGroupTravelerDewdrop] = 90 ICDGroupResetTimer[ICDGroupTravelerBurst] = 480 ICDGroupResetTimer[ICDGroupFurinaSalonSolitaire] = 1800 + ICDGroupResetTimer[ICDGroupNaviaBurst] = 720 ICDGroupEleApplicationSequence = make([][]float64, ICDGroupLength) ICDGroupEleApplicationSequence[ICDGroupDefault] = []float64{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0} @@ -170,6 +173,7 @@ func init() { ICDGroupEleApplicationSequence[ICDGroupTravelerDewdrop] = []float64{1, 0, 0, 0, 0, 0, 0, 0} ICDGroupEleApplicationSequence[ICDGroupTravelerBurst] = []float64{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0} ICDGroupEleApplicationSequence[ICDGroupFurinaSalonSolitaire] = []float64{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0} + ICDGroupEleApplicationSequence[ICDGroupNaviaBurst] = []float64{1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0} ICDGroupDamageSequence = make([][]float64, ICDGroupLength) ICDGroupDamageSequence[ICDGroupDefault] = []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} @@ -206,4 +210,5 @@ func init() { ICDGroupDamageSequence[ICDGroupTravelerDewdrop] = []float64{1, 1, 1, 1, 1, 1, 1, 1} ICDGroupDamageSequence[ICDGroupTravelerBurst] = []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} ICDGroupDamageSequence[ICDGroupFurinaSalonSolitaire] = []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} + ICDGroupDamageSequence[ICDGroupNaviaBurst] = []float64{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1} } diff --git a/pkg/core/curves/charactercurves.go b/pkg/core/curves/charactercurves.go index f2693a8dfe..44f253ad65 100644 --- a/pkg/core/curves/charactercurves.go +++ b/pkg/core/curves/charactercurves.go @@ -2876,9 +2876,9 @@ var CharBaseMap = map[keys.Char]CharBase{ HPCurve: GROW_CURVE_HP_S5, AtkCurve: GROW_CURVE_ATTACK_S5, DefCurve: GROW_CURVE_HP_S5, - BaseHP: 984.77954, - BaseAtk: 27.37140, - BaseDef: 61.74456, + BaseHP: 984.779541015625, + BaseAtk: 27.371400833129883, + BaseDef: 61.74456024169922, Specialized: attributes.CD, PromotionBonus: []PromoData{ { @@ -2890,45 +2890,45 @@ var CharBaseMap = map[keys.Char]CharBase{ }, { MaxLevel: 40, - HP: 844.35944, - Atk: 23.46679, - Def: 52.9416, + HP: 844.3594360351562, + Atk: 23.466785430908203, + Def: 52.94160079956055, Special: 0, }, { MaxLevel: 50, - HP: 1444.29907, - Atk: 40.14056, - Def: 90.558, - Special: 0.096, + HP: 1444.299072265625, + Atk: 40.14055633544922, + Def: 90.55799865722656, + Special: 0.09600000083446503, }, { MaxLevel: 60, - HP: 2244.21851, - Atk: 62.37225, - Def: 140.7132, - Special: 0.192, + HP: 2244.218505859375, + Atk: 62.37224578857422, + Def: 140.71319580078125, + Special: 0.19200000166893005, }, { MaxLevel: 70, - HP: 2844.15796, - Atk: 79.04601, - Def: 178.32961, - Special: 0.192, + HP: 2844.157958984375, + Atk: 79.04601287841797, + Def: 178.32960510253906, + Special: 0.19200000166893005, }, { MaxLevel: 80, - HP: 3444.09766, - Atk: 95.71979, - Def: 215.946, - Special: 0.288, + HP: 3444.09765625, + Atk: 95.71978759765625, + Def: 215.9459991455078, + Special: 0.2879999876022339, }, { MaxLevel: 90, - HP: 4044.03735, - Atk: 112.39355, - Def: 253.56239, - Special: 0.384, + HP: 4044.037353515625, + Atk: 112.3935546875, + Def: 253.56239318847656, + Special: 0.3840000033378601, }, }, }, diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index abd7e728d6..edf25c271e 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -75,9 +75,6 @@ var CharNameToKey = map[string]keys.Char{ "sara": keys.Sara, "lisa": keys.Lisa, "mona": keys.Mona, - "navia": keys.Navia, - "spinadirosula": keys.Navia, - "demoisselle": keys.Navia, "ningguang": keys.Ningguang, "ning": keys.Ningguang, "noelle": keys.Noelle, @@ -155,4 +152,6 @@ var CharNameToKey = map[string]keys.Char{ "freminet": keys.Freminet, "furina": keys.Furina, "furinadefontaine": keys.Furina, -} \ No newline at end of file + "navia": keys.Navia, + "demoisselle": keys.Navia, +} diff --git a/ui/packages/db/src/Data/char_data.generated.json b/ui/packages/db/src/Data/char_data.generated.json index 5f8e85c7f4..5bac9bcafe 100644 --- a/ui/packages/db/src/Data/char_data.generated.json +++ b/ui/packages/db/src/Data/char_data.generated.json @@ -870,6 +870,21 @@ "burst_energy_cost": 50 } }, + "navia": { + "id": 10000091, + "key": "navia", + "rarity": "QUALITY_ORANGE", + "body": "BODY_LADY", + "element": "Rock", + "weapon_class": "WEAPON_CLAYMORE", + "icon_name": "UI_AvatarIcon_Navia", + "skill_details": { + "skill": 10912, + "burst": 10915, + "attack": 10911, + "burst_energy_cost": 60 + } + }, "neuvillette": { "id": 10000087, "key": "neuvillette", diff --git a/ui/packages/docs/docs/reference/characters/navia.md b/ui/packages/docs/docs/reference/characters/navia.md new file mode 100644 index 0000000000..5c0dfc0f1c --- /dev/null +++ b/ui/packages/docs/docs/reference/characters/navia.md @@ -0,0 +1,44 @@ +--- +title: navia +--- + +import HitlagTable from "@site/src/components/Hitlag/HitlagTable"; +import FieldsTable from "@site/src/components/Fields/FieldsTable"; +import ParamsTable from "@site/src/components/Params/ParamsTable"; +import FramesTable from "@site/src/components/Frames/FramesTable"; +import IssuesTable from "@site/src/components/Issues/IssuesTable"; +import AoETable from "@site/src/components/AoE/AoETable"; +import NamesList from "@site/src/components/Names/NamesList"; +**import ActionsTable from "@site/src/components/Actions/ActionsTable";** + +## Frames + + + +## Hitlag Data + + + +## AoE Data + + + +## Known issues + + + +## Names + + + +## Legal Actions + + + +## Params + + + +## Fields + + diff --git a/ui/packages/docs/src/components/Actions/character_data.json b/ui/packages/docs/src/components/Actions/character_data.json index 496cd04038..b47bbcbecd 100644 --- a/ui/packages/docs/src/components/Actions/character_data.json +++ b/ui/packages/docs/src/components/Actions/character_data.json @@ -1476,6 +1476,40 @@ "legal": true } ], + "navia": [ + { + "ability": "attack", + "legal": true + }, + { + "ability": "charge", + "legal": false + }, + { + "ability": "skill", + "legal": true + }, + { + "ability": "burst", + "legal": true + }, + { + "ability": "dash", + "legal": true + }, + { + "ability": "jump", + "legal": true + }, + { + "ability": "walk", + "notes": "Normal Attacks do not have legal walk cancels recorded, but Skill and Burst do" + }, + { + "ability": "swap", + "legal": true + } + ], "neuvillette": [ { "ability": "attack", diff --git a/ui/packages/docs/src/components/AoE/character_data.json b/ui/packages/docs/src/components/AoE/character_data.json index acb9b62351..02f5c4963e 100644 --- a/ui/packages/docs/src/components/AoE/character_data.json +++ b/ui/packages/docs/src/components/AoE/character_data.json @@ -4647,6 +4647,152 @@ } ] }, + "navia": { + "normal": [ + { + "ability": "N1", + "shape": "Circle", + "center": "Player", + "offsetY": 0.5, + "radius": 2 + }, + { + "ability": "N2", + "shape": "Box", + "center": "Player", + "offsetY": -1.5, + "boxX": 2, + "boxY": 4.3 + }, + { + "ability": "N3-1", + "shape": "Box", + "center": "Player", + "offsetY": 0.3, + "boxX": 3, + "boxY": 4.5 + }, + { + "ability": "N3-2", + "shape": "Box", + "center": "Player", + "offsetY": 0.3, + "boxX": 3, + "boxY": 4.5 + }, + { + "ability": "N3-3", + "shape": "Box", + "center": "Player", + "offsetY": 0.3, + "boxX": 3, + "boxY": 4.5 + }, + { + "ability": "N4", + "shape": "Box", + "center": "Player", + "offsetY": -1.85, + "boxX": 2, + "boxY": 4.7 + } + ], + "skill": [ + { + "ability": "E-1st-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.9, + "boxY": 11.5 + }, + { + "ability": "E-2nd-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-3rd-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-4th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-5th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-6th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-7th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-8th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-9th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-10th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + }, + { + "ability": "E-11th-Bullet", + "shape": "Box", + "center": "Player", + "boxX": 0.25, + "boxY": 11.5 + } + ], + "burst": [ + { + "ability": "Q-Initial", + "shape": "Box", + "center": "Player", + "boxX": 5, + "boxY": 12 + }, + { + "ability": "Q-Fire-Support", + "shape": "Circle", + "center": "PrimaryTarget", + "radius": 3, + "notes": "The Circle Spawns in a random position, within 1.2m of the centre of the enemy. If there are no enemies within 10m, then it will spawn in a random location between 1 and 6m from the player's position" + } + ] + }, "neuvillette": { "normal": [ { diff --git a/ui/packages/docs/src/components/Fields/character_data.json b/ui/packages/docs/src/components/Fields/character_data.json index d9faa0570b..b1a3c8d83d 100644 --- a/ui/packages/docs/src/components/Fields/character_data.json +++ b/ui/packages/docs/src/components/Fields/character_data.json @@ -187,6 +187,14 @@ "desc": "Number of Declension stacks." } ], + "navia": [ + { + "fields": [ + ".navia.shrapnel" + ], + "desc": "Number of Crystal Shrapnel stacks." + } + ], "neuvillette": [ { "fields": [ diff --git a/ui/packages/docs/src/components/Frames/character_data.json b/ui/packages/docs/src/components/Frames/character_data.json index 689345718a..6d07cefb27 100644 --- a/ui/packages/docs/src/components/Frames/character_data.json +++ b/ui/packages/docs/src/components/Frames/character_data.json @@ -687,6 +687,14 @@ "count": "https://docs.google.com/spreadsheets/d/1MBeFddkD4OtHdBMncqW_j18NC-lUMqA9noVo_pH26H0/edit?usp=sharing" } ], + "navia": [ + { + "vid_credit": "shizukayuki", + "count_credit": "shizukayuki", + "vid": "https://youtu.be/qnWjXMmf6MM", + "count": "https://docs.google.com/spreadsheets/d/1M_FffTMTx11rbESklIbzgiSfm9RnWhgKcRlSEnJ08p4/edit#gid=0" + } + ], "neuvillette": [ { "vid_credit": "charliex3000", diff --git a/ui/packages/docs/src/components/Hitlag/character_data.json b/ui/packages/docs/src/components/Hitlag/character_data.json index a4120c9b56..ef0491913c 100644 --- a/ui/packages/docs/src/components/Hitlag/character_data.json +++ b/ui/packages/docs/src/components/Hitlag/character_data.json @@ -1945,6 +1945,52 @@ } ] }, + "navia": { + "attack": [ + { + "ability": "N1", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.06, + "canBeDefenseHalt": true, + "deployable": false + }, + { + "ability": "N2", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.06, + "canBeDefenseHalt": true, + "deployable": false + }, + { + "ability": "N3-1", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.01, + "canBeDefenseHalt": false, + "deployable": true + }, + { + "ability": "N3-2", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.01, + "canBeDefenseHalt": false, + "deployable": true + }, + { + "ability": "N3-3", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.01, + "canBeDefenseHalt": false, + "deployable": true + }, + { + "ability": "N4", + "hitHaltTime": 0, + "hitHaltTimeScale": 0.06, + "canBeDefenseHalt": true, + "deployable": false + } + ] + }, "neuvillette": { "skill": [ { diff --git a/ui/packages/docs/src/components/Names/character_data.json b/ui/packages/docs/src/components/Names/character_data.json index cd1e0570a4..272c431e94 100644 --- a/ui/packages/docs/src/components/Names/character_data.json +++ b/ui/packages/docs/src/components/Names/character_data.json @@ -78,6 +78,10 @@ "kusanali", "lesserlordkusanali" ], + "navia": [ + "navia", + "demoisselle" + ], "neuvillette": [ "neuv", "chiefjusticeoffontaine" diff --git a/ui/packages/docs/src/components/Params/character_data.json b/ui/packages/docs/src/components/Params/character_data.json index 1df5d6659d..79f2d086cf 100644 --- a/ui/packages/docs/src/components/Params/character_data.json +++ b/ui/packages/docs/src/components/Params/character_data.json @@ -464,6 +464,18 @@ "desc": "0 for Tap (default), value between 1 and 300 for Hold. The number is added to the minimum holding frames for Tap to turn into Hold (16)." } ], + "navia": [ + { + "ability": "skill", + "param": "hold", + "desc": "0 for Tap (default), value between 1 and 241 for Hold. The number is added to the minimum holding frames for Tap to turn into Hold (41), subtracted by 1 to account for requiring a 1 to activate" + }, + { + "ability": "skill", + "param": "shrapnel", + "desc": "The number of shrapnel use the skill with. 0 (default) uses the number acquired throughout the config, 1-6 force that many shrapnel." + } + ], "nilou": [ { "ability": "skill", diff --git a/ui/packages/ui/src/Data/char_data.generated.json b/ui/packages/ui/src/Data/char_data.generated.json index 5f8e85c7f4..5bac9bcafe 100644 --- a/ui/packages/ui/src/Data/char_data.generated.json +++ b/ui/packages/ui/src/Data/char_data.generated.json @@ -870,6 +870,21 @@ "burst_energy_cost": 50 } }, + "navia": { + "id": 10000091, + "key": "navia", + "rarity": "QUALITY_ORANGE", + "body": "BODY_LADY", + "element": "Rock", + "weapon_class": "WEAPON_CLAYMORE", + "icon_name": "UI_AvatarIcon_Navia", + "skill_details": { + "skill": 10912, + "burst": 10915, + "attack": 10911, + "burst_energy_cost": 60 + } + }, "neuvillette": { "id": 10000087, "key": "neuvillette", diff --git a/ui/packages/ui/src/Data/weapon_data.generated.json b/ui/packages/ui/src/Data/weapon_data.generated.json index c2bf2368f2..f24f0e9a83 100644 --- a/ui/packages/ui/src/Data/weapon_data.generated.json +++ b/ui/packages/ui/src/Data/weapon_data.generated.json @@ -1001,7 +1001,7 @@ "weapon_class": "WEAPON_SWORD_ONE_HAND", "image_name": "UI_EquipIcon_Sword_Psalmus" }, - "swordofnarzissenkreuz": { + "swordofnarzissenkreuz": { "id": 11428, "key": "swordofnarzissenkreuz", "rarity": 4, @@ -1148,14 +1148,14 @@ "weapon_class": "WEAPON_CATALYST", "image_name": "UI_EquipIcon_Catalyst_Phoney" }, - "ultimateoverlordsmegamagicsword": { + "ultimateoverlordsmegamagicsword": { "id": 12426, "key": "ultimateoverlordsmegamagicsword", "rarity": 4, "weapon_class": "WEAPON_CLAYMORE", "image_name": "UI_EquipIcon_Claymore_Champion" }, - "verdict": { + "verdict": { "id": 12512, "key": "verdict", "rarity": 5, diff --git a/ui/packages/ui/src/Translation/locales/IngameNames.json b/ui/packages/ui/src/Translation/locales/IngameNames.json index 5c9a6c81e3..8a398dcd43 100644 --- a/ui/packages/ui/src/Translation/locales/IngameNames.json +++ b/ui/packages/ui/src/Translation/locales/IngameNames.json @@ -121,6 +121,7 @@ "lyney": "Lyney", "lynette": "Lynette", "freminet": "Freminet", + "navia": "Navia", "neuvillette": "Neuvillette", "furina": "Furina", "aetherelectro": "Aether (Electro)", @@ -438,6 +439,7 @@ "lyney": "林尼", "lynette": "琳妮特", "freminet": "菲米尼", + "navia": "娜维娅", "neuvillette": "那维莱特", "furina": "芙宁娜", "aetherelectro": "空 (雷元素)", @@ -755,6 +757,7 @@ "lyney": "リネ", "lynette": "リネット", "freminet": "フレミネ", + "navia": "ナヴィア", "neuvillette": "ヌヴィレット", "furina": "フリーナ", "aetherelectro": "空 (雷元素)", @@ -1072,6 +1075,7 @@ "lyney": "Lyney", "lynette": "Lynette", "freminet": "Fréminet", + "navia": "Navia", "neuvillette": "Neuvillette", "furina": "Furina", "aetherelectro": "Éter (Electro)", @@ -1389,6 +1393,7 @@ "lyney": "Лини", "lynette": "Линетт", "freminet": "Фремине", + "navia": "Навия", "neuvillette": "Нёвиллет", "furina": "Фурина", "aetherelectro": "Итэр (Электро)", @@ -1706,6 +1711,7 @@ "lyney": "Lyney", "lynette": "Lynette", "freminet": "Fréminet", + "navia": "Navia", "neuvillette": "Neuvillette", "furina": "Furina", "aetherelectro": "Leer (Elektro)", From f934da1edd6f7d3d275aecb0c8baf38ca725830e Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 15:16:19 +1100 Subject: [PATCH 32/36] Fix linting, hopefully --- pkg/core/keys/char.go | 2 +- pkg/core/keys/weapon.go | 2 +- pkg/shortcut/artifacts.go | 2 +- pkg/shortcut/weapons.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/core/keys/char.go b/pkg/core/keys/char.go index f5155ab4f2..ade944d678 100644 --- a/pkg/core/keys/char.go +++ b/pkg/core/keys/char.go @@ -414,4 +414,4 @@ var CharKeyToEle = map[Char]attributes.Element{ Furina: attributes.Hydro, Navia: attributes.Geo, TestCharDoNotUse: attributes.Geo, -} \ No newline at end of file +} diff --git a/pkg/core/keys/weapon.go b/pkg/core/keys/weapon.go index 5c5a3454a4..3945e67ec5 100644 --- a/pkg/core/keys/weapon.go +++ b/pkg/core/keys/weapon.go @@ -393,4 +393,4 @@ const ( WolfFang WolfsGravestone XiphosMoonlight -) \ No newline at end of file +) diff --git a/pkg/shortcut/artifacts.go b/pkg/shortcut/artifacts.go index 2506eaddc1..0fc159a8b0 100644 --- a/pkg/shortcut/artifacts.go +++ b/pkg/shortcut/artifacts.go @@ -114,4 +114,4 @@ var SetNameToKey = map[string]keys.Set{ "wandererstroupe": keys.WanderersTroupe, "wanderers": keys.WanderersTroupe, "wt": keys.WanderersTroupe, -} \ No newline at end of file +} diff --git a/pkg/shortcut/weapons.go b/pkg/shortcut/weapons.go index f062381b08..4694bc2ec5 100644 --- a/pkg/shortcut/weapons.go +++ b/pkg/shortcut/weapons.go @@ -322,4 +322,4 @@ var WeaponNameToKey = map[string]keys.Weapon{ "wgs": keys.WolfsGravestone, "xiphosmoonlight": keys.XiphosMoonlight, "xiphos": keys.XiphosMoonlight, -} \ No newline at end of file +} From 1d7645298b1500057408c099e15cf54a2660b5b8 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 15:21:15 +1100 Subject: [PATCH 33/36] Fix linting again, hopefully --- internal/characters/navia/burst.go | 3 --- internal/characters/navia/cons.go | 2 -- pkg/simulation/imports.go | 2 +- 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index f02a7ef442..114d2a360b 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -103,7 +103,6 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { // When cannon attacks hit opponents, Navia will gain 1 stack of Crystal Shrapnel. // This effect can be triggered up to once every 2.4s. func (c *char) burstCB() combat.AttackCBFunc { - return func(a combat.AttackCB) { if c.StatusIsActive(burstICDKey) { return @@ -118,9 +117,7 @@ func (c *char) burstCB() combat.AttackCBFunc { c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index).Write("shrapnel", c.shrapnel) } - } - } // Targets a random enemy if there is an enemy present, if not, it targets a random spot diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index da47e4520b..0a4156449b 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -67,7 +67,6 @@ func (c *char) c2() combat.AttackCBFunc { c.c4(), ) } - } // When As the Sunlit Sky's Singing Salute hits an opponent, @@ -87,5 +86,4 @@ func (c *char) c4() combat.AttackCBFunc { Value: -0.2, }) } - } diff --git a/pkg/simulation/imports.go b/pkg/simulation/imports.go index 6375b57170..80c12165af 100644 --- a/pkg/simulation/imports.go +++ b/pkg/simulation/imports.go @@ -321,4 +321,4 @@ import ( _ "github.com/genshinsim/gcsim/internal/weapons/sword/travelershandysword" _ "github.com/genshinsim/gcsim/internal/weapons/sword/wolffang" _ "github.com/genshinsim/gcsim/internal/weapons/sword/xiphos" -) \ No newline at end of file +) From 37b4015fa0b0fbe6b8b44d01aebde5c0ea274593 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 16:13:26 +1100 Subject: [PATCH 34/36] third linting change --- internal/characters/navia/attack.go | 2 +- internal/characters/navia/burst.go | 1 - internal/characters/navia/cons.go | 1 - pkg/core/keys/sets.go | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) diff --git a/internal/characters/navia/attack.go b/internal/characters/navia/attack.go index 59537f7946..30c3f2a0b2 100644 --- a/internal/characters/navia/attack.go +++ b/internal/characters/navia/attack.go @@ -2,6 +2,7 @@ package navia import ( "fmt" + "github.com/genshinsim/gcsim/internal/frames" "github.com/genshinsim/gcsim/pkg/core/action" "github.com/genshinsim/gcsim/pkg/core/attacks" @@ -30,7 +31,6 @@ func init() { attackFrames[2] = frames.InitNormalCancelSlice(attackEarliestCancel[2], 48) attackFrames[2][action.ActionSkill] = 30 attackFrames[3] = frames.InitNormalCancelSlice(attackEarliestCancel[3], 93) - } func (c *char) Attack(_ map[string]int) (action.Info, error) { diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index 114d2a360b..b0ce003c85 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -115,7 +115,6 @@ func (c *char) burstCB() combat.AttackCBFunc { if c.shrapnel < 6 { c.shrapnel++ c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index).Write("shrapnel", c.shrapnel) - } } } diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 0a4156449b..94d322be69 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -24,7 +24,6 @@ func (c *char) c1(shrapnel int) { count := min(shrapnel, 3) c.ReduceActionCooldown(action.ActionBurst, count*60) c.AddEnergy("navia-c1-energy", float64(count*3)) - return } // Each stack of Crystal Shrapnel consumed will increase the CRIT Rate of this diff --git a/pkg/core/keys/sets.go b/pkg/core/keys/sets.go index 7c87cb3aa4..4f082bd4d1 100644 --- a/pkg/core/keys/sets.go +++ b/pkg/core/keys/sets.go @@ -135,4 +135,4 @@ const ( VermillionHereafter ViridescentVenerer WanderersTroupe -) \ No newline at end of file +) From 3e4926cc2bb4e2ebc26d6b80dba8be73b26c72af Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 23:25:20 +1100 Subject: [PATCH 35/36] addressed comments, updated grammar, fixed skill, icd, updated documentation --- internal/characters/navia/burst.go | 7 ++-- internal/characters/navia/cons.go | 9 ++-- internal/characters/navia/skill.go | 2 +- pkg/core/attacks/icd.go | 1 - pkg/shortcut/characters.go | 2 +- .../components/Actions/character_data.json | 2 +- .../src/components/AoE/character_data.json | 42 ++++++++++++++----- .../src/components/Frames/character_data.json | 12 ++++++ .../src/components/Hitlag/character_data.json | 16 +++---- .../src/components/Names/character_data.json | 2 +- .../src/components/Params/character_data.json | 7 +--- 11 files changed, 63 insertions(+), 39 deletions(-) diff --git a/internal/characters/navia/burst.go b/internal/characters/navia/burst.go index b0ce003c85..8e1df35d45 100644 --- a/internal/characters/navia/burst.go +++ b/internal/characters/navia/burst.go @@ -104,14 +104,13 @@ func (c *char) Burst(_ map[string]int) (action.Info, error) { // This effect can be triggered up to once every 2.4s. func (c *char) burstCB() combat.AttackCBFunc { return func(a combat.AttackCB) { - if c.StatusIsActive(burstICDKey) { + if a.Target.Type() != targets.TargettableEnemy { return } - c.AddStatus(burstICDKey, 2.4*60, true) - if a.Target.Type() != targets.TargettableEnemy { + if c.StatusIsActive(burstICDKey) { return } - + c.AddStatus(burstICDKey, 2.4*60, true) if c.shrapnel < 6 { c.shrapnel++ c.Core.Log.NewEvent("Crystal Shrapnel gained from Burst", glog.LogCharacterEvent, c.Index).Write("shrapnel", c.shrapnel) diff --git a/internal/characters/navia/cons.go b/internal/characters/navia/cons.go index 94d322be69..50e8515f9f 100644 --- a/internal/characters/navia/cons.go +++ b/internal/characters/navia/cons.go @@ -37,15 +37,14 @@ func (c *char) c2() combat.AttackCBFunc { return nil } return func(a combat.AttackCB) { - if c.StatusIsActive(c2IcdKey) { - return - } - c.AddStatus(c2IcdKey, 0.25*60, true) e := a.Target.(*enemy.Enemy) if e.Type() != targets.TargettableEnemy { return } - + if c.StatusIsActive(c2IcdKey) { + return + } + c.AddStatus(c2IcdKey, 0.25*60, true) ai := combat.AttackInfo{ ActorIndex: c.Index, Abil: "The President's Pursuit of Victory", diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index edd85f65b8..8f39c765ca 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -202,7 +202,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { if c.Base.Cons < 6 { c.shrapnel = 0 } else { - c.shrapnel = min(c.shrapnel-3, 0) // C6 keeps any more than the three + c.shrapnel = max(c.shrapnel-3, 0) // C6 keeps any more than the three } }, firingTime, diff --git a/pkg/core/attacks/icd.go b/pkg/core/attacks/icd.go index 730c1a7b9a..8d6a8a35f5 100644 --- a/pkg/core/attacks/icd.go +++ b/pkg/core/attacks/icd.go @@ -56,7 +56,6 @@ const ( ICDTagNeuvilletteC6 ICDTagFurinaChevalmarin ICDTagFurinaUsher - ICDTagNaviaBurst ICDTagLength ) diff --git a/pkg/shortcut/characters.go b/pkg/shortcut/characters.go index edf25c271e..91c51f38bf 100644 --- a/pkg/shortcut/characters.go +++ b/pkg/shortcut/characters.go @@ -153,5 +153,5 @@ var CharNameToKey = map[string]keys.Char{ "furina": keys.Furina, "furinadefontaine": keys.Furina, "navia": keys.Navia, - "demoisselle": keys.Navia, + "demoiselle": keys.Navia, } diff --git a/ui/packages/docs/src/components/Actions/character_data.json b/ui/packages/docs/src/components/Actions/character_data.json index b47bbcbecd..2fe6966fd9 100644 --- a/ui/packages/docs/src/components/Actions/character_data.json +++ b/ui/packages/docs/src/components/Actions/character_data.json @@ -1503,7 +1503,7 @@ }, { "ability": "walk", - "notes": "Normal Attacks do not have legal walk cancels recorded, but Skill and Burst do" + "notes": "Normal Attacks do not have legal walk cancels recorded, but Skill and Burst do." }, { "ability": "swap", diff --git a/ui/packages/docs/src/components/AoE/character_data.json b/ui/packages/docs/src/components/AoE/character_data.json index 02f5c4963e..e2365ed988 100644 --- a/ui/packages/docs/src/components/AoE/character_data.json +++ b/ui/packages/docs/src/components/AoE/character_data.json @@ -4710,70 +4710,90 @@ "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is 7.911°." }, { "ability": "E-3rd-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is -1.826°." }, { "ability": "E-4th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is -4.325°." }, { "ability": "E-5th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is 0.733°." }, { "ability": "E-6th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is 6.209°." }, { "ability": "E-7th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is -2.752°." }, { "ability": "E-8th-Bullet", "shape": "Box", "center": "Player", + "offsetX": 0.01, + "offsetY": 0.01, "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is 7.845°." }, { "ability": "E-9th-Bullet", "shape": "Box", "center": "Player", + "offsetX": -0.01, + "offsetY": -0.01, "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is -7.933°." }, { "ability": "E-10th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is 2.626°." }, { "ability": "E-11th-Bullet", "shape": "Box", "center": "Player", "boxX": 0.25, - "boxY": 11.5 + "boxY": 11.5, + "notes": "Box rotation angle is -9.993°." + }, + { + "ability": "E-Ceremonial-Crystalshot", + "shape": "Single", + "center": "GlobalValue", + "notes": "If any bullet hits a target, then this attack will spawn on them." } ], "burst": [ @@ -4789,7 +4809,7 @@ "shape": "Circle", "center": "PrimaryTarget", "radius": 3, - "notes": "The Circle Spawns in a random position, within 1.2m of the centre of the enemy. If there are no enemies within 10m, then it will spawn in a random location between 1 and 6m from the player's position" + "notes": "The Circle Spawns in a random position, within 1.2m of the centre of the enemy. If there are no enemies within 10m, then it will spawn in a random location between 1 and 6m from the player's position + Y: 4m." } ] }, diff --git a/ui/packages/docs/src/components/Frames/character_data.json b/ui/packages/docs/src/components/Frames/character_data.json index 6d07cefb27..d11ada0ce3 100644 --- a/ui/packages/docs/src/components/Frames/character_data.json +++ b/ui/packages/docs/src/components/Frames/character_data.json @@ -693,6 +693,18 @@ "count_credit": "shizukayuki", "vid": "https://youtu.be/qnWjXMmf6MM", "count": "https://docs.google.com/spreadsheets/d/1M_FffTMTx11rbESklIbzgiSfm9RnWhgKcRlSEnJ08p4/edit#gid=0" + }, + { + "vid_credit": "shizukayuki", + "count_credit": "shizukayuki", + "vid": "https://youtu.be/BxIFv2HUob0", + "count": "https://docs.google.com/spreadsheets/d/1M_FffTMTx11rbESklIbzgiSfm9RnWhgKcRlSEnJ08p4/edit#gid=0" + }, + { + "vid_credit": "shizukayuki", + "count_credit": "shizukayuki", + "vid": "https://youtu.be/PJu7ulF0JJo", + "count": "https://docs.google.com/spreadsheets/d/1M_FffTMTx11rbESklIbzgiSfm9RnWhgKcRlSEnJ08p4/edit#gid=0" } ], "neuvillette": [ diff --git a/ui/packages/docs/src/components/Hitlag/character_data.json b/ui/packages/docs/src/components/Hitlag/character_data.json index ef0491913c..ec6d7eda6e 100644 --- a/ui/packages/docs/src/components/Hitlag/character_data.json +++ b/ui/packages/docs/src/components/Hitlag/character_data.json @@ -1949,42 +1949,42 @@ "attack": [ { "ability": "N1", - "hitHaltTime": 0, - "hitHaltTimeScale": 0.06, + "hitHaltTime": 0.06, + "hitHaltTimeScale": 0.01, "canBeDefenseHalt": true, "deployable": false }, { "ability": "N2", - "hitHaltTime": 0, - "hitHaltTimeScale": 0.06, + "hitHaltTime": 0.06, + "hitHaltTimeScale": 0.0, "canBeDefenseHalt": true, "deployable": false }, { "ability": "N3-1", - "hitHaltTime": 0, + "hitHaltTime": 0.01, "hitHaltTimeScale": 0.01, "canBeDefenseHalt": false, "deployable": true }, { "ability": "N3-2", - "hitHaltTime": 0, + "hitHaltTime": 0.01, "hitHaltTimeScale": 0.01, "canBeDefenseHalt": false, "deployable": true }, { "ability": "N3-3", - "hitHaltTime": 0, + "hitHaltTime": 0.01, "hitHaltTimeScale": 0.01, "canBeDefenseHalt": false, "deployable": true }, { "ability": "N4", - "hitHaltTime": 0, + "hitHaltTime": 0.01, "hitHaltTimeScale": 0.06, "canBeDefenseHalt": true, "deployable": false diff --git a/ui/packages/docs/src/components/Names/character_data.json b/ui/packages/docs/src/components/Names/character_data.json index 272c431e94..976c804505 100644 --- a/ui/packages/docs/src/components/Names/character_data.json +++ b/ui/packages/docs/src/components/Names/character_data.json @@ -80,7 +80,7 @@ ], "navia": [ "navia", - "demoisselle" + "demoiselle" ], "neuvillette": [ "neuv", diff --git a/ui/packages/docs/src/components/Params/character_data.json b/ui/packages/docs/src/components/Params/character_data.json index 79f2d086cf..b6931ab7f1 100644 --- a/ui/packages/docs/src/components/Params/character_data.json +++ b/ui/packages/docs/src/components/Params/character_data.json @@ -468,12 +468,7 @@ { "ability": "skill", "param": "hold", - "desc": "0 for Tap (default), value between 1 and 241 for Hold. The number is added to the minimum holding frames for Tap to turn into Hold (41), subtracted by 1 to account for requiring a 1 to activate" - }, - { - "ability": "skill", - "param": "shrapnel", - "desc": "The number of shrapnel use the skill with. 0 (default) uses the number acquired throughout the config, 1-6 force that many shrapnel." + "desc": "0 for Tap (default), value between 1 and 241 for Hold. The number is added to the minimum holding frames for Tap to turn into Hold (41), subtracted by 1 to account for requiring a 1 to activate." } ], "nilou": [ From 56509663d1ee776222873444ef070e28089dd504 Mon Sep 17 00:00:00 2001 From: David Ji Date: Sun, 24 Dec 2023 23:39:18 +1100 Subject: [PATCH 36/36] fixed typos --- internal/characters/navia/skill.go | 4 ++-- ui/packages/docs/docs/reference/characters/navia.md | 2 +- ui/packages/docs/src/components/Hitlag/character_data.json | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/internal/characters/navia/skill.go b/internal/characters/navia/skill.go index 8f39c765ca..bec5266b3c 100644 --- a/internal/characters/navia/skill.go +++ b/internal/characters/navia/skill.go @@ -58,7 +58,7 @@ const ( skillHoldCDStart = 41 skillMaxHoldDuration = 241 // add 1f to account for hold being set to 1 for activation - bulletBoxWidth = 11.5 + bulletBoxLength = 11.5 particleICDKey = "navia-particle-icd" arkheICDKey = "navia-arkhe-icd" @@ -175,7 +175,7 @@ func (c *char) Skill(p map[string]int) (action.Info, error) { c.Core.Combat.Player(), geometry.Point{X: hitscans[i][2], Y: hitscans[i][3]}.Rotate(geometry.DegreesToDirection(hitscans[i][1])), hitscans[i][0], - bulletBoxWidth, + bulletBoxLength, )); ok { hits++ } diff --git a/ui/packages/docs/docs/reference/characters/navia.md b/ui/packages/docs/docs/reference/characters/navia.md index 5c0dfc0f1c..0e1a572b55 100644 --- a/ui/packages/docs/docs/reference/characters/navia.md +++ b/ui/packages/docs/docs/reference/characters/navia.md @@ -1,5 +1,5 @@ --- -title: navia +title: Navia --- import HitlagTable from "@site/src/components/Hitlag/HitlagTable"; diff --git a/ui/packages/docs/src/components/Hitlag/character_data.json b/ui/packages/docs/src/components/Hitlag/character_data.json index ec6d7eda6e..0cdc0e84d2 100644 --- a/ui/packages/docs/src/components/Hitlag/character_data.json +++ b/ui/packages/docs/src/components/Hitlag/character_data.json @@ -1957,7 +1957,7 @@ { "ability": "N2", "hitHaltTime": 0.06, - "hitHaltTimeScale": 0.0, + "hitHaltTimeScale": 0.01, "canBeDefenseHalt": true, "deployable": false }, @@ -1984,8 +1984,8 @@ }, { "ability": "N4", - "hitHaltTime": 0.01, - "hitHaltTimeScale": 0.06, + "hitHaltTime": 0.06, + "hitHaltTimeScale": 0.01, "canBeDefenseHalt": true, "deployable": false }