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"] }
|
tower-http = { version = "0.6", features = ["fs"] }
|
||||||
|
|
||||||
# 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"], 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
|
* `!spawnclone`: Spawn a clone of yourself
|
||||||
* `!classjob <id>`: Changes to another class/job
|
* `!classjob <id>`: Changes to another class/job
|
||||||
* `!unlockaction <id>`: Unlock an action, for example: `1` for Return and `4` for Teleport.
|
* `!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
|
### GM commands
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use icarus::Aetheryte::AetheryteSheet;
|
use icarus::Aetheryte::AetheryteSheet;
|
||||||
use icarus::ClassJob::ClassJobSheet;
|
use icarus::ClassJob::ClassJobSheet;
|
||||||
|
use icarus::EquipSlotCategory::EquipSlotCategorySheet;
|
||||||
use icarus::World::WorldSheet;
|
use icarus::World::WorldSheet;
|
||||||
use icarus::{Tribe::TribeSheet, Warp::WarpSheet};
|
use icarus::{Tribe::TribeSheet, Warp::WarpSheet};
|
||||||
use physis::common::{Language, Platform};
|
use physis::common::{Language, Platform};
|
||||||
|
@ -118,4 +119,104 @@ impl GameData {
|
||||||
|
|
||||||
Some((*pop_range_id, *zone_id))
|
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::{
|
use crate::{
|
||||||
common::{CustomizeData, ObjectId, ObjectTypeId, timestamp_secs},
|
common::{CustomizeData, ObjectId, ObjectTypeId, timestamp_secs},
|
||||||
|
inventory::Storage,
|
||||||
ipc::zone::{
|
ipc::zone::{
|
||||||
ActorControl, ActorControlCategory, ActorControlSelf, BattleNpcSubKind, ChatMessage,
|
ActorControl, ActorControlCategory, ActorControlSelf, BattleNpcSubKind, ChatMessage,
|
||||||
CommonSpawn, EventStart, NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData,
|
CommonSpawn, EventStart, NpcSpawn, ObjectKind, OnlineStatus, ServerZoneIpcData,
|
||||||
|
@ -214,6 +215,32 @@ impl ChatHandler {
|
||||||
})
|
})
|
||||||
.await;
|
.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