feat: Add overworld sprite and entrance commands to agent tool
- Implemented new commands for listing overworld sprites and retrieving entrance details. - Enhanced CLI functionality to support filtering by map, world, and sprite ID with JSON and text output formats. - Introduced tile statistics analysis command for detailed tile usage insights. - Updated function schemas and system prompts to reflect the new commands and their parameters.
This commit is contained in:
@@ -46,6 +46,15 @@ absl::Status HandleOverworldDescribeMapCommand(
|
||||
absl::Status HandleOverworldListWarpsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldListSpritesCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldGetEntranceCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleOverworldTileStatsCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
absl::Status HandleMessageListCommand(
|
||||
const std::vector<std::string>& arg_vec,
|
||||
Rom* rom_context = nullptr);
|
||||
|
||||
@@ -1180,6 +1180,346 @@ absl::Status HandleOverworldListWarpsCommand(
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldListSpritesCommand(
|
||||
const std::vector<std::string>& arg_vec, Rom* rom_context) {
|
||||
std::optional<std::string> map_value;
|
||||
std::optional<std::string> world_value;
|
||||
std::string format = "json";
|
||||
std::optional<std::string> rom_override;
|
||||
|
||||
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
||||
const std::string& token = arg_vec[i];
|
||||
if (token == "--map") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--map requires a value.");
|
||||
}
|
||||
map_value = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--map=")) {
|
||||
map_value = token.substr(6);
|
||||
} else if (token == "--world") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--world requires a value.");
|
||||
}
|
||||
world_value = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--world=")) {
|
||||
world_value = token.substr(8);
|
||||
} else if (token == "--format") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--format requires a value.");
|
||||
}
|
||||
format = absl::AsciiStrToLower(arg_vec[++i]);
|
||||
} else if (absl::StartsWith(token, "--format=")) {
|
||||
format = absl::AsciiStrToLower(token.substr(9));
|
||||
} else if (token == "--rom") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--rom requires a value.");
|
||||
}
|
||||
rom_override = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--rom=")) {
|
||||
rom_override = token.substr(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (format != "json" && format != "text") {
|
||||
return absl::InvalidArgumentError("--format must be either json or text");
|
||||
}
|
||||
|
||||
Rom rom_storage;
|
||||
Rom* rom = nullptr;
|
||||
if (rom_context != nullptr && rom_context->is_loaded() && !rom_override.has_value()) {
|
||||
rom = rom_context;
|
||||
} else {
|
||||
ASSIGN_OR_RETURN(auto rom_or, LoadRomFromPathOrFlag(rom_override));
|
||||
rom_storage = std::move(rom_or);
|
||||
rom = &rom_storage;
|
||||
}
|
||||
|
||||
zelda3::Overworld overworld_data(rom);
|
||||
auto load_status = overworld_data.Load(rom);
|
||||
if (!load_status.ok()) {
|
||||
return load_status;
|
||||
}
|
||||
|
||||
overworld::SpriteQuery query;
|
||||
if (map_value.has_value()) {
|
||||
ASSIGN_OR_RETURN(int map_id, overworld::ParseNumeric(*map_value));
|
||||
query.map_id = map_id;
|
||||
}
|
||||
if (world_value.has_value()) {
|
||||
ASSIGN_OR_RETURN(int world_id, overworld::ParseWorldSpecifier(*world_value));
|
||||
query.world = world_id;
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto sprites, overworld::CollectOverworldSprites(overworld_data, query));
|
||||
|
||||
if (format == "json") {
|
||||
std::cout << "{\n";
|
||||
std::cout << absl::StrFormat(" \"count\": %zu,\n", sprites.size());
|
||||
std::cout << " \"sprites\": [\n";
|
||||
for (size_t i = 0; i < sprites.size(); ++i) {
|
||||
const auto& sprite = sprites[i];
|
||||
std::cout << " {\n";
|
||||
std::cout << absl::StrFormat(" \"sprite_id\": \"0x%02X\",\n", sprite.sprite_id);
|
||||
std::cout << absl::StrFormat(" \"map\": \"0x%02X\",\n", sprite.map_id);
|
||||
std::cout << absl::StrFormat(" \"world\": \"%s\",\n", overworld::WorldName(sprite.world));
|
||||
std::cout << absl::StrFormat(" \"position\": {\"x\": %d, \"y\": %d}", sprite.x, sprite.y);
|
||||
if (sprite.sprite_name.has_value()) {
|
||||
std::cout << absl::StrFormat(",\n \"name\": \"%s\"", *sprite.sprite_name);
|
||||
}
|
||||
std::cout << "\n }" << (i + 1 == sprites.size() ? "" : ",") << "\n";
|
||||
}
|
||||
std::cout << " ]\n";
|
||||
std::cout << "}\n";
|
||||
} else {
|
||||
std::cout << absl::StrFormat("🎮 Overworld Sprites (%zu)\n", sprites.size());
|
||||
for (const auto& sprite : sprites) {
|
||||
std::cout << absl::StrFormat(" • Sprite 0x%02X @ Map 0x%02X (%s) pos(%d,%d)",
|
||||
sprite.sprite_id, sprite.map_id,
|
||||
overworld::WorldName(sprite.world),
|
||||
sprite.x, sprite.y);
|
||||
if (sprite.sprite_name.has_value()) {
|
||||
std::cout << " - " << *sprite.sprite_name;
|
||||
}
|
||||
std::cout << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldGetEntranceCommand(
|
||||
const std::vector<std::string>& arg_vec, Rom* rom_context) {
|
||||
std::optional<std::string> entrance_id_str;
|
||||
std::string format = "json";
|
||||
std::optional<std::string> rom_override;
|
||||
|
||||
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
||||
const std::string& token = arg_vec[i];
|
||||
if (token == "--id") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--id requires a value.");
|
||||
}
|
||||
entrance_id_str = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--id=")) {
|
||||
entrance_id_str = token.substr(5);
|
||||
} else if (token == "--format") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--format requires a value.");
|
||||
}
|
||||
format = absl::AsciiStrToLower(arg_vec[++i]);
|
||||
} else if (absl::StartsWith(token, "--format=")) {
|
||||
format = absl::AsciiStrToLower(token.substr(9));
|
||||
} else if (token == "--rom") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--rom requires a value.");
|
||||
}
|
||||
rom_override = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--rom=")) {
|
||||
rom_override = token.substr(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (!entrance_id_str.has_value()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: overworld-get-entrance --id <entrance_id> [--format <json|text>]");
|
||||
}
|
||||
|
||||
if (format != "json" && format != "text") {
|
||||
return absl::InvalidArgumentError("--format must be either json or text");
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(int entrance_id, overworld::ParseNumeric(*entrance_id_str));
|
||||
if (entrance_id < 0 || entrance_id > 255) {
|
||||
return absl::InvalidArgumentError("Entrance ID must be between 0 and 255");
|
||||
}
|
||||
|
||||
Rom rom_storage;
|
||||
Rom* rom = nullptr;
|
||||
if (rom_context != nullptr && rom_context->is_loaded() && !rom_override.has_value()) {
|
||||
rom = rom_context;
|
||||
} else {
|
||||
ASSIGN_OR_RETURN(auto rom_or, LoadRomFromPathOrFlag(rom_override));
|
||||
rom_storage = std::move(rom_or);
|
||||
rom = &rom_storage;
|
||||
}
|
||||
|
||||
zelda3::Overworld overworld_data(rom);
|
||||
auto load_status = overworld_data.Load(rom);
|
||||
if (!load_status.ok()) {
|
||||
return load_status;
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto entrance, overworld::GetEntranceDetails(overworld_data,
|
||||
static_cast<uint8_t>(entrance_id)));
|
||||
|
||||
if (format == "json") {
|
||||
std::cout << "{\n";
|
||||
std::cout << absl::StrFormat(" \"entrance_id\": %d,\n", entrance.entrance_id);
|
||||
std::cout << absl::StrFormat(" \"map\": \"0x%02X\",\n", entrance.map_id);
|
||||
std::cout << absl::StrFormat(" \"world\": \"%s\",\n", overworld::WorldName(entrance.world));
|
||||
std::cout << absl::StrFormat(" \"position\": {\"x\": %d, \"y\": %d},\n", entrance.x, entrance.y);
|
||||
std::cout << absl::StrFormat(" \"room_id\": \"0x%04X\",\n", entrance.room_id);
|
||||
std::cout << absl::StrFormat(" \"door_type_1\": \"0x%04X\",\n", entrance.door_type_1);
|
||||
std::cout << absl::StrFormat(" \"door_type_2\": \"0x%04X\",\n", entrance.door_type_2);
|
||||
std::cout << absl::StrFormat(" \"is_hole\": %s", entrance.is_hole ? "true" : "false");
|
||||
if (entrance.entrance_name.has_value()) {
|
||||
std::cout << absl::StrFormat(",\n \"entrance_name\": \"%s\"", *entrance.entrance_name);
|
||||
}
|
||||
if (entrance.room_name.has_value()) {
|
||||
std::cout << absl::StrFormat(",\n \"room_name\": \"%s\"", *entrance.room_name);
|
||||
}
|
||||
std::cout << "\n}\n";
|
||||
} else {
|
||||
std::cout << absl::StrFormat("🚪 Entrance #%d\n", entrance.entrance_id);
|
||||
if (entrance.entrance_name.has_value()) {
|
||||
std::cout << "Name: " << *entrance.entrance_name << "\n";
|
||||
}
|
||||
std::cout << absl::StrFormat("Map: 0x%02X (%s World)\n", entrance.map_id,
|
||||
overworld::WorldName(entrance.world));
|
||||
std::cout << absl::StrFormat("Position: (%d, %d)\n", entrance.x, entrance.y);
|
||||
std::cout << absl::StrFormat("→ Leads to Room 0x%04X", entrance.room_id);
|
||||
if (entrance.room_name.has_value()) {
|
||||
std::cout << " (" << *entrance.room_name << ")";
|
||||
}
|
||||
std::cout << "\n";
|
||||
std::cout << absl::StrFormat("Door Types: 0x%04X / 0x%04X\n",
|
||||
entrance.door_type_1, entrance.door_type_2);
|
||||
std::cout << absl::StrFormat("Is Hole: %s\n", entrance.is_hole ? "yes" : "no");
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleOverworldTileStatsCommand(
|
||||
const std::vector<std::string>& arg_vec, Rom* rom_context) {
|
||||
std::optional<std::string> tile_value;
|
||||
std::optional<std::string> map_value;
|
||||
std::optional<std::string> world_value;
|
||||
std::string format = "json";
|
||||
std::optional<std::string> rom_override;
|
||||
|
||||
for (size_t i = 0; i < arg_vec.size(); ++i) {
|
||||
const std::string& token = arg_vec[i];
|
||||
if (token == "--tile") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--tile requires a value.");
|
||||
}
|
||||
tile_value = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--tile=")) {
|
||||
tile_value = token.substr(7);
|
||||
} else if (token == "--map") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--map requires a value.");
|
||||
}
|
||||
map_value = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--map=")) {
|
||||
map_value = token.substr(6);
|
||||
} else if (token == "--world") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--world requires a value.");
|
||||
}
|
||||
world_value = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--world=")) {
|
||||
world_value = token.substr(8);
|
||||
} else if (token == "--format") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--format requires a value.");
|
||||
}
|
||||
format = absl::AsciiStrToLower(arg_vec[++i]);
|
||||
} else if (absl::StartsWith(token, "--format=")) {
|
||||
format = absl::AsciiStrToLower(token.substr(9));
|
||||
} else if (token == "--rom") {
|
||||
if (i + 1 >= arg_vec.size()) {
|
||||
return absl::InvalidArgumentError("--rom requires a value.");
|
||||
}
|
||||
rom_override = arg_vec[++i];
|
||||
} else if (absl::StartsWith(token, "--rom=")) {
|
||||
rom_override = token.substr(6);
|
||||
}
|
||||
}
|
||||
|
||||
if (!tile_value.has_value()) {
|
||||
return absl::InvalidArgumentError(
|
||||
"Usage: overworld-tile-stats --tile <id> [--map <id>] [--world <light|dark|special>] [--format <json|text>]");
|
||||
}
|
||||
|
||||
if (format != "json" && format != "text") {
|
||||
return absl::InvalidArgumentError("--format must be either json or text");
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(int tile_numeric, overworld::ParseNumeric(*tile_value));
|
||||
if (tile_numeric < 0 || tile_numeric > 0xFFFF) {
|
||||
return absl::InvalidArgumentError("Tile ID must be between 0x0000 and 0xFFFF");
|
||||
}
|
||||
|
||||
overworld::TileSearchOptions options;
|
||||
if (map_value.has_value()) {
|
||||
ASSIGN_OR_RETURN(int map_id, overworld::ParseNumeric(*map_value));
|
||||
options.map_id = map_id;
|
||||
}
|
||||
if (world_value.has_value()) {
|
||||
ASSIGN_OR_RETURN(int world_id, overworld::ParseWorldSpecifier(*world_value));
|
||||
options.world = world_id;
|
||||
}
|
||||
|
||||
Rom rom_storage;
|
||||
Rom* rom = nullptr;
|
||||
if (rom_context != nullptr && rom_context->is_loaded() && !rom_override.has_value()) {
|
||||
rom = rom_context;
|
||||
} else {
|
||||
ASSIGN_OR_RETURN(auto rom_or, LoadRomFromPathOrFlag(rom_override));
|
||||
rom_storage = std::move(rom_or);
|
||||
rom = &rom_storage;
|
||||
}
|
||||
|
||||
zelda3::Overworld overworld_data(rom);
|
||||
auto load_status = overworld_data.Load(rom);
|
||||
if (!load_status.ok()) {
|
||||
return load_status;
|
||||
}
|
||||
|
||||
ASSIGN_OR_RETURN(auto stats, overworld::AnalyzeTileUsage(overworld_data,
|
||||
static_cast<uint16_t>(tile_numeric),
|
||||
options));
|
||||
|
||||
if (format == "json") {
|
||||
std::cout << "{\n";
|
||||
std::cout << absl::StrFormat(" \"tile\": \"0x%04X\",\n", stats.tile_id);
|
||||
if (stats.map_id >= 0) {
|
||||
std::cout << absl::StrFormat(" \"map\": \"0x%02X\",\n", stats.map_id);
|
||||
std::cout << absl::StrFormat(" \"world\": \"%s\",\n", overworld::WorldName(stats.world));
|
||||
}
|
||||
std::cout << absl::StrFormat(" \"total_count\": %d,\n", stats.count);
|
||||
std::cout << " \"sample_positions\": [\n";
|
||||
size_t limit = std::min<size_t>(stats.positions.size(), 10);
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
const auto& pos = stats.positions[i];
|
||||
std::cout << absl::StrFormat(" {\"x\": %d, \"y\": %d}", pos.first, pos.second);
|
||||
if (i + 1 < limit) std::cout << ",";
|
||||
std::cout << "\n";
|
||||
}
|
||||
std::cout << " ]\n";
|
||||
std::cout << "}\n";
|
||||
} else {
|
||||
std::cout << absl::StrFormat("📊 Tile 0x%04X Statistics\n", stats.tile_id);
|
||||
if (stats.map_id >= 0) {
|
||||
std::cout << absl::StrFormat("Map: 0x%02X (%s World)\n", stats.map_id,
|
||||
overworld::WorldName(stats.world));
|
||||
}
|
||||
std::cout << absl::StrFormat("Total Count: %d occurrences\n", stats.count);
|
||||
if (!stats.positions.empty()) {
|
||||
std::cout << "Sample Positions (first 10):\n";
|
||||
size_t limit = std::min<size_t>(stats.positions.size(), 10);
|
||||
for (size_t i = 0; i < limit; ++i) {
|
||||
const auto& pos = stats.positions[i];
|
||||
std::cout << absl::StrFormat(" (%d, %d)\n", pos.first, pos.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return absl::OkStatus();
|
||||
}
|
||||
|
||||
absl::Status HandleMessageListCommand(
|
||||
const std::vector<std::string>& arg_vec, Rom* rom_context) {
|
||||
return yaze::cli::message::HandleMessageListCommand(arg_vec, rom_context);
|
||||
|
||||
@@ -43,7 +43,8 @@ absl::StatusOr<Rom> LoadRomFromFlag() {
|
||||
}
|
||||
|
||||
std::vector<editor::MessageData> LoadMessages(Rom* rom) {
|
||||
return editor::ReadAllTextData(rom->data(), editor::kTextData);
|
||||
// Fix: Cast away constness for ReadAllTextData, which expects uint8_t*
|
||||
return editor::ReadAllTextData(const_cast<uint8_t*>(rom->data()), editor::kTextData);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -386,6 +386,115 @@ absl::StatusOr<std::vector<TileMatch>> FindTileMatches(
|
||||
return matches;
|
||||
}
|
||||
|
||||
absl::StatusOr<std::vector<OverworldSprite>> CollectOverworldSprites(
|
||||
const zelda3::Overworld& overworld, const SpriteQuery& query) {
|
||||
std::vector<OverworldSprite> results;
|
||||
|
||||
// Iterate through all 3 game states (beginning, zelda, agahnim)
|
||||
for (int game_state = 0; game_state < 3; ++game_state) {
|
||||
const auto& sprites = overworld.sprites(game_state);
|
||||
|
||||
for (const auto& sprite : sprites) {
|
||||
// Apply filters
|
||||
if (query.sprite_id.has_value() && sprite.id() != *query.sprite_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int map_id = sprite.map_id();
|
||||
if (query.map_id.has_value() && map_id != *query.map_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Determine world from map_id
|
||||
int world = (map_id >= kSpecialWorldOffset) ? 2
|
||||
: (map_id >= kDarkWorldOffset ? 1 : 0);
|
||||
|
||||
if (query.world.has_value() && world != *query.world) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OverworldSprite entry;
|
||||
entry.sprite_id = sprite.id();
|
||||
entry.map_id = map_id;
|
||||
entry.world = world;
|
||||
entry.x = sprite.x();
|
||||
entry.y = sprite.y();
|
||||
// Sprite names would come from a label system if available
|
||||
// entry.sprite_name = GetSpriteName(sprite.id());
|
||||
|
||||
results.push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
absl::StatusOr<EntranceDetails> GetEntranceDetails(
|
||||
const zelda3::Overworld& overworld, uint8_t entrance_id) {
|
||||
const auto& entrances = overworld.entrances();
|
||||
|
||||
if (entrance_id >= entrances.size()) {
|
||||
return absl::NotFoundError(
|
||||
absl::StrFormat("Entrance %d not found (max: %d)",
|
||||
entrance_id, entrances.size() - 1));
|
||||
}
|
||||
|
||||
const auto& entrance = entrances[entrance_id];
|
||||
|
||||
EntranceDetails details;
|
||||
details.entrance_id = entrance_id;
|
||||
details.map_id = entrance.map_id_;
|
||||
|
||||
// Determine world from map_id
|
||||
details.world = (details.map_id >= kSpecialWorldOffset) ? 2
|
||||
: (details.map_id >= kDarkWorldOffset ? 1 : 0);
|
||||
|
||||
details.x = entrance.x_;
|
||||
details.y = entrance.y_;
|
||||
details.area_x = entrance.area_x_;
|
||||
details.area_y = entrance.area_y_;
|
||||
details.map_pos = entrance.map_pos_;
|
||||
details.is_hole = entrance.is_hole_;
|
||||
|
||||
// Get entrance name if available
|
||||
details.entrance_name = EntranceLabel(entrance_id);
|
||||
|
||||
return details;
|
||||
}
|
||||
|
||||
absl::StatusOr<TileStatistics> AnalyzeTileUsage(
|
||||
zelda3::Overworld& overworld, uint16_t tile_id,
|
||||
const TileSearchOptions& options) {
|
||||
|
||||
// Use FindTileMatches to get all occurrences
|
||||
ASSIGN_OR_RETURN(auto matches, FindTileMatches(overworld, tile_id, options));
|
||||
|
||||
TileStatistics stats;
|
||||
stats.tile_id = tile_id;
|
||||
stats.count = static_cast<int>(matches.size());
|
||||
|
||||
// If scoped to a specific map, store that info
|
||||
if (options.map_id.has_value()) {
|
||||
stats.map_id = *options.map_id;
|
||||
if (options.world.has_value()) {
|
||||
stats.world = *options.world;
|
||||
} else {
|
||||
ASSIGN_OR_RETURN(stats.world, InferWorldFromMapId(*options.map_id));
|
||||
}
|
||||
} else {
|
||||
stats.map_id = -1; // Indicates all maps
|
||||
stats.world = -1;
|
||||
}
|
||||
|
||||
// Store positions (convert from TileMatch to pair)
|
||||
stats.positions.reserve(matches.size());
|
||||
for (const auto& match : matches) {
|
||||
stats.positions.emplace_back(match.local_x, match.local_y);
|
||||
}
|
||||
|
||||
return stats;
|
||||
}
|
||||
|
||||
} // namespace overworld
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
@@ -95,6 +95,42 @@ struct TileSearchOptions {
|
||||
std::optional<int> world;
|
||||
};
|
||||
|
||||
struct OverworldSprite {
|
||||
uint8_t sprite_id;
|
||||
int map_id;
|
||||
int world;
|
||||
int x;
|
||||
int y;
|
||||
std::optional<std::string> sprite_name;
|
||||
};
|
||||
|
||||
struct SpriteQuery {
|
||||
std::optional<int> map_id;
|
||||
std::optional<int> world;
|
||||
std::optional<uint8_t> sprite_id;
|
||||
};
|
||||
|
||||
struct EntranceDetails {
|
||||
uint8_t entrance_id;
|
||||
int map_id;
|
||||
int world;
|
||||
int x;
|
||||
int y;
|
||||
uint8_t area_x;
|
||||
uint8_t area_y;
|
||||
uint16_t map_pos;
|
||||
bool is_hole;
|
||||
std::optional<std::string> entrance_name;
|
||||
};
|
||||
|
||||
struct TileStatistics {
|
||||
int map_id;
|
||||
int world;
|
||||
uint16_t tile_id;
|
||||
int count;
|
||||
std::vector<std::pair<int, int>> positions; // (x, y) positions
|
||||
};
|
||||
|
||||
absl::StatusOr<int> ParseNumeric(std::string_view value, int base = 0);
|
||||
absl::StatusOr<int> ParseWorldSpecifier(std::string_view value);
|
||||
absl::StatusOr<int> InferWorldFromMapId(int map_id);
|
||||
@@ -111,6 +147,16 @@ absl::StatusOr<std::vector<TileMatch>> FindTileMatches(
|
||||
zelda3::Overworld& overworld, uint16_t tile_id,
|
||||
const TileSearchOptions& options = {});
|
||||
|
||||
absl::StatusOr<std::vector<OverworldSprite>> CollectOverworldSprites(
|
||||
const zelda3::Overworld& overworld, const SpriteQuery& query);
|
||||
|
||||
absl::StatusOr<EntranceDetails> GetEntranceDetails(
|
||||
const zelda3::Overworld& overworld, uint8_t entrance_id);
|
||||
|
||||
absl::StatusOr<TileStatistics> AnalyzeTileUsage(
|
||||
zelda3::Overworld& overworld, uint16_t tile_id,
|
||||
const TileSearchOptions& options = {});
|
||||
|
||||
} // namespace overworld
|
||||
} // namespace cli
|
||||
} // namespace yaze
|
||||
|
||||
Reference in New Issue
Block a user