Documentation
¶
Index ¶
- Constants
- Variables
- func AutoWaveDelayFor(w int) float64
- func BossMod(wave int) (hp, speed float64)
- func EarlyBonus(prevWave int) int
- func ForgeContains(start, end, p Pos) bool
- func HPScale(wave int) float64
- func HookBudget(k EnemyKind) float64
- func LevelNames() []string
- func SpeedScale(wave int) float64
- func TacticalBrief(m *Map) string
- func WaveBonus(wave int) int
- func WavePreview(wave int) string
- func WavePreviewFor(m *Map, wave int) string
- func WaveTelegraph(wave int) string
- func WaveTelegraphFor(m *Map, wave int) string
- func WaveTheme(wave int) string
- type Autoplay
- type Beam
- type CellKind
- type Difficulty
- type Encounter
- type Enemy
- type EnemyKind
- type EnemySpec
- type Facing
- type Fx
- type GameStatus
- type Map
- type Mine
- type Pos
- type Projectile
- type Recruitment
- type SimResult
- type SpawnEntry
- type State
- func (s *State) Aim(t *Tower, facing Facing)
- func (s *State) Build(v Vec, k TowerKind) *Tower
- func (s *State) CanBuild(v Vec, k TowerKind) bool
- func (s *State) CanBuildSite(v Vec, k TowerKind) bool
- func (s *State) ConfigureTraining(stage int, unlocked uint32)
- func (s *State) CycleTarget(v Vec) (*Tower, TargetMode)
- func (s *State) Enemy(id int) *Enemy
- func (s *State) MineSites(t *Tower) []Mine
- func (s *State) Sell(t *Tower) int
- func (s *State) SpecialistPlaced(k TowerKind) bool
- func (s *State) StartWave() int
- func (s *State) Step(dt float64)
- func (s *State) TargetFor(t *Tower) *Enemy
- func (s *State) Tower(id int) *Tower
- func (s *State) TowerAt(v Vec) *Tower
- func (s *State) TowerAvailable(k TowerKind) bool
- func (s *State) Upgrade(t *Tower) bool
- func (s *State) UpgradeCost(t *Tower) int
- type TargetMode
- type Tower
- type TowerKind
- type TowerSpec
- type Vec
- type WaveDef
Constants ¶
const ( RelicGoldBonus = 60 RelicLivesBonus = 1 )
Campaign relic bonuses are also used when describing the reward in the UI.
const ( StartingGold = 220 StartingLives = 20 MaxWaves = 20 SellRefund = 0.7 ProjectileSpeed = 14.0 EarlyBonusBase = 8 )
const ( ForgeHalfWidth = .48 HookRecovery = 2.5 MineArmDelay = .6 )
const AllTowersMask uint32 = (1 << TowerCount) - 1
const ChainFalloff = 0.6
const ChainRange = 2.6
const MainTowersMask uint32 = (1 << TowerRuneforge) - 1
const MazeW, MazeH = 45, 13
MazeW and MazeH are the fixed playfield size used for procedural mazes.
const SampleStep = 0.25
Variables ¶
var EnemySpecs = [EnemyCount]EnemySpec{ {Name: copytext.Text("enemies.squire.name"), Short: 'o', HP: 16, Speed: 3.2, Bounty: 2}, {Name: copytext.Text("enemies.rogue.name"), Short: 'r', HP: 24, Speed: 2.6, Bounty: 5, Evasion: 0.5}, {Name: copytext.Text("enemies.mercenary.name"), Short: 'g', HP: 65, Speed: 1.6, Bounty: 8}, {Name: copytext.Text("enemies.paladin.name"), Short: 't', HP: 240, Speed: 1.05, Bounty: 20, Plate: 0.75, ArrowPlate: 0.4}, {Name: copytext.Text("enemies.necromancer.name"), Short: 's', HP: 110, Speed: 1.4, Bounty: 12, SplitN: 2, SplitHP: 0.5}, {Name: copytext.Text("enemies.player.name"), Short: 'B', HP: 1600, Speed: 0.8, Bounty: 140, Lives: 6}, {Name: copytext.Text("enemies.wizard.name"), Short: 'w', HP: 9, Speed: 2.3, Bounty: 3}, {Name: copytext.Text("enemies.centurion.name"), Short: 'D', HP: 180, Speed: 1.15, Bounty: 24, Ward: 0.8}, }
var TowerSpecs = [TowerCount]TowerSpec{ { Name: copytext.Text("defenders.gunner.name"), Short: 'G', Cost: [3]int{50, 45, 70}, Dmg: [3]float64{9, 14, 22}, Range: [3]float64{3.0, 3.4, 3.8}, RoT: [3]float64{1.3, 1.5, 1.8}, }, { Name: copytext.Text("defenders.cannonier.name"), Short: 'C', Cost: [3]int{100, 90, 140}, Dmg: [3]float64{24, 40, 64}, Range: [3]float64{2.8, 3.2, 3.6}, RoT: [3]float64{0.55, 0.6, 0.65}, Splash: 1.3, }, { Name: copytext.Text("defenders.frost.name"), Short: 'F', Cost: [3]int{75, 65, 100}, Dmg: [3]float64{5, 8, 12}, Range: [3]float64{2.6, 2.9, 3.2}, RoT: [3]float64{0.9, 1.0, 1.1}, SlowPct: 0.45, SlowDur: 1.6, }, { Name: copytext.Text("defenders.ranger.name"), Short: 'S', Cost: [3]int{150, 130, 200}, Dmg: [3]float64{70, 120, 190}, Range: [3]float64{6.0, 6.6, 7.2}, RoT: [3]float64{0.35, 0.4, 0.45}, }, { Name: copytext.Text("defenders.lightning.name"), Short: 'T', Cost: [3]int{200, 160, 240}, Dmg: [3]float64{30, 48, 75}, Range: [3]float64{3.2, 3.5, 3.9}, RoT: [3]float64{0.8, 0.9, 1.0}, Chain: [3]int{2, 3, 4}, }, { Name: copytext.Text("defenders.trebuchet.name"), Short: 'M', Cost: [3]int{250, 220, 320}, Dmg: [3]float64{70, 105, 160}, Range: [3]float64{5.0, 5.5, 6.0}, RoT: [3]float64{0.45, 0.5, 0.55}, Splash: 2.2, }, { Name: copytext.Text("defenders.gnolls.name"), Short: 'L', Cost: [3]int{80, 70, 110}, Dmg: [3]float64{6, 9, 14}, Range: [3]float64{2.2, 2.5, 2.8}, RoT: [3]float64{3.0, 3.5, 4.0}, }, { Name: copytext.Text("defenders.runeforge.name"), Short: 'R', Cost: [3]int{180, 140, 220}, Dmg: [3]float64{22, 34, 50}, Range: [3]float64{7, 8, 9}, RoT: [3]float64{1, 1, 1}, }, { Name: copytext.Text("defenders.hookmaster.name"), Short: 'H', Cost: [3]int{140, 100, 160}, Dmg: [3]float64{8, 12, 18}, Range: [3]float64{3.0, 3.4, 3.8}, RoT: [3]float64{.2, .23, .26}, Pull: [3]float64{2, 2.6, 3.2}, }, { Name: copytext.Text("defenders.sappers.name"), Short: 'P', Cost: [3]int{125, 100, 160}, Dmg: [3]float64{90, 145, 220}, Range: [3]float64{2.8, 3.2, 3.6}, RoT: [3]float64{.4, .45, .5}, Splash: 1.1, MineCap: [3]int{3, 4, 5}, }, { Name: copytext.Text("defenders.witch.name"), Short: 'W', Cost: [3]int{160, 120, 180}, Dmg: [3]float64{0, 0, 0}, Range: [3]float64{3.5, 3.9, 4.3}, RoT: [3]float64{.5, .5, .5}, Mark: [3]float64{.25, .35, .45}, MarkDur: [3]float64{3, 3.5, 4}, }, }
Functions ¶
func AutoWaveDelayFor ¶
AutoWaveDelayFor returns the break after completing wave w (the delay before wave w+1 auto-starts). Tapers 11s -> ~4.5s so tempo rises as the game escalates, in place of a fixed 12s.
func BossMod ¶
BossMod tweaks boss stats per wave: returns hp and speed multipliers. Bosses appear on wave 15 (debut), 18 (stronger), and 20 (finale, two of them). The finale bosses are the run's climax.
func EarlyBonus ¶
EarlyBonus is the gold paid for starting the next wave before the inter-wave timer ends. Modest on purpose: early-starting is a tempo choice, not a dominant income source.
func ForgeContains ¶
func HookBudget ¶
func LevelNames ¶
func LevelNames() []string
func SpeedScale ¶
func TacticalBrief ¶
func WaveBonus ¶
WaveBonus is the gold paid for clearing a wave. Front-loaded enough that even a tiny wave funds progress (BTD3's $99+round shape, scaled down).
func WavePreview ¶
func WavePreviewFor ¶
func WaveTelegraph ¶
WaveTelegraph returns a short pre-wave message for the upcoming wave, shown during the break before it (BTD3's pre-round comment habit). It telegraphs new enemy types and the boss waves. Empty for unremarkable waves.
func WaveTelegraphFor ¶
Types ¶
type Autoplay ¶
type Autoplay struct {
// contains filtered or unexported fields
}
func NewAutoplay ¶
type Difficulty ¶
type Difficulty int
const ( Normal Difficulty = iota Easy Hard )
func (Difficulty) Gold ¶
func (d Difficulty) Gold() int
func (Difficulty) HPMul ¶
func (d Difficulty) HPMul() float64
func (Difficulty) Lives ¶
func (d Difficulty) Lives() int
type Encounter ¶
type Encounter string
Encounter is attached only by authored level loaders. A zero encounter keeps the original wave schedule, including every procedural maze.
type Enemy ¶
type Enemy struct {
ID int
Kind EnemyKind
HP float64
MaxHP float64
Prog float64
PrevProg float64
Speed float64
Pos Pos
SlowUntil float64
SlowFactor float64
Bounty int
Lives int
Dead bool
Leaked bool
HitTTL float64 // >0 while the enemy flashes from a recent hit
PulledDistance float64
HookUntil float64 // shared recovery prevents several Ogres trapping a fighter
HexUntil float64
HexBonus float64
}
func (*Enemy) DamageMultiplier ¶
DamageMultiplier keeps counters deterministic. Evasion represents glancing shots, not random misses; frost disables it only for the active slow window. Splash is a property of the actual impact, not merely the tower's name.
type EnemySpec ¶
type EnemySpec struct {
Name string
Short rune
HP float64
Speed float64
Bounty int
Lives int
SplitN int
SplitHP float64
Plate float64 // reduction against small shot
ArrowPlate float64 // reduction against Ranger arrows
Ward float64 // reduction against frost and lightning
Evasion float64 // reduction against ordinary shots while not slowed
}
type GameStatus ¶
type GameStatus int
const ( StatusRunning GameStatus = iota StatusVictory StatusDefeat )
type Map ¶
type Map struct {
W, H int
Encounter Encounter
Cell []CellKind
Path []Vec
Dist []float64
TotalLen float64
Spawn Vec
Exit Vec
PathSet map[Vec]bool
Samples []Pos
// HPMul is a per-map enemy health multiplier used to tune difficulty.
HPMul float64
// GoldMul scales starting gold and income, allowing spacious floors to
// support broader defenses without changing tower prices. Zero means 1.
GoldMul float64
}
func GenerateMap ¶
GenerateMap builds a perfect maze and uses its unique route from the left edge to the right edge as the enemy path. All other open cells are buildable.
func LoadBoss ¶
LoadBoss loads the heart chamber: the endgame defense behind the unsealed heart. It is deliberately not in LevelNames, so the level select never lists it — the lair unseals it.
func MazeFromSeed ¶
MazeFromSeed builds the standard-size procedural maze for seed. It is the single place that knows mazes are MazeW×MazeH.
func (*Map) SamplePoints ¶
type Pos ¶
type Pos struct {
X, Y float64
}
type Projectile ¶
type Recruitment ¶
func RecruitmentOrder ¶
func RecruitmentOrder(stage int) []Recruitment
A recruit arrives during the preceding break, before its first useful fight.
type SimResult ¶
type SimResult struct {
Won bool
Wave int
Lives int
Gold int
Kills int
Leaks int
Towers int
Time float64
Level string
}
func RunAutoplay ¶
func RunAutoplayDiff ¶
func RunAutoplayDiff(m *Map, level string, diff Difficulty) SimResult
type SpawnEntry ¶
func BuildWave ¶
func BuildWave(wave int) []SpawnEntry
func BuildWaveFor ¶
func BuildWaveFor(m *Map, wave int) []SpawnEntry
type State ¶
type State struct {
TrainingStage int
UnlockedTowers uint32
Lesson TowerKind
LessonPending bool
Map *Map
Time float64
Diff Difficulty
Gold int
Lives int
Wave int
Status GameStatus
// SeenEnemies also remembers enemies killed on their spawning tick.
SeenEnemies [EnemyCount]bool
Enemies []*Enemy
Towers []*Tower
Projectiles []*Projectile
Beams []*Beam
Mines []*Mine
Fx []*Fx
LeakFlash float64
SpawnQueue []SpawnEntry
SpawnIdx int
NextWaveAt float64
WaveActive bool
WaveStart float64
Score int
TotalKills int
TotalLeaks int
Combo int
MaxCombo int
NextID int
// UI hints (headless-safe)
Placing TowerKind
PlacingOn bool
Cursor Vec
Selected int
}
func NewState ¶
The simulation is fully deterministic: a run is determined entirely by (map, difficulty). There is no in-engine randomness, so no seed is kept here — the only seed in the game is the maze generator's, which happens before a State exists.
func NewStateDiff ¶
func NewStateDiff(m *Map, diff Difficulty) *State
func (*State) CanBuildSite ¶ added in v1.4.1
CanBuildSite checks placement rules independently of the player's gold.
func (*State) ConfigureTraining ¶
func (*State) CycleTarget ¶
func (s *State) CycleTarget(v Vec) (*Tower, TargetMode)
CycleTarget cycles the targeting mode of the tower at v, if any. Returns the tower (possibly nil) and its new mode.
func (*State) MineSites ¶
MineSites are actual route positions, rather than nearby floor decoration. Exposing the candidates also lets selection previews and builders use the same legal placement geometry as the simulation.
func (*State) SpecialistPlaced ¶
SpecialistPlaced reports the one-per-kind limit for the current level. Selling a specialist makes its slot available again.
func (*State) StartWave ¶
StartWave begins the next wave. If it was started before the auto timer expired, the player gets an early-start bonus. Returns bonus gold.
The siege itself — the very first wave — will not open until the player has committed to the defense: at least one tower must be placed before it may start, whether by the auto timer or the early-start key.
func (*State) TowerAvailable ¶
func (*State) UpgradeCost ¶
type TargetMode ¶
type TargetMode int
TargetMode controls which in-range enemy a tower attacks.
const ( TargetFirst TargetMode = iota // furthest along the path TargetStrongest // highest current HP TargetClosest // nearest to the tower TargetModeCount )
func (TargetMode) Name ¶
func (m TargetMode) Name() string
func (TargetMode) Next ¶
func (m TargetMode) Next() TargetMode
func (TargetMode) Short ¶
func (m TargetMode) Short() string