Initial MVP
This commit is contained in:
221
scripts/GameManager.gd
Normal file
221
scripts/GameManager.gd
Normal file
@@ -0,0 +1,221 @@
|
||||
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_test_world()
|
||||
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
|
||||
Reference in New Issue
Block a user