Skip to content

Commit

Permalink
Add Ororon (#2285)
Browse files Browse the repository at this point in the history
* add olorun.

* update

---------

Co-authored-by: Dmytro Kalitin <[email protected]>
Co-authored-by: imring <[email protected]>
  • Loading branch information
3 people authored Jan 24, 2025
1 parent 753f2cf commit 36754f4
Show file tree
Hide file tree
Showing 27 changed files with 1,439 additions and 2 deletions.
90 changes: 90 additions & 0 deletions internal/characters/ororon/aimed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package ororon

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 aimedFrames [][]int

var aimedHitmarks = []int{15, 86}

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

// Aimed Shot
aimedFrames[0] = frames.InitAbilSlice(23)
aimedFrames[0][action.ActionDash] = aimedHitmarks[0]
aimedFrames[0][action.ActionJump] = aimedHitmarks[0]

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

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

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

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

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

import (
"slices"

"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"
)

const a1NSBurstKey = "ororon-a1-ns-burst"
const a1ElectroHydroKey = "ororon-a1-electro-hydro"
const a1ECTriggerKey = "ororon-a1-ec"
const a1NSTriggerKey = "ororon-a1-ns"

const a1OnSkillKey = "ororon-a1"
const a1GainIcdKey = "ororon-a1-gain-icd"
const a1DamageIcdKey = "ororon-a1-dmg-icd"
const a1Abil = "Hypersense"

const a4Key = "ororon-a4"
const a4IcdKey = "ororon-a4-icd"

func (c *char) a1Init() {
if c.Base.Ascension < 1 {
return
}
c.Core.Events.Subscribe(event.OnNightsoulBurst, func(args ...interface{}) bool {
c.nightsoulState.GeneratePoints(40)
return false
}, a1NSBurstKey)

c.Core.Events.Subscribe(event.OnEnemyHit, func(args ...interface{}) bool {
atk := args[1].(*combat.AttackEvent)

// ignores ororon himself
if atk.Info.ActorIndex == c.Index {
return false
}

switch atk.Info.Element {
case attributes.Hydro:
case attributes.Electro:
default:
return false
}

if !c.StatusIsActive(a1OnSkillKey) {
return false
}
if c.StatusIsActive(a1GainIcdKey) {
return false
}
c.AddStatus(a1GainIcdKey, 0.3*60, true)

c.nightsoulState.GeneratePoints(5)
c.SetTag(a1ElectroHydroKey, c.Tag(a1ElectroHydroKey)+1)
if c.Tag(a1ElectroHydroKey) >= 10 {
c.DeleteStatus(a1OnSkillKey)
}
return false
}, a1ElectroHydroKey)

c.Core.Events.Subscribe(event.OnElectroCharged, func(args ...interface{}) bool {
atk := args[1].(*combat.AttackEvent)
if _, ok := args[0].(*enemy.Enemy); !ok {
return false
}
c.a1NightSoulAttack(atk)
return false
}, a1ECTriggerKey)

c.Core.Events.Subscribe(event.OnEnemyDamage, func(args ...interface{}) bool {
atk := args[1].(*combat.AttackEvent)

if atk.Info.ActorIndex == c.Index {
return false
}
if !slices.Contains(atk.Info.AdditionalTags, attacks.AdditionalTagNightsoul) {
return false
}
c.a1NightSoulAttack(atk)

return false
}, a1NSTriggerKey)
}

func (c *char) a1NightSoulAttack(atk *combat.AttackEvent) {
if c.nightsoulState.Points() < 10 {
return
}
if c.StatusIsActive(a1DamageIcdKey) {
return
}
c.AddStatus(a1DamageIcdKey, 1.8*60, true)
if !c.nightsoulState.HasBlessing() {
c.a1EnterBlessing()
}
c.nightsoulState.ConsumePoints(10)
c.hypersense(1.6, a1Abil, atk.Pattern.Shape.Pos())
}

func (c *char) hypersense(mult float64, abil string, initialTargetPos geometry.Point) {
ai := combat.AttackInfo{
ActorIndex: c.Index,
Abil: abil,
AttackTag: attacks.AttackTagNone,
AdditionalTags: []attacks.AdditionalTag{attacks.AdditionalTagNightsoul},
ICDTag: attacks.ICDTagNone,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypeDefault,
Element: attributes.Electro,
Durability: 25,
Mult: mult,
HitlagFactor: 0.01,
CanBeDefenseHalted: true,
}

enemies := c.Core.Combat.EnemiesWithinArea(combat.NewCircleHitOnTarget(initialTargetPos, nil, 15), nil)
for i := 0; len(enemies) < 4 && i < len(enemies); i++ {
c.Core.QueueAttack(
ai,
combat.NewBoxHitOnTarget(
enemies[i].Pos(),
nil,
0.2,
0.2,
),
12,
12,
)
}
c.c6onHypersense()
}

func (c *char) a1EnterBlessing() {
c.nightsoulState.EnterBlessing(c.nightsoulState.Points())
c.QueueCharTask(c.nightsoulState.ExitBlessing, 6*60)
}

func (c *char) a1OnSkill() {
if c.Base.Ascension < 1 {
return
}
c.AddStatus(a1OnSkillKey, 15*60, true)
c.SetTag(a1OnSkillKey, 0)
}

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

c.Core.Events.Subscribe(event.OnEnemyHit, func(args ...interface{}) bool {
atk := args[1].(*combat.AttackEvent)
active := c.Core.Player.ActiveChar()
if atk.Info.ActorIndex != active.Index {
return false
}

switch atk.Info.AttackTag {
case attacks.AttackTagNormal:
case attacks.AttackTagExtra:
case attacks.AttackTagPlunge:
default:
return false
}

if !c.StatusIsActive(a4Key) {
return false
}
if c.StatusIsActive(a4IcdKey) {
return false
}
c.AddStatus(a4IcdKey, 60, true)

active.AddEnergy(a4Key, 3)
if active.Index != c.Index {
c.AddEnergy(a4Key, 3)
}

c.SetTag(a4Key, c.Tag(a4Key)+1)
if c.Tag(a4Key) >= 3 {
c.DeleteStatus(a4Key)
}
return false
}, a4Key)
}

