Repository: genshinsim/gcsim
Pull request #448 (2022-04-09 21:59:48)

ganyu frames update
- Updates Ganyu frames
- Sheet: https://docs.google.com/spreadsheets/d/1PDHCZKh-7eWpbE1b0OyQUCzSPHcViYZ22EDEItjiU7Y/edit?usp=sharing
- Video: https://youtu.be/ROvivlCUR3s
- Tested here: https://gcsim.app/viewer/share/perm_GXx-UDeQ3chtpj8UvH8_6

kurt22i: I had to remove the C2 CD check, because the test function for this doesn't support testing CDs that don't start immediately, and changing it to do so would require a bit of a rework
srliao: @kurt22i please include link to video here

Patch:
diff --git a/internal/characters/ganyu/abil.go b/internal/characters/ganyu/abil.go
index 9b16b0c7c..38453a0d1 100644
--- a/internal/characters/ganyu/abil.go
+++ b/internal/characters/ganyu/abil.go
@@ -41,7 +41,7 @@ func (c *char) Aimed(p map[string]int) (int, int) {
 	}
 	bloom, ok := p["bloom"]
 	if !ok {
-		bloom = 20
+		bloom = 24
 	}
 	weakspot, ok := p["weakspot"]
 
@@ -98,12 +98,12 @@ func (c *char) Skill(p map[string]int) (int, int) {
 
 	snap := c.Snapshot(&ai)
 	//flower damage immediately
-	c.Core.Combat.QueueAttackWithSnap(ai, snap, core.NewDefCircHit(2, false, core.TargettableEnemy), 30)
+	c.Core.Combat.QueueAttackWithSnap(ai, snap, core.NewDefCircHit(2, false, core.TargettableEnemy), 13)
 	//we get the orbs right away
 	c.QueueParticle("ganyu", 2, core.Cryo, 90)
 
 	//flower damage is after 6 seconds
-	c.Core.Combat.QueueAttackWithSnap(ai, snap, core.NewDefCircHit(2, false, core.TargettableEnemy), 360)
+	c.Core.Combat.QueueAttackWithSnap(ai, snap, core.NewDefCircHit(2, false, core.TargettableEnemy), 373)
 	// TODO: Particle flight time is 60s?
 	c.QueueParticle("ganyu", 2, core.Cryo, 420)
 
@@ -114,7 +114,7 @@ func (c *char) Skill(p map[string]int) (int, int) {
 		c.Core.Status.AddStatus("ganyuc6", 1800)
 	}
 
-	c.SetCD(core.ActionSkill, 600)
+	c.SetCDWithDelay(core.ActionSkill, 600, 10)
 
 	return f, a
 }
@@ -135,7 +135,7 @@ func (c *char) Burst(p map[string]int) (int, int) {
 	}
 	snap := c.Snapshot(&ai)
 
