extends Node signal room_changed(room_data: RoomData) signal log_message(message: String) signal stats_changed(hp: int, max_hp: int, inventory: Array) signal combat_started(enemy: Resource) signal combat_ended(won: bool) signal combat_log(message: String) signal damage_taken(amount: int) var game_state: GameState var rooms: Dictionary = {} # id -> RoomData var llm_service: LLMService var audio_manager: Node var current_enemy: Resource = null func _ready(): llm_service = LLMService.new() add_child(llm_service) llm_service.response_received.connect(_on_llm_response) llm_service.error_occurred.connect(func(msg): emit_signal("log_message", "Error: " + msg)) audio_manager = load("res://scripts/AudioManager.gd").new() add_child(audio_manager) rooms = WorldData.generate_procedural_world(20) start_new_game() func start_new_game(): game_state = GameState.new() game_state.current_room_id = "room_0_0" _update_stats_ui() _load_room(game_state.current_room_id) func _load_room(room_id: String): if room_id in rooms: var room = rooms[room_id] # Update Exploration if not room_id in game_state.explored_rooms: game_state.explored_rooms.append(room_id) emit_signal("room_changed", room) # Handle Description if room.generated_description != "": emit_signal("log_message", room.generated_description) else: _request_room_description(room) else: emit_signal("log_message", "Error: Room " + room_id + " not found.") func move(direction: String): var current_room = rooms.get(game_state.current_room_id) if current_room and direction in current_room.exits: game_state.current_room_id = current_room.exits[direction] _load_room(game_state.current_room_id) else: emit_signal("log_message", "You can't go that way.") func pickup_item(item_name: String): var current_room = rooms[game_state.current_room_id] var item_to_pickup = null for item in current_room.items: if item.name.to_lower() in item_name.to_lower() or item.id == item_name.to_lower(): item_to_pickup = item break if item_to_pickup: current_room.items.erase(item_to_pickup) game_state.inventory.append(item_to_pickup) _update_stats_ui() emit_signal("log_message", "You picked up " + item_to_pickup.name) else: emit_signal("log_message", "There is no " + item_name + " here.") func save_game(): var error = ResourceSaver.save(game_state, "user://savegame.tres") if error == OK: emit_signal("log_message", "Game Saved.") else: emit_signal("log_message", "Error saving game: " + str(error)) func load_game(): if ResourceLoader.exists("user://savegame.tres"): game_state = ResourceLoader.load("user://savegame.tres") _update_stats_ui() _load_room(game_state.current_room_id) emit_signal("log_message", "Game Loaded.") else: emit_signal("log_message", "No save file found.") func _update_stats_ui(): emit_signal("stats_changed", game_state.player_hp, game_state.player_max_hp, game_state.inventory) # --- Combat System --- func start_combat(enemy: Resource): current_enemy = enemy emit_signal("combat_started", enemy) emit_signal("log_message", "Combat started with " + enemy.name + "!") func player_attack(): if not current_enemy: return # Calculate damage (simple for now) var damage = 5 # Base damage # Check for weapon for item in game_state.inventory: if item.effect_type == "DAMAGE": damage += item.effect_value current_enemy.hp -= damage audio_manager.play_hit() emit_signal("combat_log", "You hit " + current_enemy.name + " for " + str(damage) + " damage.") if current_enemy.hp <= 0: _win_combat() else: _enemy_turn() func _enemy_turn(): var damage = current_enemy.damage game_state.player_hp -= damage _update_stats_ui() audio_manager.play_hit() emit_signal("damage_taken", damage) emit_signal("combat_log", current_enemy.name + " hits you for " + str(damage) + " damage.") if game_state.player_hp <= 0: emit_signal("log_message", "You died!") # Handle death (reload?) func _win_combat(): emit_signal("combat_log", "You defeated " + current_enemy.name + "!") # Remove enemy from room var room = rooms[game_state.current_room_id] room.enemies.erase(current_enemy) current_enemy = null emit_signal("combat_ended", true) func flee(): emit_signal("log_message", "You fled!") current_enemy = null emit_signal("combat_ended", false) # --- AI Integration --- func process_user_input(text: String): emit_signal("log_message", "> " + text) var prompt = _construct_system_prompt() llm_service.send_prompt(prompt, text) func _construct_system_prompt() -> String: var room = rooms[game_state.current_room_id] var prompt = """ You are the Game Master for a text adventure. Current Room: %s Description: %s Exits: %s Your goal is to interpret the user's input and return a JSON object describing the outcome. JSON Format: { "narrative": "Description of what happens...", "action": { "type": "MOVE" | "PICKUP" | "COMBAT" | "NONE", "target": "north" | "item_name" | "enemy_name" | null } } Rules: - If user says "go north" and north is an exit, return action type "MOVE", target "north". - If user says "attack goblin" and goblin is in room, return action type "COMBAT", target "goblin". - If user says "look", just describe the room in "narrative". - Keep narrative concise (2-3 sentences). - Do NOT list player stats or inventory in the narrative. """ % [room.room_name, room.description, str(room.exits.keys())] return prompt func _request_room_description(room: RoomData): var prompt = """ You are a creative writer for a text adventure. Describe the following location. Name: %s Base Details: %s Exits: %s Output JSON: { "narrative": "The atmospheric description...", "save_as_description": true } """ % [room.room_name, room.description, str(room.exits.keys())] llm_service.send_prompt(prompt, "Describe this place.") func _on_llm_response(response: Dictionary): if "narrative" in response: emit_signal("log_message", response["narrative"]) if response.get("save_as_description") == true: var room = rooms[game_state.current_room_id] room.generated_description = response["narrative"] if "action" in response: var action = response["action"] match action.get("type"): "MOVE": move(action.get("target")) "PICKUP": pickup_item(action.get("target")) "COMBAT": var target = action.get("target") var room = rooms[game_state.current_room_id] for enemy in room.enemies: if enemy.name.to_lower() in target.to_lower() or enemy.id == target.to_lower(): start_combat(enemy) break