Archived
1
0
Fork 0

Add grace-controlled grazing

This commit is contained in:
Sebastian Benjamin 2025-07-22 19:13:40 -07:00
parent 651adcc71a
commit 122590d10b
3 changed files with 37 additions and 10 deletions

View file

@ -18,6 +18,6 @@ const (
BULLET_OFFSCREEN_BUFFER_WIDTH float64 = 16.0 BULLET_OFFSCREEN_BUFFER_WIDTH float64 = 16.0
PLAYER_HIT_COL_RADIUS_MULTIPLIER float64 = 0.01 PLAYER_HIT_COL_RADIUS_MULTIPLIER float64 = 0.01
PLAYER_GRAZE_COL_RADIUS_MULTIPLIER float64 = 0.04 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 GRAZE_ADDITION_MULTIPLIER int = 1000
) )

View file

@ -4,6 +4,7 @@ import (
"context" "context"
"database/sql" "database/sql"
"encoding/json" "encoding/json"
"github.com/heroiclabs/nakama-common/runtime" "github.com/heroiclabs/nakama-common/runtime"
) )

View file

@ -15,7 +15,7 @@ type PlayerStageState struct {
updatePlayerPos bool updatePlayerPos bool
health int health int
graze int graze int
score int grazePerTick map[int64]int
deathTick int64 deathTick int64
dead bool dead bool
cancelDeath bool cancelDeath bool
@ -25,6 +25,15 @@ type PlayerStageState struct {
} }
func (s *PlayerStageState) ProcessPlayerInputs(tick int64) { 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 // Clean up inputs outside the grace window
s.playerInputs = slices.DeleteFunc(s.playerInputs, func(input *ClientUpdate) bool { s.playerInputs = slices.DeleteFunc(s.playerInputs, func(input *ClientUpdate) bool {
return tick-input.Tick > GRACE_WINDOW_TICKS return tick-input.Tick > GRACE_WINDOW_TICKS
@ -37,7 +46,7 @@ func (s *PlayerStageState) ProcessPlayerInputs(tick int64) {
// Replay each tick within the grace window // Replay each tick within the grace window
s.survivedGraceWindow = true 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 // Find the input for the current tick
var currentInput *ClientUpdate var currentInput *ClientUpdate
for _, input := range s.playerInputs { 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 the player dies in the grace window, don't cancel their death
if s.CheckPlayerDeadOnTick(t) { if s.CheckPlayerDeadOnTick(t) {
s.survivedGraceWindow = false s.survivedGraceWindow = false
for grazeT := range s.grazePerTick {
if grazeT > t {
delete(s.grazePerTick, grazeT)
}
}
break 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), grazeCol: ffi.NewCircle(STAGE_WIDTH*0.5, STAGE_HEIGHT-STAGE_HEIGHT*0.1, STAGE_WIDTH*PLAYER_GRAZE_COL_RADIUS_MULTIPLIER),
bullets: []*ffi.Bullet{}, bullets: []*ffi.Bullet{},
updatePlayerPos: true, updatePlayerPos: true,
grazePerTick: make(map[int64]int),
health: 3, health: 3,
deathTick: PLAYER_ALIVE, deathTick: PLAYER_ALIVE,
} }
@ -162,12 +180,14 @@ func (s *PlayerStageState) CheckPlayerDeadOnTick(tick int64) bool {
return false return false
} }
func (s *PlayerStageState) UpdateGrazeMultiplier(tick int64) { func (s *PlayerStageState) CalculateGrazeDelta(tick int64) int {
if slices.ContainsFunc(s.bullets, func(b *ffi.Bullet) bool { count := 0
return b.CollidesWith(s.grazeCol, tick) for _, b := range s.bullets {
}) { if b.CollidesWith(s.grazeCol, tick) {
s.graze += GRAZE_ADDITION_MULTIPLIER count++
}
} }
return count * GRAZE_ADDITION_MULTIPLIER
} }
func (s *PlayerStageState) AddBullet(b *ffi.Bullet) { 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 { func (s *PlayerStageState) MakeServerTick(tick int64) *ServerTickUpdate {
hitPosX, hitPosY := s.hitCol.GetPos() 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{ var tickData = ServerTickUpdate{
Tick: tick, Tick: tick,
PlayerHitPos: map[string]any{ PlayerHitPos: map[string]any{
@ -238,7 +264,7 @@ func (s *PlayerStageState) MakeServerTick(tick int64) *ServerTickUpdate {
}, },
StageStateDiff: s.GetBoardStateDiff(tick), StageStateDiff: s.GetBoardStateDiff(tick),
ForcePlayerPos: s.updatePlayerPos, ForcePlayerPos: s.updatePlayerPos,
Graze: s.graze, Graze: s.graze + prospectiveGraze,
Dead: s.dead, Dead: s.dead,
CancelDeath: s.cancelDeath, CancelDeath: s.cancelDeath,
DeathTick: s.deathTick, DeathTick: s.deathTick,