From 0fb1397d11b4987b8c485d7cac8d479f0e7303ed Mon Sep 17 00:00:00 2001 From: Yogi Wiguna Date: Fri, 6 Mar 2026 16:01:12 +0800 Subject: [PATCH] feat: Implement Stop n Go game mode with phase management, missions, dynamic safe zones, and associated UI and managers. --- assets/models/meshes/hover.res | Bin 3195 -> 3220 bytes assets/models/meshes/normal.res | Bin 3255 -> 3234 bytes assets/models/meshes/tiles.res | Bin 4492 -> 1890 bytes scenes/main.gd | 3 +- scripts/managers/player_input_manager.gd | 33 ++++++++---- scripts/managers/playerboard_manager.gd | 14 +++-- scripts/managers/stop_n_go_manager.gd | 64 +++++++++++------------ scripts/ui/powerup_inventory_ui.gd | 63 ++++++++++++++-------- 8 files changed, 106 insertions(+), 71 deletions(-) diff --git a/assets/models/meshes/hover.res b/assets/models/meshes/hover.res index fb44c39d082610e53a79e60787a545e6bd57fc46..79585cbe12a4b5d4363450ea674e3e10270d4ac5 100644 GIT binary patch literal 3220 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 GQ$s^<0}$Z= literal 3195 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 diff --git a/assets/models/meshes/tiles.res b/assets/models/meshes/tiles.res index 280973279a1dd2d17a7973a1834b31174b43a2bb..d710bb9982f60f231b6d01c87e021c0216a51f11 100644 GIT binary patch literal 1890 zcmV-o2c7s*Q$s@n000005C8y`5dZ)Q1^@s(0RR9fwJ-f(01y2y0G2(sKQM8U4#8mp z2PGP1fQQ4thzJ5Fvwx_YZA6xn>`k*nl13!iWIWMk)!_OooT--cLyA)}{h`oj%>Eg+ z+w5;CoB*K!qX1X7+M_+-5Qig+i{LO)$R&%(7&_RPN1L_t;6}@}1{s_KC6f2VS;zY6 zLR2z!P{stD&T3jkjg};B#CaR#6gz4&_*Rl5Hg%VH9D!1(&>KznAud4l0g$#CA9*bH1eq+H;nrXh;+insjj_k zYMEMwT((%^l4AHa9g97NovamPT~}>8i((%R>r)mzWpOD*iBaFgu^>BrFQ>*95O{&N zbuL?}{{b#Vkzxm+#83hyBF^Y#r_g9PK%gKflTomv)wCsxRZekY+G1T-%PmBu?bIkL zDO0dTPCChLd)m>HZyD%Y*A`UHOJ+_zTd}xNloI>&QtN?SMa!%m$U0DJtVMLIu5EK3 z=(n)a78_Zlo|T>jsbnjVIsY-0stN*52syV<%L=Z*6&Pt}wJE1o%Gz$F&1SRNh(wOz zO2s)v#`)4!%iS6!32NwAtevQJ8vJK*k@DhC znJ_DUkNv7Q1jc9-I)*(GAXGU>gH9nuG$Sb}DX5ic41k1DnsgYHy`UYNJB;jt zWqINl^IYiuz9qV7*2dcsli_68{@~hZIdQ829g_unP5rXbYzni6?HjUL(P<@nlGhj> zmj;#LkGMJ0a6o<8`isrHJ3|Xh@D_#?A0=1Z5`j5d=Y^Z>y(cuiwRxz~?KW|PkQ41q zM=`y=2o?*0n{YoC|BG-sF`>7u3=;qh8ADjepPW}4hYW~_)!AYsAALXKJ54@}Ew#Ku zs(3yy;4;QFlv?-72+-ly>#YO_Y(?SWDAc15`CLDVCkI4HBLg5@ebL9&V8S(1 zS5*4{tH=ieS^=W{G^rEwPv4+X`1DWtlWemKo-RkV$$Y){F6e=G$P%hPj`SN-@zkXJvEe|XTUH`92pZ(P!V}Ex-xV_;_iwd5@Q3TTqT_IVet0p zEBixk6>k#5KkVu!LS$}nea*>Vgu9_Nunp~_%o1CQX7&hjMqu4)6L_Y;rNCm<{dy@&YSia9ezP9KyjkW;iMVj zpOJ>c=sf~5TXfT{X<7U7y!i&6kjAV63x)~um%iLVgG-->}qeuWvEq` zUkuVF41oYEtFn(?PGC189LN=VrqeWkO64eAjpL#KKWw!mq>05Fo{Q#n5f3Z zBJp9SsQ+UMTsb533v#k}IdxM{H4t(}?nm2IY_FUqLXtDs&gN@F*rZ`}usxt{Ite6> zm4>RA3o7|iL#D_893zmN zF1{}oJuea!pgp+J*aNRHD77#BV3h!!2>`YcKsYd^PHRS+!lR;(97HuJ{0G77QVCc< zG9jFnI)gPH0SjFWsM?(3!R=PC#lVNcb$0p%3_qAr-6=ExHUKFAr|WFl*)@6gf_V0t zc4q227sS(Dkgjvh`su;ytJ>JvUu zQ^i58kXfv%#1P0u%r%S!-1WAFwg!n>K2j2Uq|P$&&@ZG})n8 cVTOx9Ggtwf7gDx>#VZD6XS>#xJyKIcLvIprhX4Qo literal 4492 zcmcgw&5IlLNE!oJGU4!89AA7GCubXy;@AzCs_uwCwfAGg||Ll7S`)~R`fat4e z-n4G+bE8U^>rw@$+T7G8dtx|O>&j#|1gR&+tHxXBZCJ>jYI7V^quVxn92CLpx^1*K zb-u{10~nX4o~X)Aj8NzEvWz40o0uld)Wqwfh*4_oRD^?RgRmG3DWaX}iLIw`1%Zhz zig|0+EZW9Ej`k6GPDomfohV(6t*L`5?Q|Bh#|ATnn`Yk_jC&p~&&zphT`kjx#s_n( za#uR9C{(vfmt!+=*;lZ7W7@W=_0zHFCDO!o*^{8`05A)4Z?dnSfmGpW7vb>i+1aX{ z6xk!dD-(2C+B|!DFi>gx*;7L_N&DJ4pvq^uu1zUs@@_tz)dof-*M6iD18(;W+H;^x zQ`weUQ>LcoC$iTBiYn6p6vj^%s+yO}F%X#IbM&Y-BG`_ z^A#LANvEH5Pzy&lVuKDy%c$isy<=5Qg%uAzv z?sVvvGPrv*qriTodOeF;)_B^+GoaSBZC&uLS@bL!d}}z{qAE&vROv?IOS^s%gAzjt z52cI3ukb3wDWO0_XX0Q4gonXISxZ*d#(-0Z__Z7 zs68`zVC$H8&SRT)^WAgUO75&|z!?$vX#?N6kW!LSb}`2!`(logakGo*#?B!})7WF3 zA4IQ7`=+1t=oU@d3BAXg!s|Q?Qw8(K0FI8bMVnYKXAfvQxOYURf(bIDU8e%c`Vp#h zdmvluNCuZxyq;v?{7{v8fwaWysiW+2{s8HmkH?-LzDz$tmSGTbevD6z`4r&7#6JI%mN^zGGXKW2b#r74X_zLkCO%QFknS zrrsUNHO&v)-foWAr&0@hY$mF;Z^lf{o49evw-F-6U?_?th4#TBfkG8wYZ;B0o-bkv#9V&+l-dof%q}l+OUK;O?EdqF<3O!jzFb%LZ1+7Vx$LkqaxoU8r zQlWa98u3Wp@5;?XX=^Bz=VSn+by0v+Rb9~%lZMExkbn|ls%eJP`glFfbF=}~8UjSv z(W4VklzQ6AwVROKRV?mghY3UU8Phgy_Rg1B=qijis!b3|Y;=UGFza6_#k-bWZc+RJp>o7aa z9;bHk@K|SsWh@{rp{0^PrdJmK-04GTOttjp+gE0Lsb$$)d%pw5l}ru&#$?qUAm8j} zX$_W)XW75x%~7O_WAdga_T@9foI=x92qL#e08YzrVP#_p9r_e3d~(8l9dF-RBhRgfHl0-^uLo_g;v6W1nU0$zS?A`0g8aa%ZEFZ1~*6?C|dt z^n>>6zi<9g;pe(Da|0*o#V9kq9nXVVJK=8tf4cl=`SI$5)yJ!kmY*&^Sbeg*wfyrx z-(7yPd>8$#<=vC-fAQMM7Y|-Te^>esS9ecV7_T1OJ^66;_WgJ6SG%`&-@bo)x7vN@ z{wpW%uRdRW_T}fR_gAl+eD)>9v;Ci=i4n3RvGFis^ic%%b@Y!xO;6zcB>Ef3<4@!L z4EksBWBNI~Z=&aBnRotEG|6w^E@4aZWuD}dPI^J8{T`&W3zyNiQ|RsE|1kdsf4M^Q diff --git a/scenes/main.gd b/scenes/main.gd index 044b4ec..29d884b 100644 --- a/scenes/main.gd +++ b/scenes/main.gd @@ -633,9 +633,10 @@ func _start_game(): if obstacle_manager and LobbyManager.game_mode == "Stop n Go": obstacle_manager.spawn_random_obstacles(15) - # Spawn mission tiles BEFORE countdown but AFTER walls (Stop n Go only) + # Spawn mission and power-up tiles BEFORE countdown but AFTER walls (Stop n Go only) if LobbyManager.game_mode == "Stop n Go" and stop_n_go_manager: stop_n_go_manager.setup_mission_tiles() + stop_n_go_manager.spawn_initial_powerups() # Ensure power-ups exist before 1,2,3 Go # Spawn Static Tektons BEFORE countdown (Free Mode Only) # Exclude for Stop n Go and Tekton Doors diff --git a/scripts/managers/player_input_manager.gd b/scripts/managers/player_input_manager.gd index 4cab3f7..db2be04 100644 --- a/scripts/managers/player_input_manager.gd +++ b/scripts/managers/player_input_manager.gd @@ -84,18 +84,29 @@ func handle_unhandled_input(event): # --- Keyboard Shortcuts (Event-based) --- if event is InputEventKey and event.pressed and not event.echo: + var mode = LobbyManager.get_game_mode() + var is_sng = mode == GameMode.Mode.STOP_N_GO + match event.keycode: KEY_KP_1, KEY_1, KEY_KP_2, KEY_2, KEY_KP_3, KEY_3, KEY_KP_4, KEY_4: - # Unified Mapping for all modes: 1-Speed, 2-Wall, 3-Freeze, 4-Ghost - match event.keycode: - KEY_KP_1, KEY_1: - player.activate_powerup(0) # FASTER_SPEED - KEY_KP_2, KEY_2: - player.activate_powerup(2) # BLOCK_FLOOR - KEY_KP_3, KEY_3: - player.activate_powerup(1) # AREA_FREEZE - KEY_KP_4, KEY_4: - player.activate_powerup(3) # INVISIBLE_MODE + if is_sng: + # Stop n Go Mapping: 1-Speed, 2-Ghost + match event.keycode: + KEY_KP_1, KEY_1: + player.activate_powerup(0) # FASTER_SPEED + KEY_KP_2, KEY_2: + player.activate_powerup(3) # INVISIBLE_MODE + else: + # Unified Mapping for all modes: 1-Speed, 2-Wall, 3-Freeze, 4-Ghost + match event.keycode: + KEY_KP_1, KEY_1: + player.activate_powerup(0) # FASTER_SPEED + KEY_KP_2, KEY_2: + player.activate_powerup(2) # BLOCK_FLOOR + KEY_KP_3, KEY_3: + player.activate_powerup(1) # AREA_FREEZE + KEY_KP_4, KEY_4: + player.activate_powerup(3) # INVISIBLE_MODE # KEY_R: # player.auto_put_item() KEY_Q: @@ -117,7 +128,7 @@ func handle_unhandled_input(event): KEY_G: if not player.is_carrying_tekton and player.powerup_manager: if player.powerup_manager.can_use_special(): - player.grab_tekton() + player.grab_tekton() # Handle spawn point selection if not yet selected diff --git a/scripts/managers/playerboard_manager.gd b/scripts/managers/playerboard_manager.gd index 6c982de..179d870 100644 --- a/scripts/managers/playerboard_manager.gd +++ b/scripts/managers/playerboard_manager.gd @@ -307,8 +307,11 @@ func bot_try_grab_item() -> bool: special_tiles_manager.add_powerup_from_item(item) player.playerboard[empty_slot] = item - player.rpc("sync_grid_item", current_cell.x, current_cell.y, current_cell.z, -1) - player.rpc("sync_playerboard", player.playerboard) + + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + main.rpc("sync_grid_item", current_cell.x, current_cell.y, current_cell.z, -1) + main.rpc("sync_playerboard", player.name.to_int(), player.playerboard) player.has_performed_action = true player.action_points -= 1 _check_goal_completion() @@ -331,8 +334,11 @@ func bot_try_grab_item() -> bool: if empty_slot != -1: if player.is_multiplayer_authority(): player.playerboard[empty_slot] = item - player.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) - player.rpc("sync_playerboard", player.playerboard) + + var main = player.get_tree().get_root().get_node_or_null("Main") + if main: + main.rpc("sync_grid_item", cell.x, cell.y, cell.z, -1) + main.rpc("sync_playerboard", player.name.to_int(), player.playerboard) player.has_performed_action = true player.action_points -= 1 return true diff --git a/scripts/managers/stop_n_go_manager.gd b/scripts/managers/stop_n_go_manager.gd index 95fb87d..290fe8e 100644 --- a/scripts/managers/stop_n_go_manager.gd +++ b/scripts/managers/stop_n_go_manager.gd @@ -22,6 +22,16 @@ var safe_zone_spawned: bool = false # Power-Up Tile Spawning const POWERUP_TILES = [11, 14] # Speed (11) and Ghost (14) const POWERUP_SPAWN_COUNT: int = 5 # Number of power-up tiles to spawn +var powerups_spawned: bool = false +var stop_phase_occurred: bool = false + +const PERMANENT_POWERUP_LOCATIONS: Array[Vector2i] = [ + Vector2i(4, 3), # Area 1 + Vector2i(8, 7), # Area 2 + Vector2i(11, 4), # Area 3 + Vector2i(15, 8), # Area 4 + Vector2i(18, 5) # Area 5 +] var current_phase: Phase = Phase.GO var phase_timer: float = GO_DURATION @@ -231,6 +241,7 @@ func _start_phase(phase: Phase): rpc("sync_phase", phase_name, phase_timer) if phase == Phase.STOP: + stop_phase_occurred = true # --- DYNAMIC SAFE ZONE: Penalize players outside the zone --- if safe_zone_spawned: var all_players = get_tree().get_nodes_in_group("Players") @@ -238,7 +249,7 @@ func _start_phase(phase: Phase): if not _is_in_safe_zone(p.current_position): _scatter_player_tiles(p) - # --- POWER-UP TILES: Spawn 5 Speed & Ghost tiles --- + # Refresh power-ups every STOP phase _spawn_powerup_tiles() # If GO phase starts, clear all STOP phase freezes and safe zone @@ -323,6 +334,11 @@ func setup_mission_tiles(): if multiplayer.is_server(): _spawn_mission_tiles() +func spawn_initial_powerups(): + """Public wrapper to spawn powerups before game start.""" + if multiplayer.is_server(): + _spawn_powerup_tiles() + func _spawn_mission_tiles(): var gridmap = get_parent().get_node_or_null("EnhancedGridMap") if not gridmap: @@ -679,7 +695,7 @@ func sync_clear_safe_zone(centers_to_clear: Array): # ============================================================================= func _spawn_powerup_tiles(): - """Server: Spawn 5 Speed & Ghost power-up tiles at random walkable positions.""" + """Server: Spawn 5 permanent power-up tiles at fixed positions.""" if not multiplayer.is_server(): return var main = get_node_or_null("/root/Main") @@ -688,40 +704,24 @@ func _spawn_powerup_tiles(): var gridmap = main.get_node_or_null("EnhancedGridMap") if not gridmap: return - # Collect valid positions (walkable floor, no existing item on Floor 1) - var valid_positions: Array[Vector2i] = [] - for x in range(gridmap.columns): - for z in range(gridmap.rows): - var floor_tile = gridmap.get_cell_item(Vector3i(x, 0, z)) - # Skip void, obstacles, start, finish - if floor_tile == -1 or floor_tile == TILE_OBSTACLE: - continue - # Skip cells that already have items on Floor 1 - var existing_item = gridmap.get_cell_item(Vector3i(x, 1, z)) - if existing_item != -1: - continue - valid_positions.append(Vector2i(x, z)) + print("[StopNGo] Spawning/Refreshing 5 static power-up tiles...") - if valid_positions.is_empty(): - print("[StopNGo] WARNING: No valid positions for power-up tiles!") - return - - # Shuffle and pick up to POWERUP_SPAWN_COUNT positions - var rng = RandomNumberGenerator.new() - rng.randomize() - valid_positions.shuffle() - - var spawn_count = min(POWERUP_SPAWN_COUNT, valid_positions.size()) - - for i in range(spawn_count): - var pos = valid_positions[i] - var tile_id = POWERUP_TILES[rng.randi() % POWERUP_TILES.size()] + for i in range(PERMANENT_POWERUP_LOCATIONS.size()): + var pos = PERMANENT_POWERUP_LOCATIONS[i] + + # Set Floor 0 beneath power-up to ID 1 (Hover pattern) - Static PADS + gridmap.set_cell_item(Vector3i(pos.x, 0, pos.y), 1) + + # Cycle through the available power-up types + var tile_id = POWERUP_TILES[i % POWERUP_TILES.size()] # Place on Floor 1 gridmap.set_cell_item(Vector3i(pos.x, 1, pos.y), tile_id) - # Sync to all clients + # Sync both floor and tile to all clients and host if can_rpc(): - main.rpc("sync_grid_item", pos.x, 1, pos.y, tile_id) + main.rpc("sync_grid_item", pos.x, 0, pos.y, 1) # Sync floor change (Pad on) + main.rpc("sync_grid_item", pos.x, 1, pos.y, tile_id) # Sync power-up - print("[StopNGo] Spawned %d power-up tiles (Speed & Ghost)" % spawn_count) + powerups_spawned = true + print("[StopNGo] Static power-up refresh completed.") diff --git a/scripts/ui/powerup_inventory_ui.gd b/scripts/ui/powerup_inventory_ui.gd index 34b9c02..8fe4da0 100644 --- a/scripts/ui/powerup_inventory_ui.gd +++ b/scripts/ui/powerup_inventory_ui.gd @@ -40,9 +40,16 @@ func _ready(): _setup_btn(2, wall_btn) _setup_btn(3, ghost_btn) - # All skills available with new logic - if wall_btn: wall_btn.visible = true - if freeze_btn: freeze_btn.visible = true + # All skills available with new logic, but restricted in Stop n Go + if is_restricted: + if wall_btn: wall_btn.visible = false + if freeze_btn: freeze_btn.visible = false + # Re-setup shortcut labels for restricted mode + _update_shortcuts_for_mode(true) + else: + if wall_btn: wall_btn.visible = true + if freeze_btn: freeze_btn.visible = true + _update_shortcuts_for_mode(false) print("[PowerUpUI] UI Initialization Complete. Mapped %d buttons." % icon_containers.size()) @@ -91,39 +98,49 @@ func _setup_btn(effect_id: int, btn: Button): var sc_lbl = Label.new() sc_lbl.name = "ShortcutLabel" sc_lbl.mouse_filter = Control.MOUSE_FILTER_IGNORE - - # Position: Top Left of the button sc_lbl.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT sc_lbl.vertical_alignment = VERTICAL_ALIGNMENT_TOP - - # Anchor to top left sc_lbl.set_anchors_preset(Control.PRESET_TOP_LEFT) - - # Offset to be close to the corner sc_lbl.offset_left = 0 - sc_lbl.offset_top = -5 # Close to the button top - + sc_lbl.offset_top = -5 sc_lbl.add_theme_font_size_override("font_size", 16) sc_lbl.add_theme_color_override("font_outline_color", Color.BLACK) sc_lbl.add_theme_constant_override("outline_size", 4) - # Add color override to make it distinct (optional, but good for visibility) sc_lbl.add_theme_color_override("font_color", Color(0.9, 0.9, 0.9)) - - # Determine Label Text based on Effect ID - var key_text = "" - # Consistent mapping: 1, 2, 3, 4 + btn.add_child(sc_lbl) + + _update_btn_shortcut(effect_id, btn) + + # Connect click + if not btn.pressed.is_connected(_on_btn_pressed): + btn.pressed.connect(_on_btn_pressed.bind(effect_id)) + +func _update_shortcuts_for_mode(is_restricted: bool): + for effect_id in icon_containers: + var btn = icon_containers[effect_id] + _update_btn_shortcut(effect_id, btn) + +func _update_btn_shortcut(effect_id: int, btn: Button): + var sc_lbl = btn.get_node_or_null("ShortcutLabel") + if not sc_lbl: return + + var mode = LobbyManager.get_game_mode() + var is_sng = mode == GameMode.Mode.STOP_N_GO + + var key_text = "" + if is_sng: + match effect_id: + 0: key_text = "1" # Speed + 3: key_text = "2" # Ghost + _: key_text = "" # Others hidden/disabled + else: match effect_id: 0: key_text = "1" # Speed 2: key_text = "2" # Wall 1: key_text = "3" # Freeze 3: key_text = "4" # Ghost - - sc_lbl.text = key_text - btn.add_child(sc_lbl) - - # Connect click - if not btn.pressed.is_connected(_on_btn_pressed): - btn.pressed.connect(_on_btn_pressed.bind(effect_id)) + + sc_lbl.text = key_text func _on_btn_pressed(effect_id: int): print("[PowerUpUI] Clicked Button %d" % effect_id)