mirror of
https://github.com/redstrate/Kawari.git
synced 2025-07-10 07:57:46 +00:00
Refactor gamedata to use a more general get_item_info function (#96)
-You can search for items by id or name -The logic is all centrally located, eliminating all of the duplicated loops everywhere
This commit is contained in:
parent
a83d455e86
commit
01b396a656
4 changed files with 120 additions and 88 deletions
|
@ -802,21 +802,14 @@ async fn client_loop(
|
|||
result = game_data.get_gilshop_item(*event_id, *item_index as u16);
|
||||
}
|
||||
|
||||
if let Some((item_id, price_mid)) = result {
|
||||
if connection.player_data.inventory.currency.gil.quantity >= price_mid as u32 {
|
||||
if let Some(item_info) = result {
|
||||
if connection.player_data.inventory.currency.gil.quantity >= item_info.price_mid as u32 {
|
||||
// TODO: send the proper response packets!
|
||||
connection.player_data.inventory.currency.gil.quantity -= price_mid as u32;
|
||||
connection.player_data.inventory.add_in_next_free_slot(Item::new(1, item_id as u32));
|
||||
connection.player_data.inventory.currency.gil.quantity -= item_info.price_mid as u32;
|
||||
connection.player_data.inventory.add_in_next_free_slot(Item::new(1, item_info.id as u32));
|
||||
connection.send_inventory(false).await;
|
||||
// TODO: send an actual system notice, this is just a placeholder to provide feedback that the player actually bought something.
|
||||
let result;
|
||||
{
|
||||
let mut game_data = connection.gamedata.lock().unwrap();
|
||||
result = game_data.get_item_name(item_id as u32);
|
||||
}
|
||||
let fallback = "<Error loading item name!>".to_string();
|
||||
let item_name = result.unwrap_or(fallback);
|
||||
connection.send_message(&format!("You obtained one or more items: {} (id: {})!", item_name, item_id)).await;
|
||||
connection.send_message(&format!("You obtained one or more items: {} (id: {})!", item_info.name, item_info.id)).await;
|
||||
} else {
|
||||
connection.send_message("Insufficient gil to buy item. Nice try bypassing the client-side check!").await;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,28 @@ impl Default for GameData {
|
|||
}
|
||||
}
|
||||
|
||||
/// Struct detailing various information about an item, pulled from the Items sheet.
|
||||
#[derive(Default)]
|
||||
pub struct ItemInfo {
|
||||
/// The item's textual name.
|
||||
pub name: String,
|
||||
/// The item's id number.
|
||||
pub id: u32,
|
||||
/// The item's price, when sold by an NPC.
|
||||
pub price_mid: u32,
|
||||
/// The item's price, when sold to an NPC by the player.
|
||||
pub price_low: u32,
|
||||
/// The item's equip category.
|
||||
pub equip_category: u8,
|
||||
pub primary_model_id: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum ItemInfoQuery {
|
||||
ById(u32),
|
||||
ByName(String),
|
||||
}
|
||||
|
||||
impl GameData {
|
||||
pub fn new() -> Self {
|
||||
let config = get_config();
|
||||
|
@ -98,20 +120,86 @@ impl GameData {
|
|||
})
|
||||
}
|
||||
|
||||
/// Gets the primary model ID for a given item ID
|
||||
pub fn get_primary_model_id(&mut self, item_id: u32) -> Option<u64> {
|
||||
for page in &self.item_pages {
|
||||
if let Some(row) = page.get_row(item_id) {
|
||||
/// Gets various information from the Item sheet.
|
||||
pub fn get_item_info(&mut self, query: ItemInfoQuery) -> Option<ItemInfo> {
|
||||
let mut result = None;
|
||||
'outer: for page in &self.item_pages {
|
||||
match query {
|
||||
ItemInfoQuery::ById(ref query_item_id) => {
|
||||
if let Some(row) = page.get_row(*query_item_id) {
|
||||
let ExcelRowKind::SingleRow(item_row) = row else {
|
||||
panic!("Expected a single row!")
|
||||
panic!("Expected a single row!");
|
||||
};
|
||||
result = Some((item_row, query_item_id));
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
|
||||
ItemInfoQuery::ByName(ref query_item_name) => {
|
||||
for row in &page.rows {
|
||||
let ExcelRowKind::SingleRow(single_row) = &row.kind else {
|
||||
panic!("Expected a single row!");
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt64(id) = &item_row.columns[47] else {
|
||||
let physis::exd::ColumnData::String(item_name) = &single_row.columns[9]
|
||||
else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
return Some(*id);
|
||||
if !item_name
|
||||
.to_lowercase()
|
||||
.contains(&query_item_name.to_lowercase())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
result = Some((single_row.clone(), &row.row_id));
|
||||
break 'outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some((matched_row, item_id)) = result {
|
||||
let physis::exd::ColumnData::String(name) = &matched_row.columns[9] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt8(equip_category) = &matched_row.columns[17] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt32(price_mid) = &matched_row.columns[25] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt32(price_low) = &matched_row.columns[26] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt64(primary_model_id) = &matched_row.columns[47] else {
|
||||
panic!("Unexpected type!");
|
||||
};
|
||||
|
||||
let item_info = ItemInfo {
|
||||
id: *item_id,
|
||||
name: name.to_string(),
|
||||
price_mid: *price_mid,
|
||||
price_low: *price_low,
|
||||
equip_category: *equip_category,
|
||||
primary_model_id: *primary_model_id,
|
||||
};
|
||||
|
||||
return Some(item_info);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
/// Gets the primary model ID for a given item ID
|
||||
pub fn get_primary_model_id(&mut self, item_id: u32) -> Option<u64> {
|
||||
if let Some(item_info) = self.get_item_info(ItemInfoQuery::ById(item_id)) {
|
||||
return Some(item_info.primary_model_id);
|
||||
}
|
||||
|
||||
None
|
||||
|
@ -160,50 +248,6 @@ impl GameData {
|
|||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
pub fn get_item_name(&mut self, item_id: u32) -> Option<String> {
|
||||
for page in &self.item_pages {
|
||||
if let Some(row) = page.get_row(item_id) {
|
||||
let ExcelRowKind::SingleRow(item_row) = row else {
|
||||
panic!("Expected a single row!")
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::String(item_name) = &item_row.columns[9] else {
|
||||
panic!("Unexpected type!")
|
||||
};
|
||||
|
||||
return Some(item_name.clone());
|
||||
}
|
||||
}
|
||||
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)?;
|
||||
|
@ -349,24 +393,12 @@ impl GameData {
|
|||
}
|
||||
|
||||
/// Gets the item and its cost from the specified shop.
|
||||
pub fn get_gilshop_item(&mut self, gilshop_id: u32, index: u16) -> Option<(i32, i32)> {
|
||||
pub fn get_gilshop_item(&mut self, gilshop_id: u32, index: u16) -> Option<ItemInfo> {
|
||||
let sheet = GilShopItemSheet::read_from(&mut self.game_data, Language::None)?;
|
||||
let row = sheet.get_subrow(gilshop_id, index)?;
|
||||
let item_id = row.Item().into_i32()?;
|
||||
for page in &self.item_pages {
|
||||
if let Some(row) = page.get_row(*item_id as u32) {
|
||||
let ExcelRowKind::SingleRow(item_row) = row else {
|
||||
panic!("Expected a single row!")
|
||||
};
|
||||
|
||||
let physis::exd::ColumnData::UInt32(price_mid) = &item_row.columns[25] else {
|
||||
panic!("Unexpected type!")
|
||||
};
|
||||
|
||||
return Some((*item_id, *price_mid as i32));
|
||||
}
|
||||
}
|
||||
None
|
||||
self.get_item_info(ItemInfoQuery::ById(*item_id as u32))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ pub use position::Position;
|
|||
|
||||
mod gamedata;
|
||||
pub use gamedata::GameData;
|
||||
pub use gamedata::TerritoryNameKind;
|
||||
pub use gamedata::{ItemInfo, ItemInfoQuery, TerritoryNameKind};
|
||||
|
||||
pub mod workdefinitions;
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{
|
||||
common::ItemInfoQuery,
|
||||
inventory::{Item, Storage},
|
||||
ipc::zone::{ChatMessage, GameMasterRank},
|
||||
world::ToServer,
|
||||
|
@ -57,15 +58,19 @@ impl ChatHandler {
|
|||
{
|
||||
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();
|
||||
if let Some(item_info) =
|
||||
gamedata.get_item_info(ItemInfoQuery::ByName(name.to_string()))
|
||||
{
|
||||
let slot = gamedata
|
||||
.get_equipslot_category(item_info.equip_category)
|
||||
.unwrap();
|
||||
|
||||
connection
|
||||
.player_data
|
||||
.inventory
|
||||
.equipped
|
||||
.get_slot_mut(slot)
|
||||
.id = id;
|
||||
.id = item_info.id;
|
||||
connection
|
||||
.player_data
|
||||
.inventory
|
||||
|
@ -84,11 +89,13 @@ impl ChatHandler {
|
|||
{
|
||||
let mut gamedata = connection.gamedata.lock().unwrap();
|
||||
|
||||
if let Some((_, id)) = gamedata.get_item_by_name(name) {
|
||||
if let Some(item_info) =
|
||||
gamedata.get_item_info(ItemInfoQuery::ByName(name.to_string()))
|
||||
{
|
||||
connection
|
||||
.player_data
|
||||
.inventory
|
||||
.add_in_next_free_slot(Item::new(1, id));
|
||||
.add_in_next_free_slot(Item::new(1, item_info.id));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue