1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-06-30 11:47:45 +00:00

Implement the //gm teri_info command, and fix a typo (#56)

Implement the `//gm teri_info` command, and fix a typo. Said typo was in gamedata.rs, confusing the FingerL column for the SoulCrystal column.
This commit is contained in:
thedax 2025-06-23 13:39:37 -04:00 committed by GitHub
parent 50530e31e4
commit 82e5021979
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 65 additions and 4 deletions

View file

@ -104,4 +104,4 @@ rkon = { version = "0.1" }
tower-http = { version = "0.6", features = ["fs", "cors"] }
# excel sheet data
icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate"], default-features = false }
icarus = { git = "https://github.com/redstrate/Icarus", branch = "ver/2025.04.16.0000.0000", features = ["Warp", "Tribe", "ClassJob", "World", "TerritoryType", "Race", "Aetheryte", "EquipSlotCategory", "Action", "WeatherRate", "PlaceName"], default-features = false }

View file

@ -131,4 +131,5 @@ These GM commands are implemented in the FFXIV protocol, but only some of them a
* `//gm aetheryte <on/off> <id>`: Unlock an Aetheryte.
* `//gm speed <multiplier>`: Increases your movement speed by `multiplier`.
* `//gm orchestrion <on/off> <id>`: Unlock an Orchestrion song.
* `//gm exp <amount>` Adds the specified amount of EXP to the current class/job.
* `//gm exp <amount>`: Adds the specified amount of EXP to the current class/job.
* `//gm teri_info`: Displays information about the current zone. Currently displays zone id, weather, internal zone name, parent region name, and place/display name.

View file

@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
use kawari::RECEIVE_BUFFER_SIZE;
use kawari::common::Position;
use kawari::common::{GameData, timestamp_secs};
use kawari::common::{GameData, TerritoryNameKind, timestamp_secs};
use kawari::config::get_config;
use kawari::inventory::Item;
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
@ -692,6 +692,31 @@ async fn client_loop(
connection.player_data.set_current_exp(connection.player_data.current_exp() + amount);
connection.update_class_info().await;
}
GameMasterCommandType::TerritoryInfo => {
let id: u32 = connection.zone.as_ref().unwrap().id.into();
let weather_id;
let internal_name;
let place_name;
let region_name;
{
let mut game_data = connection.gamedata.lock().unwrap();
// TODO: Maybe the current weather should be cached somewhere like the zone id?
weather_id = game_data
.get_weather(id)
.unwrap_or(1) as u16;
let fallback = "<Unable to load name!>";
internal_name = game_data.get_territory_name(id, TerritoryNameKind::Internal).unwrap_or(fallback.to_string());
region_name = game_data.get_territory_name(id, TerritoryNameKind::Region).unwrap_or(fallback.to_string());
place_name = game_data.get_territory_name(id, TerritoryNameKind::Place).unwrap_or(fallback.to_string());
}
connection.send_message(format!(concat!("Territory Info for zone {}:\n",
"Current weather: {}\n",
"Internal name: {}\n",
"Region name: {}\n",
"Place name: {}"), id, weather_id, internal_name, region_name, place_name).as_str()).await;
}
}
}
ClientZoneIpcData::ZoneJump {

View file

@ -2,6 +2,7 @@ use icarus::Action::ActionSheet;
use icarus::Aetheryte::AetheryteSheet;
use icarus::ClassJob::ClassJobSheet;
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
use icarus::PlaceName::PlaceNameSheet;
use icarus::TerritoryType::TerritoryTypeSheet;
use icarus::WeatherRate::WeatherRateSheet;
use icarus::World::WorldSheet;
@ -125,6 +126,31 @@ impl GameData {
Some((*pop_range_id, *zone_id))
}
// Retrieves a zone's internal name, place name or parent region name.
pub fn get_territory_name(
&mut self,
zone_id: u32,
which: TerritoryNameKind,
) -> Option<String> {
let sheet = TerritoryTypeSheet::read_from(&mut self.game_data, Language::None)?;
let row = sheet.get_row(zone_id)?;
let offset = match which {
TerritoryNameKind::Internal => {
return row.Name().into_string().cloned();
}
TerritoryNameKind::Region => row.PlaceNameRegion().into_u16()?,
TerritoryNameKind::Place => row.PlaceName().into_u16()?,
};
let sheet = PlaceNameSheet::read_from(&mut self.game_data, Language::English)?;
let row = sheet.get_row(*offset as u32)?;
let value = row.Name().into_string()?;
Some(value.clone())
}
/// Find an item's equip category and id by name, if it exists.
pub fn get_item_by_name(&mut self, name: &str) -> Option<(u8, u32)> {
for page in &self.item_pages {
@ -217,7 +243,7 @@ impl GameData {
return Some(12);
}
let soul_crystal = row.FingerL().into_i8()?;
let soul_crystal = row.SoulCrystal().into_i8()?;
if *soul_crystal == 1 {
return Some(13);
}
@ -291,3 +317,10 @@ impl GameData {
self.get_weather_rate(*weather_rate_id as u32)
}
}
// Simple enum for GameData::get_territory_name
pub enum TerritoryNameKind {
Internal,
Region,
Place,
}

View file

@ -12,6 +12,7 @@ pub use position::Position;
mod gamedata;
pub use gamedata::GameData;
pub use gamedata::TerritoryNameKind;
pub mod workdefinitions;

View file

@ -148,6 +148,7 @@ pub enum GameMasterCommandType {
Orchestrion = 0x74,
GiveItem = 0xC8,
Aetheryte = 0x5E,
TerritoryInfo = 0x25D,
}
#[binrw]