Remove Apex Dynamics faction, retune bullet speed/size
Drop Apex Dynamics from the playable roster (2 races now: Inner Sphere Navy, Outer Rim Collective) to simplify the 2D art pipeline; World's race-offer logic just shuffles both remaining races instead of picking 2 of N. Also fix bullets using a constant muzzle velocity instead of adding the shooter's own velocity, and shrink the bullet hitbox/sprite. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -40,6 +40,8 @@ README for how to run/extend it.
|
||||
|
||||
18. **Engine-flame sprite swap on thrust** — 6 of item 17's 9 ships (ISN's Patriot/Barrage/Behemoth, ORC's Rail-Jack/Scrap-Spitter/Iron-Clad) gained a second `_flame.png` sprite alongside their idle one, cropped from the same concept sheets' flame-frame columns (ISN's sheet has a clean idle/flame pair per ship; ORC's only unambiguous case was Rail-Jack's brighter 3rd-column flame, so Scrap-Spitter/Iron-Clad's flame PNGs are just copies of their idle sprite for now — their sheet's other columns are damage/weapon-fire poses, not more exhaust). Behemoth's flame was composited from two separate sheet crops (clean idle hull + a flame-only crop of just its thrusters) rather than cropped directly, since the sheet's flame frame has a large red shield-ability graphic overlapping the hull that isn't exhaust. Apex's 3 ships (Lancet/Pulsar/Sovereign) have no flame art — their concept sheet never drew a thrust pose — so they're untouched and always show their one sprite. Wired into gameplay via a new optional `flame_path` per ship in `team_select.gd`'s `RACES`, threaded through `PlayerRegistry` (`submit_local_loadout`/`register_bot`/the loadout RPCs, new `ship_flame_path` field, default `""`) so bots and remote peers all get it too, not just the local owner. `ship_movement.gd`'s `_set_thrust_sprite()` swaps the `Sprite2D` texture based on whether the up/down thrust key is *currently held*, not current speed — this game's movement has no drag, so a first cut keyed off `velocity.length()` left the flame on long after letting off the gas since a released ship keeps drifting at speed. The owner predicts this locally the instant it samples input (`_owner_tick`); the server does the same for bots/itself (`_server_tick`) and broadcasts its authoritative value as a new `thrusting` bool on the existing `_receive_state` RPC so every other peer's view of a ship — which never sees raw input, only replicated state — stays in sync too, with no separate RPC needed. Ships with no `flame_path` (Apex) just skip the check entirely (`_flame_texture == null` short-circuits).
|
||||
|
||||
19. **Apex Dynamics faction removed** — cut down to 2 playable races (Inner Sphere Navy, Outer Rim Collective) to simplify the 2D art pipeline. `team_select.gd`'s `RACES` array lost its Apex Dynamics block (Lancet/Pulsar/Sovereign), with ISN/ORC renumbered to `id` 1/2. `world.gd`'s `decide_offered_races()` no longer picks "2 of N" races — with only 2 total, that logic was vestigial, so it now just shuffles the order of both remaining races (kept for spawn-side/left-right variety, since `team_select.gd`/`ship_movement.gd` still key off `offered[0]`/`offered[1]`). `overview/racesclasses.md`'s faction-ability Rock-Paper-Scissors loop (which structurally needed 3 legs) is rewritten as a single ISN-shield-vs-ORC-mines counter relationship; `overview.md`/`bots.md`/`structure.md`'s "2 of 3 races" language updated to reflect both races always being fielded. `assets/images/ships/apex/` and `assets/sound/ships/apex/` are left on disk but unreferenced, same as the older unused `terran/mech/vorg` folders from item 17 — not deleted since that wasn't asked for.
|
||||
|
||||
## Current Tasks
|
||||
|
||||
- [ ] Asteroids and environment hazards
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
## Rules
|
||||
|
||||
- Each casual match picks **2 of the 3 races** at random
|
||||
- Each casual match fields both races (order presented is randomized)
|
||||
- Bots fill empty slots until the match reaches **7v7** (14 total)
|
||||
- Once 7v7 is reached, **no more bots are added** — additional human players replace bots as they join
|
||||
- Bots are removed when real players join their team slot
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
## Concept
|
||||
|
||||
A 2D pixel-art top-down space shooter inspired by **Subspace Continuum**, built for **Steam Deck and PC**. Three alien races fight for control of a galaxy. Fast matchmaking like Rocket League — quick in, quick out.
|
||||
A 2D pixel-art top-down space shooter inspired by **Subspace Continuum**, built for **Steam Deck and PC**. Two alien races fight for control of a galaxy. Fast matchmaking like Rocket League — quick in, quick out.
|
||||
|
||||
## Core Loop
|
||||
|
||||
1. Launch → Main Menu
|
||||
2. Enter callsign → click CASUAL
|
||||
3. World loads → pick your race (2 of 3 randomly offered per match) → pick your ship
|
||||
3. World loads → pick your race (both offered every match) → pick your ship
|
||||
4. Fight in a 25v25 battle
|
||||
5. Return to main menu
|
||||
|
||||
@@ -25,7 +25,7 @@ A 2D pixel-art top-down space shooter inspired by **Subspace Continuum**, built
|
||||
Main Menu
|
||||
└─ CASUAL ──► World loads immediately (game active)
|
||||
└─ TeamSelect overlay appears
|
||||
├─ Pick race (2 random of 3 offered)
|
||||
├─ Pick race (both offered every match)
|
||||
└─ Pick ship (4 available, sprites shown)
|
||||
└─ Player spawns centred, invincible briefly
|
||||
└─ ESC / Start ──► Pause menu overlay
|
||||
@@ -38,7 +38,7 @@ Main Menu
|
||||
|
||||
## Races
|
||||
|
||||
3 playable races chosen from a pool of 10 concepts — see `racesclasses.md`. Each race has 5 ship classes:
|
||||
2 playable races (a third, Apex Dynamics, was cut to simplify the art pipeline) — see `racesclasses.md`. Each race has 5 ship classes:
|
||||
|
||||
| Class | Role |
|
||||
|-------|------|
|
||||
@@ -52,8 +52,8 @@ Races are visually distinct — critical for reading a 25v25 battlefield at a gl
|
||||
|
||||
## Match Setup
|
||||
|
||||
- Each match randomly picks **2 of the 3 races** to field
|
||||
- Player picks which of those 2 they fight for, then picks their ship
|
||||
- Both races are fielded every match (order they're presented in is randomized)
|
||||
- Player picks which of the 2 they fight for, then picks their ship
|
||||
- Bot fill ensures a minimum of **7v7** in casual; no bots added beyond that
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
+16
-39
@@ -2,30 +2,21 @@
|
||||
|
||||
## Setting
|
||||
|
||||
Three factions fight inside the massive shielding, pipes, and superstructure of
|
||||
Two factions fight inside the massive shielding, pipes, and superstructure of
|
||||
refineries, shipyards, and mining stations — not open void. Grounded, blue-collar
|
||||
sci-fi in the vein of *The Expanse* (corporate hegemony vs. disciplined inner-planet
|
||||
military vs. gritty outer-belt miners), but built as a fast, arcade 2D tactical
|
||||
arena shooter rather than a hard-sci-fi sim. Tight factory corridors and choke
|
||||
points are the map language — see `multiplayer.md`.
|
||||
sci-fi in the vein of *The Expanse* (disciplined inner-planet military vs. gritty
|
||||
outer-belt miners), but built as a fast, arcade 2D tactical arena shooter rather
|
||||
than a hard-sci-fi sim. Tight factory corridors and choke points are the map
|
||||
language — see `multiplayer.md`.
|
||||
|
||||
> **Status:** This replaces the previous 10-alien-race concept pool and the
|
||||
> Terran Republic / Mechanos Sovereignty / Vorg Swarm roster. Doc-only for now —
|
||||
> game code (`team_select.gd` RACES data, `GameConfig`, existing
|
||||
> `assets/images/ships/<race>/` art) still reflects the old races until that work
|
||||
> is scheduled.
|
||||
> Terran Republic / Mechanos Sovereignty / Vorg Swarm roster. A third faction,
|
||||
> Apex Dynamics (The Corporate Syndicate), was cut entirely to simplify the art
|
||||
> pipeline — no code or docs should reference it going forward.
|
||||
|
||||
---
|
||||
|
||||
## The 3 Factions
|
||||
|
||||
### Apex Dynamics — The Corporate Syndicate
|
||||
**Weapon type:** High-tech energy (coherent light / plasma)
|
||||
**Visual:** Sleek, pearlescent-white hulls with razor-sharp geometric lines and
|
||||
glowing neon-blue engine trails — looks more like a high-end luxury smartphone
|
||||
than a military weapon.
|
||||
**Feel:** High-tech and precise. Wins by not being seen until it's too late.
|
||||
**Tactical ability:** **Active Camouflage** — see [Faction Abilities](#faction-abilities-universal-system)
|
||||
## The 2 Factions
|
||||
|
||||
### Inner Sphere Navy (ISN) — The Military
|
||||
**Weapon type:** Standard-issue ballistics / ordnance
|
||||
@@ -55,14 +46,6 @@ Every faction fields the same 3-ship structure, re-skinned to its own theme:
|
||||
| **Gunner** | Rapid-fire spray/burst, more shots for less damage each |
|
||||
| **Tank** | Bigger and slower; fires both bullets and missiles |
|
||||
|
||||
### Apex Dynamics
|
||||
|
||||
| Ship | Class | Weapon |
|
||||
|------|-------|--------|
|
||||
| **Lancet** | Fighter | A single, high-precision bolt of blue coherent light |
|
||||
| **Pulsar** | Gunner | A wide, rapid-fire fan of low-damage green plasma pulses |
|
||||
| **Sovereign** | Tank | Homing micro-missiles alongside dual heavy plasma batteries |
|
||||
|
||||
### Inner Sphere Navy (ISN)
|
||||
|
||||
| Ship | Class | Weapon |
|
||||
@@ -85,7 +68,7 @@ Every faction fields the same 3-ship structure, re-skinned to its own theme:
|
||||
|
||||
Each faction has exactly one tactical ability. Every ship in that faction gets
|
||||
it — what changes per class is scale, not kind, so the ability reads instantly
|
||||
off a ship's faction regardless of which of the 3 hulls it's flying. Same energy
|
||||
off a ship's faction regardless of which of the 2 hulls it's flying. Same energy
|
||||
cost and cooldown across factions; duration/impact scales with the hull:
|
||||
|
||||
| Class | Scaling rule |
|
||||
@@ -94,12 +77,6 @@ cost and cooldown across factions; duration/impact scales with the hull:
|
||||
| Gunner | Baseline cooldown and effect |
|
||||
| Tank | Long cooldown, largest/heaviest effect |
|
||||
|
||||
### Apex Dynamics — Active Camouflage
|
||||
Ship turns 90% transparent and disappears from radar. Firing or taking damage
|
||||
breaks it instantly. Fighter gets a quick, short slip; Tank's lasts longest but
|
||||
is riskiest to commit a slow hull to.
|
||||
**Use:** Slip past a defender in a narrow pipe, flank, or break a missile lock.
|
||||
|
||||
### Inner Sphere Navy — Overcharged Aegis
|
||||
A frontal hard-light barrier absorbs 100% of incoming damage from the front for
|
||||
a few seconds. Sides and rear stay exposed, turn rate drops, and the ship can't
|
||||
@@ -117,15 +94,15 @@ larger blast radius; Fighter's arms faster but hits smaller.
|
||||
**Use:** Cover a retreat down a corridor, or seed a choke point/objective before
|
||||
a push.
|
||||
|
||||
### The Rock-Paper-Scissors Loop
|
||||
### Faction Interaction
|
||||
|
||||
With only 2 factions, the abilities form a single counter rather than a loop:
|
||||
|
||||
- **ORC mines counter Apex cloak** — a cloaked ship blundering through a mined
|
||||
corridor eats the blast, which also breaks the cloak.
|
||||
- **ISN shield counters ORC mines** — the frontal barrier absorbs a mine
|
||||
detonation, clearing the path for the team behind it.
|
||||
- **Apex cloak counters ISN shield** — a slow, frontal-shielded push leaves the
|
||||
ISN ship's flank and engines open for a cloaked ship to slip around and
|
||||
punish.
|
||||
- **ORC mines punish a shield push once it drops** — the shield can't fire and
|
||||
loses turn rate while up, so a mine dropped in its path (or behind it, once
|
||||
the barrier expires) still threatens the push.
|
||||
|
||||
No ability wins a fight by itself — it depends on the map and the moment it's
|
||||
used, same as a smoke/flash/molotov in CS:GO.
|
||||
|
||||
@@ -104,7 +104,7 @@ Player (CharacterBody2D) ← ship_movement.gd
|
||||
```
|
||||
main_menu.tscn
|
||||
├── QUICK PLAY → matchmaking queue (casual) → world.tscn
|
||||
│ ├── TeamSelect overlay: pick race (2 random of 3 offered)
|
||||
│ ├── TeamSelect overlay: pick race (both offered every match)
|
||||
│ ├── TeamSelect overlay: pick ship
|
||||
│ └── Player spawns → game live
|
||||
├── SERVER SELECT → server_select.tscn → pick a server → world.tscn (same TeamSelect flow)
|
||||
|
||||
@@ -15,7 +15,7 @@ var ship_rotation_speed_held: float = 2.0
|
||||
# fire their bullets in a side-by-side pair instead of Fighter's single shot
|
||||
# (see ship_movement.gd's _server_process_shoot()).
|
||||
var ship_fire_rate: float = 0.15
|
||||
var bullet_speed: float = 450.0
|
||||
var bullet_speed: float = 750.0
|
||||
var ship_bullet_damage_by_role: Dictionary = {"Fighter": 100, "Gunner": 25, "Tank": 50}
|
||||
var ship_bullet_count_by_role: Dictionary = {"Fighter": 1, "Gunner": 2, "Tank": 2}
|
||||
var bullet_side_spacing: float = 14.0
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
extends CanvasLayer
|
||||
class_name TeamSelect
|
||||
|
||||
# Movement speed factor by role, shared across all 3 factions so e.g. every
|
||||
# Movement speed factor by role, shared across both factions so e.g. every
|
||||
# faction's Fighter moves at the same speed as every other faction's.
|
||||
const SPEED_BY_ROLE := {
|
||||
"Fighter": 1.3,
|
||||
@@ -11,31 +11,7 @@ const SPEED_BY_ROLE := {
|
||||
|
||||
const RACES := [
|
||||
{
|
||||
"id": 1, "name": "APEX DYNAMICS", "sub": "Sleek pearlescent-white corporate fleet — precision energy weapons, wins by not being seen",
|
||||
"color": Color(0.55, 0.85, 1.0),
|
||||
"ships": [
|
||||
{
|
||||
"name": "LANCET", "role": "Fighter",
|
||||
"desc": "A single, high-precision bolt of blue coherent light. Built to poke from range and vanish before anything can return fire — the sharpest shot in the fleet, if you can land it.",
|
||||
"path": "res://assets/images/ships/apex/lancet.png", "scale": 0.296, "speed_factor": SPEED_BY_ROLE.Fighter,
|
||||
"sound_path": "res://assets/sound/ships/apex/apex_1.wav",
|
||||
},
|
||||
{
|
||||
"name": "PULSAR", "role": "Gunner",
|
||||
"desc": "A wide, rapid-fire fan of low-damage green plasma pulses. Trades precision for volume — spray enough bolts and something connects.",
|
||||
"path": "res://assets/images/ships/apex/pulsar.png", "scale": 0.373, "speed_factor": SPEED_BY_ROLE.Gunner,
|
||||
"sound_path": "res://assets/sound/ships/apex/apex_1.wav",
|
||||
},
|
||||
{
|
||||
"name": "SOVEREIGN", "role": "Tank",
|
||||
"desc": "Homing micro-missiles alongside dual heavy plasma batteries. Slow and telegraphed, but its lock-on missiles punish anyone who tries to just outrun it.",
|
||||
"path": "res://assets/images/ships/apex/sovereign.png", "scale": 0.348, "speed_factor": SPEED_BY_ROLE.Tank,
|
||||
"sound_path": "res://assets/sound/ships/apex/apex_1.wav",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 2, "name": "INNER SPHERE NAVY", "sub": "Gunmetal-gray navy of Earth, Mars, and Mercury — disciplined, frontal, built to hold a line",
|
||||
"id": 1, "name": "INNER SPHERE NAVY", "sub": "Gunmetal-gray navy of Earth, Mars, and Mercury — disciplined, frontal, built to hold a line",
|
||||
"color": Color(0.80, 0.32, 0.32),
|
||||
"ships": [
|
||||
{
|
||||
@@ -62,7 +38,7 @@ const RACES := [
|
||||
],
|
||||
},
|
||||
{
|
||||
"id": 3, "name": "OUTER RIM COLLECTIVE", "sub": "Gritty, jury-rigged mining rebels — kinetic weapons scavenged and welded onto anything that flies",
|
||||
"id": 2, "name": "OUTER RIM COLLECTIVE", "sub": "Gritty, jury-rigged mining rebels — kinetic weapons scavenged and welded onto anything that flies",
|
||||
"color": Color(0.82, 0.52, 0.24),
|
||||
"ships": [
|
||||
{
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
[ext_resource type="Script" path="res://ships/bullet.gd" id="1"]
|
||||
|
||||
[sub_resource type="CircleShape2D" id="CircleShape2D_1"]
|
||||
radius = 4.0
|
||||
radius = 2.8
|
||||
|
||||
[node name="Bullet" type="Area2D"]
|
||||
collision_layer = 0
|
||||
@@ -15,4 +15,4 @@ shape = SubResource("CircleShape2D_1")
|
||||
|
||||
[node name="Polygon2D" type="Polygon2D" parent="."]
|
||||
color = Color(1, 0.9, 0.2, 1)
|
||||
polygon = PackedVector2Array(5, 0, 4.33, 2.5, 2.5, 4.33, 0, 5, -2.5, 4.33, -4.33, 2.5, -5, 0, -4.33, -2.5, -2.5, -4.33, 0, -5, 2.5, -4.33, 4.33, -2.5)
|
||||
polygon = PackedVector2Array(3.5, 0, 3.031, 1.75, 1.75, 3.031, 0, 3.5, -1.75, 3.031, -3.031, 1.75, -3.5, 0, -3.031, -1.75, -1.75, -3.031, 0, -3.5, 1.75, -3.031, 3.031, -1.75)
|
||||
|
||||
@@ -33,8 +33,8 @@ var _invincible_timer: float = 0.0
|
||||
var _ship_speed_factor: float = 1.0
|
||||
|
||||
# Idle (no-flame) vs engine-flame sprite swap — see _update_thrust_visual().
|
||||
# _flame_texture stays null for ships with no flame_path (e.g. Apex), which
|
||||
# just keeps _update_thrust_visual() from ever touching the sprite.
|
||||
# _flame_texture stays null for ships with no flame_path, which just keeps
|
||||
# _update_thrust_visual() from ever touching the sprite.
|
||||
var _idle_texture: Texture2D
|
||||
var _flame_texture: Texture2D
|
||||
var _thrusting: bool = false
|
||||
@@ -443,7 +443,11 @@ func _server_process_shoot(delta: float, input: Dictionary) -> void:
|
||||
var forward := Vector2.UP.rotated(rotation)
|
||||
var right := Vector2.RIGHT.rotated(rotation)
|
||||
var base_pos := global_position + forward * 40.0
|
||||
var bullet_vel := forward * GameConfig.bullet_speed + velocity
|
||||
# Constant muzzle velocity regardless of the shooter's own speed --
|
||||
# used to add `velocity` on top, which made bullets faster fired
|
||||
# from a ship moving forward and slower (even reversed-looking)
|
||||
# fired while drifting backward.
|
||||
var bullet_vel := forward * GameConfig.bullet_speed
|
||||
var spacing := GameConfig.bullet_side_spacing
|
||||
var start := -spacing * (count - 1) / 2.0
|
||||
for i in count:
|
||||
|
||||
@@ -36,15 +36,16 @@ func _ready() -> void:
|
||||
spawn_peer(peer_id)
|
||||
|
||||
|
||||
# Picks the match's 2 offered races once and caches them, so every caller
|
||||
# Picks the match's offered races once and caches them, so every caller
|
||||
# (the server's own TeamSelect and every remote client's request) converges
|
||||
# on the same pair regardless of call order. Also kicks off bot fill for
|
||||
# those 2 races — see bots/bot_manager.gd.
|
||||
# those races — see bots/bot_manager.gd. Both factions are always offered
|
||||
# (only order is randomized, for spawn-side variety).
|
||||
func decide_offered_races() -> Array:
|
||||
if _offered_race_ids.is_empty():
|
||||
var ids := range(1, TeamSelect.RACES.size() + 1)
|
||||
var ids: Array = TeamSelect.RACES.map(func(race): return race.id)
|
||||
ids.shuffle()
|
||||
_offered_race_ids = [ids[0], ids[1]]
|
||||
_offered_race_ids = ids
|
||||
# Deferred: this can run from TeamSelect._ready(), which (as World's
|
||||
# child) fires before World's own _ready() — before _players/_spawner
|
||||
# are assigned. Deferring runs it after the whole scene's _ready
|
||||
|
||||
Reference in New Issue
Block a user