Removes boss bar and updates hivemind
This commit is contained in:
+5
-5
@@ -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 8–10 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 8–10 seconds. No warning before teleport — position resets without notice. Punishes clustering near the boss.
|
||||||
@@ -85,7 +85,7 @@ Periodically broadcasts a signal that summons 2–3 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 | 4–5 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 | 4–5 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 | 5–6 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 | 5–6 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 | 2–3 rooms + boss | Room composition tightens. Anchors in tighter spaces. Charging station. Hivemind — three phases, everything converges. Victory. |
|
| Floor 3 | 2–3 rooms + boss | Room composition tightens. Anchors in tighter spaces. Hivemind — three phases, everything converges. Victory. |
|
||||||
|
|
||||||
**Target run time**: 20–30 minutes.
|
**Target run time**: 20–30 minutes.
|
||||||
|
|
||||||
|
|||||||
@@ -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="."]
|
||||||
|
|||||||
@@ -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="."]
|
||||||
|
|||||||
@@ -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
@@ -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),
|
||||||
])
|
# ])
|
||||||
|
|||||||
Reference in New Issue
Block a user