Removes boss bar and updates hivemind

This commit is contained in:
2026-03-10 09:35:03 -04:00
parent 3684517d1b
commit b1f453bb27
5 changed files with 85 additions and 85 deletions
+5 -5
View File
@@ -43,7 +43,7 @@ All enemies are corrupted Adaptive Maintenance Units — same chassis class as t
A containment unit whose crowd-control protocol has turned inward — it is now attempting to contain the entire room, including the player. Slow and methodical. The room itself becomes the threat. A containment unit whose crowd-control protocol has turned inward — it is now attempting to contain the entire room, including the player. Slow and methodical. The room itself becomes the threat.
**Room**: Large. No environmental hazards on entry. Preceded by a charging station (full HP restore before entry). **Room**: Large. No environmental hazards on entry.
### Movement ### Movement
Patrols the room perimeter in a slow, continuous circuit. Never crosses the center. Fully predictable path — the danger comes from what it leaves behind, not from the boss itself. Patrols the room perimeter in a slow, continuous circuit. Never crosses the center. Fully predictable path — the danger comes from what it leaves behind, not from the boss itself.
@@ -64,7 +64,7 @@ Drops a stationary proximity mine at its current patrol position each cycle. Min
A communications hub unit whose broadcast function corrupted into something aggressive. Erratic, unpredictable positioning. Forces the player to stay mobile and read the room constantly. A communications hub unit whose broadcast function corrupted into something aggressive. Erratic, unpredictable positioning. Forces the player to stay mobile and read the room constantly.
**Room**: Large. No environmental hazards on entry. Preceded by a charging station (full HP restore before entry). **Room**: Large. No environmental hazards on entry.
### Movement ### Movement
Anchors near room center but teleports to a random position every 810 seconds. No warning before teleport — position resets without notice. Punishes clustering near the boss. Anchors near room center but teleports to a random position every 810 seconds. No warning before teleport — position resets without notice. Punishes clustering near the boss.
@@ -85,7 +85,7 @@ Periodically broadcasts a signal that summons 23 Drifters. Not full waves —
What the corruption becomes when it fully realizes itself. Individual units lose their original purpose and merge into a single machine that was never meant to exist. What the corruption becomes when it fully realizes itself. Individual units lose their original purpose and merge into a single machine that was never meant to exist.
**Room**: Large. Largest boss room in the run. Preceded by a charging station (full HP restore before entry). **Room**: Large. Largest boss room in the run.
### Phase 1 — Assembly ### Phase 1 — Assembly
No central target yet. Corrupted units flood the room in waves — Drifters, Repeaters, Anchors. As each one dies, it is pulled toward the center and physically joins the assembling structure (visual indicator builds up). Player must clear everything they've learned across floors 1 and 2 simultaneously, under pressure. The Hivemind core cannot be damaged during this phase. No central target yet. Corrupted units flood the room in waves — Drifters, Repeaters, Anchors. As each one dies, it is pulled toward the center and physically joins the assembling structure (visual indicator builds up). Player must clear everything they've learned across floors 1 and 2 simultaneously, under pressure. The Hivemind core cannot be damaged during this phase.
@@ -205,7 +205,7 @@ One item on a pedestal. No enemies. Safe. 20% chance to also contain a lore term
Unmanned. No NPC keeper. Sells 2 items + 1 consumable at fixed scrap prices. No haggling. Purely transactional — all NPC personality lives in the hub. Unmanned. No NPC keeper. Sells 2 items + 1 consumable at fixed scrap prices. No haggling. Purely transactional — all NPC personality lives in the hub.
**Boss room** **Boss room**
One boss per floor. Each preceded by a charging station room (no enemies, full HP restore). Boss rooms are the largest rooms in the run. One boss per floor. Boss rooms are the largest rooms in the run. No boss health bar — player reads boss state through visual phase transitions and the climax (blinking/dying) phase. Keeps tension high and encourages reading the fight, not the UI.
| Floor | Boss | | Floor | Boss |
|---|---| |---|---|
@@ -237,7 +237,7 @@ Both hazard types included in VS.
|---|---|---| |---|---|---|
| Floor 1 | 45 rooms + boss | Learn the rhythm. One item. Maybe find the shop. First Repeater feels like a puzzle. Warden fight — room fills with mines, learn to manage space. | | Floor 1 | 45 rooms + boss | Learn the rhythm. One item. Maybe find the shop. First Repeater feels like a puzzle. Warden fight — room fills with mines, learn to manage space. |
| Floor 2 | 56 rooms + boss | Mixed threats. Find a body part. Build takes shape. One tense near-death moment. Relay fight — erratic, keeps moving, adds split focus. | | Floor 2 | 56 rooms + boss | Mixed threats. Find a body part. Build takes shape. One tense near-death moment. Relay fight — erratic, keeps moving, adds split focus. |
| Floor 3 | 23 rooms + boss | Room composition tightens. Anchors in tighter spaces. Charging station. Hivemind — three phases, everything converges. Victory. | | Floor 3 | 23 rooms + boss | Room composition tightens. Anchors in tighter spaces. Hivemind — three phases, everything converges. Victory. |
**Target run time**: 2030 minutes. **Target run time**: 2030 minutes.
+1 -1
View File
@@ -10,7 +10,7 @@ radius = 40.0
[node name="Hivemind" type="CharacterBody2D"] [node name="Hivemind" type="CharacterBody2D"]
collision_layer = 8 collision_layer = 8
collision_mask = 1 collision_mask = 3
script = ExtResource("1") script = ExtResource("1")
[node name="Collision" type="CollisionShape2D" parent="."] [node name="Collision" type="CollisionShape2D" parent="."]
+1 -1
View File
@@ -8,7 +8,7 @@ radius = 10.0
[node name="Player" type="CharacterBody2D"] [node name="Player" type="CharacterBody2D"]
collision_layer = 2 collision_layer = 2
collision_mask = 1 collision_mask = 9
script = ExtResource("1") script = ExtResource("1")
[node name="Collision" type="CollisionShape2D" parent="."] [node name="Collision" type="CollisionShape2D" parent="."]
+3 -4
View File
@@ -138,13 +138,13 @@ func _ready() -> void:
_core_visual.visible = false # hidden during assembly _core_visual.visible = false # hidden during assembly
_core_visual.scale = Vector2(_core_scale, _core_scale) _core_visual.scale = Vector2(_core_scale, _core_scale)
# Disable and scale down collision during assembly # Scale down collision during assembly (contact area disabled, but hitbox active for projectiles)
if _contact_area: if _contact_area:
_contact_area.monitoring = false _contact_area.monitoring = false
_contact_area.monitorable = false _contact_area.monitorable = false
_contact_area.scale = Vector2(_core_scale, _core_scale) _contact_area.scale = Vector2(_core_scale, _core_scale)
if _collision_shape: if _collision_shape:
_collision_shape.disabled = true # Keep hitbox enabled but scaled - boss is invulnerable during assembly via take_damage()
_collision_shape.scale = Vector2(_core_scale, _core_scale) _collision_shape.scale = Vector2(_core_scale, _core_scale)
@@ -310,13 +310,12 @@ func _enter_active() -> void:
_core_visual.visible = true _core_visual.visible = true
_core_visual.scale = Vector2(1.0, 1.0) _core_visual.scale = Vector2(1.0, 1.0)
# Enable and scale up collision # Enable contact area and scale up collision
if _contact_area: if _contact_area:
_contact_area.monitoring = true _contact_area.monitoring = true
_contact_area.monitorable = true _contact_area.monitorable = true
_contact_area.scale = Vector2(1.0, 1.0) _contact_area.scale = Vector2(1.0, 1.0)
if _collision_shape: if _collision_shape:
_collision_shape.disabled = false
_collision_shape.scale = Vector2(1.0, 1.0) _collision_shape.scale = Vector2(1.0, 1.0)
_teleport_timer = 2.0 _teleport_timer = 2.0
+75 -74
View File
@@ -27,11 +27,11 @@ const HEART_SPACING := 52 # px between heart centers
const ORIGIN := Vector2(32, 32) # center of first heart (screen px) const ORIGIN := Vector2(32, 32) # center of first heart (screen px)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Boss bar # DEPRECATED — Boss bar (commented out)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
const BOSS_BAR_W := 400.0 #const BOSS_BAR_W := 400.0
const BOSS_BAR_H := 18.0 #const BOSS_BAR_H := 18.0
const BOSS_BAR_POS := Vector2(280.0, 510.0) # bottom center (960×540 viewport) #const BOSS_BAR_POS := Vector2(760.0, 1040.0) # bottom center (1920×1080 viewport)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# State # State
@@ -40,11 +40,11 @@ const BOSS_BAR_POS := Vector2(280.0, 510.0) # bottom center (960×540 viewport
var _slots: Array = [] var _slots: Array = []
var _credits_label: Label var _credits_label: Label
# Boss bar nodes (null when not active) # DEPRECATED — Boss bar nodes (commented out)
var _boss_bar_root: Node2D = null #var _boss_bar_root: Node2D = null
var _boss_bar_fill: Polygon2D = null #var _boss_bar_fill: Polygon2D = null
var _boss_bar_label: Label = null #var _boss_bar_label: Label = null
var _boss_max_hp: float = 1.0 #var _boss_max_hp: float = 1.0
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Boot # Boot
@@ -52,8 +52,9 @@ var _boss_max_hp: float = 1.0
func _ready() -> void: func _ready() -> void:
EventBus.player_health_changed.connect(_on_health_changed) EventBus.player_health_changed.connect(_on_health_changed)
EventBus.credits_changed.connect(_on_credits_changed) EventBus.credits_changed.connect(_on_credits_changed)
EventBus.boss_health_changed.connect(_on_boss_health_changed) # DEPRECATED — Boss bar signal connections (commented out)
EventBus.boss_died.connect(_on_boss_died) #EventBus.boss_health_changed.connect(_on_boss_health_changed)
#EventBus.boss_died.connect(_on_boss_died)
_build_credits_label() _build_credits_label()
# Initialise once the scene is fully set up # Initialise once the scene is fully set up
call_deferred("_init_from_player") call_deferred("_init_from_player")
@@ -139,67 +140,67 @@ func _on_health_changed(current_hp: float, max_hp: float) -> void:
_update_display(current_hp) _update_display(current_hp)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
# Boss health bar # DEPRECATED — Boss health bar (commented out)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
var _boss_name: String = "" #var _boss_name: String = ""
#
func _on_boss_health_changed(current_hp: float, max_hp: float, boss_name: String) -> void: #func _on_boss_health_changed(current_hp: float, max_hp: float, boss_name: String) -> void:
if _boss_bar_root == null: # if _boss_bar_root == null:
_build_boss_bar(max_hp, boss_name) # _build_boss_bar(max_hp, boss_name)
elif _boss_name != boss_name: # elif _boss_name != boss_name:
# Different boss, update label # # Different boss, update label
_boss_name = boss_name # _boss_name = boss_name
if _boss_bar_label != null: # if _boss_bar_label != null:
_boss_bar_label.text = boss_name # _boss_bar_label.text = boss_name
_boss_max_hp = max_hp # _boss_max_hp = max_hp
_update_boss_bar(current_hp) # _update_boss_bar(current_hp)
#
func _on_boss_died() -> void: #func _on_boss_died() -> void:
if _boss_bar_root != null: # if _boss_bar_root != null:
_boss_bar_root.queue_free() # _boss_bar_root.queue_free()
_boss_bar_root = null # _boss_bar_root = null
_boss_bar_fill = null # _boss_bar_fill = null
_boss_bar_label = null # _boss_bar_label = null
_boss_name = "" # _boss_name = ""
#
func _build_boss_bar(max_hp: float, boss_name: String) -> void: #func _build_boss_bar(max_hp: float, boss_name: String) -> void:
_boss_max_hp = max_hp # _boss_max_hp = max_hp
_boss_name = boss_name # _boss_name = boss_name
#
_boss_bar_root = Node2D.new() # _boss_bar_root = Node2D.new()
_boss_bar_root.position = BOSS_BAR_POS # _boss_bar_root.position = BOSS_BAR_POS
add_child(_boss_bar_root) # add_child(_boss_bar_root)
#
# Name label # # Name label
_boss_bar_label = Label.new() # _boss_bar_label = Label.new()
_boss_bar_label.text = boss_name # _boss_bar_label.text = boss_name
_boss_bar_label.position = Vector2(0, -22) # _boss_bar_label.position = Vector2(0, -22)
_boss_bar_root.add_child(_boss_bar_label) # _boss_bar_root.add_child(_boss_bar_label)
#
# Background # # Background
var bg := Polygon2D.new() # var bg := Polygon2D.new()
bg.polygon = PackedVector2Array([ # bg.polygon = PackedVector2Array([
Vector2(0, 0), Vector2(BOSS_BAR_W, 0), # Vector2(0, 0), Vector2(BOSS_BAR_W, 0),
Vector2(BOSS_BAR_W, BOSS_BAR_H), Vector2(0, BOSS_BAR_H), # Vector2(BOSS_BAR_W, BOSS_BAR_H), Vector2(0, BOSS_BAR_H),
]) # ])
bg.color = Color(0.15, 0.05, 0.05) # bg.color = Color(0.15, 0.05, 0.05)
_boss_bar_root.add_child(bg) # _boss_bar_root.add_child(bg)
#
# Fill # # Fill
_boss_bar_fill = Polygon2D.new() # _boss_bar_fill = Polygon2D.new()
_boss_bar_fill.polygon = PackedVector2Array([ # _boss_bar_fill.polygon = PackedVector2Array([
Vector2(0, 0), Vector2(BOSS_BAR_W, 0), # Vector2(0, 0), Vector2(BOSS_BAR_W, 0),
Vector2(BOSS_BAR_W, BOSS_BAR_H), Vector2(0, BOSS_BAR_H), # Vector2(BOSS_BAR_W, BOSS_BAR_H), Vector2(0, BOSS_BAR_H),
]) # ])
_boss_bar_fill.color = Color(0.85, 0.1, 0.1) # _boss_bar_fill.color = Color(0.85, 0.1, 0.1)
_boss_bar_root.add_child(_boss_bar_fill) # _boss_bar_root.add_child(_boss_bar_fill)
#
func _update_boss_bar(current_hp: float) -> void: #func _update_boss_bar(current_hp: float) -> void:
if _boss_bar_fill == null: # if _boss_bar_fill == null:
return # return
var pct := clampf(current_hp / _boss_max_hp, 0.0, 1.0) # var pct := clampf(current_hp / _boss_max_hp, 0.0, 1.0)
var w := BOSS_BAR_W * pct # var w := BOSS_BAR_W * pct
_boss_bar_fill.polygon = PackedVector2Array([ # _boss_bar_fill.polygon = PackedVector2Array([
Vector2(0, 0), Vector2(w, 0), # Vector2(0, 0), Vector2(w, 0),
Vector2(w, BOSS_BAR_H), Vector2(0, BOSS_BAR_H), # Vector2(w, BOSS_BAR_H), Vector2(0, BOSS_BAR_H),
]) # ])