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:
parent
50530e31e4
commit
82e5021979
6 changed files with 65 additions and 4 deletions
|
@ -104,4 +104,4 @@ rkon = { version = "0.1" }
|
||||||
tower-http = { version = "0.6", features = ["fs", "cors"] }
|
tower-http = { version = "0.6", features = ["fs", "cors"] }
|
||||||
|
|
||||||
# excel sheet data
|
# 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 }
|
||||||
|
|
3
USAGE.md
3
USAGE.md
|
@ -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 aetheryte <on/off> <id>`: Unlock an Aetheryte.
|
||||||
* `//gm speed <multiplier>`: Increases your movement speed by `multiplier`.
|
* `//gm speed <multiplier>`: Increases your movement speed by `multiplier`.
|
||||||
* `//gm orchestrion <on/off> <id>`: Unlock an Orchestrion song.
|
* `//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.
|
||||||
|
|
|
@ -4,7 +4,7 @@ use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use kawari::RECEIVE_BUFFER_SIZE;
|
use kawari::RECEIVE_BUFFER_SIZE;
|
||||||
use kawari::common::Position;
|
use kawari::common::Position;
|
||||||
use kawari::common::{GameData, timestamp_secs};
|
use kawari::common::{GameData, TerritoryNameKind, timestamp_secs};
|
||||||
use kawari::config::get_config;
|
use kawari::config::get_config;
|
||||||
use kawari::inventory::Item;
|
use kawari::inventory::Item;
|
||||||
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
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.player_data.set_current_exp(connection.player_data.current_exp() + amount);
|
||||||
connection.update_class_info().await;
|
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 {
|
ClientZoneIpcData::ZoneJump {
|
||||||
|
|
|
@ -2,6 +2,7 @@ use icarus::Action::ActionSheet;
|
||||||
use icarus::Aetheryte::AetheryteSheet;
|
use icarus::Aetheryte::AetheryteSheet;
|
||||||
use icarus::ClassJob::ClassJobSheet;
|
use icarus::ClassJob::ClassJobSheet;
|
||||||
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
|
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
|
||||||
|
use icarus::PlaceName::PlaceNameSheet;
|
||||||
use icarus::TerritoryType::TerritoryTypeSheet;
|
use icarus::TerritoryType::TerritoryTypeSheet;
|
||||||
use icarus::WeatherRate::WeatherRateSheet;
|
use icarus::WeatherRate::WeatherRateSheet;
|
||||||
use icarus::World::WorldSheet;
|
use icarus::World::WorldSheet;
|
||||||
|
@ -125,6 +126,31 @@ impl GameData {
|
||||||
Some((*pop_range_id, *zone_id))
|
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.
|
/// 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)> {
|
pub fn get_item_by_name(&mut self, name: &str) -> Option<(u8, u32)> {
|
||||||
for page in &self.item_pages {
|
for page in &self.item_pages {
|
||||||
|
@ -217,7 +243,7 @@ impl GameData {
|
||||||
return Some(12);
|
return Some(12);
|
||||||
}
|
}
|
||||||
|
|
||||||
let soul_crystal = row.FingerL().into_i8()?;
|
let soul_crystal = row.SoulCrystal().into_i8()?;
|
||||||
if *soul_crystal == 1 {
|
if *soul_crystal == 1 {
|
||||||
return Some(13);
|
return Some(13);
|
||||||
}
|
}
|
||||||
|
@ -291,3 +317,10 @@ impl GameData {
|
||||||
self.get_weather_rate(*weather_rate_id as u32)
|
self.get_weather_rate(*weather_rate_id as u32)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Simple enum for GameData::get_territory_name
|
||||||
|
pub enum TerritoryNameKind {
|
||||||
|
Internal,
|
||||||
|
Region,
|
||||||
|
Place,
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ pub use position::Position;
|
||||||
|
|
||||||
mod gamedata;
|
mod gamedata;
|
||||||
pub use gamedata::GameData;
|
pub use gamedata::GameData;
|
||||||
|
pub use gamedata::TerritoryNameKind;
|
||||||
|
|
||||||
pub mod workdefinitions;
|
pub mod workdefinitions;
|
||||||
|
|
||||||
|
|
|
@ -148,6 +148,7 @@ pub enum GameMasterCommandType {
|
||||||
Orchestrion = 0x74,
|
Orchestrion = 0x74,
|
||||||
GiveItem = 0xC8,
|
GiveItem = 0xC8,
|
||||||
Aetheryte = 0x5E,
|
Aetheryte = 0x5E,
|
||||||
|
TerritoryInfo = 0x25D,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[binrw]
|
#[binrw]
|
||||||
|
|
Loading…
Add table
Reference in a new issue