Skip to content
Open
24 changes: 24 additions & 0 deletions internal/character/ruanmei/attack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ruanmei

import (
"github.com/simimpact/srsim/pkg/engine/info"
"github.com/simimpact/srsim/pkg/key"
"github.com/simimpact/srsim/pkg/model"
)

const Normal key.Attack = "ruanmei-normal"

func (c *char) Attack(target key.TargetID, state info.ActionState) {
c.engine.Attack(info.Attack{
Key: Normal,
Source: c.id,
Targets: []key.TargetID{target},
DamageType: model.DamageType_ICE,
AttackType: model.AttackType_NORMAL,
BaseDamage: info.DamageMap{
model.DamageFormula_BY_ATK: atk[c.info.AttackLevelIndex()],
},
StanceDamage: 30.0,
EnergyGain: 20.0,
})
}
164 changes: 164 additions & 0 deletions internal/character/ruanmei/data.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

64 changes: 64 additions & 0 deletions internal/character/ruanmei/eidolon.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package ruanmei

import (
"github.com/simimpact/srsim/pkg/engine/event"
"github.com/simimpact/srsim/pkg/engine/info"
"github.com/simimpact/srsim/pkg/engine/modifier"
"github.com/simimpact/srsim/pkg/engine/prop"
"github.com/simimpact/srsim/pkg/model"
)

const (
E1 = "ruanmei-e1"
E2 = "ruanmei-e2"
E4 = "ruanmei-e4"
E4Listener = "ruanmei-e4-listener"
)

func init() {
modifier.Register(E1, modifier.Config{
Stacking: modifier.ReplaceBySource,
Listeners: modifier.Listeners{
OnBeforeHitAll: applyE1,
},
})
modifier.Register(E2, modifier.Config{
StatusType: model.StatusType_STATUS_BUFF,
Listeners: modifier.Listeners{
OnBeforeHitAll: applyE2,
},
})
// E4 is summarized to 1 buff mod
modifier.Register(E4, modifier.Config{
Stacking: modifier.ReplaceBySource,
StatusType: model.StatusType_STATUS_BUFF,
CanDispel: true,
})
// Causes known bug (?) of the breaking hit not benefitting from E4 as this should apply with OnBeforeBeingBreak
modifier.Register(E4Listener, modifier.Config{
Stacking: modifier.ReplaceBySource,
BehaviorFlags: []model.BehaviorFlag{model.BehaviorFlag_REMOVE_WHEN_SOURCE_DEAD},
Listeners: modifier.Listeners{
OnBeforeBeingBreak: addE4,
},
})
}

func applyE1(mod *modifier.Instance, e event.HitStart) {
e.Hit.Defender.AddProperty(E1, prop.DEFPercent, -0.2)
}

func applyE2(mod *modifier.Instance, e event.HitStart) {
if mod.Engine().HasBehaviorFlag(e.Defender, model.BehaviorFlag_BREAK) {
e.Hit.Attacker.AddProperty(E2, prop.ATKPercent, 0.4)
}
}

func addE4(mod *modifier.Instance) {
mod.Engine().AddModifier(mod.Source(), info.Modifier{
Name: E4,
Source: mod.Source(),
Stats: info.PropMap{prop.BreakEffect: 1},
Duration: 3,
})
}
57 changes: 57 additions & 0 deletions internal/character/ruanmei/ruanmei.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package ruanmei

import (
"github.com/simimpact/srsim/pkg/engine"
"github.com/simimpact/srsim/pkg/engine/info"
"github.com/simimpact/srsim/pkg/engine/target/character"
"github.com/simimpact/srsim/pkg/key"
"github.com/simimpact/srsim/pkg/model"
)

func init() {
character.Register(key.RuanMei, character.Config{
Create: NewInstance,
Rarity: 5,
Element: model.DamageType_ICE,
Path: model.Path_HARMONY,
MaxEnergy: 130,
Promotions: promotions,
Traces: traces,
SkillInfo: character.SkillInfo{
Attack: character.Attack{
SPAdd: 1,
TargetType: model.TargetType_ENEMIES,
},
Skill: character.Skill{
SPNeed: 1,
TargetType: model.TargetType_SELF,
},
Ult: character.Ult{
TargetType: model.TargetType_ALLIES,
},
Technique: character.Technique{
TargetType: model.TargetType_SELF,
IsAttack: false,
},
},
})
}

type char struct {
engine engine.Engine
id key.TargetID
info info.Character
}

func NewInstance(engine engine.Engine, id key.TargetID, charInfo info.Character) info.CharInstance {
c := &char{
engine: engine,
id: id,
info: charInfo,
}

c.initTalent()
c.initTraces()
c.initUlt()
return c
}
76 changes: 76 additions & 0 deletions internal/character/ruanmei/skill.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package ruanmei

import (
"github.com/simimpact/srsim/pkg/engine/info"
"github.com/simimpact/srsim/pkg/engine/modifier"
"github.com/simimpact/srsim/pkg/engine/prop"
"github.com/simimpact/srsim/pkg/key"
"github.com/simimpact/srsim/pkg/model"
)

const (
SkillMain = "ruanmei-skill"
OvertoneDmgBuff = "ruanmei-skill-overtone-dmg-buff"
OvertoneBreakEfficiency = "ruanmei-skill-overtone-weakness-break-efficiency"
)

func init() {
modifier.Register(SkillMain, modifier.Config{
Stacking: modifier.ReplaceBySource,
TickMoment: modifier.ModifierPhase1End,
Listeners: modifier.Listeners{
OnAdd: addOvertone,
OnRemove: removeOvertone,
},
})
modifier.Register(OvertoneDmgBuff, modifier.Config{
Stacking: modifier.ReplaceBySource,
StatusType: model.StatusType_STATUS_BUFF,
})
modifier.Register(OvertoneBreakEfficiency, modifier.Config{
Stacking: modifier.ReplaceBySource,
StatusType: model.StatusType_STATUS_BUFF,
})
}

func (c *char) Skill(target key.TargetID, state info.ActionState) {
c.engine.AddModifier(c.id, info.Modifier{
Name: SkillMain,
Source: c.id,
})
}

// Overtone is summarized from 4 "sub" mods with 2 being purely for display to only 2 "sub" mods
func addOvertone(mod *modifier.Instance) {
// Calculate how much DMG Bonus should be given
rm, _ := mod.Engine().CharacterInfo(mod.Owner())
dmgAmt := skillDmg[rm.SkillLevelIndex()]
if rm.Traces["103"] {
dmgAmtA6 := 0.06 * float64(int((mod.OwnerStats().BreakEffect()-1.2)/0.1))
if dmgAmtA6 > 0.36 {
dmgAmtA6 = 0.36
}
dmgAmt += dmgAmtA6
}
for _, trg := range mod.Engine().Characters() {
mod.Engine().AddModifier(trg, info.Modifier{
Name: OvertoneDmgBuff,
Source: mod.Owner(),
Stats: info.PropMap{prop.AllDamagePercent: dmgAmt},
})
mod.Engine().AddModifier(trg, info.Modifier{
Name: OvertoneBreakEfficiency,
Source: mod.Owner(),
Stats: info.PropMap{prop.AllStanceDMGPercent: 0.5},
})
}
}

func removeOvertone(mod *modifier.Instance) {
for _, trg := range mod.Engine().Characters() {
mod.Engine().RemoveModifier(trg, OvertoneDmgBuff)
mod.Engine().RemoveModifier(trg, OvertoneBreakEfficiency)
}
}

// Technique doing Insert with autocast Skill (without consuming SP)
Loading
Loading