Initial MVP

This commit is contained in:
Aodhan Collins
2026-01-26 02:57:40 +00:00
commit b42521a008
33 changed files with 1004 additions and 0 deletions

221
scripts/GameManager.gd Normal file
View 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