-	c.Core.Status.AddStatus("ganyuburst", 15*60)
+	c.Core.Status.AddStatus("ganyuburst", 15*60+130)
 
 	rad, ok := p["radius"]
 	if !ok {
@@ -148,7 +148,8 @@ func (c *char) Burst(p map[string]int) (int, int) {
 	lastHit := make(map[core.Target]int)
 	// ccc := 0
 	//tick every .3 sec, every fifth hit is targetted i.e. 1, 0, 0, 0, 0, 1
-	for delay := 0; delay < 900; delay += 18 {
+	//first hit at 148
+	for delay := a; delay < 900+a; delay += 18 {
 		c.AddTask(func() {
 			//check if this hits first
 			target := -1
@@ -172,7 +173,7 @@ func (c *char) Burst(p map[string]int) (int, int) {
 			}
 			//deal dmg
 			c.Core.Combat.QueueAttackWithSnap(ai, snap, core.NewDefCircHit(9, false, core.TargettableEnemy), 0)
-		}, "ganyu-q", delay+f)
+		}, "ganyu-q", delay)
 
 	}
 	// c.AddTask(func() {
@@ -182,7 +183,7 @@ func (c *char) Burst(p map[string]int) (int, int) {
 	//a4 every .3 seconds for the duration of the burst, add ice dmg up to active char for 1sec
 	//duration is 15 seconds
 	//starts from end of cast
-	for i := f; i < 900+f; i += 18 {
+	for i := a; i < 900+a; i += 18 {
 		t := i
 		c.AddTask(func() {
 			active := c.Core.Chars[c.Core.ActiveChar]
@@ -195,7 +196,7 @@ func (c *char) Burst(p map[string]int) (int, int) {
 				},
 				Expiry: c.Core.F + 60,
 			})
-			if t >= 900-18 {
+			if t >= 900+a-18 {
 				c.Core.Log.NewEvent("a4 last tick", core.LogCharacterEvent, c.Index, "ends_on", c.Core.F+60)
 			}
 		}, "ganyu-a4", i)
@@ -222,9 +223,9 @@ func (c *char) Burst(p map[string]int) (int, int) {
 	}
 
 	//add cooldown to sim
-	c.SetCDWithDelay(core.ActionBurst, 15*60, 8)
+	c.SetCD(core.ActionBurst, 15*60)
 	//use up energy
-	c.ConsumeEnergy(8)
+	c.ConsumeEnergy(3)
 
 	return f, a
 }
diff --git a/internal/characters/ganyu/frames.go b/internal/characters/ganyu/frames.go
index 57f58db92..8a0d3f6d3 100644
--- a/internal/characters/ganyu/frames.go
+++ b/internal/characters/ganyu/frames.go
@@ -6,23 +6,30 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 	switch a {
 	case core.ActionAttack:
 		f := 0
+		a := 0
 		switch c.NormalCounter {
 		//TODO: need to add atkspd mod
 		case 0:
-			f = 18 //frames from keqing lib
+			f = 13
+			a = 19
 		case 1:
-			f = 43 - 18
+			f = 14
+			a = 27
 		case 2:
-			f = 73 - 43
+			f = 20
+			a = 38
 		case 3:
-			f = 117 - 73
+			f = 26
+			a = 37
 		case 4:
-			f = 153 - 117
+			f = 21
+			a = 28
 		case 5:
-			f = 190 - 153
+			f = 22
+			a = 59
 		}
 		f = int(float64(f) / (1 + c.Stat(core.AtkSpd)))
-		return f, f
+		return f, a
 	case core.ActionAim:
 		//check for c6, if active then return 10, otherwise 115
 		if c.Core.Status.Duration("ganyuc6") > 0 {
@@ -30,13 +37,60 @@ func (c *char) ActionFrames(a core.ActionType, p map[string]int) (int, int) {
 			c.Core.Status.DeleteStatus("ganyuc6")
 			return 10, 10
 		}
-		return 115, 115 //frames from keqing lib
+		return 103, 113
 	case core.ActionSkill:
-		return 30, 30 //ok
+		return 13, 28 //ok
 	case core.ActionBurst:
-		return 122, 122 //ok
+		return 122, 130 //ok
 	default:
 		c.Core.Log.NewEventBuildMsg(core.LogActionEvent, c.Index, "unknown action (invalid frames): ", a.String())
 		return 0, 0
 	}
 }
+
+func (c *char) InitCancelFrames() {
+	//normal cancels
+	c.SetNormalCancelFrames(0, core.ActionAttack, 19-13) //n1 -> next attack
+	//c.SetNormalCancelFrames(0, core.Actionaim, 35-10) //n1 -> aim
+
+	c.SetNormalCancelFrames(1, core.ActionAttack, 27-14) //n2 -> next attack
+	//c.SetNormalCancelFrames(1, core.Actionaim, 29-13) //n2 -> aim
+
+	c.SetNormalCancelFrames(2, core.ActionAttack, 38-20) //n3 -> next attack
+	//c.SetNormalCancelFrames(2, core.Actionaim, 35-19) //n3 -> aim
+
+	c.SetNormalCancelFrames(3, core.ActionAttack, 37-26) //n4 -> next attack
+	//c.SetNormalCancelFrames(3, core.Actionaim, 33-17) //n4 -> aim
+
+	c.SetNormalCancelFrames(4, core.ActionAttack, 28-21) //n5 -> next attack
+	//c.SetNormalCancelFrames(4, core.Actionaim, 33-17) //n5 -> aim
+
+	c.SetNormalCancelFrames(5, core.ActionAttack, 59-22) //n6 -> next attack (n1)
+	//c.SetNormalCancelFrames(5, core.Actionaim, 33-17) //n6 -> aim
+
+	//todo: get separate counts for each cancel, currently using generic frames for all of them
+	c.SetAbilCancelFrames(core.ActionAim, core.ActionAttack, 113-103) //aim -> n1
+	c.SetAbilCancelFrames(core.ActionAim, core.ActionAim, 113-103)    //aim -> aim
+	c.SetAbilCancelFrames(core.ActionAim, core.ActionSkill, 113-103)  //aim -> skill
+	c.SetAbilCancelFrames(core.ActionAim, core.ActionBurst, 113-103)  //aim -> burst
+	c.SetAbilCancelFrames(core.ActionAim, core.ActionSwap, 113-103)   //aim -> swap
+
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionAttack, 124-122) //burst -> n1
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionAim, 124-122)    //burst -> aim (assumed)
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionSkill, 124-122)  //burst -> skill
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionDash, 124-122)   //burst -> dash
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionJump, 124-122)   //burst -> jump
+	c.SetAbilCancelFrames(core.ActionBurst, core.ActionSwap, 122-122)   //burst -> swap
+
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionAttack, 28-13)
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionAim, 28-13) //assumed
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionBurst, 28-13)
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionDash, 28-13)
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionJump, 28-13)
+	c.SetAbilCancelFrames(core.ActionSkill, core.ActionSwap, 27-13)
+
+}
+
+func (c *char) ActionInterruptableDelay(next core.ActionType, p map[string]int) int {
+	return c.Tmpl.ActionInterruptableDelay(next, p)
+}
diff --git a/internal/characters/ganyu/ganyu.go b/internal/characters/ganyu/ganyu.go
index 77ea73241..fbc10a503 100644
--- a/internal/characters/ganyu/ganyu.go
+++ b/internal/characters/ganyu/ganyu.go
@@ -34,6 +34,7 @@ func NewChar(s *core.Core, p core.CharacterProfile) (core.Character, error) {
 	c.BurstCon = 3
 	c.SkillCon = 5
 	c.CharZone = core.ZoneLiyue
+	c.InitCancelFrames()
 
 	c.a1Expiry = -1
 


Test patch:
diff --git a/internal/characters/ganyu/ganyu_test.go b/internal/characters/ganyu/ganyu_test.go
index e87baa48e..866337d3d 100644
--- a/internal/characters/ganyu/ganyu_test.go
+++ b/internal/characters/ganyu/ganyu_test.go
@@ -29,7 +29,7 @@ func TestCD(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	err = testhelper.TestSkillCDSingleCharge(c, x, 600)
+		err = testhelper.TestSkillCDSingleCharge(c, x, 600+10)
 	if err != nil {
 		t.Error(err)
 	}
@@ -43,7 +43,7 @@ func TestC2CD(t *testing.T) {
 		t.Error(err)
 		t.FailNow()
 	}
-	err = testhelper.TestSkillCDDoubleCharge(c, x, []int{600, 600})
+	err = testhelper.TestSkillCooldown(c, x, []int{600+10, 600})
 	if err != nil {
 		t.Error(err)
 	}