feat: completing tutorial
This commit is contained in:
@@ -24,6 +24,10 @@ func _ready() -> void:
|
||||
break
|
||||
await get_tree().process_frame
|
||||
elapsed += get_process_delta_time()
|
||||
|
||||
# Pause match timer during tutorial
|
||||
if main_scene and main_scene.get("goals_cycle_manager"):
|
||||
main_scene.goals_cycle_manager.set_process(false)
|
||||
|
||||
if not local_player:
|
||||
print("Tutorial Error: Local player not found.")
|
||||
@@ -78,15 +82,17 @@ func _run_tutorial() -> void:
|
||||
# Grid Highlight
|
||||
overlay.clear_highlight()
|
||||
await overlay.display_text("Look at the board! You will see [color=gold]Normal Tiles[/color] and shiny [color=cyan]Holo Tiles[/color].", true)
|
||||
|
||||
# ==============================================================================
|
||||
# --- INTERACTIVE 1: NORMAL TILE ---
|
||||
var normal_pos = _find_closest_tile(local_player.current_position, [7, 8, 9, 10])
|
||||
# ==============================================================================
|
||||
var normal_pos = _find_closest_tile(local_player.current_position, [7, 8, 9, 10], 4)
|
||||
if normal_pos != Vector2i(-1, -1):
|
||||
_highlight_floor_cell(normal_pos, Color(1.0, 1.0, 0.0)) # Yellow box
|
||||
overlay.display_text("I've highlighted a [color=gold]Normal Tile[/color] with a yellow box. Walk over to it and press [color=red]SPACE[/color] to Grab it!", false)
|
||||
else:
|
||||
overlay.display_text("Walk up to any Normal Tile and press [color=red]SPACE[/color] to Grab it!", false)
|
||||
|
||||
overlay.set_objective("Walk to a [color=gold]Normal Tile[/color] and press [color=red]SPACE[/color] to grab it.")
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
|
||||
@@ -100,13 +106,15 @@ func _run_tutorial() -> void:
|
||||
break
|
||||
await get_tree().process_frame
|
||||
|
||||
overlay.hide_objective()
|
||||
_clear_floor_highlights()
|
||||
get_tree().paused = true
|
||||
overlay.show_overlay()
|
||||
|
||||
await overlay.display_text("Outstanding! You secured a tile.", true)
|
||||
|
||||
# ==============================================================================
|
||||
# --- SCORE AND GOALS SECTION ---
|
||||
# ==============================================================================
|
||||
overlay.highlight_zone("Leaderboard")
|
||||
await overlay.display_text("Your main objective is to climb the [color=gold]Leaderboard[/color] on the right by scoring points.", true)
|
||||
|
||||
@@ -114,54 +122,159 @@ func _run_tutorial() -> void:
|
||||
await overlay.display_text("You earn points by collecting tiles, but the real score comes from completing your [color=gold]Playerboard Goals[/color]!", true)
|
||||
await overlay.display_text("Fill your entire board shape to complete a goal. This grants massive points, clears the board, and ranks you up!", true)
|
||||
|
||||
# --- TEKTONS SECTION ---
|
||||
overlay.clear_highlight()
|
||||
await overlay.display_text("Now, what about those little creatures running around the map? Those are [color=cyan]Tektons[/color]!", true)
|
||||
await overlay.display_text("Grabbing a dynamic Tekton gives you [color=gold]PASSIVE score points[/color] over time and spawns bonus tiles for you while you carry it.", true)
|
||||
await overlay.display_text("Hold onto them as long as you can... but watch out, opponents will try to RAM you and steal them!", true)
|
||||
await overlay.display_text("Also keep an eye on [color=gray]Static Tektons[/color] standing on podiums. They periodically throw new free tiles onto the board for you to collect!", true)
|
||||
|
||||
# --- INTERACTIVE 2: HOLO TILE ---
|
||||
# ==============================================================================
|
||||
# --- POWER UP SECTION ---
|
||||
# ==============================================================================
|
||||
await overlay.display_text("Now, let's talk about [color=cyan]Holo Tiles[/color]. They grant you special Power-Ups!", true)
|
||||
|
||||
var holo_pos = _find_closest_tile(local_player.current_position, [11, 12, 13, 14])
|
||||
if holo_pos != Vector2i(-1, -1):
|
||||
_highlight_floor_cell(holo_pos, Color(0.0, 1.0, 1.0)) # Cyan box
|
||||
overlay.display_text("I've highlighted a [color=cyan]Holo Tile[/color] with a cyan box. Go ahead and grab it!", false)
|
||||
else:
|
||||
# Fallback: force spawn one if the RNG map generation didn't place any
|
||||
holo_pos = local_player.current_position + Vector2i(0, 1)
|
||||
var gridmap = local_player.get("enhanced_gridmap")
|
||||
if gridmap:
|
||||
gridmap.set_cell_item(Vector3i(holo_pos.x, 1, holo_pos.y), 11) # Spawn speed boost
|
||||
_highlight_floor_cell(holo_pos, Color(0.0, 1.0, 1.0))
|
||||
overlay.display_text("I just dropped a Holo Tile near you (cyan box). Go ahead and grab it!", false)
|
||||
overlay.clear_highlight()
|
||||
await overlay.display_text("There are [color=cyan]4 types of Power-Ups[/color] from Holo Tiles. Let me walk you through each one, one by one.", true)
|
||||
|
||||
var powerup_ids = [11, 12, 13, 14]
|
||||
var powerup_names = {
|
||||
11: "[color=gold]Speed Boost[/color] (Make you run fast)",
|
||||
12: "[color=aqua]Area Freeze[/color] (Slows nearby opponents)",
|
||||
13: "[color=gray]Iron Wall[/color] (Blocks opponent path)",
|
||||
14: "[color=white]Ghost Mode[/color] (Invisibility and ram immunity)"
|
||||
}
|
||||
var powerup_enums = {11: 0, 12: 1, 13: 2, 14: 3}
|
||||
var gridmap = local_player.get("enhanced_gridmap")
|
||||
|
||||
for pid in powerup_ids:
|
||||
overlay.clear_highlight()
|
||||
overlay.show_powerup_card(pid)
|
||||
await overlay.display_text("Next is %s. I will drop this Holo Tile near you." % powerup_names[pid], true)
|
||||
overlay.hide_powerup_card()
|
||||
|
||||
var ppos = local_player.current_position + Vector2i(0, 1)
|
||||
if gridmap:
|
||||
gridmap.set_cell_item(Vector3i(ppos.x, 1, ppos.y), pid)
|
||||
_highlight_floor_cell(ppos, Color(0.0, 1.0, 1.0))
|
||||
|
||||
overlay.display_text("Grab the tile, press [color=yellow]'F'[/color] to use it, and for some skills press it again or Click to confirm!", false)
|
||||
|
||||
overlay.set_objective("Grab the [color=cyan]Holo Tile[/color] and press [color=yellow]'F'[/color] to use %s." % powerup_names[pid])
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
|
||||
# Wait for it to be grabbed
|
||||
while gridmap and gridmap.get_cell_item(Vector3i(ppos.x, 1, ppos.y)) == pid:
|
||||
await get_tree().process_frame
|
||||
|
||||
# Highlight the Power-Up item slot now that they picked it up
|
||||
overlay.highlight_zone("PowerUpItem")
|
||||
|
||||
# Wait for it to be used
|
||||
var effect_enum = powerup_enums[pid]
|
||||
while local_player.get("special_tiles_manager") and local_player.special_tiles_manager.inventory.get(effect_enum, false) == true:
|
||||
await get_tree().process_frame
|
||||
|
||||
overlay.clear_highlight()
|
||||
|
||||
overlay.hide_objective()
|
||||
_clear_floor_highlights()
|
||||
get_tree().paused = true
|
||||
overlay.show_overlay()
|
||||
await overlay.display_text("Excellent! You used %s." % powerup_names[pid], true)
|
||||
|
||||
# ==============================================================================
|
||||
# --- TEKTONS SECTION ---
|
||||
# ==============================================================================
|
||||
overlay.clear_highlight()
|
||||
# --- DYNAMIC TEKTON FOCUS ---
|
||||
var dynamic_tektons = get_tree().get_nodes_in_group("Tektons")
|
||||
var cam_mgr = main_scene.get("camera_context_manager") if main_scene else null
|
||||
if dynamic_tektons.size() > 0 and cam_mgr:
|
||||
# Find nearest dynamic tekton
|
||||
var nearest = null
|
||||
var nearest_dist = 999999
|
||||
for tk in dynamic_tektons:
|
||||
if tk.get("is_static_turret") == false:
|
||||
var dist = local_player.global_position.distance_to(tk.global_position)
|
||||
if dist < nearest_dist:
|
||||
nearest_dist = dist
|
||||
nearest = tk
|
||||
|
||||
if nearest:
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
cam_mgr.set_player(nearest)
|
||||
await get_tree().create_timer(1.2, true).timeout
|
||||
|
||||
var cam = main_scene.get_node_or_null("Camera3D200")
|
||||
if cam and cam is Camera3D:
|
||||
var screen_pos = cam.unproject_position(nearest.global_position)
|
||||
overlay.highlight_rect(Rect2(screen_pos.x - 75, screen_pos.y - 75, 150, 150))
|
||||
|
||||
await overlay.display_text("Now, what about those little creatures running around the map? Those are [color=cyan]Tektons[/color]!", true)
|
||||
|
||||
if cam_mgr:
|
||||
cam_mgr.set_player(local_player)
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
overlay.clear_highlight()
|
||||
await get_tree().create_timer(0.8, true).timeout
|
||||
# ----------------------------
|
||||
|
||||
await overlay.display_text("Grabbing a dynamic Tekton gives you [color=gold]PASSIVE score points[/color] over time and spawns bonus tiles for you while you carry it.", true)
|
||||
await overlay.display_text("Hold onto them as long as you can... but watch out, opponents will try to RAM you and steal them!", true)
|
||||
|
||||
await overlay.display_text("Go and catch one of the Tektons running around!", false)
|
||||
overlay.set_objective("Walk into a [color=cyan]Tekton[/color] or press [color=red]G[/color] near one to grab it.")
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
|
||||
initial_items = local_player.playerboard.count(-1)
|
||||
while true:
|
||||
if holo_pos != Vector2i(-1, -1) and local_player.get("enhanced_gridmap"):
|
||||
if local_player.enhanced_gridmap.get_cell_item(Vector3i(holo_pos.x, 1, holo_pos.y)) == -1:
|
||||
break
|
||||
elif local_player.playerboard.count(-1) < initial_items:
|
||||
break
|
||||
while not local_player.is_carrying_tekton:
|
||||
await get_tree().process_frame
|
||||
|
||||
_clear_floor_highlights()
|
||||
|
||||
overlay.hide_objective()
|
||||
get_tree().paused = true
|
||||
overlay.show_overlay()
|
||||
await overlay.display_text("Great job! Notice your score passively increasing.", true)
|
||||
|
||||
# --- POWER UP SECTION ---
|
||||
# Highlight Power Up Icon
|
||||
overlay.highlight_zone("PowerUpItem")
|
||||
await overlay.display_text("Nice! You grabbed a Holo Tile — that earned you a special [color=cyan]Power-Up[/color] item!", true)
|
||||
await overlay.display_text("There are [color=cyan]4 types of Power-Ups[/color] from Holo Tiles. Let me walk you through each one.", true)
|
||||
await overlay.show_powerup_showcase()
|
||||
await overlay.display_text("To activate your stored Power-Up, press [color=yellow]'F'[/color]. If you don't like this key, you can remap it in [color=gold]Settings[/color].", true)
|
||||
await overlay.display_text("But you won't hold it forever! The Tekton runs away when your carry timer reaches 0, or you can manually throw it by pressing [color=red]G[/color].", false)
|
||||
overlay.set_objective("Wait for the [color=cyan]Tekton[/color] to escape, or press [color=red]G[/color] to throw it!")
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
|
||||
while local_player.is_carrying_tekton:
|
||||
await get_tree().process_frame
|
||||
|
||||
overlay.hide_objective()
|
||||
get_tree().paused = true
|
||||
overlay.show_overlay()
|
||||
await overlay.display_text("Oops, it got away! But don't worry, you can always catch another one.", true)
|
||||
|
||||
# ==============================================================================
|
||||
# --- STATIC TEKTON FOCUS ---
|
||||
# ==============================================================================
|
||||
var static_stands = get_tree().get_nodes_in_group("StaticTektonStands")
|
||||
cam_mgr = main_scene.get("camera_context_manager") if main_scene else null
|
||||
|
||||
if static_stands.size() > 0 and cam_mgr:
|
||||
var target_stand = static_stands[0]
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_ALWAYS # Allow camera to slide while paused
|
||||
cam_mgr.set_player(target_stand)
|
||||
|
||||
# Give it time to pan over
|
||||
await get_tree().create_timer(1.2, true).timeout
|
||||
|
||||
# Dim the screen around it
|
||||
var cam = main_scene.get_node_or_null("Camera3D200")
|
||||
if cam and cam is Camera3D:
|
||||
var screen_pos = cam.unproject_position(target_stand.global_position)
|
||||
overlay.highlight_rect(Rect2(screen_pos.x - 100, screen_pos.y - 120, 200, 200))
|
||||
|
||||
await overlay.display_text("Also keep an eye on [color=gray]Static Tektons[/color] standing on podiums. They periodically throw new free tiles onto the board for you to collect!", true)
|
||||
|
||||
if cam_mgr:
|
||||
cam_mgr.set_player(local_player)
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
overlay.clear_highlight()
|
||||
|
||||
# Let it pan back briefly
|
||||
await get_tree().create_timer(0.8, true).timeout
|
||||
# ==============================================================================
|
||||
# --- BATTERY GAUGE & RAMMING SECTION ---
|
||||
# ==============================================================================
|
||||
# Switch to highlight Power Bar / Battery Gauge
|
||||
overlay.highlight_zone("PowerBar")
|
||||
await overlay.display_text("Next, look at your [color=cyan]Battery Gauge[/color] — it [color=yellow]fills up automatically over time[/color]. You don't need to do anything to charge it.", true)
|
||||
@@ -187,15 +300,41 @@ func _run_tutorial() -> void:
|
||||
spawned_bot.add_to_group("Bots", true)
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
# Freeze it so its AI doesn't run away during the tutorial
|
||||
var bc = spawned_bot.get_node_or_null("BotController")
|
||||
if bc:
|
||||
bc.set_process(false)
|
||||
bc.set_physics_process(false)
|
||||
spawned_bot.set("is_frozen", true)
|
||||
bc.set_process(true)
|
||||
bc.set_physics_process(true)
|
||||
|
||||
if spawned_bot:
|
||||
await overlay.display_text("Let's try it. I'm placing a Bot right next to you, and I'll instantly fill your Battery Gauge to max.", true)
|
||||
# Always unfreeze in case it was caught in the earlier Area Freeze blast
|
||||
spawned_bot.set("is_frozen", false)
|
||||
var target_bc = spawned_bot.get_node_or_null("BotController")
|
||||
if target_bc:
|
||||
target_bc.set_process(true)
|
||||
target_bc.set_physics_process(true)
|
||||
|
||||
await overlay.display_text("Let's try it. There is a Bot roaming the map right now. I will fill your Battery Gauge to max.", true)
|
||||
|
||||
# --- CAMERA PAN TO BOT ---
|
||||
cam_mgr = main_scene.get("camera_context_manager") if main_scene else null
|
||||
if cam_mgr:
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_ALWAYS
|
||||
cam_mgr.set_player(spawned_bot)
|
||||
await get_tree().create_timer(1.2, true).timeout
|
||||
|
||||
var cam = main_scene.get_node_or_null("Camera3D200")
|
||||
if cam and cam is Camera3D:
|
||||
var screen_pos = cam.unproject_position(spawned_bot.global_position)
|
||||
overlay.highlight_rect(Rect2(screen_pos.x - 75, screen_pos.y - 100, 150, 150))
|
||||
|
||||
await overlay.display_text("Behold, your opponent! Their AI is active so you will have to hunt them down.", true)
|
||||
|
||||
if cam_mgr:
|
||||
cam_mgr.set_player(local_player)
|
||||
cam_mgr.process_mode = Node.PROCESS_MODE_INHERIT
|
||||
overlay.clear_highlight()
|
||||
await get_tree().create_timer(0.8, true).timeout
|
||||
# ------------------------
|
||||
|
||||
# Instantly grant FULL BOOST
|
||||
if local_player.get("powerup_manager"):
|
||||
@@ -204,41 +343,42 @@ func _run_tutorial() -> void:
|
||||
local_player.powerup_manager.acquire_smash_bonus()
|
||||
local_player.powerup_manager.acquire_smash_bonus()
|
||||
|
||||
var n_pos = local_player.current_position + Vector2i(1, 0)
|
||||
if local_player.get("enhanced_gridmap"):
|
||||
var neighbors = local_player.enhanced_gridmap.get_neighbors(local_player.current_position, 0)
|
||||
for n in neighbors:
|
||||
if n.get("is_walkable"):
|
||||
n_pos = n.position
|
||||
break
|
||||
|
||||
# Safely set bot position
|
||||
spawned_bot.current_position = n_pos
|
||||
if local_player.get("enhanced_gridmap") and local_player.enhanced_gridmap.has_method("grid_to_world"):
|
||||
spawned_bot.global_position = local_player.enhanced_gridmap.grid_to_world(n_pos)
|
||||
|
||||
overlay.display_text("Press 'Q' to enter Attack Mode, then Walk into the Bot!", false)
|
||||
overlay.display_text("Press 'Q' to enter Attack Mode, then chase the Bot down to RAM it!", false)
|
||||
|
||||
overlay.set_objective("Press [color=yellow]'Q'[/color] to enter [color=red]ATTACK MODE[/color], then chase and RAM the bot!")
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
|
||||
# Wait for the player to push the bot (bot's position changes)
|
||||
var bot_old_pos = spawned_bot.current_position
|
||||
while spawned_bot.current_position == bot_old_pos:
|
||||
# Wait for the player to push the bot
|
||||
while true:
|
||||
if spawned_bot.get("is_knock_mode") == true:
|
||||
break
|
||||
|
||||
# Fallback: if player's attack mode deactivated early despite having timer left, they likely successfully rammed
|
||||
var t = local_player.get("attack_mode_timer")
|
||||
if local_player.get("is_attack_mode") == false and t != null and t > 0.0 and t < 3.0:
|
||||
break
|
||||
|
||||
await get_tree().process_frame
|
||||
|
||||
overlay.hide_objective()
|
||||
get_tree().paused = true
|
||||
overlay.show_overlay()
|
||||
await overlay.display_text("Smashing! You pushed it away. Notice your Battery Gauge was consumed.", true)
|
||||
|
||||
await overlay.display_text("Beware! Your opponents can RAM you to steal your tiles too. Get ready to defend yourself against the Bots!", true)
|
||||
|
||||
await overlay.display_text("Tutorial Complete! Returning to the Main Menu.", true)
|
||||
|
||||
overlay.hide_overlay()
|
||||
get_tree().paused = false
|
||||
_set_bots_enabled(true)
|
||||
|
||||
if main_scene and main_scene.get("goals_cycle_manager"):
|
||||
main_scene.goals_cycle_manager.set_process(true)
|
||||
|
||||
LobbyManager.is_tutorial_mode = false
|
||||
queue_free()
|
||||
LobbyManager.leave_room()
|
||||
get_tree().change_scene_to_file("res://scenes/lobby.tscn")
|
||||
|
||||
# ==============================================================================
|
||||
# Helper Floor Highlighting and Tile Detection Logic
|
||||
@@ -246,7 +386,7 @@ func _run_tutorial() -> void:
|
||||
|
||||
var _floor_highlights: Array[Node] = []
|
||||
|
||||
func _find_closest_tile(start_pos: Vector2i, types: Array) -> Vector2i:
|
||||
func _find_closest_tile(start_pos: Vector2i, types: Array, min_dist: int = 0) -> Vector2i:
|
||||
var gridmap = local_player.get("enhanced_gridmap")
|
||||
if not gridmap: return Vector2i(-1, -1)
|
||||
|
||||
@@ -258,7 +398,7 @@ func _find_closest_tile(start_pos: Vector2i, types: Array) -> Vector2i:
|
||||
var item = gridmap.get_cell_item(Vector3i(x, 1, z))
|
||||
if item in types:
|
||||
var dist = abs(start_pos.x - x) + abs(start_pos.y - z)
|
||||
if dist < best_dist:
|
||||
if dist >= min_dist and dist < best_dist:
|
||||
best_dist = dist
|
||||
best_pos = Vector2i(x, z)
|
||||
return best_pos
|
||||
|
||||
Reference in New Issue
Block a user