1
Fork 0
mirror of https://github.com/redstrate/Kawari.git synced 2025-05-19 17:27:45 +00:00

Add !equip command to quickly change your equipped item

See USAGE, this is actually extremely useful! No more hunting for item
IDs!
This commit is contained in:
Joshua Goins 2025-05-12 22:32:03 -04:00
parent d1bbfdfbfd
commit 5accb992a9
4 changed files with 130 additions and 1 deletions

View file

@ -104,4 +104,4 @@ rkon = { version = "0.1" }
tower-http = { version = "0.6", features = ["fs"] }
# 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"], 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"], default-features = false }

View file

@ -88,6 +88,7 @@ These special debug commands start with `!` and are custom to Kawari.
* `!spawnclone`: Spawn a clone of yourself
* `!classjob <id>`: Changes to another class/job
* `!unlockaction <id>`: Unlock an action, for example: `1` for Return and `4` for Teleport.
* `!equip <name>`: Forcefully equip an item, useful for bypassing class/job and other client restrictions. This will *overwrite* any item in that slot!
### GM commands

View file

@ -1,5 +1,6 @@
use icarus::Aetheryte::AetheryteSheet;
use icarus::ClassJob::ClassJobSheet;
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
use icarus::World::WorldSheet;
use icarus::{Tribe::TribeSheet, Warp::WarpSheet};
use physis::common::{Language, Platform};
@ -118,4 +119,104 @@ impl GameData {
Some((*pop_range_id, *zone_id))
}
/// 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 {
for row in &page.rows {
let ExcelRowKind::SingleRow(single_row) = &row.kind else {
panic!("Expected a single row!")
};
let physis::exd::ColumnData::String(item_name) = &single_row.columns[9] else {
panic!("Unexpected type!");
};
if !item_name.to_lowercase().contains(&name.to_lowercase()) {
continue;
}
let physis::exd::ColumnData::UInt8(equip_category) = &single_row.columns[17] else {
panic!("Unexpected type!");
};
return Some((*equip_category, row.row_id));
}
}
None
}
/// Turn an equip slot category id into a slot for the equipped inventory
pub fn get_equipslot_category(&mut self, equipslot_id: u8) -> Option<u16> {
let sheet = EquipSlotCategorySheet::read_from(&mut self.game_data, Language::None)?;
let row = sheet.get_row(equipslot_id as u32)?;
let main_hand = row.MainHand().into_i8()?;
if *main_hand == 1 {
return Some(0);
}
let off_hand = row.OffHand().into_i8()?;
if *off_hand == 1 {
return Some(1);
}
let head = row.Head().into_i8()?;
if *head == 1 {
return Some(2);
}
let body = row.Body().into_i8()?;
if *body == 1 {
return Some(3);
}
let gloves = row.Gloves().into_i8()?;
if *gloves == 1 {
return Some(4);
}
let legs = row.Legs().into_i8()?;
if *legs == 1 {
return Some(6);
}
let feet = row.Feet().into_i8()?;
if *feet == 1 {
return Some(7);
}
let ears = row.Ears().into_i8()?;
if *ears == 1 {
return Some(8);
}
let neck = row.Neck().into_i8()?;
if *neck == 1 {
return Some(9);
}
let wrists = row.Wrists().into_i8()?;
if *wrists == 1 {
return Some(10);
}
let right_finger = row.FingerR().into_i8()?;
if *right_finger == 1 {
return Some(11);
}
let left_finger = row.FingerL().into_i8()?;
if *left_finger == 1 {
return Some(12);
}
let soul_crystal = row.FingerL().into_i8()?;
if *soul_crystal == 1 {
return Some(13);
}
None
}
}

View file

@ -1,5 +1,6 @@
use crate::{
common::{CustomizeData, ObjectId, ObjectTypeId, timestamp_secs},
inventory::Storage,
ipc::zone::{
ActorControl, ActorControlCategory, ActorControlSelf, BattleNpcSubKind, ChatMessage,
CommonSpawn, EventStart, NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData,
@ -214,6 +215,32 @@ impl ChatHandler {
})
.await;
}
"!equip" => {
let (_, name) = chat_message.message.split_once(' ').unwrap();
{
let mut gamedata = connection.gamedata.lock().unwrap();
if let Some((equip_category, id)) = gamedata.get_item_by_name(name) {
let slot = gamedata.get_equipslot_category(equip_category).unwrap();
connection
.player_data
.inventory
.equipped
.get_slot_mut(slot as u16)
.id = id;
connection
.player_data
.inventory
.equipped
.get_slot_mut(slot as u16)
.quantity = 1;
}
}
connection.send_inventory(true).await;
}
_ => {}
}
}