refactor
This commit is contained in:
Vendored
+1
-1
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"godotTools.editorPath.godot4": "/home/beng/Documents/GodotProjects/Godot_v4.5.1-stable_linux.x86_64"
|
"godotTools.editorPath.godot4": "c:\\Users\\beng\\Godot\\Editors\\4.5.1-stable\\Godot_v4.5.1-stable_win64.exe"
|
||||||
}
|
}
|
||||||
@@ -212,20 +212,20 @@ func fill_floor(item_index: int, floor_index: int):
|
|||||||
set_cell_item(cell_pos, item_index, current_orientation)
|
set_cell_item(cell_pos, item_index, current_orientation)
|
||||||
|
|
||||||
# Randomization functions
|
# Randomization functions
|
||||||
func randomize_grid(floor_index: int = -1):
|
func randomize_grid(floor_index: int = -1, custom_rng_callable: Callable = Callable()):
|
||||||
if floor_index == -1:
|
if floor_index == -1:
|
||||||
# Default auto-randomize behavior:
|
# Default auto-randomize behavior:
|
||||||
# Preserve Floor 0 (Ground)
|
# Preserve Floor 0 (Ground)
|
||||||
# Shuffle Floor 1 (Objects/Items)
|
# Shuffle Floor 1 (Objects/Items)
|
||||||
shuffle_floor_objects(1)
|
shuffle_floor_objects(1)
|
||||||
else:
|
else:
|
||||||
randomize_floor(floor_index)
|
randomize_floor(floor_index, custom_rng_callable)
|
||||||
|
|
||||||
update_grid_data()
|
update_grid_data()
|
||||||
initialize_astar()
|
initialize_astar()
|
||||||
update_astar_costs()
|
update_astar_costs()
|
||||||
|
|
||||||
func randomize_floor(floor_index: int):
|
func randomize_floor(floor_index: int, custom_rng_callable: Callable = Callable()):
|
||||||
if not mesh_library:
|
if not mesh_library:
|
||||||
print("Error: No MeshLibrary assigned to GridMap")
|
print("Error: No MeshLibrary assigned to GridMap")
|
||||||
return
|
return
|
||||||
@@ -235,10 +235,6 @@ func randomize_floor(floor_index: int):
|
|||||||
var rng = RandomNumberGenerator.new()
|
var rng = RandomNumberGenerator.new()
|
||||||
rng.randomize()
|
rng.randomize()
|
||||||
|
|
||||||
var scarcity_mgr = null
|
|
||||||
if floor_index == 1:
|
|
||||||
scarcity_mgr = load("res://scripts/managers/scarcity_manager.gd")
|
|
||||||
|
|
||||||
for x in range(columns):
|
for x in range(columns):
|
||||||
for z in range(rows):
|
for z in range(rows):
|
||||||
# IMPORTANT: Only place items if Floor 0 has a walkable tile
|
# IMPORTANT: Only place items if Floor 0 has a walkable tile
|
||||||
@@ -249,9 +245,9 @@ func randomize_floor(floor_index: int):
|
|||||||
set_cell_item(Vector3i(x, floor_index, z), -1) # Clear item if no ground
|
set_cell_item(Vector3i(x, floor_index, z), -1) # Clear item if no ground
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If Floor 1 and ScarcityManager exists, use it
|
# Use custom callable if provided
|
||||||
if floor_index == 1 and scarcity_mgr:
|
if custom_rng_callable.is_valid():
|
||||||
var item_index = scarcity_mgr.get_random_tile_id()
|
var item_index = custom_rng_callable.call()
|
||||||
set_cell_item(Vector3i(x, floor_index, z), item_index)
|
set_cell_item(Vector3i(x, floor_index, z), item_index)
|
||||||
else:
|
else:
|
||||||
# Default behavior
|
# Default behavior
|
||||||
@@ -505,7 +501,7 @@ func initialize_astar():
|
|||||||
|
|
||||||
update_astar_costs()
|
update_astar_costs()
|
||||||
|
|
||||||
func find_path(start: Vector2, end: Vector2, floor_index: int = 0, clear_path_visual: bool = true) -> Array:
|
func find_path(start: Vector2, end: Vector2, floor_index: int = 0, clear_path_visual: bool = true, visualize: bool = true) -> Array:
|
||||||
var astar = astar_by_floor.get(floor_index)
|
var astar = astar_by_floor.get(floor_index)
|
||||||
if not astar:
|
if not astar:
|
||||||
return []
|
return []
|
||||||
@@ -514,14 +510,15 @@ func find_path(start: Vector2, end: Vector2, floor_index: int = 0, clear_path_vi
|
|||||||
var end_point = end.y * columns + end.x
|
var end_point = end.y * columns + end.x
|
||||||
path = astar.get_point_path(start_point, end_point)
|
path = astar.get_point_path(start_point, end_point)
|
||||||
|
|
||||||
if clear_path_visual:
|
if visualize:
|
||||||
clear_path_visualization(floor_index)
|
if clear_path_visual:
|
||||||
|
clear_path_visualization(floor_index)
|
||||||
|
|
||||||
set_cell_item(Vector3i(start.x, floor_index, start.y), start_item)
|
set_cell_item(Vector3i(start.x, floor_index, start.y), start_item)
|
||||||
set_cell_item(Vector3i(end.x, floor_index, end.y), end_item)
|
set_cell_item(Vector3i(end.x, floor_index, end.y), end_item)
|
||||||
for point in path:
|
for point in path:
|
||||||
if point != start and point != end:
|
if point != start and point != end:
|
||||||
set_cell_item(Vector3i(point.x, floor_index, point.y), hover_item)
|
set_cell_item(Vector3i(point.x, floor_index, point.y), hover_item)
|
||||||
|
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,10 @@ var admin_panel_instance: Control
|
|||||||
# Store current match ID for copy function
|
# Store current match ID for copy function
|
||||||
var current_match_id: String = ""
|
var current_match_id: String = ""
|
||||||
|
|
||||||
|
# Scarcity Controls (Injected via code since we can't edit scene)
|
||||||
|
var scarcity_option: OptionButton
|
||||||
|
var scarcity_label: Label
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
# Check if user is authenticated
|
# Check if user is authenticated
|
||||||
if not AuthManager.is_logged_in():
|
if not AuthManager.is_logged_in():
|
||||||
@@ -73,6 +77,9 @@ func _ready():
|
|||||||
# Load character textures
|
# Load character textures
|
||||||
_load_character_textures()
|
_load_character_textures()
|
||||||
|
|
||||||
|
# Inject Scarcity UI
|
||||||
|
call_deferred("_setup_scarcity_ui")
|
||||||
|
|
||||||
# Get player slot references
|
# Get player slot references
|
||||||
_setup_player_slots()
|
_setup_player_slots()
|
||||||
|
|
||||||
@@ -135,6 +142,7 @@ func _ready():
|
|||||||
LobbyManager.enable_cycle_timer_changed.connect(_on_enable_cycle_timer_changed)
|
LobbyManager.enable_cycle_timer_changed.connect(_on_enable_cycle_timer_changed)
|
||||||
LobbyManager.character_changed.connect(_on_character_changed)
|
LobbyManager.character_changed.connect(_on_character_changed)
|
||||||
LobbyManager.area_changed.connect(_on_area_changed)
|
LobbyManager.area_changed.connect(_on_area_changed)
|
||||||
|
LobbyManager.scarcity_mode_changed.connect(_on_scarcity_mode_changed)
|
||||||
LobbyManager.player_list_changed.connect(_update_player_slots)
|
LobbyManager.player_list_changed.connect(_update_player_slots)
|
||||||
|
|
||||||
# Connect NakamaManager signals
|
# Connect NakamaManager signals
|
||||||
@@ -158,6 +166,35 @@ func _load_character_textures() -> void:
|
|||||||
else:
|
else:
|
||||||
print("[Lobby] Character texture not found: ", tex_path)
|
print("[Lobby] Character texture not found: ", tex_path)
|
||||||
|
|
||||||
|
func _setup_scarcity_ui() -> void:
|
||||||
|
"""Inject scarcity controls into SettingsSection."""
|
||||||
|
if not duration_option: return
|
||||||
|
|
||||||
|
var settings_section = duration_option.get_parent()
|
||||||
|
if not settings_section: return
|
||||||
|
|
||||||
|
# Create OptionButton for Host
|
||||||
|
scarcity_option = OptionButton.new()
|
||||||
|
scarcity_option.add_item("Normal") # ID 0
|
||||||
|
scarcity_option.add_item("Aggressive") # ID 1
|
||||||
|
scarcity_option.add_item("Chaos") # ID 2
|
||||||
|
scarcity_option.selected = 0
|
||||||
|
scarcity_option.tooltip_text = "Select Scarcity Mode"
|
||||||
|
scarcity_option.name = "ScarcityOption"
|
||||||
|
scarcity_option.item_selected.connect(_on_scarcity_selected)
|
||||||
|
|
||||||
|
# Create Label for Clients
|
||||||
|
scarcity_label = Label.new()
|
||||||
|
scarcity_label.text = "Normal"
|
||||||
|
scarcity_label.name = "ScarcityLabel"
|
||||||
|
# Copy style from duration_text_label if possible, usually fine default
|
||||||
|
|
||||||
|
# Add to scene
|
||||||
|
settings_section.add_child(scarcity_option)
|
||||||
|
settings_section.add_child(scarcity_label)
|
||||||
|
|
||||||
|
# Initial visibility update will handle showing correct one
|
||||||
|
|
||||||
func _setup_player_slots() -> void:
|
func _setup_player_slots() -> void:
|
||||||
"""Get references to all player slot nodes."""
|
"""Get references to all player slot nodes."""
|
||||||
player_slots.clear()
|
player_slots.clear()
|
||||||
@@ -279,6 +316,21 @@ func _on_random_spawn_toggled(toggled_on):
|
|||||||
func _on_enable_timer_toggled(toggled_on):
|
func _on_enable_timer_toggled(toggled_on):
|
||||||
LobbyManager.set_enable_cycle_timer(toggled_on)
|
LobbyManager.set_enable_cycle_timer(toggled_on)
|
||||||
|
|
||||||
|
func _on_scarcity_selected(index: int) -> void:
|
||||||
|
if not LobbyManager.is_host: return
|
||||||
|
var mode = scarcity_option.get_item_text(index)
|
||||||
|
LobbyManager.set_scarcity_mode(mode)
|
||||||
|
|
||||||
|
func _on_scarcity_mode_changed(mode: String) -> void:
|
||||||
|
if scarcity_option:
|
||||||
|
for i in range(scarcity_option.item_count):
|
||||||
|
if scarcity_option.get_item_text(i) == mode:
|
||||||
|
scarcity_option.selected = i
|
||||||
|
break
|
||||||
|
if scarcity_label:
|
||||||
|
scarcity_label.text = mode
|
||||||
|
|
||||||
|
|
||||||
func _update_random_spawn_label(enabled: bool) -> void:
|
func _update_random_spawn_label(enabled: bool) -> void:
|
||||||
if random_spawn_label:
|
if random_spawn_label:
|
||||||
random_spawn_label.text = "Random ✓" if enabled else "Random ✗"
|
random_spawn_label.text = "Random ✓" if enabled else "Random ✗"
|
||||||
@@ -345,6 +397,11 @@ func _on_room_joined(room_data: Dictionary) -> void:
|
|||||||
_on_randomize_spawn_changed(LobbyManager.randomize_spawn)
|
_on_randomize_spawn_changed(LobbyManager.randomize_spawn)
|
||||||
_on_enable_cycle_timer_changed(LobbyManager.enable_cycle_timer)
|
_on_enable_cycle_timer_changed(LobbyManager.enable_cycle_timer)
|
||||||
|
|
||||||
|
# Scarcity Update
|
||||||
|
if scarcity_option: scarcity_option.visible = is_host
|
||||||
|
if scarcity_label: scarcity_label.visible = not is_host
|
||||||
|
_on_scarcity_mode_changed(LobbyManager.scarcity_mode)
|
||||||
|
|
||||||
# Area selector: only host can interact
|
# Area selector: only host can interact
|
||||||
area_left_btn.disabled = not is_host
|
area_left_btn.disabled = not is_host
|
||||||
area_right_btn.disabled = not is_host
|
area_right_btn.disabled = not is_host
|
||||||
|
|||||||
+92
-17
@@ -39,6 +39,61 @@ func _ready():
|
|||||||
await get_tree().process_frame
|
await get_tree().process_frame
|
||||||
_auto_start_from_lobby()
|
_auto_start_from_lobby()
|
||||||
|
|
||||||
|
# Ensure grid is randomized with Scarcity if server
|
||||||
|
if multiplayer.is_server():
|
||||||
|
randomize_game_grid()
|
||||||
|
_setup_tile_respawn_timer()
|
||||||
|
|
||||||
|
func _setup_tile_respawn_timer():
|
||||||
|
# Configure respawn rate based on Scarcity Mode from Lobby
|
||||||
|
var mode = LobbyManager.get_scarcity_mode()
|
||||||
|
var interval = 0.0
|
||||||
|
|
||||||
|
match mode:
|
||||||
|
"Aggressive": interval = 5.0
|
||||||
|
"Chaos": interval = 1.0 # Very fast!
|
||||||
|
"Normal": interval = 0.0 # No periodic respawn (only refill when empty)
|
||||||
|
_: interval = 0.0
|
||||||
|
|
||||||
|
if interval > 0:
|
||||||
|
var timer = Timer.new()
|
||||||
|
timer.name = "TileRespawnTimer"
|
||||||
|
timer.wait_time = interval
|
||||||
|
timer.autostart = true
|
||||||
|
timer.one_shot = false
|
||||||
|
timer.timeout.connect(_on_tile_respawn_timeout)
|
||||||
|
add_child(timer)
|
||||||
|
print("[Main] TileRespawnTimer started with interval: %.1f (Mode: %s)" % [interval, mode])
|
||||||
|
|
||||||
|
func _on_tile_respawn_timeout():
|
||||||
|
if not multiplayer.is_server(): return
|
||||||
|
|
||||||
|
var enhanced_gridmap = $EnhancedGridMap
|
||||||
|
if not enhanced_gridmap: return
|
||||||
|
|
||||||
|
# Try to find an empty spot (max 10 retries)
|
||||||
|
for i in range(10):
|
||||||
|
var x = randi() % enhanced_gridmap.columns
|
||||||
|
var z = randi() % enhanced_gridmap.rows
|
||||||
|
var cell = Vector3i(x, 1, z)
|
||||||
|
|
||||||
|
if enhanced_gridmap.get_cell_item(cell) == -1:
|
||||||
|
# Check floor 0
|
||||||
|
if enhanced_gridmap.get_cell_item(Vector3i(x, 0, z)) != -1:
|
||||||
|
# Check player occupancy
|
||||||
|
var occupied = false
|
||||||
|
for player in get_tree().get_nodes_in_group("Players"):
|
||||||
|
if Vector2i(player.current_position.x, player.current_position.y) == Vector2i(x, z):
|
||||||
|
occupied = true
|
||||||
|
break
|
||||||
|
|
||||||
|
if not occupied:
|
||||||
|
# Spawn!
|
||||||
|
var new_item = ScarcityController.get_random_tile_id()
|
||||||
|
sync_grid_item(x, 1, z, new_item)
|
||||||
|
rpc("sync_grid_item", x, 1, z, new_item)
|
||||||
|
return # Spawned one, done for this tick
|
||||||
|
|
||||||
func _init_managers():
|
func _init_managers():
|
||||||
# Create and attach scene managers
|
# Create and attach scene managers
|
||||||
ui_manager = load("res://scripts/managers/ui_manager.gd").new()
|
ui_manager = load("res://scripts/managers/ui_manager.gd").new()
|
||||||
@@ -148,10 +203,11 @@ func add_message_to_bar(player_name: String, message: String, type: int = Messag
|
|||||||
# Powerup gets extra pulse effect
|
# Powerup gets extra pulse effect
|
||||||
if type == MessageType.POWERUP:
|
if type == MessageType.POWERUP:
|
||||||
await entrance_tween.finished
|
await entrance_tween.finished
|
||||||
var pulse_tween = create_tween()
|
if is_instance_valid(label):
|
||||||
pulse_tween.set_loops(2)
|
var pulse_tween = create_tween()
|
||||||
pulse_tween.tween_property(label, "scale", Vector2(1.1, 1.1), 0.15).set_trans(Tween.TRANS_SINE)
|
pulse_tween.set_loops(2)
|
||||||
pulse_tween.tween_property(label, "scale", Vector2(1.0, 1.0), 0.15).set_trans(Tween.TRANS_SINE)
|
pulse_tween.tween_property(label, "scale", Vector2(1.1, 1.1), 0.15).set_trans(Tween.TRANS_SINE)
|
||||||
|
pulse_tween.tween_property(label, "scale", Vector2(1.0, 1.0), 0.15).set_trans(Tween.TRANS_SINE)
|
||||||
|
|
||||||
# Remove oldest messages if over limit
|
# Remove oldest messages if over limit
|
||||||
while message_container.get_child_count() > MAX_MESSAGES:
|
while message_container.get_child_count() > MAX_MESSAGES:
|
||||||
@@ -913,24 +969,28 @@ func randomize_item_at_position(grid_position: Vector2i):
|
|||||||
var current_item = enhanced_gridmap.get_cell_item(cell)
|
var current_item = enhanced_gridmap.get_cell_item(cell)
|
||||||
|
|
||||||
# If current item exists, replace it (scarcity aware)
|
# If current item exists, replace it (scarcity aware)
|
||||||
if current_item != -1:
|
# If current item exists OR we are forcing a spawn on valid ground
|
||||||
var scarcity_mgr = load("res://scripts/managers/scarcity_manager.gd")
|
var floor_0_item = enhanced_gridmap.get_cell_item(Vector3i(grid_position.x, 0, grid_position.y))
|
||||||
|
var is_ground = (floor_0_item != -1) # Simple check, or check specific ground items
|
||||||
|
|
||||||
|
# Prevent stacking on players
|
||||||
|
if is_ground:
|
||||||
|
for player in get_tree().get_nodes_in_group("Players"):
|
||||||
|
if Vector2i(player.current_position.x, player.current_position.y) == grid_position:
|
||||||
|
is_ground = false
|
||||||
|
break
|
||||||
|
|
||||||
|
if is_ground:
|
||||||
var new_item = 7
|
var new_item = 7
|
||||||
|
# Use ScarcityController
|
||||||
|
new_item = ScarcityController.get_random_tile_id()
|
||||||
|
|
||||||
if scarcity_mgr:
|
# If we are replacing an existing item, try to ensure it changes
|
||||||
new_item = scarcity_mgr.get_random_tile_id()
|
if current_item != -1:
|
||||||
# Try to ensure it changes, but respect scarcity
|
|
||||||
var max_retries = 3
|
var max_retries = 3
|
||||||
while new_item == current_item and max_retries > 0:
|
while new_item == current_item and max_retries > 0:
|
||||||
new_item = scarcity_mgr.get_random_tile_id()
|
new_item = ScarcityController.get_random_tile_id()
|
||||||
max_retries -= 1
|
max_retries -= 1
|
||||||
else:
|
|
||||||
# Fallback
|
|
||||||
var rng = RandomNumberGenerator.new()
|
|
||||||
rng.randomize()
|
|
||||||
new_item = rng.randi_range(7, 10)
|
|
||||||
while new_item == current_item:
|
|
||||||
new_item = rng.randi_range(7, 10)
|
|
||||||
|
|
||||||
sync_grid_item(cell.x, cell.y, cell.z, new_item)
|
sync_grid_item(cell.x, cell.y, cell.z, new_item)
|
||||||
rpc("sync_grid_item", cell.x, cell.y, cell.z, new_item)
|
rpc("sync_grid_item", cell.x, cell.y, cell.z, new_item)
|
||||||
@@ -946,6 +1006,21 @@ func sync_grid_item(x: int, y: int, z: int, item: int):
|
|||||||
if enhanced_gridmap:
|
if enhanced_gridmap:
|
||||||
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
enhanced_gridmap.set_cell_item(Vector3i(x, y, z), item)
|
||||||
|
|
||||||
|
# Sync grid update (no need to sync whole grid if we do it at start, but if we do it late we might need to sync)
|
||||||
|
# For simplicity, we trust the grid syncs via normal mechanisms or initial state.
|
||||||
|
|
||||||
|
func randomize_game_grid():
|
||||||
|
var enhanced_gridmap = $EnhancedGridMap
|
||||||
|
if enhanced_gridmap:
|
||||||
|
# Randomize Floor 1 using ScarcityController
|
||||||
|
enhanced_gridmap.randomize_floor(1, ScarcityController.get_random_tile_id)
|
||||||
|
|
||||||
|
# Sync to clients if needed (usually handled by initial state sync or explicit item syncs)
|
||||||
|
# Since Main.gd doesn't have a "Sync Floor" RPC, we rely on clients running the same seed or syncing individual cells.
|
||||||
|
# For now, let's assume server authority + sync on connect handles it, or add sync loop if critical.
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Goals Cycle & Leaderboard UI
|
# Goals Cycle & Leaderboard UI
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
|
|||||||
@@ -307,7 +307,8 @@ func _try_move() -> bool:
|
|||||||
Vector2(actor.current_position),
|
Vector2(actor.current_position),
|
||||||
Vector2(final_target),
|
Vector2(final_target),
|
||||||
0,
|
0,
|
||||||
false
|
false,
|
||||||
|
false # Disable visualization
|
||||||
)
|
)
|
||||||
|
|
||||||
var next_step = Vector2i(-1, -1)
|
var next_step = Vector2i(-1, -1)
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
class_name ScarcityController
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
# ScarcityController - Logic for tile generation based on ScarcityModel
|
||||||
|
|
||||||
|
static func get_random_tile_id() -> int:
|
||||||
|
var rng = RandomNumberGenerator.new()
|
||||||
|
rng.randomize()
|
||||||
|
|
||||||
|
var weights = ScarcityModel.get_tile_weights()
|
||||||
|
var total_weight = 0
|
||||||
|
|
||||||
|
# Calculate total weight
|
||||||
|
for tile in weights:
|
||||||
|
total_weight += weights[tile]
|
||||||
|
|
||||||
|
var roll = rng.randi_range(0, total_weight - 1)
|
||||||
|
var current = 0
|
||||||
|
|
||||||
|
for tile in weights:
|
||||||
|
current += weights[tile]
|
||||||
|
if roll < current:
|
||||||
|
return tile
|
||||||
|
|
||||||
|
return ScarcityModel.TILE_HEART # Fallback
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://4ocyvmq6fbmx
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://cray2jslf200d
|
||||||
@@ -34,6 +34,10 @@ var randomize_spawn: bool = true # Default enabled
|
|||||||
var enable_cycle_timer: bool = false # Default disabled
|
var enable_cycle_timer: bool = false # Default disabled
|
||||||
signal enable_cycle_timer_changed(enabled: bool)
|
signal enable_cycle_timer_changed(enabled: bool)
|
||||||
|
|
||||||
|
# Scarcity setting
|
||||||
|
var scarcity_mode: String = "Normal" # Normal, Aggressive, Chaos
|
||||||
|
signal scarcity_mode_changed(mode: String)
|
||||||
|
|
||||||
# Character and area selection
|
# Character and area selection
|
||||||
var available_characters: Array[String] = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
var available_characters: Array[String] = ["Bob", "Gatot", "Masbro", "Oldpop"]
|
||||||
var available_areas: Array[String] = ["Desert", "Forest", "City", "Factory"]
|
var available_areas: Array[String] = ["Desert", "Forest", "City", "Factory"]
|
||||||
@@ -203,6 +207,26 @@ func sync_enable_cycle_timer(enabled: bool) -> void:
|
|||||||
func get_enable_cycle_timer() -> bool:
|
func get_enable_cycle_timer() -> bool:
|
||||||
return enable_cycle_timer
|
return enable_cycle_timer
|
||||||
|
|
||||||
|
# =============================================================================
|
||||||
|
# Scarcity Mode
|
||||||
|
# =============================================================================
|
||||||
|
|
||||||
|
func set_scarcity_mode(mode: String) -> void:
|
||||||
|
"""Host sets scarcity mode. Syncs to clients."""
|
||||||
|
scarcity_mode = mode
|
||||||
|
if is_host:
|
||||||
|
rpc("sync_scarcity_mode", mode)
|
||||||
|
|
||||||
|
@rpc("authority", "call_local", "reliable")
|
||||||
|
func sync_scarcity_mode(mode: String) -> void:
|
||||||
|
scarcity_mode = mode
|
||||||
|
# Update ScarcityModel immediately
|
||||||
|
ScarcityModel.set_mode(mode)
|
||||||
|
emit_signal("scarcity_mode_changed", mode)
|
||||||
|
|
||||||
|
func get_scarcity_mode() -> String:
|
||||||
|
return scarcity_mode
|
||||||
|
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
# Character Selection
|
# Character Selection
|
||||||
# =============================================================================
|
# =============================================================================
|
||||||
@@ -297,6 +321,8 @@ func start_game() -> void:
|
|||||||
rpc("sync_match_duration", match_duration)
|
rpc("sync_match_duration", match_duration)
|
||||||
# Sync timer setting
|
# Sync timer setting
|
||||||
rpc("sync_enable_cycle_timer", enable_cycle_timer)
|
rpc("sync_enable_cycle_timer", enable_cycle_timer)
|
||||||
|
# Sync scarcity mode
|
||||||
|
rpc("sync_scarcity_mode", scarcity_mode)
|
||||||
|
|
||||||
# Notify all clients to start
|
# Notify all clients to start
|
||||||
rpc("_on_game_starting")
|
rpc("_on_game_starting")
|
||||||
|
|||||||
@@ -170,22 +170,18 @@ func _check_and_refill_grid_if_needed(server_gridmap: Node):
|
|||||||
var has_items = false
|
var has_items = false
|
||||||
var item_list = server_gridmap.mesh_library.get_item_list() if server_gridmap.mesh_library else []
|
var item_list = server_gridmap.mesh_library.get_item_list() if server_gridmap.mesh_library else []
|
||||||
|
|
||||||
# We can't efficiently iterate all cells, but we can check usage via get_used_cells_by_item is too specific
|
# Iterate used cells to check for ANY item on floor 1
|
||||||
# Iterating columns/rows is safe for this map size (10x10 usually)
|
var used_cells = server_gridmap.get_used_cells()
|
||||||
for x in range(server_gridmap.columns):
|
for cell in used_cells:
|
||||||
for z in range(server_gridmap.rows):
|
if cell.y == 1: # Floor 1
|
||||||
var item = server_gridmap.get_cell_item(Vector3i(x, 1, z))
|
has_items = true
|
||||||
if item != -1:
|
|
||||||
has_items = true
|
|
||||||
break
|
|
||||||
if has_items:
|
|
||||||
break
|
break
|
||||||
|
|
||||||
if not has_items:
|
if not has_items:
|
||||||
print("[PlayerboardManager] Floor 1 empty! Respawning tiles with Scarcity...")
|
print("[PlayerboardManager] Floor 1 empty! Respawning tiles with Scarcity...")
|
||||||
# Call randomize_floor on floor 1
|
# Call randomize_floor on floor 1 using ScarcityController
|
||||||
# Since Main owns gridmap, we can sync this or just do it server side (which we are)
|
# ScarcityController is a global class, so we can pass its static function as a Callable
|
||||||
server_gridmap.randomize_floor(1)
|
server_gridmap.randomize_floor(1, ScarcityController.get_random_tile_id)
|
||||||
|
|
||||||
# We need to sync the ENTIRE floor to clients.
|
# We need to sync the ENTIRE floor to clients.
|
||||||
# EnhancedGridMap doesn't have a "Sync Floor" RPC built-in to Main, only single cells or array update.
|
# EnhancedGridMap doesn't have a "Sync Floor" RPC built-in to Main, only single cells or array update.
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
extends Node
|
|
||||||
class_name ScarcityManager
|
|
||||||
|
|
||||||
# ScarcityManager - Handles weighted random, tile generation for gameplay balance
|
|
||||||
|
|
||||||
const TILES = [7, 8, 9, 10]
|
|
||||||
const SPECIAL_TILES = [11, 12, 13, 14]
|
|
||||||
|
|
||||||
# Weights (higher = more common)
|
|
||||||
# Standard tiles: Heart, Diamond, Star, Coin
|
|
||||||
const TILE_WEIGHTS = {
|
|
||||||
7: 100, # Heart
|
|
||||||
8: 100, # Diamond
|
|
||||||
9: 100, # Star
|
|
||||||
10: 100 # Coin
|
|
||||||
}
|
|
||||||
|
|
||||||
# Special tiles: Burn, Spawn, Freeze, Block, Invisible
|
|
||||||
const SPECIAL_WEIGHTS = {
|
|
||||||
11: 5,
|
|
||||||
12: 5,
|
|
||||||
13: 5,
|
|
||||||
14: 5
|
|
||||||
}
|
|
||||||
|
|
||||||
static func get_random_tile_id() -> int:
|
|
||||||
var rng = RandomNumberGenerator.new()
|
|
||||||
rng.randomize()
|
|
||||||
|
|
||||||
var total_weight = 0
|
|
||||||
var pool = {}
|
|
||||||
|
|
||||||
# Add standard tiles
|
|
||||||
for id in TILE_WEIGHTS:
|
|
||||||
pool[id] = TILE_WEIGHTS[id]
|
|
||||||
total_weight += TILE_WEIGHTS[id]
|
|
||||||
|
|
||||||
# Add special tiles
|
|
||||||
for id in SPECIAL_WEIGHTS:
|
|
||||||
pool[id] = SPECIAL_WEIGHTS[id]
|
|
||||||
total_weight += SPECIAL_WEIGHTS[id]
|
|
||||||
|
|
||||||
var roll = rng.randi_range(0, total_weight - 1)
|
|
||||||
var current = 0
|
|
||||||
|
|
||||||
for id in pool:
|
|
||||||
current += pool[id]
|
|
||||||
if roll < current:
|
|
||||||
return id
|
|
||||||
|
|
||||||
return 7 # Fallback to Heart
|
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
class_name ScarcityModel
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
# ScarcityModel - Data definitions for tile scarcity
|
||||||
|
|
||||||
|
# Standard tiles
|
||||||
|
const TILE_HEART = 7
|
||||||
|
const TILE_DIAMOND = 8
|
||||||
|
const TILE_STAR = 9
|
||||||
|
const TILE_COIN = 10
|
||||||
|
|
||||||
|
# Special tiles (Holo)
|
||||||
|
const TILE_BURN = 11
|
||||||
|
const TILE_SPAWN = 12
|
||||||
|
const TILE_FREEZE = 13
|
||||||
|
const TILE_BLOCK = 14
|
||||||
|
|
||||||
|
const STANDARD_TILES = [
|
||||||
|
TILE_HEART, TILE_DIAMOND, TILE_STAR, TILE_COIN
|
||||||
|
]
|
||||||
|
|
||||||
|
const SPECIAL_TILES = [
|
||||||
|
TILE_BURN, TILE_SPAWN, TILE_FREEZE, TILE_BLOCK
|
||||||
|
]
|
||||||
|
|
||||||
|
# Weights (higher = more common)
|
||||||
|
const STANDARD_WEIGHT = 100
|
||||||
|
const SPECIAL_WEIGHT = 5
|
||||||
|
|
||||||
|
# Configuration
|
||||||
|
static var current_mode: String = "Normal"
|
||||||
|
|
||||||
|
# Mode Weights (Special Tile Weight)
|
||||||
|
const WEIGHTS = {
|
||||||
|
"Normal": 5, # 5% chance relative to 100
|
||||||
|
"Aggressive": 25, # 25% chance
|
||||||
|
"Chaos": 50 # 50% chance
|
||||||
|
}
|
||||||
|
|
||||||
|
static func set_mode(mode: String):
|
||||||
|
if mode in WEIGHTS:
|
||||||
|
current_mode = mode
|
||||||
|
|
||||||
|
static func get_tile_weights() -> Dictionary:
|
||||||
|
var weights = {}
|
||||||
|
var special_weight = WEIGHTS.get(current_mode, 5)
|
||||||
|
|
||||||
|
# Standard tiles
|
||||||
|
for tile in STANDARD_TILES:
|
||||||
|
weights[tile] = STANDARD_WEIGHT
|
||||||
|
|
||||||
|
# Special tiles
|
||||||
|
for tile in SPECIAL_TILES:
|
||||||
|
weights[tile] = special_weight
|
||||||
|
|
||||||
|
return weights
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://dh2t1v7y0e61q
|
||||||
Reference in New Issue
Block a user