diff --git a/server/game-modes/battle-royale/consts.go b/server/game-modes/battle-royale/consts.go index b476401..646e24f 100644 --- a/server/game-modes/battle-royale/consts.go +++ b/server/game-modes/battle-royale/consts.go @@ -18,6 +18,6 @@ const ( BULLET_OFFSCREEN_BUFFER_WIDTH float64 = 16.0 PLAYER_HIT_COL_RADIUS_MULTIPLIER float64 = 0.01 PLAYER_GRAZE_COL_RADIUS_MULTIPLIER float64 = 0.04 - PLAYER_DEATH_TIMER_MAX int64 = 180 + PLAYER_DEATH_TIMER_MAX int64 = 240 GRAZE_ADDITION_MULTIPLIER int = 1000 ) diff --git a/server/game-modes/battle-royale/game-loop.go b/server/game-modes/battle-royale/game-loop.go index ebbde42..2ebf6f9 100644 --- a/server/game-modes/battle-royale/game-loop.go +++ b/server/game-modes/battle-royale/game-loop.go @@ -4,6 +4,7 @@ import ( "context" "database/sql" "encoding/json" + "github.com/heroiclabs/nakama-common/runtime" ) diff --git a/server/game-modes/battle-royale/player-stage.go b/server/game-modes/battle-royale/player-stage.go index a2aacfb..9d13963 100644 --- a/server/game-modes/battle-royale/player-stage.go +++ b/server/game-modes/battle-royale/player-stage.go @@ -15,7 +15,7 @@ type PlayerStageState struct { updatePlayerPos bool health int graze int - score int + grazePerTick map[int64]int deathTick int64 dead bool cancelDeath bool @@ -25,6 +25,15 @@ type PlayerStageState struct { } func (s *PlayerStageState) ProcessPlayerInputs(tick int64) { + // Lock-in the earned graze + cutoffTick := tick - GRACE_WINDOW_TICKS + for t, v := range s.grazePerTick { + if t < cutoffTick { + s.graze += v + delete(s.grazePerTick, t) + } + } + // Clean up inputs outside the grace window s.playerInputs = slices.DeleteFunc(s.playerInputs, func(input *ClientUpdate) bool { return tick-input.Tick > GRACE_WINDOW_TICKS @@ -37,7 +46,7 @@ func (s *PlayerStageState) ProcessPlayerInputs(tick int64) { // Replay each tick within the grace window s.survivedGraceWindow = true - for t := tick - GRACE_WINDOW_TICKS; t < tick; t++ { + for t := tick - GRACE_WINDOW_TICKS; t <= tick; t++ { // Find the input for the current tick var currentInput *ClientUpdate for _, input := range s.playerInputs { @@ -58,7 +67,15 @@ func (s *PlayerStageState) ProcessPlayerInputs(tick int64) { // If the player dies in the grace window, don't cancel their death if s.CheckPlayerDeadOnTick(t) { s.survivedGraceWindow = false + + for grazeT := range s.grazePerTick { + if grazeT > t { + delete(s.grazePerTick, grazeT) + } + } break + } else { + s.grazePerTick[t] = s.CalculateGrazeDelta(t) } } } @@ -69,6 +86,7 @@ func NewPlayerStage() *PlayerStageState { grazeCol: ffi.NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, STAGE_WIDTH*PLAYER_GRAZE_COL_RADIUS_MULTIPLIER), bullets: []*ffi.Bullet{}, updatePlayerPos: true, + grazePerTick: make(map[int64]int), health: 3, deathTick: PLAYER_ALIVE, } @@ -162,12 +180,14 @@ func (s *PlayerStageState) CheckPlayerDeadOnTick(tick int64) bool { return false } -func (s *PlayerStageState) UpdateGrazeMultiplier(tick int64) { - if slices.ContainsFunc(s.bullets, func(b *ffi.Bullet) bool { - return b.CollidesWith(s.grazeCol, tick) - }) { - s.graze += GRAZE_ADDITION_MULTIPLIER +func (s *PlayerStageState) CalculateGrazeDelta(tick int64) int { + count := 0 + for _, b := range s.bullets { + if b.CollidesWith(s.grazeCol, tick) { + count++ + } } + return count * GRAZE_ADDITION_MULTIPLIER } func (s *PlayerStageState) AddBullet(b *ffi.Bullet) { @@ -223,7 +243,13 @@ func (s *PlayerStageState) GetBoardStateDiff(tick int64) []map[string]any { func (s *PlayerStageState) MakeServerTick(tick int64) *ServerTickUpdate { hitPosX, hitPosY := s.hitCol.GetPos() - grazePosX, grazePosY := s.hitCol.GetPos() + grazePosX, grazePosY := s.grazeCol.GetPos() + + prospectiveGraze := 0 + for _, v := range s.grazePerTick { + prospectiveGraze += v + } + var tickData = ServerTickUpdate{ Tick: tick, PlayerHitPos: map[string]any{ @@ -238,7 +264,7 @@ func (s *PlayerStageState) MakeServerTick(tick int64) *ServerTickUpdate { }, StageStateDiff: s.GetBoardStateDiff(tick), ForcePlayerPos: s.updatePlayerPos, - Graze: s.graze, + Graze: s.graze + prospectiveGraze, Dead: s.dead, CancelDeath: s.cancelDeath, DeathTick: s.deathTick,