diff --git a/server/bullets.go b/server/bullets.go new file mode 100644 index 0000000..421c4f2 --- /dev/null +++ b/server/bullets.go @@ -0,0 +1,33 @@ +package main + +const ( + BULLET_LINEAR = iota +) + +const ( + STAGE_WIDTH float64 = 90.0 + STAGE_HEIGHT float64 = 160.0 + BULLET_KILL_BUFFER_WIDTH float64 = 16.0 +) + +type Bullet struct { + Class int + SpawnTime int64 + SpawnX float64 + SpawnY float64 + Parameters []float64 +} + +func (b Bullet) GetCurrentPos(tick int64) (float64, float64) { + switch b.Class { + case BULLET_LINEAR: + return b.SpawnX + (b.Parameters[0] * float64(tick-b.SpawnTime)), b.SpawnY + (b.Parameters[1] * float64(tick-b.SpawnTime)) + default: + return b.SpawnX, b.SpawnY + } +} + +func (b Bullet) BeyondKillBoundary(tick int64) bool { + x, y := b.GetCurrentPos(tick) + return (x < 0.0-BULLET_KILL_BUFFER_WIDTH) || (x > STAGE_WIDTH+BULLET_KILL_BUFFER_WIDTH) || (y < 0.0-BULLET_KILL_BUFFER_WIDTH) || (y > STAGE_HEIGHT+BULLET_KILL_BUFFER_WIDTH) +} diff --git a/server/collision.go b/server/collision.go new file mode 100644 index 0000000..06ab7d0 --- /dev/null +++ b/server/collision.go @@ -0,0 +1 @@ +package main diff --git a/server/main.go b/server/main.go index 7de5470..247327c 100644 --- a/server/main.go +++ b/server/main.go @@ -3,26 +3,51 @@ package main import ( "context" "database/sql" + "encoding/json" + "math/rand" + "slices" "github.com/heroiclabs/nakama-common/runtime" ) +const ( + MATCH_LOADING = iota + MATCH_START + SPAWN_BULLET + MATCH_END +) + // Interface for registering match handlers type BattleRoyaleMatch struct{} +type PlayerStageState struct { + xPos float64 + yPos float64 + bullets []Bullet +} + +type PresenceState struct { // present time! hahahahahahahah! + presence runtime.Presence + stageState PlayerStageState +} + // In-memory game state type BattleRoyaleMatchState struct { - presences map[string]runtime.Presence - emptyTicks int + tickRate int + currentMatchPhase int + presences map[string]*PresenceState + emptyTicks int } // Run on match start, initializes game state and sets tick rate func (m *BattleRoyaleMatch) MatchInit(ctx context.Context, logger runtime.Logger, db *sql.DB, nk runtime.NakamaModule, params map[string]interface{}) (interface{}, int, string) { + tickRate := 20 // MatchLoop invocations per second state := &BattleRoyaleMatchState{ - presences: map[string]runtime.Presence{}, - emptyTicks: 0, + tickRate: tickRate, + presences: map[string]*PresenceState{}, + emptyTicks: 0, + currentMatchPhase: MATCH_LOADING, } - tickRate := 1 // MatchLoop invocations per second label := "" return state, tickRate, label } @@ -49,7 +74,14 @@ func (m *BattleRoyaleMatch) MatchJoin(ctx context.Context, logger runtime.Logger } for i := 0; i < len(presences); i++ { - lobbyState.presences[presences[i].GetSessionId()] = presences[i] + lobbyState.presences[presences[i].GetSessionId()] = &PresenceState{ + presence: presences[i], + stageState: PlayerStageState{ + xPos: STAGE_WIDTH * 0.5, + yPos: STAGE_HEIGHT - STAGE_HEIGHT*0.1, + bullets: []Bullet{}, + }, + } } return lobbyState @@ -111,6 +143,36 @@ func (m *BattleRoyaleMatch) MatchLoop(ctx context.Context, logger runtime.Logger return nil } + // Test bullet spawning + if tick%20 == 0 { + for _, v := range lobbyState.presences { + vel := rand.Float64()*(STAGE_WIDTH/float64(lobbyState.tickRate)) + 1.0 + bullet := Bullet{ + BULLET_LINEAR, + tick, + STAGE_WIDTH * rand.Float64(), + STAGE_HEIGHT * rand.Float64(), + []float64{vel, vel}, + } + + data, err := json.Marshal(bullet) + if err != nil { + logger.Error("Error marshalling bullet data", err) + } else { + v.stageState.bullets = append(v.stageState.bullets, bullet) + reliable := true + dispatcher.BroadcastMessage(SPAWN_BULLET, data, nil, nil, reliable) + } + } + } + + // Bullet cleanup + for _, v := range lobbyState.presences { + v.stageState.bullets = slices.DeleteFunc(v.stageState.bullets, func(b Bullet) bool { + return b.BeyondKillBoundary(tick) + }) + } + return lobbyState }