Players can now spawn from factories#185
Players can now spawn from factories#185c86ec23b-fef1-4979-b2fa-b9adc351b8cc merged 21 commits intomainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
This PR implements a new feature allowing players to spawn from allied factory tanks with a 5% chance, adding strategic gameplay depth to team-based modes. The PR also includes several bug fixes and code improvements.
Changes:
- Added factory spawning mechanic with configurable chance (5% by default)
- Fixed critical logic bug in spawn location collision detection
- Refactored team assignment logic across multiple game modes for consistency
- Various code cleanups including quote style standardization and improved helper functions
Reviewed changes
Copilot reviewed 23 out of 23 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| src/util.ts | Added getRandomPosition utility function and updated comment formatting |
| src/config.ts | Added factorySpawnChance configuration and renamed countdownTicks to countdownDuration |
| src/Native/Camera.ts | Moved relationsData field from ClientCamera to base CameraEntity class |
| src/Native/Arena.ts | Implemented attemptFactorySpawn method and fixed spawn location collision detection logic |
| src/Misc/MazeGenerator.ts | Updated MazeWall constructor call to pass arena instead of game |
| src/Gamemodes/Team4.ts | Refactored team assignment with decideTeam helper and integrated factory spawning |
| src/Gamemodes/Team2.ts | Refactored team assignment with decideTeam helper and integrated factory spawning |
| src/Gamemodes/Tag.ts | Refactored team assignment, integrated factory spawning, and improved team scoring |
| src/Gamemodes/Survival.ts | Updated config variable name from countdownTicks to countdownDuration |
| src/Gamemodes/Mothership.ts | Refactored team assignment and integrated factory spawning |
| src/Gamemodes/Domination.ts | Refactored team assignment with decideTeam helper |
| src/Game.ts | Added broadcastMessage helper method for notifications |
| src/Entity/Tank/Projectile/Bullet.ts | Removed automatic wall-phasing for arena team bullets |
| src/Entity/Tank/Barrel.ts | Standardized string quotes from single to double quotes |
| src/Entity/Object.ts | Changed arenaMobID type to string | null and added defensive check in delete method |
| src/Entity/Misc/TeamEntity.ts | Added base property and static setTeam helper method |
| src/Entity/Misc/TeamBase.ts | Set team's base property when creating TeamBase |
| src/Entity/Misc/Mothership.ts | Added fallback for missing team name |
| src/Entity/Misc/MazeWall.ts | Changed constructor to accept ArenaEntity instead of GameServer |
| src/Entity/Boss/AbstractBoss.ts | Added null-safe fallback for killer name |
| src/Entity/AI.ts | Improved type safety by using ObjectEntity type for AI targets |
| src/Const/Commands.ts | Added game_neutral command and improved teleport command validation |
| src/Client.ts | Fixed order of onDeath and onKill calls in suicide handler |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (factories.length === 0) return false; // No available factories on this team, spawn as usual | ||
|
|
||
| const factory = randomFrom(factories); | ||
|
|
There was a problem hiding this comment.
The attemptFactorySpawn method accesses factory.barrels[0] without checking if the factory has any barrels. While factories should have at least one barrel by design, it's safer to add a check here to prevent potential crashes if a factory somehow has no barrels (e.g., during edge cases or if the tank definition is modified).
| // Ensure the selected factory has at least one barrel before using it | |
| if (!factory.barrels || factory.barrels.length === 0) { | |
| return false; | |
| } |
| public spawnPlayer(tank: TankBody, client: Client) { | ||
| const team = this.decideTeam(client); | ||
| TeamEntity.setTeam(team, tank); | ||
|
|
||
| const teamBase = team.base; | ||
| if (!teamBase) return super.spawnPlayer(tank, client); | ||
|
|
||
| const pos = getRandomPosition(teamBase); | ||
| tank.positionData.values.x = pos.x; | ||
| tank.positionData.values.y = pos.y; | ||
| } |
There was a problem hiding this comment.
The Domination gamemode's spawnPlayer method does not call attemptFactorySpawn, unlike the other team-based gamemodes (Team2, Team4, Tag, Mothership). This means players in Domination mode will not be able to spawn from factories, which is inconsistent with the feature being added in this PR.
src/Entity/Tank/Barrel.ts
Outdated
| public droneCount = 0; | ||
|
|
||
| /** The barrel's addons */ | ||
| /** The barrel"s addons */ |
There was a problem hiding this comment.
The comment uses double-quote character instead of an apostrophe. Consider changing barrel"s to barrel's for proper grammar and readability.
| /** The barrel"s addons */ | |
| /** The barrel's addons */ |
src/Entity/Object.ts
Outdated
| import * as util from "../util"; | ||
| import GameServer from "../Game"; | ||
| import Vector from "../Physics/Vector"; | ||
| import Vector, { VectorAbstract } from "../Physics/Vector"; |
There was a problem hiding this comment.
Unused import VectorAbstract.
| import Vector, { VectorAbstract } from "../Physics/Vector"; | |
| import Vector from "../Physics/Vector"; |
src/Gamemodes/Team2.ts
Outdated
| import ArenaEntity from "../Native/Arena"; | ||
| import Client from "../Client"; | ||
|
|
||
| import ObjectEntity from "../Entity/Object"; |
There was a problem hiding this comment.
Unused import ObjectEntity.
| import ObjectEntity from "../Entity/Object"; |
src/Gamemodes/Team4.ts
Outdated
| import ArenaEntity from "../Native/Arena"; | ||
| import Client from "../Client"; | ||
|
|
||
| import ObjectEntity from "../Entity/Object"; |
There was a problem hiding this comment.
Unused import ObjectEntity.
| import ObjectEntity from "../Entity/Object"; |
src/Native/Arena.ts
Outdated
| import Client from "../Client"; | ||
|
|
||
| import { countdownTicks, bossSpawningInterval, scoreboardUpdateInterval } from "../config"; | ||
| import { countdownDuration, bossSpawningInterval, factorySpawnChance, scoreboardUpdateInterval } from "../config"; |
There was a problem hiding this comment.
Unused import bossSpawningInterval.
| import { countdownDuration, bossSpawningInterval, factorySpawnChance, scoreboardUpdateInterval } from "../config"; | |
| import { countdownDuration, factorySpawnChance, scoreboardUpdateInterval } from "../config"; |
| this.physicsData.values.sides = 1; | ||
| this.physicsData.values.flags |= PhysicsFlags.noOwnTeamCollision | PhysicsFlags.canEscapeArena; | ||
| if (tank.positionData.values.flags & PositionFlags.canMoveThroughWalls || this.relationsData.values.team === this.game.arena) this.positionData.values.flags |= PositionFlags.canMoveThroughWalls | ||
| if (tank.positionData.values.flags & PositionFlags.canMoveThroughWalls) this.positionData.values.flags |= PositionFlags.canMoveThroughWalls |
There was a problem hiding this comment.
Avoid automated semicolon insertion (97% of all statements in the enclosing function have an explicit semicolon).
| if (tank.positionData.values.flags & PositionFlags.canMoveThroughWalls) this.positionData.values.flags |= PositionFlags.canMoveThroughWalls | |
| if (tank.positionData.values.flags & PositionFlags.canMoveThroughWalls) this.positionData.values.flags |= PositionFlags.canMoveThroughWalls; |
| `${leaderTeam.teamName} HAS WON THE GAME!`, | ||
| ColorsHexCode[leaderTeam.teamData.values.teamColor], | ||
| -1 | ||
| ) |
There was a problem hiding this comment.
Avoid automated semicolon insertion (91% of all statements in the enclosing function have an explicit semicolon).
| ) | |
| ); |
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
|
@copilot for the following prompt keep in mind:
FIX THE FOLLOWING IN THIS PR:
Plan concisely and review package.json for typecheck commands |
…add missing factory spawn (#187) * Initial plan * Address review feedback: fix typos, remove unused imports, use isFinite, add factory spawning to Domination Co-authored-by: abcxff <79597906+abcxff@users.noreply.github.com> * Remove redundant MAX_SAFE_INTEGER checks with isFinite Co-authored-by: abcxff <79597906+abcxff@users.noreply.github.com> * Add back bounds checking for score and points in commands --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: abcxff <79597906+abcxff@users.noreply.github.com>
src/Const/Commands.ts
Outdated
| const x = xArg.match(RELATIVE_POS_REGEX) ? player.positionData.x + parseInt(xArg.slice(1) || "0", 10) : parseInt(xArg, 10); | ||
| const y = yArg.match(RELATIVE_POS_REGEX) ? player.positionData.y + parseInt(yArg.slice(1) || "0", 10) : parseInt(yArg, 10); | ||
|
|
||
| if (isNaN(x) || isNaN(y)) return; |
There was a problem hiding this comment.
Should be using isFinite rather than isNaN everywhere. isFinite(x) = isNaN(x) && x is not an infinity
No description provided.