func (c *char) makeA4cb() func(combat.AttackCB) {
if c.Base.Ascension < 4 {
return nil
}
return func(a combat.AttackCB) {
if a.Target.Type() != targets.TargettableEnemy {
return
}
c.AddStatus(a4Key, 15*60, true)
c.SetTag(a4Key, 0)
}
}
67 changes: 67 additions & 0 deletions internal/characters/ororon/attack.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package ororon

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
var attackHitmarks = []int{12, 10, 20}

const normalHitNum = 3

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

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

attackFrames[1] = frames.InitNormalCancelSlice(attackHitmarks[1], 53) // N2 -> Walk
attackFrames[1][action.ActionAttack] = 27
attackFrames[1][action.ActionAim] = 19

attackFrames[2] = frames.InitNormalCancelSlice(attackHitmarks[2], 70) // N3 -> N1
attackFrames[2][action.ActionWalk] = 61
}

func (c *char) Attack(p map[string]int) (action.Info, error) {
travel, ok := p["travel"]
if !ok {
travel = 10
}

ai := combat.AttackInfo{
ActorIndex: c.Index,
Abil: fmt.Sprintf("Normal %v", c.NormalCounter),
AttackTag: attacks.AttackTagNormal,
ICDTag: attacks.ICDTagNormalAttack,
ICDGroup: attacks.ICDGroupDefault,
StrikeType: attacks.StrikeTypePierce,
Element: attributes.Physical,
Durability: 25,
Mult: attack[c.NormalCounter][c.TalentLvlAttack()],
}
ap := combat.NewBoxHit(c.Core.Combat.Player(), c.Core.Combat.PrimaryTarget(), geometry.Point{Y: -0.5}, 0.1, 1)
c.Core.QueueAttack(
ai,
ap,
attackHitmarks[c.NormalCounter],
attackHitmarks[c.NormalCounter]+travel,
)

defer c.AdvanceNormalIndex()

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

0 comments on commit 36754f4

Please sign in to comment.