mirror of
https://github.com/redstrate/Kawari.git
synced 2025-07-10 16:07:45 +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);
|
result = game_data.get_gilshop_item(*event_id, *item_index as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some((item_id, price_mid)) = result {
|
if let Some(item_info) = result {
|
||||||
if connection.player_data.inventory.currency.gil.quantity >= price_mid as u32 {
|
if connection.player_data.inventory.currency.gil.quantity >= item_info.price_mid as u32 {
|
||||||
// TODO: send the proper response packets!
|
// TODO: send the proper response packets!
|
||||||
connection.player_data.inventory.currency.gil.quantity -= price_mid 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_id as u32));
|
connection.player_data.inventory.add_in_next_free_slot(Item::new(1, item_info.id as u32));
|
||||||
connection.send_inventory(false).await;
|
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.
|
// TODO: send an actual system notice, this is just a placeholder to provide feedback that the player actually bought something.
|
||||||
let result;
|
connection.send_message(&format!("You obtained one or more items: {} (id: {})!", item_info.name, item_info.id)).await;
|
||||||
{
|
|
||||||
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;
|
|
||||||
} else {
|
} else {
|
||||||
connection.send_message("Insufficient gil to buy item. Nice try bypassing the client-side check!").await;
|
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 {
|
impl GameData {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
let config = get_config();
|
let config = get_config();
|
||||||
|
@ -98,20 +120,86 @@ impl GameData {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the primary model ID for a given item ID
|
/// Gets various information from the Item sheet.
|
||||||
pub fn get_primary_model_id(&mut self, item_id: u32) -> Option<u64> {
|
pub fn get_item_info(&mut self, query: ItemInfoQuery) -> Option<ItemInfo> {
|
||||||
for page in &self.item_pages {
|
let mut result = None;
|
||||||
if let Some(row) = page.get_row(item_id) {
|
'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 {
|
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!");
|
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
|
None
|
||||||
|
@ -160,50 +248,6 @@ impl GameData {
|
||||||
Some(value.clone())
|
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
|
/// 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> {
|
pub fn get_equipslot_category(&mut self, equipslot_id: u8) -> Option<u16> {
|
||||||
let sheet = EquipSlotCategorySheet::read_from(&mut self.game_data, Language::None)?;
|
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.
|
/// 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 sheet = GilShopItemSheet::read_from(&mut self.game_data, Language::None)?;
|
||||||
let row = sheet.get_subrow(gilshop_id, index)?;
|
let row = sheet.get_subrow(gilshop_id, index)?;
|
||||||
let item_id = row.Item().into_i32()?;
|
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 {
|
self.get_item_info(ItemInfoQuery::ById(*item_id as u32))
|
||||||
panic!("Unexpected type!")
|
|
||||||
};
|
|
||||||
|
|
||||||
return Some((*item_id, *price_mid as i32));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub use position::Position;
|
||||||
|
|
||||||
mod gamedata;
|
mod gamedata;
|
||||||
pub use gamedata::GameData;
|
pub use gamedata::GameData;
|
||||||
pub use gamedata::TerritoryNameKind;
|
pub use gamedata::{ItemInfo, ItemInfoQuery, TerritoryNameKind};
|
||||||
|
|
||||||
pub mod workdefinitions;
|
pub mod workdefinitions;
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
|
common::ItemInfoQuery,
|
||||||
inventory::{Item, Storage},
|
inventory::{Item, Storage},
|
||||||
ipc::zone::{ChatMessage, GameMasterRank},
|
ipc::zone::{ChatMessage, GameMasterRank},
|
||||||
world::ToServer,
|
world::ToServer,
|
||||||
|
@ -57,15 +58,19 @@ impl ChatHandler {
|
||||||
{
|
{
|
||||||
let mut gamedata = connection.gamedata.lock().unwrap();
|
let mut gamedata = connection.gamedata.lock().unwrap();
|
||||||
|
|
||||||
if let Some((equip_category, id)) = gamedata.get_item_by_name(name) {
|
if let Some(item_info) =
|
||||||
let slot = gamedata.get_equipslot_category(equip_category).unwrap();
|
gamedata.get_item_info(ItemInfoQuery::ByName(name.to_string()))
|
||||||
|
{
|
||||||
|
let slot = gamedata
|
||||||
|
.get_equipslot_category(item_info.equip_category)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
connection
|
connection
|
||||||
.player_data
|
.player_data
|
||||||
.inventory
|
.inventory
|
||||||
.equipped
|
.equipped
|
||||||
.get_slot_mut(slot)
|
.get_slot_mut(slot)
|
||||||
.id = id;
|
.id = item_info.id;
|
||||||
connection
|
connection
|
||||||
.player_data
|
.player_data
|
||||||
.inventory
|
.inventory
|
||||||
|
@ -84,11 +89,13 @@ impl ChatHandler {
|
||||||
{
|
{
|
||||||
let mut gamedata = connection.gamedata.lock().unwrap();
|
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
|
connection
|
||||||
.player_data
|
.player_data
|
||||||
.inventory
|
.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