mirror of
https://github.com/redstrate/Kawari.git
synced 2025-06-30 11:47:45 +00:00
Begin implementing currency, add //gm gil command and more
This doesn't work yet though , and I'm not sure why. I also fixed a bug where new characters doesn't get their inventories initialized properly.
This commit is contained in:
parent
03609fb8c1
commit
01697c8f62
10 changed files with 156 additions and 31 deletions
1
USAGE.md
1
USAGE.md
|
@ -133,3 +133,4 @@ These GM commands are implemented in the FFXIV protocol, but only some of them a
|
|||
* `//gm orchestrion <on/off> <id>`: Unlock an Orchestrion song.
|
||||
* `//gm exp <amount>`: Adds the specified amount of EXP to the current class/job.
|
||||
* `//gm teri_info`: Displays information about the current zone. Currently displays zone id, weather, internal zone name, parent region name, and place/display name.
|
||||
* `//gm gil <amount>`: Adds the specified amount of gil to the player
|
||||
|
|
|
@ -169,6 +169,11 @@
|
|||
"name": "ActorControlTarget",
|
||||
"opcode": 593,
|
||||
"size": 28
|
||||
},
|
||||
{
|
||||
"name": "CurrencyCrystalInfo",
|
||||
"opcode": 548,
|
||||
"size": 32
|
||||
}
|
||||
],
|
||||
"ClientZoneIpcType": [
|
||||
|
|
|
@ -6,7 +6,7 @@ use kawari::RECEIVE_BUFFER_SIZE;
|
|||
use kawari::common::Position;
|
||||
use kawari::common::{GameData, TerritoryNameKind, timestamp_secs};
|
||||
use kawari::config::get_config;
|
||||
use kawari::inventory::Item;
|
||||
use kawari::inventory::{Item, Storage};
|
||||
use kawari::ipc::chat::{ServerChatIpcData, ServerChatIpcSegment};
|
||||
use kawari::ipc::zone::{
|
||||
ActorControlCategory, ActorControlSelf, PlayerEntry, PlayerSpawn, PlayerStatus, SocialList,
|
||||
|
@ -716,7 +716,12 @@ async fn client_loop(
|
|||
"Internal name: {}\n",
|
||||
"Region name: {}\n",
|
||||
"Place name: {}"), id, weather_id, internal_name, region_name, place_name).as_str()).await;
|
||||
}
|
||||
},
|
||||
GameMasterCommandType::Gil => {
|
||||
let amount = *arg0;
|
||||
connection.player_data.inventory.currency.get_slot_mut(0).quantity += amount;
|
||||
connection.send_inventory(false).await;
|
||||
},
|
||||
}
|
||||
}
|
||||
ClientZoneIpcData::ZoneJump {
|
||||
|
|
40
src/inventory/currency.rs
Normal file
40
src/inventory/currency.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Item, Storage};
|
||||
|
||||
#[derive(Clone, Copy, Deserialize, Serialize, Debug)]
|
||||
pub struct CurrencyStorage {
|
||||
pub gil: Item,
|
||||
}
|
||||
|
||||
impl Default for CurrencyStorage {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
gil: Item { quantity: 0, id: 1 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for CurrencyStorage {
|
||||
fn max_slots(&self) -> u32 {
|
||||
1
|
||||
}
|
||||
|
||||
fn num_items(&self) -> u32 {
|
||||
self.gil.quantity
|
||||
}
|
||||
|
||||
fn get_slot_mut(&mut self, index: u16) -> &mut Item {
|
||||
match index {
|
||||
0 => &mut self.gil,
|
||||
_ => panic!("{} is not a valid src_container_index?!?", index),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_slot(&self, index: u16) -> &Item {
|
||||
match index {
|
||||
0 => &self.gil,
|
||||
_ => panic!("{} is not a valid src_container_index?!?", index),
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,11 +2,19 @@ use serde::{Deserialize, Serialize};
|
|||
|
||||
use super::{Item, Storage};
|
||||
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
pub struct GenericStorage<const N: usize> {
|
||||
pub slots: Vec<Item>,
|
||||
}
|
||||
|
||||
impl<const N: usize> GenericStorage<N> {
|
||||
pub fn default() -> Self {
|
||||
Self {
|
||||
slots: vec![Item::default(); N],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<const N: usize> Storage for GenericStorage<N> {
|
||||
fn max_slots(&self) -> u32 {
|
||||
N as u32
|
||||
|
|
|
@ -18,6 +18,9 @@ pub use item::Item;
|
|||
mod storage;
|
||||
pub use storage::{ContainerType, Storage};
|
||||
|
||||
mod currency;
|
||||
pub use currency::CurrencyStorage;
|
||||
|
||||
const MAX_NORMAL_STORAGE: usize = 35;
|
||||
const MAX_LARGE_STORAGE: usize = 50;
|
||||
|
||||
|
@ -37,6 +40,7 @@ pub struct Inventory {
|
|||
pub armoury_bracelet: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||
pub armoury_rings: GenericStorage<MAX_LARGE_STORAGE>,
|
||||
pub armoury_soul_crystal: GenericStorage<MAX_NORMAL_STORAGE>,
|
||||
pub currency: CurrencyStorage,
|
||||
}
|
||||
|
||||
impl Default for Inventory {
|
||||
|
@ -56,6 +60,7 @@ impl Default for Inventory {
|
|||
armoury_bracelet: GenericStorage::default(),
|
||||
armoury_rings: GenericStorage::default(),
|
||||
armoury_soul_crystal: GenericStorage::default(),
|
||||
currency: CurrencyStorage::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -201,6 +206,7 @@ impl Inventory {
|
|||
ContainerType::Inventory2 => &mut self.pages[2],
|
||||
ContainerType::Inventory3 => &mut self.pages[3],
|
||||
ContainerType::Equipped => &mut self.equipped,
|
||||
ContainerType::Currency => &mut self.currency,
|
||||
ContainerType::ArmoryOffWeapon => &mut self.armoury_off_hand,
|
||||
ContainerType::ArmoryHead => &mut self.armoury_head,
|
||||
ContainerType::ArmoryBody => &mut self.armoury_body,
|
||||
|
@ -223,6 +229,7 @@ impl Inventory {
|
|||
ContainerType::Inventory2 => &self.pages[2],
|
||||
ContainerType::Inventory3 => &self.pages[3],
|
||||
ContainerType::Equipped => &self.equipped,
|
||||
ContainerType::Currency => &self.currency,
|
||||
ContainerType::ArmoryOffWeapon => &self.armoury_off_hand,
|
||||
ContainerType::ArmoryHead => &self.armoury_head,
|
||||
ContainerType::ArmoryBody => &self.armoury_body,
|
||||
|
|
|
@ -15,6 +15,8 @@ pub enum ContainerType {
|
|||
|
||||
Equipped = 1000,
|
||||
|
||||
Currency = 2000,
|
||||
|
||||
ArmoryOffWeapon = 3200,
|
||||
ArmoryHead = 3201,
|
||||
ArmoryBody = 3202,
|
||||
|
|
16
src/ipc/zone/currency_info.rs
Normal file
16
src/ipc/zone/currency_info.rs
Normal file
|
@ -0,0 +1,16 @@
|
|||
use binrw::binrw;
|
||||
|
||||
#[binrw]
|
||||
#[brw(little)]
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct CurrencyInfo {
|
||||
pub sequence: u32,
|
||||
pub container: u16,
|
||||
pub slot: u16,
|
||||
pub quantity: u32,
|
||||
pub unk1: u32,
|
||||
pub catalog_id: u32,
|
||||
pub unk2: u32,
|
||||
pub unk3: u32,
|
||||
pub unk4: u32,
|
||||
}
|
|
@ -79,6 +79,9 @@ pub use equip::Equip;
|
|||
mod client_trigger;
|
||||
pub use client_trigger::{ClientTrigger, ClientTriggerCommand};
|
||||
|
||||
mod currency_info;
|
||||
pub use currency_info::CurrencyInfo;
|
||||
|
||||
use crate::common::ObjectTypeId;
|
||||
use crate::common::Position;
|
||||
use crate::common::read_string;
|
||||
|
@ -149,6 +152,7 @@ pub enum GameMasterCommandType {
|
|||
GiveItem = 0xC8,
|
||||
Aetheryte = 0x5E,
|
||||
TerritoryInfo = 0x25D,
|
||||
Gil = 0xC9,
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
@ -244,6 +248,8 @@ pub enum ServerZoneIpcData {
|
|||
},
|
||||
/// Used to control target information
|
||||
ActorControlTarget(ActorControlTarget),
|
||||
/// Used to update the player's currencies
|
||||
CurrencyCrystalInfo(CurrencyInfo),
|
||||
}
|
||||
|
||||
#[binrw]
|
||||
|
@ -374,7 +380,8 @@ pub enum ClientZoneIpcData {
|
|||
event_id: u32,
|
||||
},
|
||||
#[br(pre_assert(*magic == ClientZoneIpcType::EventHandlerReturn))]
|
||||
EventHandlerReturn { // TODO: This is actually EventYieldHandler
|
||||
EventHandlerReturn {
|
||||
// TODO: This is actually EventYieldHandler
|
||||
handler_id: u32,
|
||||
scene: u16,
|
||||
error_code: u8,
|
||||
|
|
|
@ -12,15 +12,16 @@ use crate::{
|
|||
OBFUSCATION_ENABLED_MODE,
|
||||
common::{GameData, ObjectId, ObjectTypeId, Position, timestamp_secs},
|
||||
config::{WorldConfig, get_config},
|
||||
inventory::{Inventory, Item},
|
||||
inventory::{ContainerType, Inventory, Item},
|
||||
ipc::{
|
||||
chat::ServerChatIpcSegment,
|
||||
zone::{
|
||||
ActionEffect, ActionRequest, ActionResult, ActorControl, ActorControlCategory,
|
||||
ActorControlSelf, ActorControlTarget, ClientZoneIpcSegment, CommonSpawn, ContainerInfo,
|
||||
DisplayFlag, EffectKind, Equip, GameMasterRank, InitZone, ItemInfo, Move, NpcSpawn,
|
||||
ObjectKind, PlayerStats, PlayerSubKind, ServerZoneIpcData, ServerZoneIpcSegment,
|
||||
StatusEffect, StatusEffectList, UpdateClassInfo, Warp, WeatherChange,
|
||||
CurrencyInfo, DisplayFlag, EffectKind, Equip, GameMasterRank, InitZone, ItemInfo, Move,
|
||||
NpcSpawn, ObjectKind, PlayerStats, PlayerSubKind, ServerZoneIpcData,
|
||||
ServerZoneIpcSegment, StatusEffect, StatusEffectList, UpdateClassInfo, Warp,
|
||||
WeatherChange,
|
||||
},
|
||||
},
|
||||
opcodes::ServerZoneIpcType,
|
||||
|
@ -484,6 +485,38 @@ impl ZoneConnection {
|
|||
let mut sequence = 0;
|
||||
|
||||
for (container_type, container) in &self.player_data.inventory.clone() {
|
||||
// currencies
|
||||
if container_type == ContainerType::Currency {
|
||||
let mut send_currency = async |slot_index: u16, item: &Item| {
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::CurrencyCrystalInfo,
|
||||
timestamp: timestamp_secs(),
|
||||
data: ServerZoneIpcData::CurrencyCrystalInfo(CurrencyInfo {
|
||||
sequence,
|
||||
container: item.id as u16,
|
||||
slot: slot_index,
|
||||
quantity: item.quantity,
|
||||
catalog_id: item.id,
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
self.send_segment(PacketSegment {
|
||||
source_actor: self.player_data.actor_id,
|
||||
target_actor: self.player_data.actor_id,
|
||||
segment_type: SegmentType::Ipc,
|
||||
data: SegmentData::Ipc { data: ipc },
|
||||
})
|
||||
.await;
|
||||
};
|
||||
|
||||
for i in 0..container.max_slots() {
|
||||
send_currency(i as u16, container.get_slot(i as u16)).await;
|
||||
}
|
||||
} else {
|
||||
// items
|
||||
|
||||
let mut send_slot = async |slot_index: u16, item: &Item| {
|
||||
let ipc = ServerZoneIpcSegment {
|
||||
op_code: ServerZoneIpcType::UpdateItem,
|
||||
|
@ -512,6 +545,7 @@ impl ZoneConnection {
|
|||
for i in 0..container.max_slots() {
|
||||
send_slot(i as u16, container.get_slot(i as u16)).await;
|
||||
}
|
||||
}
|
||||
|
||||
// inform the client of container state
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue