Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions internal/tests/reactable/mutable_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package reactable_test

import (
"math"
"testing"

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

func TestNonMutableVape(t *testing.T) {
c, _ := makeCore(0)

// create enemy with hydro aura
trg := enemy.New(c, info.EnemyProfile{
Level: 100,
Resist: make(map[attributes.Element]float64),
Pos: info.Coord{
X: 0,
Y: 0,
R: 1,
},
Element: attributes.Hydro,
ElementDurability: 25,
})
c.Combat.AddEnemy(trg)

err := c.Init()
if err != nil {
t.Errorf("error initializing core: %v", err)
t.FailNow()
}

count := 0
c.Events.Subscribe(event.OnVaporize, func(args ...interface{}) bool {
count++
return false
}, "vaporize")

c.QueueAttackEvent(&combat.AttackEvent{
Info: combat.AttackInfo{
Element: attributes.Pyro,
Durability: 25,
},
Pattern: combat.NewCircleHitOnTarget(geometry.Point{}, nil, 100),
}, 0)
advanceCoreFrame(c)

if float64(trg.Durability[reactable.Pyro]) > 0.000001 {
t.Errorf(
"expected pyro=%v, got pyro=%v",
0,
trg.Durability[reactable.Pyro],
)
}
if math.Abs(float64(trg.Durability[reactable.Hydro])-25) > 0.000001 {
t.Errorf(
"expected hydro=%v, got hydro=%v",
25,
trg.Durability[reactable.Hydro],
)
}
if count != 1 {
t.Errorf(
"expected %v vaporizes, got %v",
1,
count,
)
}
}
1 change: 1 addition & 0 deletions internal/tests/reactable/reactable_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ func makeCore(trgCount int) (*core.Core, []*enemy.Enemy) {
Y: 0,
R: 1,
},
Element: attributes.NoElement,
})
trgs = append(trgs, e)
c.Combat.AddEnemy(e)
Expand Down
3 changes: 3 additions & 0 deletions pkg/core/info/enemy.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package info

import (
"github.com/genshinsim/gcsim/pkg/core/attributes"
"github.com/genshinsim/gcsim/pkg/core/reactions"
"github.com/genshinsim/gcsim/pkg/model"
)

Expand All @@ -19,6 +20,8 @@ type EnemyProfile struct {
HpGrowCurve model.MonsterCurveType `json:"-"`
Id int `json:"-"`
MonsterName string `json:"monster_name"`
Element attributes.Element `json:"element"`
ElementDurability reactions.Durability `json:"element_durability"`
Modified bool `json:"modified"`
}

Expand Down
58 changes: 58 additions & 0 deletions pkg/enemy/enemy.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/genshinsim/gcsim/pkg/core/geometry"
"github.com/genshinsim/gcsim/pkg/core/glog"
"github.com/genshinsim/gcsim/pkg/core/info"
"github.com/genshinsim/gcsim/pkg/core/reactions"
"github.com/genshinsim/gcsim/pkg/core/targets"
"github.com/genshinsim/gcsim/pkg/modifier"
"github.com/genshinsim/gcsim/pkg/queue"
Expand Down Expand Up @@ -53,6 +54,24 @@ func New(core *core.Core, p info.EnemyProfile) *Enemy {
e.hp = p.HP
e.maxhp = p.HP
}
if p.Element != attributes.NoElement && p.ElementDurability > 0 {
e.ApplySelfInfusion(p.Element, p.ElementDurability, -1)

var mod reactable.Modifier
switch p.Element {
case attributes.Electro:
mod = reactable.Electro
case attributes.Hydro:
mod = reactable.Hydro
case attributes.Pyro:
mod = reactable.Pyro
case attributes.Cryo:
mod = reactable.Cryo
case attributes.Dendro:
mod = reactable.Dendro
}
e.Reactable.Mutable[mod] = false
}
return e
}

Expand Down Expand Up @@ -81,3 +100,42 @@ func (e *Enemy) SetDirectionToClosestEnemy() {}
func (e *Enemy) CalcTempDirection(trg geometry.Point) geometry.Point {
return geometry.DefaultDirection()
}

func (e *Enemy) ApplySelfInfusion(ele attributes.Element, dur reactions.Durability, f int) {
e.Core.Log.NewEventBuildMsg(glog.LogEnemyEvent, -1, "self infusion applied to enemy: "+ele.String()).
Write("index", e.Key()).
Write("durability", dur).
Write("duration", f)
// we're assuming self infusion isn't subject to 0.8x multiplier
// also no real sanity check
if ele == attributes.Frozen {
return
}
var mod reactable.Modifier
switch ele {
case attributes.Electro:
mod = reactable.Electro
case attributes.Hydro:
mod = reactable.Hydro
case attributes.Pyro:
mod = reactable.Pyro
case attributes.Cryo:
mod = reactable.Cryo
case attributes.Dendro:
mod = reactable.Dendro
}

// we're assuming refill maintains the same decay rate?
if e.Durability[mod] > reactable.ZeroDur {
// make sure we're not adding more than incoming
if e.Durability[mod] < dur {
e.Durability[mod] = dur
}
return
}
// otherwise calculate decay based on specified f (in frames)
e.Durability[mod] = dur
if f > 0 {
e.DecayRate[mod] = dur / reactions.Durability(f)
}
}
3 changes: 3 additions & 0 deletions pkg/enemy/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ func ConfigureTarget(profile *info.EnemyProfile, name string, params TargetParam
enemyInfo.ParticleDropCount = profile.ParticleDropCount
enemyInfo.ParticleElement = profile.ParticleElement
enemyInfo.ParticleDrops = []*model.MonsterHPDrop{}
} else {
enemyInfo.ParticleElement = attributes.NoElement
}
*profile = enemyInfo
return nil
Expand Down Expand Up @@ -92,5 +94,6 @@ func getMonsterInfo(name string) (info.EnemyProfile, error) {
HpGrowCurve: result.BaseStats.HpCurve,
Id: int(result.Id),
MonsterName: result.Key,
Element: attributes.NoElement,
}, nil
}
2 changes: 2 additions & 0 deletions pkg/gcs/ast/item.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ const (
keywordParticleDropCount // particle_drop_count
keywordParticleElement // particle_element
keywordHurt // hurt
keywordElement // element
keywordElementDurability // element_durability

// Keywords specific to gcsim appears after this
itemKeys
Expand Down
2 changes: 2 additions & 0 deletions pkg/gcs/ast/keys.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ var key = map[string]TokenType{
"resist": keywordResist,
"energy": keywordEnergy,
"hurt": keywordHurt,
"element": keywordElement,
"element_durability": keywordElementDurability,
// commands
// team keywords
// flags
Expand Down
23 changes: 22 additions & 1 deletion pkg/gcs/ast/parseTarget.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (

"github.com/genshinsim/gcsim/pkg/core/attributes"
"github.com/genshinsim/gcsim/pkg/core/info"
"github.com/genshinsim/gcsim/pkg/core/reactions"
"github.com/genshinsim/gcsim/pkg/enemy"
)

Expand All @@ -14,6 +15,7 @@ func parseTarget(p *Parser) (parseFn, error) {
var r info.EnemyProfile
r.Resist = make(map[attributes.Element]float64)
r.ParticleElement = attributes.NoElement
r.Element = attributes.NoElement
for n := p.next(); n.Typ != itemEOF; n = p.next() {
switch n.Typ {
case itemIdentifier:
Expand Down Expand Up @@ -122,7 +124,6 @@ func parseTarget(p *Parser) (parseFn, error) {
}
r.ParticleDropThreshold = amt
r.ParticleDrops = nil // separate particle system
r.ParticleElement = attributes.NoElement
r.Modified = true
case keywordParticleDropCount:
item, err := p.acceptSeqReturnLast(itemAssign, itemNumber)
Expand Down Expand Up @@ -157,6 +158,26 @@ func parseTarget(p *Parser) (parseFn, error) {

r.Resist[eleKeys[s]] += amt
r.Modified = true
case keywordElement:
item, err := p.acceptSeqReturnLast(itemAssign, itemElementKey)
if err != nil {
return nil, err
}
if ele, ok := eleKeys[item.Val]; ok {
r.Element = ele
}
r.Modified = true
case keywordElementDurability:
item, err := p.acceptSeqReturnLast(itemAssign, itemNumber)
if err != nil {
return nil, err
}
dur, err := itemNumberToFloat64(item)
if err != nil {
return nil, err
}
r.ElementDurability = reactions.Durability(dur)
r.Modified = true
case itemTerminateLine:
p.res.Targets = append(p.res.Targets, r)
return parseRows, nil
Expand Down
12 changes: 8 additions & 4 deletions pkg/reactable/electrocharged.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,10 +118,14 @@ func (r *Reactable) TryAddEC(a *combat.AttackEvent) bool {
}

func (r *Reactable) waneEC() {
r.Durability[Electro] -= 10
r.Durability[Electro] = max(0, r.Durability[Electro])
r.Durability[Hydro] -= 10
r.Durability[Hydro] = max(0, r.Durability[Hydro])
if r.Mutable[Electro] {
r.Durability[Electro] -= 10
r.Durability[Electro] = max(0, r.Durability[Electro])
}
if r.Mutable[Hydro] {
r.Durability[Hydro] -= 10
r.Durability[Hydro] = max(0, r.Durability[Hydro])
}
r.core.Log.NewEvent("ec wane",
glog.LogElementEvent,
-1,
Expand Down
23 changes: 21 additions & 2 deletions pkg/reactable/reactable.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@ func (r *Modifier) UnmarshalJSON(b []byte) error {
type Reactable struct {
Durability [EndModifier]reactions.Durability
DecayRate [EndModifier]reactions.Durability
Mutable [EndModifier]bool
// Source []int //source frame of the aura
self combat.Target
core *core.Core
Expand Down Expand Up @@ -134,6 +135,10 @@ const frzDecayCap reactions.Durability = 10.0 / 60.0
const ZeroDur reactions.Durability = 0.00000000001

func (r *Reactable) Init(self combat.Target, c *core.Core) *Reactable {
for i := Invalid; i < EndModifier; i++ {
r.Mutable[i] = true
}

r.self = self
r.core = c
r.DecayRate[Frozen] = frzDecayCap
Expand Down Expand Up @@ -250,6 +255,9 @@ func (r *Reactable) attachOrRefillNormalEle(mod Modifier, dur reactions.Durabili
}

func (r *Reactable) attachOverlap(mod Modifier, amt, length reactions.Durability) {
if !r.Mutable[mod] {
return
}
if r.Durability[mod] > ZeroDur {
add := max(amt-r.Durability[mod], 0)
if add > 0 {
Expand All @@ -264,6 +272,9 @@ func (r *Reactable) attachOverlap(mod Modifier, amt, length reactions.Durability
}

func (r *Reactable) attachOverlapRefreshDuration(mod Modifier, amt, length reactions.Durability) {
if !r.Mutable[mod] {
return
}
if amt < r.Durability[mod] {
return
}
Expand All @@ -277,6 +288,9 @@ func (r *Reactable) attachBurning() {
}

func (r *Reactable) addDurability(mod Modifier, amt reactions.Durability) {
if !r.Mutable[mod] {
return
}
r.Durability[mod] += amt
r.core.Events.Emit(event.OnAuraDurabilityAdded, r.self, mod, amt)
}
Expand Down Expand Up @@ -329,7 +343,9 @@ func (r *Reactable) reduce(e attributes.Element, dur, factor reactions.Durabilit
// reset decay rate to 0
}

r.Durability[i] -= red
if r.Mutable[i] {
r.Durability[i] -= red
}

if red > reduced {
reduced = red
Expand Down Expand Up @@ -362,7 +378,7 @@ func (r *Reactable) Tick() {
if r.DecayRate[i] == 0 {
continue
}
if r.Durability[i] > ZeroDur {
if r.Durability[i] > ZeroDur && r.Mutable[i] {
r.Durability[i] -= r.DecayRate[i]
r.deplete(i)
}
Expand All @@ -387,6 +403,9 @@ func (r *Reactable) Tick() {
if r.Durability[i] < ZeroDur {
continue
}
if !r.Mutable[i] {
continue
}
rate := r.DecayRate[i]
if r.Durability[BurningFuel] > ZeroDur {
rate = r.DecayRate[BurningFuel]
Expand Down