mirror of
https://github.com/redstrate/Kawari.git
synced 2025-07-10 07:57:46 +00:00
Implement items costing money now (#94)
-Includes an extra check for trying to bypass the client-side -Update dependencies -Include a message that selling isn't supported yet -Display a message indicating an item was bought
This commit is contained in:
parent
927c093915
commit
9bed7595cc
4 changed files with 70 additions and 19 deletions
6
Cargo.lock
generated
6
Cargo.lock
generated
|
@ -1048,7 +1048,7 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "physis"
|
name = "physis"
|
||||||
version = "0.5.0"
|
version = "0.5.0"
|
||||||
source = "git+https://github.com/redstrate/physis#862b16b681e8593f2b327442deddc12922209531"
|
source = "git+https://github.com/redstrate/physis#5a5896a1261c13732ec856fbb9badbdd5da196d6"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"binrw",
|
"binrw",
|
||||||
"bitflags",
|
"bitflags",
|
||||||
|
@ -1124,9 +1124,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest"
|
name = "reqwest"
|
||||||
version = "0.12.21"
|
version = "0.12.22"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "4c8cea6b35bcceb099f30173754403d2eba0a5dc18cea3630fccd88251909288"
|
checksum = "cbc931937e6ca3a06e3b6c0aa7841849b160a90351d6ab467a8b9b9959767531"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64",
|
"base64",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
|
|
@ -309,6 +309,7 @@ common_events = {
|
||||||
-- NPC shops that accept gil for purchasing items
|
-- NPC shops that accept gil for purchasing items
|
||||||
generic_gil_shops = {
|
generic_gil_shops = {
|
||||||
262157, -- Tanie <Florist>, New Gridania
|
262157, -- Tanie <Florist>, New Gridania
|
||||||
|
262197, -- Gerulf <Independent Culinarian>, Limsa Lominsa: The Lower Decks
|
||||||
263220, -- Neon <Air-wheeler dealer>, Solution Nine
|
263220, -- Neon <Air-wheeler dealer>, Solution Nine
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -792,23 +792,43 @@ async fn client_loop(
|
||||||
// TODO: Likely rename this opcode if non-gil shops also use this same opcode
|
// TODO: Likely rename this opcode if non-gil shops also use this same opcode
|
||||||
ClientZoneIpcData::GilShopTransaction { event_id, unk1: _, buy_sell_mode, item_index, item_quantity, unk2: _ } => {
|
ClientZoneIpcData::GilShopTransaction { event_id, unk1: _, buy_sell_mode, item_index, item_quantity, unk2: _ } => {
|
||||||
tracing::info!("Client is interacting with a shop! {event_id:#?} {buy_sell_mode:#?} {item_quantity:#?} {item_index:#?}");
|
tracing::info!("Client is interacting with a shop! {event_id:#?} {buy_sell_mode:#?} {item_quantity:#?} {item_index:#?}");
|
||||||
|
const BUY: u32 = 1;
|
||||||
|
const SELL: u32 = 2;
|
||||||
|
|
||||||
let item_id;
|
if *buy_sell_mode == BUY {
|
||||||
{
|
let result;
|
||||||
let mut game_data = connection.gamedata.lock().unwrap();
|
{
|
||||||
item_id = game_data.get_gilshop_item(*event_id, *item_index as u16);
|
let mut game_data = connection.gamedata.lock().unwrap();
|
||||||
}
|
result = game_data.get_gilshop_item(*event_id, *item_index as u16);
|
||||||
|
}
|
||||||
|
|
||||||
if let Some(item_id) = item_id {
|
if let Some((item_id, price_mid)) = result {
|
||||||
// TODO: adjust their gil, and send the proper response packets!
|
if connection.player_data.inventory.currency.gil.quantity >= price_mid as u32 {
|
||||||
connection.send_message("Shops are not implemented fully yet. Giving you a free item...").await;
|
// 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.add_in_next_free_slot(Item::new(1, item_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.
|
||||||
|
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;
|
||||||
|
} else {
|
||||||
|
connection.send_message("Insufficient gil to buy item. Nice try bypassing the client-side check!").await;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
connection.send_message(&format!("Unable to find shop item, this is a bug in Kawari!")).await;
|
||||||
|
}
|
||||||
|
} else if *buy_sell_mode == SELL {
|
||||||
|
// TODO: Implement selling items back to shops
|
||||||
|
connection.send_message("Selling items to shops is not yet implemented. Cancelling event...").await;
|
||||||
} else {
|
} else {
|
||||||
connection.send_message(&format!("Unable to find shop item, this is a bug in Kawari!")).await;
|
tracing::error!("Received unknown transaction mode {buy_sell_mode}!");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cancel the event for now so the client doesn't get stuck
|
// Cancel the event for now so the client doesn't get stuck
|
||||||
connection.event_finish(*event_id).await;
|
connection.event_finish(*event_id).await;
|
||||||
}
|
}
|
||||||
|
|
|
@ -187,6 +187,23 @@ impl GameData {
|
||||||
None
|
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)?;
|
||||||
|
@ -331,12 +348,25 @@ impl GameData {
|
||||||
self.classjob_exp_indexes.get(classjob_id as usize).copied()
|
self.classjob_exp_indexes.get(classjob_id as usize).copied()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the item and it's 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> {
|
pub fn get_gilshop_item(&mut self, gilshop_id: u32, index: u16) -> Option<(i32, i32)> {
|
||||||
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()?;
|
||||||
|
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!")
|
||||||
|
};
|
||||||
|
|
||||||
row.Item().into_i32().copied()
|
let physis::exd::ColumnData::UInt32(price_mid) = &item_row.columns[25] else {
|
||||||
|
panic!("Unexpected type!")
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some((*item_id, *price_mid as i32));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue