feat: Implement tile scarcity model for tile generation and power-up inventory UI for player interaction.
This commit is contained in:
+46
-10
@@ -11,6 +11,7 @@ var screen_shake_manager
|
|||||||
var touch_controls
|
var touch_controls
|
||||||
var camera_context_manager
|
var camera_context_manager
|
||||||
var stop_n_go_manager
|
var stop_n_go_manager
|
||||||
|
var stop_n_go_winner_id: int = -1 # Track who finished first in Stop n Go mode
|
||||||
var obstacle_manager
|
var obstacle_manager
|
||||||
|
|
||||||
# Minimal local state
|
# Minimal local state
|
||||||
@@ -598,8 +599,8 @@ func _start_game():
|
|||||||
_assign_random_spawn_positions()
|
_assign_random_spawn_positions()
|
||||||
|
|
||||||
# PRE-GAME COUNTDOWN (3s)
|
# PRE-GAME COUNTDOWN (3s)
|
||||||
# Spawn static obstacles before countdown starts
|
# Spawn static obstacles before countdown starts (Stop n Go only)
|
||||||
if obstacle_manager:
|
if obstacle_manager and LobbyManager.game_mode == "Stop n Go":
|
||||||
obstacle_manager.spawn_random_obstacles(15)
|
obstacle_manager.spawn_random_obstacles(15)
|
||||||
|
|
||||||
# Spawn mission tiles BEFORE countdown but AFTER walls (Stop n Go only)
|
# Spawn mission tiles BEFORE countdown but AFTER walls (Stop n Go only)
|
||||||
@@ -1625,8 +1626,17 @@ func _on_leaderboard_updated(sorted_scores: Array):
|
|||||||
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
|
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
|
||||||
sorted_players.append({"node": p, "score": score})
|
sorted_players.append({"node": p, "score": score})
|
||||||
|
|
||||||
# Sort by score descending
|
# Sort by score descending (with Stop n Go winner priority)
|
||||||
sorted_players.sort_custom(func(a, b): return a.score > b.score)
|
if LobbyManager.game_mode == "Stop n Go" and stop_n_go_winner_id != -1:
|
||||||
|
sorted_players.sort_custom(func(a, b):
|
||||||
|
var a_id = a.node.name.to_int()
|
||||||
|
var b_id = b.node.name.to_int()
|
||||||
|
if a_id == stop_n_go_winner_id: return true
|
||||||
|
if b_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sorted_players.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
# Assign rank
|
# Assign rank
|
||||||
for i in range(sorted_players.size()):
|
for i in range(sorted_players.size()):
|
||||||
@@ -1647,6 +1657,9 @@ func _on_global_timer_updated(time_remaining: float):
|
|||||||
@rpc("any_peer", "call_local", "reliable")
|
@rpc("any_peer", "call_local", "reliable")
|
||||||
func sync_game_end_stop_n_go(winner_id: int):
|
func sync_game_end_stop_n_go(winner_id: int):
|
||||||
print("[STOP n GO] Game ended! Winner: ", winner_id)
|
print("[STOP n GO] Game ended! Winner: ", winner_id)
|
||||||
|
stop_n_go_winner_id = winner_id
|
||||||
|
if goals_cycle_manager:
|
||||||
|
goals_cycle_manager.stop_n_go_winner_id = winner_id
|
||||||
|
|
||||||
var winner_name = "Player " + str(winner_id)
|
var winner_name = "Player " + str(winner_id)
|
||||||
var player_node = get_node_or_null(str(winner_id))
|
var player_node = get_node_or_null(str(winner_id))
|
||||||
@@ -1736,14 +1749,23 @@ func _show_game_over_panel():
|
|||||||
leaderboard_container.add_theme_constant_override("separation", 15)
|
leaderboard_container.add_theme_constant_override("separation", 15)
|
||||||
inner_vbox.add_child(leaderboard_container)
|
inner_vbox.add_child(leaderboard_container)
|
||||||
|
|
||||||
# Get final scores
|
|
||||||
var player_scores = []
|
var player_scores = []
|
||||||
for p in get_tree().get_nodes_in_group("Players"):
|
for p in get_tree().get_nodes_in_group("Players"):
|
||||||
player_scores.append({
|
player_scores.append({
|
||||||
|
"peer_id": p.name.to_int(),
|
||||||
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
"name": p.display_name if not p.display_name.is_empty() else str(p.name),
|
||||||
"score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0
|
"score": goals_cycle_manager.get_player_score(p.name.to_int()) if goals_cycle_manager else 0
|
||||||
})
|
})
|
||||||
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
|
||||||
|
# Custom Sort for Stop n Go: Winner always first
|
||||||
|
if LobbyManager.game_mode == "Stop n Go" and stop_n_go_winner_id != -1:
|
||||||
|
player_scores.sort_custom(func(a, b):
|
||||||
|
if a.peer_id == stop_n_go_winner_id: return true
|
||||||
|
if b.peer_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
player_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
# Display each player
|
# Display each player
|
||||||
for i in range(min(player_scores.size(), 8)):
|
for i in range(min(player_scores.size(), 8)):
|
||||||
@@ -1867,8 +1889,15 @@ func sync_leaderboard_data(player_data: Array):
|
|||||||
if not vbox:
|
if not vbox:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Sort by score descending
|
# Sort by score descending (with Stop n Go winner priority)
|
||||||
player_data.sort_custom(func(a, b): return a.score > b.score)
|
if LobbyManager.game_mode == "Stop n Go" and stop_n_go_winner_id != -1:
|
||||||
|
player_data.sort_custom(func(a, b):
|
||||||
|
if a.peer_id == stop_n_go_winner_id: return true
|
||||||
|
if b.peer_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
player_data.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
# Update entries
|
# Update entries
|
||||||
_render_leaderboard_entries(player_data)
|
_render_leaderboard_entries(player_data)
|
||||||
@@ -1895,8 +1924,15 @@ func _update_leaderboard_display():
|
|||||||
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
|
var score = goals_cycle_manager.get_player_score(peer_id) if goals_cycle_manager else 0
|
||||||
player_data.append({"peer_id": peer_id, "name": p.display_name if not p.display_name.is_empty() else str(p.name), "score": score})
|
player_data.append({"peer_id": peer_id, "name": p.display_name if not p.display_name.is_empty() else str(p.name), "score": score})
|
||||||
|
|
||||||
# Sort by score descending
|
# Sort by score descending (with Stop n Go winner priority)
|
||||||
player_data.sort_custom(func(a, b): return a.score > b.score)
|
if LobbyManager.game_mode == "Stop n Go" and stop_n_go_winner_id != -1:
|
||||||
|
player_data.sort_custom(func(a, b):
|
||||||
|
if a.peer_id == stop_n_go_winner_id: return true
|
||||||
|
if b.peer_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
player_data.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
_render_leaderboard_entries(player_data)
|
_render_leaderboard_entries(player_data)
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ var is_match_active: bool = false
|
|||||||
# Score tracking: peer_id -> score
|
# Score tracking: peer_id -> score
|
||||||
var player_scores: Dictionary = {}
|
var player_scores: Dictionary = {}
|
||||||
var player_goal_counts: Dictionary = {} # peer_id -> count
|
var player_goal_counts: Dictionary = {} # peer_id -> count
|
||||||
|
var stop_n_go_winner_id: int = -1 # Track winner for Stop n Go sorting
|
||||||
|
|
||||||
# Reference to main scene
|
# Reference to main scene
|
||||||
var main_scene: Node = null
|
var main_scene: Node = null
|
||||||
@@ -299,8 +300,16 @@ func _update_leaderboard():
|
|||||||
var sorted_scores = []
|
var sorted_scores = []
|
||||||
for peer_id in player_scores.keys():
|
for peer_id in player_scores.keys():
|
||||||
sorted_scores.append({"peer_id": peer_id, "score": player_scores[peer_id]})
|
sorted_scores.append({"peer_id": peer_id, "score": player_scores[peer_id]})
|
||||||
|
# Custom Sort for Stop n Go
|
||||||
|
if stop_n_go_winner_id != -1:
|
||||||
|
sorted_scores.sort_custom(func(a, b):
|
||||||
|
if a.peer_id == stop_n_go_winner_id: return true
|
||||||
|
if b.peer_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sorted_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
sorted_scores.sort_custom(func(a, b): return a.score > b.score)
|
|
||||||
emit_signal("leaderboard_updated", sorted_scores)
|
emit_signal("leaderboard_updated", sorted_scores)
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -432,7 +441,17 @@ func get_leaderboard() -> Array:
|
|||||||
var sorted_scores = []
|
var sorted_scores = []
|
||||||
for peer_id in player_scores.keys():
|
for peer_id in player_scores.keys():
|
||||||
sorted_scores.append({"peer_id": peer_id, "score": player_scores[peer_id]})
|
sorted_scores.append({"peer_id": peer_id, "score": player_scores[peer_id]})
|
||||||
sorted_scores.sort_custom(func(a, b): return a.score > b.score)
|
|
||||||
|
# Custom Sort for Stop n Go
|
||||||
|
if stop_n_go_winner_id != -1:
|
||||||
|
sorted_scores.sort_custom(func(a, b):
|
||||||
|
if a.peer_id == stop_n_go_winner_id: return true
|
||||||
|
if b.peer_id == stop_n_go_winner_id: return false
|
||||||
|
return a.score > b.score
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
sorted_scores.sort_custom(func(a, b): return a.score > b.score)
|
||||||
|
|
||||||
return sorted_scores
|
return sorted_scores
|
||||||
|
|
||||||
func get_time_remaining() -> float:
|
func get_time_remaining() -> float:
|
||||||
|
|||||||
@@ -89,14 +89,24 @@ func handle_unhandled_input(event):
|
|||||||
# --- Keyboard Shortcuts (Event-based) ---
|
# --- Keyboard Shortcuts (Event-based) ---
|
||||||
if event is InputEventKey and event.pressed and not event.echo:
|
if event is InputEventKey and event.pressed and not event.echo:
|
||||||
match event.keycode:
|
match event.keycode:
|
||||||
KEY_KP_1, KEY_1:
|
KEY_KP_1, KEY_1, KEY_KP_2, KEY_2, KEY_KP_3, KEY_3, KEY_KP_4, KEY_4:
|
||||||
player.activate_powerup(0) # FASTER_SPEED
|
var is_sng = LobbyManager.game_mode == "Stop n Go"
|
||||||
KEY_KP_2, KEY_2:
|
match event.keycode:
|
||||||
player.activate_powerup(2) # BLOCK_FLOOR
|
KEY_KP_1, KEY_1:
|
||||||
KEY_KP_3, KEY_3:
|
player.activate_powerup(0) # FASTER_SPEED
|
||||||
player.activate_powerup(1) # AREA_FREEZE
|
KEY_KP_2, KEY_2:
|
||||||
KEY_KP_4, KEY_4:
|
if is_sng:
|
||||||
player.activate_powerup(3) # INVISIBLE_MODE
|
player.activate_powerup(1) # AREA_FREEZE (StopNGo)
|
||||||
|
else:
|
||||||
|
player.activate_powerup(2) # BLOCK_FLOOR (Free)
|
||||||
|
KEY_KP_3, KEY_3:
|
||||||
|
if is_sng:
|
||||||
|
player.activate_powerup(3) # INVISIBLE_MODE (StopNGo)
|
||||||
|
else:
|
||||||
|
player.activate_powerup(1) # AREA_FREEZE (Free)
|
||||||
|
KEY_KP_4, KEY_4:
|
||||||
|
if not is_sng:
|
||||||
|
player.activate_powerup(3) # INVISIBLE_MODE (Free)
|
||||||
# KEY_R:
|
# KEY_R:
|
||||||
# player.auto_put_item()
|
# player.auto_put_item()
|
||||||
KEY_Q:
|
KEY_Q:
|
||||||
|
|||||||
@@ -461,8 +461,11 @@ func spawn_powerups_around(center: Vector2i, force_powerups: bool = true):
|
|||||||
if rng.randf() < 0.7:
|
if rng.randf() < 0.7:
|
||||||
item_id = rng.randi_range(7, 10)
|
item_id = rng.randi_range(7, 10)
|
||||||
else:
|
else:
|
||||||
# 30% Chance for PowerUp (11-14)
|
# 30% Chance for PowerUp (Exclude Wall 13 only in Stop n Go)
|
||||||
item_id = rng.randi_range(11, 14)
|
if LobbyManager.game_mode == "Stop n Go":
|
||||||
|
item_id = [11, 12, 14].pick_random()
|
||||||
|
else:
|
||||||
|
item_id = rng.randi_range(11, 14)
|
||||||
|
|
||||||
var cell = Vector3i(pos.x, 1, pos.y)
|
var cell = Vector3i(pos.x, 1, pos.y)
|
||||||
|
|
||||||
|
|||||||
@@ -245,11 +245,13 @@ func _ensure_shortcut_label(btn: Button, button_name: String):
|
|||||||
if btn.has_node("ShortcutLabel"):
|
if btn.has_node("ShortcutLabel"):
|
||||||
# Update Label content if it exists to match potential remapping
|
# Update Label content if it exists to match potential remapping
|
||||||
var existing_lbl = btn.get_node("ShortcutLabel")
|
var existing_lbl = btn.get_node("ShortcutLabel")
|
||||||
|
var is_sng = LobbyManager.game_mode == "Stop n Go"
|
||||||
|
|
||||||
match button_name:
|
match button_name:
|
||||||
"Grab": existing_lbl.text = "Space"
|
"Grab": existing_lbl.text = "Space" if is_sng else ""
|
||||||
"Put": existing_lbl.text = ""
|
"Put": existing_lbl.text = ""
|
||||||
"AttackMode": existing_lbl.text = "Q"
|
"AttackMode": existing_lbl.text = "Q" if is_sng else ""
|
||||||
"SpawnBoost": existing_lbl.text = "E"
|
"SpawnBoost": existing_lbl.text = "E" if is_sng else ""
|
||||||
return
|
return
|
||||||
|
|
||||||
# Add Keyboard Shortcut Label
|
# Add Keyboard Shortcut Label
|
||||||
|
|||||||
@@ -50,7 +50,10 @@ static func get_tile_weights() -> Dictionary:
|
|||||||
weights[tile] = STANDARD_WEIGHT
|
weights[tile] = STANDARD_WEIGHT
|
||||||
|
|
||||||
# Special tiles
|
# Special tiles
|
||||||
|
var is_sng = LobbyManager.game_mode == "Stop n Go"
|
||||||
for tile in SPECIAL_TILES:
|
for tile in SPECIAL_TILES:
|
||||||
|
if is_sng and tile == TILE_FREEZE:
|
||||||
|
continue # Hide Wall Block only in Stop n Go
|
||||||
weights[tile] = special_weight
|
weights[tile] = special_weight
|
||||||
|
|
||||||
return weights
|
return weights
|
||||||
|
|||||||
@@ -29,7 +29,10 @@ func _ready():
|
|||||||
# We use 0, 1, 2, 3 to match SpecialTilesManager.SpecialEffect enum
|
# We use 0, 1, 2, 3 to match SpecialTilesManager.SpecialEffect enum
|
||||||
_setup_btn(0, container.get_node_or_null("SpeedBtn"))
|
_setup_btn(0, container.get_node_or_null("SpeedBtn"))
|
||||||
_setup_btn(1, container.get_node_or_null("FreezeAreaBtn"))
|
_setup_btn(1, container.get_node_or_null("FreezeAreaBtn"))
|
||||||
_setup_btn(2, container.get_node_or_null("WallBtn"))
|
var wall_btn = container.get_node_or_null("WallBtn")
|
||||||
|
_setup_btn(2, wall_btn)
|
||||||
|
if wall_btn and LobbyManager.game_mode == "Stop n Go":
|
||||||
|
wall_btn.visible = false # Hide Wall Power-up only in Stop n Go
|
||||||
_setup_btn(3, container.get_node_or_null("GhostBtn"))
|
_setup_btn(3, container.get_node_or_null("GhostBtn"))
|
||||||
|
|
||||||
print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size())
|
print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size())
|
||||||
@@ -103,16 +106,20 @@ func _setup_btn(effect_id: int, btn: Button):
|
|||||||
sc_lbl.add_theme_color_override("font_color", Color(0.9, 0.9, 0.9))
|
sc_lbl.add_theme_color_override("font_color", Color(0.9, 0.9, 0.9))
|
||||||
|
|
||||||
# Determine Label Text based on Effect ID
|
# Determine Label Text based on Effect ID
|
||||||
# 0: Speed -> 1
|
|
||||||
# 2: Wall -> 2
|
|
||||||
# 1: Freeze -> 3
|
|
||||||
# 3: Ghost -> 4
|
|
||||||
var key_text = ""
|
var key_text = ""
|
||||||
match effect_id:
|
if LobbyManager.game_mode == "Stop n Go":
|
||||||
0: key_text = "1"
|
# Stop n Go Mapping: 1, 2, 3 (No Wall)
|
||||||
2: key_text = "2"
|
match effect_id:
|
||||||
1: key_text = "3"
|
0: key_text = "1"
|
||||||
3: key_text = "4"
|
1: key_text = "2"
|
||||||
|
3: key_text = "3"
|
||||||
|
else:
|
||||||
|
# Free Mode Mapping: 1, 2, 3, 4 (Original)
|
||||||
|
match effect_id:
|
||||||
|
0: key_text = "1"
|
||||||
|
2: key_text = "2"
|
||||||
|
1: key_text = "3"
|
||||||
|
3: key_text = "4"
|
||||||
|
|
||||||
sc_lbl.text = key_text
|
sc_lbl.text = key_text
|
||||||
btn.add_child(sc_lbl)
|
btn.add_child(sc_lbl)
|
||||||
|
|||||||
Reference in New Issue
Block a user