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:
parent
d1bbfdfbfd
commit
5accb992a9
4 changed files with 130 additions and 1 deletions
|
@ -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 }
|
||||
|
|
1
USAGE.md
1
USAGE.md
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue