Add God mode
This commit is contained in:
parent
e6213d7863
commit
846ef25bdb
5 changed files with 123 additions and 52 deletions
17
flags.go
Normal file
17
flags.go
Normal file
|
@ -0,0 +1,17 @@
|
|||
//go:build !js || !wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
|
||||
"code.rocket9labs.com/tslocum/arcticwarfare/game"
|
||||
)
|
||||
|
||||
func parseFlags() *game.Game {
|
||||
var godMode bool
|
||||
flag.BoolVar(&godMode, "god", false, "God mode")
|
||||
flag.Parse()
|
||||
|
||||
return game.NewGame(godMode)
|
||||
}
|
11
flags_web.go
Normal file
11
flags_web.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
//go:build js && wasm
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"code.rocket9labs.com/tslocum/arcticwarfare/game"
|
||||
)
|
||||
|
||||
func parseFlags() *game.Game {
|
||||
return game.NewGame(false)
|
||||
}
|
119
game/game.go
119
game/game.go
|
@ -50,10 +50,12 @@ var tankNames = []string{
|
|||
}
|
||||
|
||||
const (
|
||||
SnowSize = 1024
|
||||
tankSize = 100
|
||||
tankOffset = 25
|
||||
roundOffset = 30
|
||||
SnowSize = 1024
|
||||
tankSize = 100
|
||||
tankOffset = 25
|
||||
roundOffset = 30
|
||||
cooldownWidth = 30
|
||||
cooldownHeight = 5
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -62,6 +64,8 @@ var (
|
|||
|
||||
TreadMarkImage = ebiten.NewImage(3, 3)
|
||||
RoundImage = ebiten.NewImage(4, 4)
|
||||
|
||||
CooldownImage = ebiten.NewImage(cooldownWidth, cooldownHeight)
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -78,6 +82,7 @@ func init() {
|
|||
|
||||
TreadMarkImage.Fill(color.RGBA{0, 0, 0, 7})
|
||||
RoundImage.Fill(color.RGBA{0, 0, 0, 255})
|
||||
CooldownImage.Fill(color.RGBA{0, 0, 255, 255})
|
||||
}
|
||||
|
||||
type Game struct {
|
||||
|
@ -86,7 +91,7 @@ type Game struct {
|
|||
computerPlayers []int
|
||||
}
|
||||
|
||||
func NewGame() *Game {
|
||||
func NewGame(godMode bool) *Game {
|
||||
ebiten.SetVsyncEnabled(true)
|
||||
ebiten.SetScreenClearedEveryFrame(true)
|
||||
ebiten.SetTPS(100)
|
||||
|
@ -96,7 +101,7 @@ func NewGame() *Game {
|
|||
ebiten.SetWindowTitle("Arctic Warfare")
|
||||
|
||||
g := &Game{
|
||||
sim: NewSimulation(),
|
||||
sim: NewSimulation(godMode),
|
||||
}
|
||||
const numComputerPlayers = 7
|
||||
for i := 0; i < numComputerPlayers+1; i++ {
|
||||
|
@ -113,7 +118,7 @@ const rad = math.Pi * 2
|
|||
func (g *Game) setComputerInputs() {
|
||||
const targetDistance = 150
|
||||
const fireDistance = 750
|
||||
states, _ := g.sim.State()
|
||||
states, _, _ := g.sim.State()
|
||||
nearestTarget := func(player int, x float64, y float64) (float64, float64) {
|
||||
var nearestX, nearestY = -1.0, -1.0
|
||||
var nearestDistance float64
|
||||
|
@ -226,7 +231,7 @@ func (g *Game) Update() error {
|
|||
|
||||
g.sim.Tick()
|
||||
|
||||
playerStates, _ := g.sim.State()
|
||||
playerStates, _, _ := g.sim.State()
|
||||
|
||||
const size = 11
|
||||
for _, state := range playerStates {
|
||||
|
@ -262,7 +267,7 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
screen.DrawImage(TreadMarkImage, op)
|
||||
}
|
||||
|
||||
playerStates, roundStates := g.sim.State()
|
||||
playerStates, roundStates, lastFire := g.sim.State()
|
||||
|
||||
{
|
||||
const size = 4
|
||||
|
@ -277,41 +282,75 @@ func (g *Game) Draw(screen *ebiten.Image) {
|
|||
}
|
||||
}
|
||||
|
||||
for i, state := range playerStates {
|
||||
if i == 0 {
|
||||
viewX, viewY = state.X-screenWidth/2+50, state.Y-screenHeight/2+50
|
||||
if viewX < 0 {
|
||||
viewX = 0
|
||||
} else if viewX > worldWidth*worldTileSize-screenWidth {
|
||||
viewX = worldWidth*worldTileSize - screenWidth
|
||||
for j := 0; j < 2; j++ {
|
||||
for i, state := range playerStates {
|
||||
if (j == 0 && !state.Hit) || (j == 1 && state.Hit) {
|
||||
continue
|
||||
}
|
||||
if viewY < 0 {
|
||||
viewY = 0
|
||||
} else if viewY > worldHeight*worldTileSize-screenHeight {
|
||||
viewY = worldHeight*worldTileSize - screenHeight
|
||||
}
|
||||
}
|
||||
|
||||
if i == 0 {
|
||||
viewX, viewY = state.X-screenWidth/2+50, state.Y-screenHeight/2+50
|
||||
if viewX < 0 {
|
||||
viewX = 0
|
||||
} else if viewX > worldWidth*worldTileSize-screenWidth {
|
||||
viewX = worldWidth*worldTileSize - screenWidth
|
||||
}
|
||||
if viewY < 0 {
|
||||
viewY = 0
|
||||
} else if viewY > worldHeight*worldTileSize-screenHeight {
|
||||
viewY = worldHeight*worldTileSize - screenHeight
|
||||
}
|
||||
}
|
||||
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(-51, -51)
|
||||
op.GeoM.Rotate(math.Pi/2 + state.R)
|
||||
op.GeoM.Translate(51, 51)
|
||||
op.GeoM.Translate(float64(state.X), float64(state.Y))
|
||||
if state.Hit {
|
||||
op.ColorScale.ScaleWithColor(color.RGBA{128, 128, 128, 255})
|
||||
}
|
||||
offset(op)
|
||||
screen.DrawImage(tankHullImages[state.S], op)
|
||||
op = &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(-51, -51)
|
||||
op.GeoM.Rotate(math.Pi/2 + state.R + state.T)
|
||||
op.GeoM.Translate(51, 51)
|
||||
op.GeoM.Translate(float64(state.X), float64(state.Y))
|
||||
offset(op)
|
||||
if state.Hit {
|
||||
op.ColorScale.ScaleWithColor(color.RGBA{128, 128, 128, 255})
|
||||
}
|
||||
screen.DrawImage(tankTurretImages[state.S], op)
|
||||
}
|
||||
}
|
||||
|
||||
for i, state := range playerStates {
|
||||
if state.Hit {
|
||||
continue
|
||||
}
|
||||
last := lastFire[i]
|
||||
delta := g.sim.offset + g.sim.frame - last
|
||||
if last == 0 || delta >= fireCooldown {
|
||||
continue
|
||||
}
|
||||
CooldownImage.Fill(color.RGBA{0, 0, 0, 0})
|
||||
pct := float64(delta) / float64(fireCooldown)
|
||||
w := float64(cooldownWidth) * pct
|
||||
if w < 2 {
|
||||
w = 2
|
||||
}
|
||||
fillColor := color.RGBA{100, 0, 0, 255}
|
||||
if pct >= 0.66 {
|
||||
fillColor = color.RGBA{200, 128, 0, 255}
|
||||
} else if pct >= 0.33 {
|
||||
fillColor = color.RGBA{150, 60, 0, 255}
|
||||
}
|
||||
CooldownImage.SubImage(image.Rect(0, 0, int(w), cooldownHeight)).(*ebiten.Image).Fill(fillColor)
|
||||
op := &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(-51, -51)
|
||||
op.GeoM.Rotate(math.Pi/2 + state.R)
|
||||
op.GeoM.Translate(51, 51)
|
||||
op.GeoM.Translate(float64(state.X), float64(state.Y))
|
||||
if state.Hit {
|
||||
op.ColorScale.ScaleWithColor(color.RGBA{128, 128, 128, 255})
|
||||
}
|
||||
op.GeoM.Translate(state.X+tankSize/2-w/2, state.Y+10)
|
||||
offset(op)
|
||||
screen.DrawImage(tankHullImages[state.S], op)
|
||||
op = &ebiten.DrawImageOptions{}
|
||||
op.GeoM.Translate(-51, -51)
|
||||
op.GeoM.Rotate(math.Pi/2 + state.R + state.T)
|
||||
op.GeoM.Translate(51, 51)
|
||||
op.GeoM.Translate(float64(state.X), float64(state.Y))
|
||||
offset(op)
|
||||
if state.Hit {
|
||||
op.ColorScale.ScaleWithColor(color.RGBA{128, 128, 128, 255})
|
||||
}
|
||||
screen.DrawImage(tankTurretImages[state.S], op)
|
||||
screen.DrawImage(CooldownImage, op)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ func (b bitfield) Has(flag bitfield) bool {
|
|||
return b&flag != 0
|
||||
}
|
||||
|
||||
const fireCooldown = 1000
|
||||
|
||||
type playerState struct {
|
||||
X float64
|
||||
Y float64
|
||||
|
@ -56,14 +58,16 @@ type Simulation struct {
|
|||
rounds [][]*roundState
|
||||
lastFire []map[int]int
|
||||
offset int
|
||||
godMode bool
|
||||
}
|
||||
|
||||
func NewSimulation() *Simulation {
|
||||
func NewSimulation(godMode bool) *Simulation {
|
||||
s := &Simulation{}
|
||||
s.inputs = append(s.inputs, make(map[int]bitfield))
|
||||
s.states = append(s.states, make(map[int]*playerState))
|
||||
s.lastFire = append(s.lastFire, make(map[int]int))
|
||||
s.rounds = append(s.rounds, nil)
|
||||
s.godMode = godMode
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -121,6 +125,9 @@ ROUNDS:
|
|||
continue
|
||||
}
|
||||
if p.In(image.Rect(int(playerState.X+tankOffset), int(playerState.Y+tankOffset), int(playerState.X+tankSize-tankOffset), int(playerState.Y+tankSize-tankOffset))) {
|
||||
if s.godMode && player == 0 {
|
||||
continue
|
||||
}
|
||||
playerState.Hit = true
|
||||
continue ROUNDS
|
||||
}
|
||||
|
@ -146,8 +153,6 @@ ROUNDS:
|
|||
var dr float64
|
||||
var dt float64
|
||||
if !state.Hit {
|
||||
const fireCooldown = 1000
|
||||
|
||||
if input.Has(InputFire) {
|
||||
lastFire := s.lastFire[s.frame][i]
|
||||
if lastFire == 0 || s.offset+s.frame-lastFire >= fireCooldown {
|
||||
|
@ -202,16 +207,16 @@ ROUNDS:
|
|||
dx, dy = -float64(speed)*math.Cos(state.R), -float64(speed)*math.Sin(state.R)
|
||||
case left == 1 && right == 0:
|
||||
dx, dy = float64(speed*turn)*math.Cos(state.R), float64(speed*turn)*math.Sin(state.R)
|
||||
dr = -0.01
|
||||
dr = 0.01
|
||||
case left == -1 && right == 0:
|
||||
dx, dy = -float64(speed*turn)*math.Cos(state.R), -float64(speed*turn)*math.Sin(state.R)
|
||||
dr = 0.01
|
||||
dr = -0.01
|
||||
case left == 0 && right == 1:
|
||||
dx, dy = float64(speed*turn)*math.Cos(state.R), float64(speed*turn)*math.Sin(state.R)
|
||||
dr = 0.01
|
||||
dr = -0.01
|
||||
case left == 0 && right == -1:
|
||||
dx, dy = -float64(speed*turn)*math.Cos(state.R), -float64(speed*turn)*math.Sin(state.R)
|
||||
dr = -0.01
|
||||
dr = 0.01
|
||||
}
|
||||
|
||||
const rotateSpeed = 0.02
|
||||
|
@ -279,9 +284,9 @@ func (s *Simulation) Clear(frame int) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (s *Simulation) State() (map[int]*playerState, []*roundState) {
|
||||
func (s *Simulation) State() (map[int]*playerState, []*roundState, map[int]int) {
|
||||
if s.frame == 0 {
|
||||
return s.states[s.frame], s.rounds[s.frame]
|
||||
return s.states[s.frame], s.rounds[s.frame], s.lastFire[s.frame]
|
||||
}
|
||||
return s.states[s.frame-1], s.rounds[s.frame-1]
|
||||
return s.states[s.frame-1], s.rounds[s.frame-1], s.lastFire[s.frame-1]
|
||||
}
|
||||
|
|
3
main.go
3
main.go
|
@ -3,12 +3,11 @@ package main
|
|||
import (
|
||||
"log"
|
||||
|
||||
"code.rocket9labs.com/tslocum/arcticwarfare/game"
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := ebiten.RunGame(game.NewGame())
|
||||
err := ebiten.RunGame(parseFlags())
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue