mirror of
https://github.com/Nighthawk42/YodaStoriesNG.git
synced 2026-08-30 10:12:26 +00:00
Fix zone selection and add bot diagnostics
- Filter planet grid to only use 18x18 outdoor zones (exclude 9x9 rooms) - Add room exit detection in MissionSolver - Auto-start bot for debugging - Add zone grid visualization - Improve zone connection logging - Fix HandleBotAction direction handling Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
1d10d368a3
commit
1d962c20ce
@@ -612,6 +612,55 @@ public class MissionBot
|
||||
Console.WriteLine($"[BOT] Puzzle steps: {mission.PuzzleChain.Count}");
|
||||
Console.WriteLine($"[BOT] Current step: {mission.CurrentStep + 1}");
|
||||
}
|
||||
|
||||
// Log current zone connections
|
||||
LogCurrentZoneConnections();
|
||||
}
|
||||
|
||||
private void LogCurrentZoneConnections()
|
||||
{
|
||||
var world = _worldGenerator.CurrentWorld;
|
||||
if (world == null)
|
||||
{
|
||||
Console.WriteLine("[BOT] No world loaded");
|
||||
return;
|
||||
}
|
||||
|
||||
var zoneId = _state.CurrentZoneId;
|
||||
Console.WriteLine($"[BOT] Current zone: {zoneId} at position ({_state.PlayerX}, {_state.PlayerY})");
|
||||
|
||||
if (world.Connections.TryGetValue(zoneId, out var conn))
|
||||
{
|
||||
Console.WriteLine($"[BOT] Zone connections: N={conn.North?.ToString() ?? "none"}, S={conn.South?.ToString() ?? "none"}, E={conn.East?.ToString() ?? "none"}, W={conn.West?.ToString() ?? "none"}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[BOT] WARNING: Zone {zoneId} has no connection entry!");
|
||||
Console.WriteLine($"[BOT] Total connections in world: {world.Connections.Count}");
|
||||
Console.WriteLine($"[BOT] Connection zone IDs: {string.Join(", ", world.Connections.Keys.OrderBy(k => k).Take(20))}...");
|
||||
}
|
||||
|
||||
// Check if this zone is in the grid
|
||||
bool foundInGrid = false;
|
||||
if (world.Grid != null)
|
||||
{
|
||||
for (int y = 0; y < 10 && !foundInGrid; y++)
|
||||
{
|
||||
for (int x = 0; x < 10 && !foundInGrid; x++)
|
||||
{
|
||||
if (world.Grid[y, x] == zoneId)
|
||||
{
|
||||
Console.WriteLine($"[BOT] Zone {zoneId} is at grid position ({x}, {y})");
|
||||
foundInGrid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundInGrid)
|
||||
{
|
||||
Console.WriteLine($"[BOT] WARNING: Zone {zoneId} is NOT in the world grid (may be Dagobah or room)");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,6 +65,13 @@ public class MissionSolver
|
||||
var phase = GetCurrentPhase();
|
||||
var world = _worldGenerator.CurrentWorld;
|
||||
|
||||
// First priority: If we're in a room (small indoor zone), find the exit door
|
||||
var doorExit = CheckForRoomExit();
|
||||
if (doorExit != null)
|
||||
{
|
||||
return doorExit;
|
||||
}
|
||||
|
||||
switch (phase)
|
||||
{
|
||||
case MissionPhase.TalkToYoda:
|
||||
@@ -385,6 +392,42 @@ public class MissionSolver
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if we're in a small room zone that needs an exit door.
|
||||
/// Rooms are typically 9x9 zones with DoorExit objects.
|
||||
/// </summary>
|
||||
private BotObjective? CheckForRoomExit()
|
||||
{
|
||||
var zone = _state.CurrentZone;
|
||||
if (zone == null) return null;
|
||||
|
||||
// Rooms are typically 9x9 (or smaller than 18x18 outdoor zones)
|
||||
bool isRoom = zone.Width < 18 || zone.Height < 18;
|
||||
if (!isRoom) return null;
|
||||
|
||||
// Find exit door in this room
|
||||
var exitDoor = zone.Objects.FirstOrDefault(o =>
|
||||
o.Type == ZoneObjectType.DoorExit ||
|
||||
o.Type == ZoneObjectType.Teleporter);
|
||||
|
||||
if (exitDoor != null)
|
||||
{
|
||||
Console.WriteLine($"[BOT] In room zone {_state.CurrentZoneId} ({zone.Width}x{zone.Height}), found exit at ({exitDoor.X},{exitDoor.Y})");
|
||||
return new BotObjective
|
||||
{
|
||||
Type = ObjectiveType.EnterDoor,
|
||||
Description = $"Exit room via door at ({exitDoor.X},{exitDoor.Y})",
|
||||
TargetX = exitDoor.X,
|
||||
TargetY = exitDoor.Y,
|
||||
TargetZoneId = exitDoor.Argument != 0xFFFF ? exitDoor.Argument : null
|
||||
};
|
||||
}
|
||||
|
||||
// No exit door found in room - this shouldn't happen
|
||||
Console.WriteLine($"[BOT] WARNING: In room zone {_state.CurrentZoneId} but no exit door found!");
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds Yoda NPC in current zone.
|
||||
/// </summary>
|
||||
|
||||
@@ -193,11 +193,14 @@ public unsafe class GameEngine : IDisposable
|
||||
_actionExecutor.OnDialogue += (speaker, text) => _messages.ShowDialogue(speaker, text);
|
||||
}
|
||||
|
||||
// Initialize bot (but don't start it)
|
||||
// Initialize bot and auto-start for debugging
|
||||
if (_worldGenerator != null)
|
||||
{
|
||||
_bot = new MissionBot(_state, _gameData!, _worldGenerator);
|
||||
_bot.OnActionRequested += HandleBotAction;
|
||||
// Auto-start bot for debugging zone transitions
|
||||
_bot.Start();
|
||||
_messages.ShowMessage("Bot AUTO-STARTED - Press B to disable", MessageType.System);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -686,14 +686,21 @@ public class WorldGenerator
|
||||
// Initialize grid
|
||||
CurrentWorld.Grid = new int?[GridSize, GridSize];
|
||||
|
||||
// Find zones matching the mission's planet type
|
||||
// Find outdoor zones matching the mission's planet type (18x18 only, not 9x9 rooms)
|
||||
var planetZones = _gameData.Zones
|
||||
.Where(z => z.Planet == CurrentMission.Planet && z.Width > 0)
|
||||
.Where(z => z.Planet == CurrentMission.Planet && z.Width == 18 && z.Height == 18)
|
||||
.ToList();
|
||||
|
||||
// Also find indoor rooms for this planet (for door connections later)
|
||||
var roomZones = _gameData.Zones
|
||||
.Where(z => z.Planet == CurrentMission.Planet && z.Width > 0 && z.Width < 18)
|
||||
.ToList();
|
||||
|
||||
Console.WriteLine($"Planet {CurrentMission.Planet}: found {planetZones.Count} outdoor zones, {roomZones.Count} room zones");
|
||||
|
||||
if (planetZones.Count == 0)
|
||||
{
|
||||
Console.WriteLine($"Warning: No zones found for planet {CurrentMission.Planet}");
|
||||
Console.WriteLine($"Warning: No outdoor zones found for planet {CurrentMission.Planet}");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -702,9 +709,8 @@ public class WorldGenerator
|
||||
var townZones = planetZones.Where(z => z.Type == ZoneType.Town).ToList();
|
||||
var goalZones = planetZones.Where(z => z.Type == ZoneType.Goal).ToList();
|
||||
var puzzleZones = planetZones.Where(z => z.Type == ZoneType.Trade || z.Type == ZoneType.Use || z.Type == ZoneType.Find).ToList();
|
||||
var roomZones = planetZones.Where(z => z.Type == ZoneType.Room).ToList();
|
||||
|
||||
// If no categorization, use all zones
|
||||
// If no categorization, use all outdoor zones
|
||||
if (emptyZones.Count == 0) emptyZones = planetZones;
|
||||
|
||||
// 1. Place Landing Cell near center (one of 4 central squares)
|
||||
@@ -795,6 +801,9 @@ public class WorldGenerator
|
||||
{
|
||||
if (CurrentWorld == null) return;
|
||||
|
||||
Console.WriteLine($"[WORLD] Setting up zone connections for {GridSize}x{GridSize} grid...");
|
||||
int connectionCount = 0;
|
||||
|
||||
for (int y = 0; y < GridSize; y++)
|
||||
{
|
||||
for (int x = 0; x < GridSize; x++)
|
||||
@@ -815,8 +824,29 @@ public class WorldGenerator
|
||||
connections.South = CurrentWorld.Grid[y + 1, x];
|
||||
|
||||
CurrentWorld.Connections[zoneId.Value] = connections;
|
||||
|
||||
// Count actual connections
|
||||
if (connections.North.HasValue) connectionCount++;
|
||||
if (connections.South.HasValue) connectionCount++;
|
||||
if (connections.East.HasValue) connectionCount++;
|
||||
if (connections.West.HasValue) connectionCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"[WORLD] Created {CurrentWorld.Connections.Count} zone entries with {connectionCount} total connections");
|
||||
|
||||
// Print grid visualization
|
||||
Console.WriteLine("[WORLD] Zone grid (10x10):");
|
||||
for (int y = 0; y < GridSize; y++)
|
||||
{
|
||||
var row = "";
|
||||
for (int x = 0; x < GridSize; x++)
|
||||
{
|
||||
var zoneId = CurrentWorld.Grid[y, x];
|
||||
row += zoneId.HasValue ? $"{zoneId.Value,4}" : " .";
|
||||
}
|
||||
Console.WriteLine($" {row}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -698,75 +698,66 @@ public class DtaParser
|
||||
{
|
||||
var startPos = _reader.BaseStream.Position;
|
||||
var endPos = startPos + length;
|
||||
|
||||
// Debug: dump first 32 bytes to understand structure
|
||||
var debugBytes = _reader.ReadBytes(Math.Min(32, (int)length));
|
||||
_reader.BaseStream.Seek(startPos, SeekOrigin.Begin);
|
||||
Console.WriteLine($"PUZ2 first 32 bytes: {BitConverter.ToString(debugBytes)}");
|
||||
|
||||
// Try to find IPUZ markers in the section
|
||||
int puzzleId = 0;
|
||||
|
||||
// First, read the puzzle count (2 bytes)
|
||||
var puzzleCount = _reader.ReadUInt16();
|
||||
Console.WriteLine($"PUZ2 section: expecting {puzzleCount} puzzles");
|
||||
|
||||
while (_reader.BaseStream.Position < endPos - 4 && puzzleId < puzzleCount)
|
||||
while (_reader.BaseStream.Position < endPos - 8)
|
||||
{
|
||||
// Look for IPUZ marker (similar to IZON in zones)
|
||||
var markerPos = _reader.BaseStream.Position;
|
||||
var markerBytes = _reader.ReadBytes(4);
|
||||
var marker = System.Text.Encoding.ASCII.GetString(markerBytes);
|
||||
|
||||
if (marker != "IPUZ")
|
||||
if (marker == "IPUZ")
|
||||
{
|
||||
// Not at IPUZ marker, try to find it
|
||||
// Found IPUZ marker - read puzzle size
|
||||
var puzzleSize = _reader.ReadUInt32();
|
||||
var puzzleDataStart = _reader.BaseStream.Position;
|
||||
|
||||
var puzzle = new Puzzle { Id = puzzleId++ };
|
||||
|
||||
// IPUZ format: type(2), item1(2), item2(2), unknown(2), unknown(2), then 5 strings
|
||||
if (puzzleSize >= 10)
|
||||
{
|
||||
var puzzleType = _reader.ReadUInt16();
|
||||
puzzle.Type = (PuzzleType)puzzleType;
|
||||
puzzle.Item1 = _reader.ReadUInt16();
|
||||
puzzle.Item2 = _reader.ReadUInt16();
|
||||
_reader.ReadUInt16(); // unknown
|
||||
_reader.ReadUInt16(); // unknown
|
||||
|
||||
// Read strings
|
||||
for (int i = 0; i < 5 && _reader.BaseStream.Position < puzzleDataStart + puzzleSize; i++)
|
||||
{
|
||||
var strLen = _reader.ReadUInt16();
|
||||
if (strLen > 0 && strLen < 500)
|
||||
{
|
||||
var strBytes = _reader.ReadBytes(strLen);
|
||||
puzzle.Strings.Add(System.Text.Encoding.ASCII.GetString(strBytes).TrimEnd('\0'));
|
||||
}
|
||||
else if (strLen == 0)
|
||||
{
|
||||
puzzle.Strings.Add("");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to end of this puzzle
|
||||
_reader.BaseStream.Seek(puzzleDataStart + puzzleSize, SeekOrigin.Begin);
|
||||
_data.Puzzles.Add(puzzle);
|
||||
|
||||
if (puzzleId <= 5)
|
||||
Console.WriteLine($" Puzzle {puzzle.Id}: Type={puzzle.Type}, Item1={puzzle.Item1}, Item2={puzzle.Item2}, Str0={puzzle.Strings.FirstOrDefault() ?? ""}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Not at IPUZ, advance by 1 byte and try again
|
||||
_reader.BaseStream.Seek(markerPos + 1, SeekOrigin.Begin);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Found IPUZ marker - read puzzle size
|
||||
var puzzleSize = _reader.ReadUInt32();
|
||||
var puzzleDataStart = _reader.BaseStream.Position;
|
||||
|
||||
var puzzle = new Puzzle { Id = puzzleId++ };
|
||||
|
||||
// IPUZ format (after size):
|
||||
// - 2 bytes: puzzle type (0=Quest, 1=Transport, 2=Trade, 3=Use, 4=Goal)
|
||||
// - 2 bytes: item1 (item to bring/find)
|
||||
// - 2 bytes: item2 (reward item or NPC tile)
|
||||
// - 2 bytes: unknown flags
|
||||
// - 2 bytes: unknown (possibly planet)
|
||||
// - 5 strings (length-prefixed)
|
||||
var puzzleType = _reader.ReadUInt16();
|
||||
puzzle.Type = (PuzzleType)puzzleType;
|
||||
puzzle.Item1 = _reader.ReadUInt16();
|
||||
puzzle.Item2 = _reader.ReadUInt16();
|
||||
var unknown2 = _reader.ReadUInt16();
|
||||
var unknown3 = _reader.ReadUInt16();
|
||||
|
||||
// Read puzzle strings (5 strings typically)
|
||||
// String 0: Puzzle name/description (e.g., "Give MUSHROOM to YODA")
|
||||
// String 1: Hint text
|
||||
// String 2-4: Additional dialogue
|
||||
for (int i = 0; i < 5; i++)
|
||||
{
|
||||
if (_reader.BaseStream.Position >= puzzleDataStart + puzzleSize)
|
||||
break;
|
||||
|
||||
var strLen = _reader.ReadUInt16();
|
||||
if (strLen > 0 && strLen < 1000)
|
||||
{
|
||||
var strBytes = _reader.ReadBytes(strLen);
|
||||
puzzle.Strings.Add(System.Text.Encoding.ASCII.GetString(strBytes).TrimEnd('\0'));
|
||||
}
|
||||
else if (strLen == 0)
|
||||
{
|
||||
puzzle.Strings.Add("");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Invalid length, skip rest
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip to end of this puzzle's data
|
||||
_reader.BaseStream.Seek(puzzleDataStart + puzzleSize, SeekOrigin.Begin);
|
||||
_data.Puzzles.Add(puzzle);
|
||||
}
|
||||
|
||||
// Ensure we're at the end
|
||||
|
||||
Reference in New Issue
Block a user