Compare commits

...

14 Commits

  1. 13
      data/quests.toml
  2. 77
      src/bin/main.rs
  3. 27
      src/entity/gateway/entitygateway.rs
  4. 136
      src/entity/gateway/inmemory.rs
  5. 26
      src/entity/gateway/postgres/migrations/V0001__initial.sql
  6. 277
      src/entity/gateway/postgres/models.rs
  7. 131
      src/entity/gateway/postgres/postgres.rs
  8. 30
      src/entity/item/armor.rs
  9. 37
      src/entity/item/esweapon.rs
  10. 58
      src/entity/item/mag.rs
  11. 73
      src/entity/item/mod.rs
  12. 28
      src/entity/item/shield.rs
  13. 21
      src/entity/item/tech.rs
  14. 26
      src/entity/item/tool.rs
  15. 31
      src/entity/item/unit.rs
  16. 48
      src/entity/item/weapon.rs
  17. 4
      src/login/character.rs
  18. 5
      src/ship/drops/generic_armor.rs
  19. 5
      src/ship/drops/generic_shield.rs
  20. 2
      src/ship/drops/generic_unit.rs
  21. 5
      src/ship/drops/generic_weapon.rs
  22. 5
      src/ship/drops/rare_drop_table.rs
  23. 6
      src/ship/drops/tech_table.rs
  24. 3
      src/ship/drops/tool_table.rs
  25. 123
      src/ship/items/inventory.rs
  26. 256
      src/ship/items/manager.rs
  27. 2
      src/ship/map/area.rs
  28. 8
      src/ship/map/enemy.rs
  29. 4
      src/ship/map/maps.rs
  30. 2
      src/ship/map/object.rs
  31. 2
      src/ship/map/variant.rs
  32. 2
      src/ship/packet/builder/message.rs
  33. 11
      src/ship/packet/handler/message.rs
  34. 2
      src/ship/packet/handler/quest.rs
  35. 2
      src/ship/ship.rs
  36. 3
      src/ship/shops/armor.rs
  37. 15
      src/ship/shops/tool.rs
  38. 1
      src/ship/shops/weapon.rs
  39. 35
      tests/test_bank.rs
  40. 289
      tests/test_item_modifiers.rs
  41. 18
      tests/test_item_pickup.rs
  42. 13
      tests/test_item_use.rs
  43. 3
      tests/test_mags.rs
  44. 9
      tests/test_menu_actions.rs
  45. 2
      tests/test_rooms.rs
  46. 5
      tests/test_shops.rs

13
data/quests.toml

@ -1,6 +1,6 @@
[Extermination] [Extermination]
list_order = 1 list_order = 1
description = "I am a description"
description = "kill some shit"
[[Extermination.quests]] [[Extermination.quests]]
bin = "q058-ret-bb.bin" bin = "q058-ret-bb.bin"
@ -32,4 +32,13 @@ dat = "q233-ext-bb.dat"
[[Retrieval.quests]] [[Retrieval.quests]]
bin = "q236-ext-bb.bin" bin = "q236-ext-bb.bin"
dat = "q236-ext-bb.dat" dat = "q236-ext-bb.dat"
#drop_table = "q102-drops"
#drop_table = "q102-drops"
[Shop]
list_order = 3
description = "buy some shit"
[[Shop.quests]]
bin = "q219-shp-bb.bin"
dat = "q219-shp-bb.dat"
#drop_table = "q204-drops"

77
src/bin/main.rs

@ -74,7 +74,7 @@ fn main() {
character.meseta = 999999; character.meseta = 999999;
let character = entity_gateway.create_character(character).await.unwrap(); let character = entity_gateway.create_character(character).await.unwrap();
for _ in 0..3 {
for _ in 0..3usize {
entity_gateway.create_item( entity_gateway.create_item(
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Weapon( item: item::ItemDetail::Weapon(
@ -84,6 +84,7 @@ fn main() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -93,12 +94,13 @@ fn main() {
}).await.unwrap(); }).await.unwrap();
} }
for _ in 0..8 {
for _ in 0..8usize {
entity_gateway.create_item( entity_gateway.create_item(
NewItemEntity { NewItemEntity {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -119,6 +121,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: false, tekked: false,
wrapping: Some(item::WrappingPaper::BlackYellow),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -136,6 +139,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -153,6 +157,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -170,6 +175,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -187,6 +193,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -208,6 +215,7 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::FedToMag { location: item::ItemLocation::FedToMag {
@ -223,6 +231,7 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -234,6 +243,8 @@ fn main() {
item: ItemDetail::Tool ( item: ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
// wrapping: None,
wrapping: Some(item::WrappingPaper::PinkYellowGreen),
} }
), ),
location: item::ItemLocation::Consumed, location: item::ItemLocation::Consumed,
@ -251,6 +262,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
None,], None,],
tekked: false, tekked: false,
wrapping: None,
} }
), ),
location: ItemLocation::Bank { location: ItemLocation::Bank {
@ -266,6 +278,7 @@ fn main() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: Some(item::WrappingPaper::Green),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -280,6 +293,7 @@ fn main() {
shield: item::shield::ShieldType::Barrier, shield: item::shield::ShieldType::Barrier,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
wrapping: Some(item::WrappingPaper::Green),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -293,6 +307,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -306,6 +321,8 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
// wrapping: None,
wrapping: Some(item::WrappingPaper::YellowBlue),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -319,6 +336,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -332,6 +350,7 @@ fn main() {
item::unit::Unit { item::unit::Unit {
unit: item::unit::UnitType::PriestMind, unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus), modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -341,8 +360,56 @@ fn main() {
).await.unwrap(); ).await.unwrap();
let item13 = entity_gateway.create_item( let item13 = entity_gateway.create_item(
NewItemEntity { NewItemEntity {
item: ItemDetail::Mag(
item::mag::Mag::baby_mag(5)
item: item::ItemDetail::Mag(item::mag::Mag::wrapped_baby_mag(5)),
location: ItemLocation::Inventory {
character_id: character.id,
}
}
).await.unwrap();
// wrapping monomates doesn't do anything
let item14 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
// wrapping: None,
wrapping: Some(item::WrappingPaper::Yellow),
}
),
location: ItemLocation::Inventory {
character_id: character.id,
}
}
).await.unwrap();
/* wrapping techs is no bueno */
// let item15 = entity_gateway.create_item(
// NewItemEntity {
// item: ItemDetail::TechniqueDisk (
// item::tech::TechniqueDisk {
// tech: item::tech::Technique::Foie,
// level: 5,
// // wrapping: None,
// wrapping: Some(item::WrappingPaper::Blue),
// }
// ),
// location: ItemLocation::Inventory {
// character_id: character.id,
// }
// }
// ).await.unwrap();
let item16 = entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::ESWeapon (
item::esweapon::ESWeapon {
esweapon: item::esweapon::ESWeaponType::Hammer,
special: Some(item::esweapon::ESWeaponSpecial::Hell),
name: "BAN".to_owned(),
grind: 69u8,
wrapping: Some(item::WrappingPaper::LightBlueOrange),
}
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -359,7 +426,7 @@ fn main() {
}; };
entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap(); entity_gateway.set_character_equips(&character.id, &equipped).await.unwrap();
let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13]);
let inventory = item::InventoryEntity::new(vec![item0, item1, item2_w, item3, item4, item5_m, item6, item7_a, item8_s, item9_u0, item10_u1, item11_u2, item12_u3, item13, item14, /*item15,*/ item16]);
entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap(); entity_gateway.set_character_inventory(&character.id, &inventory).await.unwrap();
entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap(); entity_gateway.set_character_bank(&character.id, &item::BankEntity::default(), item::BankName("".into())).await.unwrap();
} }

27
src/entity/gateway/entitygateway.rs

@ -85,6 +85,33 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn add_esweapon_modifier(&mut self, _item_id: &ItemEntityId, _modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_armor_modifier(&mut self, _item_id: &ItemEntityId, _modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_unit_modifier(&mut self, _item_id: &ItemEntityId, _modifier: unit::UnitModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_shield_modifier(&mut self, _item_id: &ItemEntityId, _modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_mag_modifier(&mut self, _item_id: &ItemEntityId, _modifier: mag::MagModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_tech_modifier(&mut self, _item_id: &ItemEntityId, _modifier: tech::TechModifier) -> Result<(), GatewayError> {
unimplemented!();
}
async fn add_tool_modifier(&mut self, _item_id: &ItemEntityId, _modifier: tool::ToolModifier) -> Result<(), GatewayError> {
unimplemented!();
}
/* /*
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {

136
src/entity/gateway/inmemory.rs

@ -17,8 +17,14 @@ pub struct InMemoryGateway {
inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>, inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>,
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>, banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>, equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>, weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
esweapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<esweapon::ESWeaponModifier>>>>,
armor_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<armor::ArmorModifier>>>>,
unit_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<unit::UnitModifier>>>>,
shield_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<shield::ShieldModifier>>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
tech_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<tech::TechModifier>>>>,
tool_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<tool::ToolModifier>>>>,
} }
impl InMemoryGateway { impl InMemoryGateway {
@ -31,8 +37,14 @@ impl InMemoryGateway {
inventories: Arc::new(Mutex::new(BTreeMap::new())), inventories: Arc::new(Mutex::new(BTreeMap::new())),
banks: Arc::new(Mutex::new(BTreeMap::new())), banks: Arc::new(Mutex::new(BTreeMap::new())),
equips: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
esweapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
armor_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
unit_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
shield_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
tech_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
tool_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -52,8 +64,46 @@ impl InMemoryGateway {
} }
ItemDetail::Weapon(weapon) ItemDetail::Weapon(weapon)
}, },
ItemDetail::ESWeapon(mut esweapon) => {
if let Some(esweapon_modifiers) = self.esweapon_modifiers.lock().unwrap().get(&item.id) {
for esweapon_modifier in esweapon_modifiers.iter() {
esweapon.apply_modifier(&esweapon_modifier);
}
}
ItemDetail::ESWeapon(esweapon)
},
ItemDetail::Armor(mut armor) => {
if let Some(armor_modifiers) = self.armor_modifiers.lock().unwrap().get(&item.id) {
for armor_modifier in armor_modifiers.iter() {
armor.apply_modifier(&armor_modifier);
}
}
ItemDetail::Armor(armor)
},
ItemDetail::Unit(mut unit) => {
if let Some(unit_modifiers) = self.unit_modifiers.lock().unwrap().get(&item.id) {
for unit_modifier in unit_modifiers.iter() {
unit.apply_modifier(&unit_modifier);
}
}
ItemDetail::Unit(unit)
},
ItemDetail::Shield(mut shield) => {
if let Some(shield_modifiers) = self.shield_modifiers.lock().unwrap().get(&item.id) {
for shield_modifier in shield_modifiers.iter() {
shield.apply_modifier(&shield_modifier);
}
}
ItemDetail::Shield(shield)
},
ItemDetail::Mag(mag) => { ItemDetail::Mag(mag) => {
let mut mag = mag::Mag::baby_mag(mag.color as u16);
let mut mag = {
if mag.wrapping.is_some() {
mag::Mag::wrapped_baby_mag(mag.color as u16)
} else {
mag::Mag::baby_mag(mag.color as u16)
}
};
if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) { if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) {
for mag_modifier in mag_modifiers.iter() { for mag_modifier in mag_modifiers.iter() {
match mag_modifier { match mag_modifier {
@ -81,12 +131,24 @@ impl InMemoryGateway {
} }
} }
ItemDetail::Mag(mag) ItemDetail::Mag(mag)
}
_ => {
item.item
}
},
ItemDetail::TechniqueDisk(mut tech) => {
if let Some(tech_modifiers) = self.tech_modifiers.lock().unwrap().get(&item.id) {
for tech_modifier in tech_modifiers.iter() {
tech.apply_modifier(&tech_modifier);
}
}
ItemDetail::TechniqueDisk(tech)
},
ItemDetail::Tool(mut tool) => {
if let Some(tool_modifiers) = self.tool_modifiers.lock().unwrap().get(&item.id) {
for tool_modifier in tool_modifiers.iter() {
tool.apply_modifier(&tool_modifier);
}
}
ItemDetail::Tool(tool)
},
}; };
item item
}) })
}) })
@ -273,6 +335,62 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
async fn add_esweapon_modifier(&mut self, item_id: &ItemEntityId, modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
self.esweapon_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_armor_modifier(&mut self, item_id: &ItemEntityId, modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
self.armor_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_unit_modifier(&mut self, item_id: &ItemEntityId, modifier: unit::UnitModifier) -> Result<(), GatewayError> {
self.unit_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_shield_modifier(&mut self, item_id: &ItemEntityId, modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
self.shield_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_mag_modifier(&mut self, item_id: &ItemEntityId, modifier: mag::MagModifier) -> Result<(), GatewayError> {
self.mag_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_tech_modifier(&mut self, item_id: &ItemEntityId, modifier: tech::TechModifier) -> Result<(), GatewayError> {
self.tech_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn add_tool_modifier(&mut self, item_id: &ItemEntityId, modifier: tool::ToolModifier) -> Result<(), GatewayError> {
self.tool_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> { async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
println!("getting inv"); println!("getting inv");
let inventories = self.inventories.lock().unwrap(); let inventories = self.inventories.lock().unwrap();
@ -299,7 +417,7 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
// TOOD: impl bank name
// TODO: impl bank name
async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> { async fn set_character_bank(&mut self, char_id: &CharacterEntityId, bank: &BankEntity, _bank_name: BankName) -> Result<(), GatewayError> {
let mut banks = self.banks.lock().unwrap(); let mut banks = self.banks.lock().unwrap();
banks.insert(*char_id, bank.clone()); banks.insert(*char_id, bank.clone());

26
src/entity/gateway/postgres/migrations/V0001__initial.sql

@ -50,7 +50,7 @@ create table player_character (
prop_y real not null, prop_y real not null,
techs bytea not null, techs bytea not null,
config bytea not null, config bytea not null,
infoboard varchar(172) not null, infoboard varchar(172) not null,
guildcard varchar(172) not null, guildcard varchar(172) not null,
@ -93,14 +93,14 @@ create table weapon_modifier (
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table armor_modifier (
armor integer references item (id) not null,
create table esweapon_modifier (
esweapon integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table shield_modifier (
shield integer references item (id) not null,
create table armor_modifier (
armor integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
@ -111,8 +111,8 @@ create table unit_modifier (
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table esweapon_modifier (
esweapon integer references item (id) not null,
create table shield_modifier (
shield integer references item (id) not null,
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
@ -122,3 +122,15 @@ create table mag_modifier (
modifier jsonb not null, modifier jsonb not null,
created_at timestamptz default current_timestamp not null created_at timestamptz default current_timestamp not null
); );
create table tech_modifier (
tech integer references item (id) not null,
modifier jsonb not null,
created_at timestamptz default current_timestamp not null
);
create table tool_modifier (
tool integer references item (id) not null,
modifier jsonb not null,
created_at timestamptz default current_timestamp not null
);

277
src/entity/gateway/postgres/models.rs

@ -178,7 +178,6 @@ impl From<SectionID> for PgSectionId {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgCharacter { pub struct PgCharacter {
pub id: i32, pub id: i32,
@ -277,8 +276,6 @@ impl Into<CharacterEntity> for PgCharacter {
pub struct PgGuildCard { pub struct PgGuildCard {
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgWeapon { pub struct PgWeapon {
weapon: weapon::WeaponType, weapon: weapon::WeaponType,
@ -286,6 +283,7 @@ pub struct PgWeapon {
grind: u8, grind: u8,
attrs: HashMap<weapon::Attribute, i8>, attrs: HashMap<weapon::Attribute, i8>,
tekked: bool, tekked: bool,
wrapping: Option<WrappingPaper>,
} }
impl From<weapon::Weapon> for PgWeapon { impl From<weapon::Weapon> for PgWeapon {
@ -296,6 +294,7 @@ impl From<weapon::Weapon> for PgWeapon {
grind: other.grind, grind: other.grind,
attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(), attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(),
tekked: other.tekked, tekked: other.tekked,
wrapping: other.wrapping,
} }
} }
} }
@ -316,6 +315,7 @@ impl Into<weapon::Weapon> for PgWeapon {
grind: self.grind, grind: self.grind,
attrs: attrs, attrs: attrs,
tekked: self.tekked, tekked: self.tekked,
wrapping: self.wrapping,
} }
} }
} }
@ -326,12 +326,52 @@ pub struct PgWeaponModifier {
pub modifier: sqlx::types::Json<weapon::WeaponModifier>, pub modifier: sqlx::types::Json<weapon::WeaponModifier>,
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct PgESWeapon {
esweapon: esweapon::ESWeaponType,
special: Option<esweapon::ESWeaponSpecial>,
name: String,
grind: u8,
wrapping: Option<WrappingPaper>,
}
impl From<esweapon::ESWeapon> for PgESWeapon {
fn from(other: esweapon::ESWeapon) -> PgESWeapon {
PgESWeapon {
esweapon: other.esweapon,
special: other.special,
name: other.name,
grind: other.grind,
wrapping: other.wrapping,
}
}
}
impl Into<esweapon::ESWeapon> for PgESWeapon {
fn into(self) -> esweapon::ESWeapon {
esweapon::ESWeapon {
esweapon: self.esweapon,
special: self.special,
name: self.name,
grind: self.grind,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgESWeaponModifier {
pub esweapon: i32,
pub modifier: sqlx::types::Json<esweapon::ESWeaponModifier>,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgArmor { pub struct PgArmor {
armor: armor::ArmorType, armor: armor::ArmorType,
dfp: u8, dfp: u8,
evp: u8, evp: u8,
slots: u8, slots: u8,
wrapping: Option<WrappingPaper>, // TODO: check if this clobbers slots
} }
impl From<armor::Armor> for PgArmor { impl From<armor::Armor> for PgArmor {
@ -341,6 +381,7 @@ impl From<armor::Armor> for PgArmor {
dfp: other.dfp, dfp: other.dfp,
evp: other.evp, evp: other.evp,
slots: other.slots, slots: other.slots,
wrapping: other.wrapping,
} }
} }
} }
@ -352,41 +393,22 @@ impl Into<armor::Armor> for PgArmor {
dfp: self.dfp, dfp: self.dfp,
evp: self.evp, evp: self.evp,
slots: self.slots, slots: self.slots,
wrapping: self.wrapping,
} }
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct PgShield {
shield: shield::ShieldType,
dfp: u8,
evp: u8,
}
impl From<shield::Shield> for PgShield {
fn from(other: shield::Shield) -> PgShield {
PgShield {
shield: other.shield,
dfp: other.dfp,
evp: other.evp,
}
}
}
impl Into<shield::Shield> for PgShield {
fn into(self) -> shield::Shield {
shield::Shield {
shield: self.shield,
dfp: self.dfp,
evp: self.evp,
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgArmorModifier {
pub armor: i32,
pub modifier: sqlx::types::Json<armor::ArmorModifier>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgUnit { pub struct PgUnit {
unit: unit::UnitType, unit: unit::UnitType,
modifier: Option<unit::UnitModifier>, modifier: Option<unit::UnitModifier>,
wrapping: Option<WrappingPaper>,
} }
impl From<unit::Unit> for PgUnit { impl From<unit::Unit> for PgUnit {
@ -394,6 +416,7 @@ impl From<unit::Unit> for PgUnit {
PgUnit { PgUnit {
unit: other.unit, unit: other.unit,
modifier: other.modifier, modifier: other.modifier,
wrapping: other.wrapping,
} }
} }
} }
@ -403,53 +426,51 @@ impl Into<unit::Unit> for PgUnit {
unit::Unit { unit::Unit {
unit: self.unit, unit: self.unit,
modifier: self.modifier, modifier: self.modifier,
wrapping: self.wrapping,
} }
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTool {
pub tool: tool::ToolType,
#[derive(Debug, sqlx::FromRow)]
pub struct PgUnitModifier {
pub unit: i32,
pub modifier: sqlx::types::Json<unit::UnitModifier>,
} }
impl From<tool::Tool> for PgTool {
fn from(other: tool::Tool) -> PgTool {
PgTool {
tool: other.tool,
}
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgShield {
shield: shield::ShieldType,
dfp: u8,
evp: u8,
wrapping: Option<WrappingPaper>,
} }
impl Into<tool::Tool> for PgTool {
fn into(self) -> tool::Tool {
tool::Tool {
tool: self.tool,
impl From<shield::Shield> for PgShield {
fn from(other: shield::Shield) -> PgShield {
PgShield {
shield: other.shield,
dfp: other.dfp,
evp: other.evp,
wrapping: other.wrapping,
} }
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTechDisk {
tech: tech::Technique,
level: u32,
}
impl From<tech::TechniqueDisk> for PgTechDisk {
fn from(other: tech::TechniqueDisk) -> PgTechDisk {
PgTechDisk {
tech: other.tech,
level: other.level,
impl Into<shield::Shield> for PgShield {
fn into(self) -> shield::Shield {
shield::Shield {
shield: self.shield,
dfp: self.dfp,
evp: self.evp,
wrapping: self.wrapping,
} }
} }
} }
impl Into<tech::TechniqueDisk> for PgTechDisk {
fn into(self) -> tech::TechniqueDisk {
tech::TechniqueDisk {
tech: self.tech,
level: self.level
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgShieldModifier {
pub shield: i32,
pub modifier: sqlx::types::Json<shield::ShieldModifier>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@ -457,6 +478,7 @@ pub struct PgMag {
mag: mag::MagType, mag: mag::MagType,
synchro: u8, synchro: u8,
color: u8, color: u8,
wrapping: Option<WrappingPaper>,
} }
impl From<mag::Mag> for PgMag { impl From<mag::Mag> for PgMag {
@ -465,85 +487,107 @@ impl From<mag::Mag> for PgMag {
mag: other.mag, mag: other.mag,
synchro: other.synchro, synchro: other.synchro,
color: other.color, color: other.color,
wrapping: other.wrapping,
} }
} }
} }
impl Into<mag::Mag> for PgMag { impl Into<mag::Mag> for PgMag {
fn into(self) -> mag::Mag { fn into(self) -> mag::Mag {
/*mag::Mag {
mag: self.mag,
synchro: self.synchro,
color: self.color,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
iq: 0,
photon_blast: [None; 3],
class: CharacterClass::HUmar,
id: SectionID::Viridia,
}*/
let mut mag = mag::Mag::baby_mag(self.color as u16); let mut mag = mag::Mag::baby_mag(self.color as u16);
mag.mag = self.mag; mag.mag = self.mag;
mag.synchro = self.synchro; mag.synchro = self.synchro;
mag.wrapping = self.wrapping;
mag mag
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgESWeapon {
esweapon: esweapon::ESWeaponType,
special: Option<esweapon::ESWeaponSpecial>,
name: String,
grind: u8,
pub struct PgTechDisk {
tech: tech::Technique,
level: u32,
wrapping: Option<WrappingPaper>,
} }
impl From<esweapon::ESWeapon> for PgESWeapon {
fn from(other: esweapon::ESWeapon) -> PgESWeapon {
PgESWeapon {
esweapon: other.esweapon,
special: other.special,
name: other.name,
grind: other.grind,
impl From<tech::TechniqueDisk> for PgTechDisk {
fn from(other: tech::TechniqueDisk) -> PgTechDisk {
PgTechDisk {
tech: other.tech,
level: other.level,
wrapping: other.wrapping,
} }
} }
} }
impl Into<esweapon::ESWeapon> for PgESWeapon {
fn into(self) -> esweapon::ESWeapon {
esweapon::ESWeapon {
esweapon: self.esweapon,
special: self.special,
name: self.name,
grind: self.grind,
impl Into<tech::TechniqueDisk> for PgTechDisk {
fn into(self) -> tech::TechniqueDisk {
tech::TechniqueDisk {
tech: self.tech,
level: self.level,
wrapping: self.wrapping,
} }
} }
} }
#[derive(Debug, sqlx::FromRow)]
pub struct PgTechModifier {
pub tool: i32,
pub modifier: sqlx::types::Json<tech::TechModifier>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgTool {
pub tool: tool::ToolType,
wrapping: Option<WrappingPaper>,
}
impl From<tool::Tool> for PgTool {
fn from(other: tool::Tool) -> PgTool {
PgTool {
tool: other.tool,
wrapping: other.wrapping,
}
}
}
impl Into<tool::Tool> for PgTool {
fn into(self) -> tool::Tool {
tool::Tool {
tool: self.tool,
wrapping: self.wrapping,
}
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgToolModifier {
pub tool: i32,
pub modifier: sqlx::types::Json<tool::ToolModifier>,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgItemDetail { pub enum PgItemDetail {
Weapon(PgWeapon), Weapon(PgWeapon),
ESWeapon(PgESWeapon),
Armor(PgArmor), Armor(PgArmor),
Shield(PgShield),
Unit(PgUnit), Unit(PgUnit),
Tool(PgTool),
TechDisk(PgTechDisk),
Shield(PgShield),
Mag(PgMag), Mag(PgMag),
ESWeapon(PgESWeapon),
TechDisk(PgTechDisk),
Tool(PgTool),
} }
impl From<ItemDetail> for PgItemDetail { impl From<ItemDetail> for PgItemDetail {
fn from(other: ItemDetail) -> PgItemDetail { fn from(other: ItemDetail) -> PgItemDetail {
match other { match other {
ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()), ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()),
ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()),
ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()), ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()),
ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.into()),
ItemDetail::Unit(unit) => PgItemDetail::Unit(unit.into()), ItemDetail::Unit(unit) => PgItemDetail::Unit(unit.into()),
ItemDetail::Tool(tool) => PgItemDetail::Tool(tool.into()),
ItemDetail::TechniqueDisk(tech_disk) => PgItemDetail::TechDisk(tech_disk.into()),
ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.into()),
ItemDetail::Mag(mag) => PgItemDetail::Mag(mag.into()), ItemDetail::Mag(mag) => PgItemDetail::Mag(mag.into()),
ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()),
ItemDetail::TechniqueDisk(tech_disk) => PgItemDetail::TechDisk(tech_disk.into()),
ItemDetail::Tool(tool) => PgItemDetail::Tool(tool.into()),
} }
} }
} }
@ -552,13 +596,13 @@ impl Into<ItemDetail> for PgItemDetail {
fn into(self) -> ItemDetail { fn into(self) -> ItemDetail {
match self { match self {
PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()), PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()),
PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()),
PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()), PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()),
PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.into()),
PgItemDetail::Unit(unit) => ItemDetail::Unit(unit.into()), PgItemDetail::Unit(unit) => ItemDetail::Unit(unit.into()),
PgItemDetail::Tool(tool) => ItemDetail::Tool(tool.into()),
PgItemDetail::TechDisk(tech_disk) => ItemDetail::TechniqueDisk(tech_disk.into()),
PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.into()),
PgItemDetail::Mag(mag) => ItemDetail::Mag(mag.into()), PgItemDetail::Mag(mag) => ItemDetail::Mag(mag.into()),
PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()),
PgItemDetail::TechDisk(tech_disk) => ItemDetail::TechniqueDisk(tech_disk.into()),
PgItemDetail::Tool(tool) => ItemDetail::Tool(tool.into()),
} }
} }
} }
@ -569,7 +613,6 @@ pub struct PgItem {
pub item: sqlx::types::Json<PgItemDetail>, pub item: sqlx::types::Json<PgItemDetail>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgItemLocationDetail { pub enum PgItemLocationDetail {
Inventory { Inventory {
@ -627,7 +670,6 @@ impl Into<ItemLocation> for PgItemLocationDetail {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgItemLocation { pub struct PgItemLocation {
//pub id: i32, //pub id: i32,
@ -635,13 +677,14 @@ pub struct PgItemLocation {
created_at: chrono::DateTime<chrono::Utc>, created_at: chrono::DateTime<chrono::Utc>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum PgMagModifierDetail { pub enum PgMagModifierDetail {
FeedMag(i32), FeedMag(i32),
BankMag, BankMag,
MagCell(i32), MagCell(i32),
OwnerChange(CharacterClass, SectionID)
OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
} }
impl From<mag::MagModifier> for PgMagModifierDetail { impl From<mag::MagModifier> for PgMagModifierDetail {
@ -651,6 +694,8 @@ impl From<mag::MagModifier> for PgMagModifierDetail {
mag::MagModifier::BankMag => PgMagModifierDetail::BankMag, mag::MagModifier::BankMag => PgMagModifierDetail::BankMag,
mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32), mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32),
mag::MagModifier::OwnerChange(class, section_id) => PgMagModifierDetail::OwnerChange(class, section_id), mag::MagModifier::OwnerChange(class, section_id) => PgMagModifierDetail::OwnerChange(class, section_id),
mag::MagModifier::WrapPresent => PgMagModifierDetail::WrapPresent,
mag::MagModifier::UnwrapPresent => PgMagModifierDetail::UnwrapPresent,
} }
} }
} }
@ -662,6 +707,8 @@ impl Into<mag::MagModifier> for PgMagModifierDetail {
PgMagModifierDetail::BankMag => mag::MagModifier::BankMag, PgMagModifierDetail::BankMag => mag::MagModifier::BankMag,
PgMagModifierDetail::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)), PgMagModifierDetail::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)),
PgMagModifierDetail::OwnerChange(class, section_id) => mag::MagModifier::OwnerChange(class, section_id), PgMagModifierDetail::OwnerChange(class, section_id) => mag::MagModifier::OwnerChange(class, section_id),
PgMagModifierDetail::WrapPresent => mag::MagModifier::WrapPresent,
PgMagModifierDetail::UnwrapPresent => mag::MagModifier::UnwrapPresent,
} }
} }
} }
@ -673,7 +720,6 @@ pub struct PgMagModifier {
created_at: chrono::DateTime<chrono::Utc>, created_at: chrono::DateTime<chrono::Utc>,
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgItemEntity { pub struct PgItemEntity {
pub id: i32, pub id: i32,
@ -697,7 +743,6 @@ impl Into<ItemEntity> for PgItemWithLocation {
} }
} }
#[derive(Debug, sqlx::FromRow)] #[derive(Debug, sqlx::FromRow)]
pub struct PgMagModifierWithParameters { pub struct PgMagModifierWithParameters {
pub mag: i32, pub mag: i32,
@ -706,7 +751,6 @@ pub struct PgMagModifierWithParameters {
pub cell: Option<sqlx::types::Json<PgTool>>, pub cell: Option<sqlx::types::Json<PgTool>>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum PgInventoryItemEntity { pub enum PgInventoryItemEntity {
@ -732,11 +776,11 @@ pub struct PgEquipped {
pchar: i32, pchar: i32,
weapon: Option<i32>, weapon: Option<i32>,
armor: Option<i32>, armor: Option<i32>,
shield: Option<i32>,
unit0: Option<i32>, unit0: Option<i32>,
unit1: Option<i32>, unit1: Option<i32>,
unit2: Option<i32>, unit2: Option<i32>,
unit3: Option<i32>, unit3: Option<i32>,
shield: Option<i32>,
mag: Option<i32>, mag: Option<i32>,
} }
@ -745,12 +789,12 @@ impl Into<EquippedEntity> for PgEquipped {
EquippedEntity { EquippedEntity {
weapon: self.weapon.map(|i| ItemEntityId(i as u32)), weapon: self.weapon.map(|i| ItemEntityId(i as u32)),
armor: self.armor.map(|i| ItemEntityId(i as u32)), armor: self.armor.map(|i| ItemEntityId(i as u32)),
shield: self.shield.map(|i| ItemEntityId(i as u32)),
unit: [self.unit0.map(|i| ItemEntityId(i as u32)), unit: [self.unit0.map(|i| ItemEntityId(i as u32)),
self.unit1.map(|i| ItemEntityId(i as u32)),
self.unit2.map(|i| ItemEntityId(i as u32)),
self.unit3.map(|i| ItemEntityId(i as u32)),
self.unit1.map(|i| ItemEntityId(i as u32)),
self.unit2.map(|i| ItemEntityId(i as u32)),
self.unit3.map(|i| ItemEntityId(i as u32)),
], ],
shield: self.shield.map(|i| ItemEntityId(i as u32)),
mag: self.mag.map(|i| ItemEntityId(i as u32)), mag: self.mag.map(|i| ItemEntityId(i as u32)),
} }
} }
@ -762,13 +806,12 @@ impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
pchar: char_equips.0.0 as i32, pchar: char_equips.0.0 as i32,
weapon: char_equips.1.weapon.map(|i| i.0 as i32), weapon: char_equips.1.weapon.map(|i| i.0 as i32),
armor: char_equips.1.armor.map(|i| i.0 as i32), armor: char_equips.1.armor.map(|i| i.0 as i32),
shield: char_equips.1.shield.map(|i| i.0 as i32),
unit0: char_equips.1.unit[0].map(|i| i.0 as i32), unit0: char_equips.1.unit[0].map(|i| i.0 as i32),
unit1: char_equips.1.unit[1].map(|i| i.0 as i32), unit1: char_equips.1.unit[1].map(|i| i.0 as i32),
unit2: char_equips.1.unit[2].map(|i| i.0 as i32), unit2: char_equips.1.unit[2].map(|i| i.0 as i32),
unit3: char_equips.1.unit[3].map(|i| i.0 as i32), unit3: char_equips.1.unit[3].map(|i| i.0 as i32),
shield: char_equips.1.shield.map(|i| i.0 as i32),
mag: char_equips.1.mag.map(|i| i.0 as i32), mag: char_equips.1.mag.map(|i| i.0 as i32),
} }
} }
} }

131
src/entity/gateway/postgres/postgres.rs

@ -65,6 +65,74 @@ impl PostgresGateway {
ItemDetail::Weapon(weapon) ItemDetail::Weapon(weapon)
}, },
ItemDetail::ESWeapon(mut esweapon) => {
let q = r#"select esweapon, modifier
from esweapon_modifier
where esweapon = $1
order by created_at"#;
let esweapon_modifiers = sqlx::query_as::<_, PgESWeaponModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
esweapon_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
esweapon.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::ESWeapon(esweapon)
},
ItemDetail::Armor(mut armor) => {
let q = r#"select armor, modifier
from armor_modifier
where armor = $1
order by created_at"#;
let armor_modifiers = sqlx::query_as::<_, PgArmorModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
armor_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
armor.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Armor(armor)
},
ItemDetail::Unit(mut unit) => {
let q = r#"select unit, modifier
from unit_modifier
where unit = $1
order by created_at"#;
let unit_modifiers = sqlx::query_as::<_, PgUnitModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
unit_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
unit.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Unit(unit)
},
ItemDetail::Shield(mut shield) => {
let q = r#"select shield, modifier
from shield_modifier
where shield = $1
order by created_at"#;
let shield_modifiers = sqlx::query_as::<_, PgShieldModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
shield_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
shield.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Shield(shield)
},
ItemDetail::Mag(mut mag) => { ItemDetail::Mag(mut mag) => {
let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell
from mag_modifier from mag_modifier
@ -91,6 +159,8 @@ impl PostgresGateway {
mag::MagModifier::OwnerChange(class, section_id) => { mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(class, section_id) mag.change_owner(class, section_id)
}, },
mag::MagModifier::WrapPresent => {mag.apply_modifier(&modifier)},
mag::MagModifier::UnwrapPresent => {mag.apply_modifier(&modifier)},
} }
}).await; }).await;
@ -416,6 +486,62 @@ impl EntityGateway for PostgresGateway {
Ok(()) Ok(())
} }
async fn add_esweapon_modifier(&mut self, item_id: &ItemEntityId, modifier: esweapon::ESWeaponModifier) -> Result<(), GatewayError> {
sqlx::query("insert into esweapon_modifier (esweapon, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_armor_modifier(&mut self, item_id: &ItemEntityId, modifier: armor::ArmorModifier) -> Result<(), GatewayError> {
sqlx::query("insert into armor_modifier (armor, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_unit_modifier(&mut self, item_id: &ItemEntityId, modifier: unit::UnitModifier) -> Result<(), GatewayError> {
sqlx::query("insert into unit_modifier (unit, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_shield_modifier(&mut self, item_id: &ItemEntityId, modifier: shield::ShieldModifier) -> Result<(), GatewayError> {
sqlx::query("insert into shield_modifier (shield, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_mag_modifier(&mut self, item_id: &ItemEntityId, modifier: mag::MagModifier) -> Result<(), GatewayError> {
sqlx::query("insert into mag_modifier (mag, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_tech_modifier(&mut self, item_id: &ItemEntityId, modifier: tech::TechModifier) -> Result<(), GatewayError> {
sqlx::query("insert into tech_modifier (tech, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
async fn add_tool_modifier(&mut self, item_id: &ItemEntityId, modifier: tool::ToolModifier) -> Result<(), GatewayError> {
sqlx::query("insert into tool_modifier (tool, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
/* /*
async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
let q = r#"select * from ( let q = r#"select * from (
@ -540,7 +666,6 @@ impl EntityGateway for PostgresGateway {
} }
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2") sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
.bind(char_id.0) .bind(char_id.0)
.bind(sqlx::types::Json(inventory)) .bind(sqlx::types::Json(inventory))
@ -582,16 +707,16 @@ impl EntityGateway for PostgresGateway {
} }
async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> { async fn set_character_equips(&mut self, char_id: &CharacterEntityId, equips: &EquippedEntity) -> Result<(), GatewayError> {
sqlx::query(r#"insert into equipped (pchar, weapon, armor, shield, unit0, unit1, unit2, unit3, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
sqlx::query(r#"insert into equipped (pchar, weapon, armor, unit0, unit1, unit2, unit3, shield, mag) values ($1, $2, $3, $4, $5, $6, $7, $8, $9)
on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#) on conflict (pchar) do update set weapon=$2, armor=$3, shield=$4, unit0=$5, unit1=$6, unit2=$7, unit3=$8, mag=$9"#)
.bind(char_id.0) .bind(char_id.0)
.bind(equips.weapon.map(|i| i.0 as i32)) .bind(equips.weapon.map(|i| i.0 as i32))
.bind(equips.armor.map(|i| i.0 as i32)) .bind(equips.armor.map(|i| i.0 as i32))
.bind(equips.shield.map(|i| i.0 as i32))
.bind(equips.unit[0].map(|i| i.0 as i32)) .bind(equips.unit[0].map(|i| i.0 as i32))
.bind(equips.unit[1].map(|i| i.0 as i32)) .bind(equips.unit[1].map(|i| i.0 as i32))
.bind(equips.unit[2].map(|i| i.0 as i32)) .bind(equips.unit[2].map(|i| i.0 as i32))
.bind(equips.unit[3].map(|i| i.0 as i32)) .bind(equips.unit[3].map(|i| i.0 as i32))
.bind(equips.shield.map(|i| i.0 as i32))
.bind(equips.mag.map(|i| i.0 as i32)) .bind(equips.mag.map(|i| i.0 as i32))
.execute(&self.pool) .execute(&self.pool)
.await?; .await?;

30
src/entity/item/armor.rs

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::ItemEntityId;
use crate::entity::item::{ItemEntityId, WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -288,27 +288,31 @@ impl ArmorType {
} }
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ArmorModifier { pub enum ArmorModifier {
AddSlot { AddSlot {
addslot: ItemEntityId, addslot: ItemEntityId,
}
},
WrapPresent,
UnwrapPresent,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Armor { pub struct Armor {
pub armor: ArmorType, pub armor: ArmorType,
pub dfp: u8, pub dfp: u8,
pub evp: u8, pub evp: u8,
pub slots: u8, pub slots: u8,
pub wrapping: Option<WrappingPaper>,
} }
impl Armor { impl Armor {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
result[0..3].copy_from_slice(&self.armor.value()); result[0..3].copy_from_slice(&self.armor.value());
if self.wrapping.is_some() {
result[4] += 0x40;
}
result[5] = self.slots; result[5] = self.slots;
result[6] = self.dfp; result[6] = self.dfp;
result[8] = self.evp; result[8] = self.evp;
@ -317,16 +321,32 @@ impl Armor {
pub fn from_bytes(data: [u8; 16]) -> Result<Armor, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Armor, ItemParseError> {
let a = ArmorType::parse_type([data[0], data[1], data[2]]); let a = ArmorType::parse_type([data[0], data[1], data[2]]);
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(0) // always use default wrapping paper to preserve slot info
} else {
None
}
};
if a.is_ok() { if a.is_ok() {
Ok(Armor { Ok(Armor {
armor: a.unwrap(), armor: a.unwrap(),
dfp: data[6], dfp: data[6],
evp: data[8], evp: data[8],
slots: data[5], slots: data[5],
wrapping: w,
}) })
} }
else { else {
Err(ItemParseError::InvalidArmorBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidArmorBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &ArmorModifier) {
match modifier {
ArmorModifier::WrapPresent => {self.wrapping = Some(WrappingPaper::WhitePink)},
ArmorModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
}
}
} }

37
src/entity/item/esweapon.rs

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
// TODO: actually use this // TODO: actually use this
#[derive(Debug)] #[derive(Debug)]
pub enum ItemParseError { pub enum ItemParseError {
@ -169,12 +170,21 @@ impl ESWeaponSpecial {
} }
} }
#[derive(Debug, Serialize, Deserialize)]
pub enum ESWeaponModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ESWeapon { pub struct ESWeapon {
pub esweapon: ESWeaponType, pub esweapon: ESWeaponType,
pub special: Option<ESWeaponSpecial>, pub special: Option<ESWeaponSpecial>,
pub name: String, pub name: String,
pub grind: u8, pub grind: u8,
pub wrapping: Option<WrappingPaper>
} }
impl ESWeapon { impl ESWeapon {
@ -184,6 +194,7 @@ impl ESWeapon {
special: None, special: None,
name: "".to_owned(), name: "".to_owned(),
grind: 0, grind: 0,
wrapping: None,
} }
} }
@ -241,7 +252,10 @@ impl ESWeapon {
result[1] = 0x70 + self.esweapon.to_value(); result[1] = 0x70 + self.esweapon.to_value();
result[2] = self.special.map(|s| s.to_value()).unwrap_or(0); result[2] = self.special.map(|s| s.to_value()).unwrap_or(0);
result[3] = self.grind; result[3] = self.grind;
//result[4] = tekked/untekked flag
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
}
result[6..12].clone_from_slice(&self.bytes_from_name()); result[6..12].clone_from_slice(&self.bytes_from_name());
result result
} }
@ -252,12 +266,28 @@ impl ESWeapon {
let special = ESWeaponSpecial::from_value(bytes[2]); let special = ESWeaponSpecial::from_value(bytes[2]);
let grind = bytes[3]; let grind = bytes[3];
let name = ESWeapon::name_from_bytes(&bytes[6..12]); let name = ESWeapon::name_from_bytes(&bytes[6..12]);
let wrapping = {
if bytes[4] & 0x40 == 0x40 {
WrappingPaper::from(bytes[5])
} else {
None
}
};
ESWeapon { ESWeapon {
esweapon: esweapon, esweapon: esweapon,
special: special.ok(), special: special.ok(),
grind: grind, grind: grind,
name: name, name: name,
wrapping: wrapping,
}
}
pub fn apply_modifier(&mut self, modifier: &ESWeaponModifier) {
match modifier {
ESWeaponModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
ESWeaponModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
} }
} }
} }
@ -276,6 +306,7 @@ mod test {
special: Some(ESWeaponSpecial::Berserk), special: Some(ESWeaponSpecial::Berserk),
grind: 137u8, grind: 137u8,
name: "JAKESERV".to_owned(), name: "JAKESERV".to_owned(),
wrapping: None,
}); });
} }
@ -287,6 +318,7 @@ mod test {
special: Some(ESWeaponSpecial::Chaos), special: Some(ESWeaponSpecial::Chaos),
grind: 72u8, grind: 72u8,
name: "PSYCHO".to_owned(), name: "PSYCHO".to_owned(),
wrapping: None,
}; };
let bytes = testweapon.as_bytes(); let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0x7B, 0x09, 0x48, 0x00, 0x00, 0x82, 0x13, 0xE4, 0x68, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00]); assert_eq!(bytes, [0x00, 0x7B, 0x09, 0x48, 0x00, 0x00, 0x82, 0x13, 0xE4, 0x68, 0xBC, 0x00, 0x00, 0x00, 0x00, 0x00]);
@ -300,10 +332,9 @@ mod test {
special: Some(ESWeaponSpecial::Spirit), special: Some(ESWeaponSpecial::Spirit),
grind: 105u8, grind: 105u8,
name: "YUGIOH".to_owned(), name: "YUGIOH".to_owned(),
wrapping: None,
}; };
let bytes = testweapon.as_bytes(); let bytes = testweapon.as_bytes();
assert_eq!(bytes, [0x00, 0xA7, 0x0B, 0x69, 0x00, 0x00, 0x83, 0x35, 0x9D, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00]); assert_eq!(bytes, [0x00, 0xA7, 0x0B, 0x69, 0x00, 0x00, 0x83, 0x35, 0x9D, 0x2F, 0xA0, 0x00, 0x00, 0x00, 0x00, 0x00]);
} }
} }

58
src/entity/item/mag.rs

@ -2,7 +2,7 @@ use std::collections::HashMap;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::tool::ToolType; use crate::entity::item::tool::ToolType;
use crate::entity::character::{CharacterClass, SectionID}; use crate::entity::character::{CharacterClass, SectionID};
use crate::entity::item::ItemEntityId;
use crate::entity::item::{ItemEntityId, WrappingPaper};
use std::io::Read; use std::io::Read;
use std::cmp::Ordering::{Less, Greater, Equal}; use std::cmp::Ordering::{Less, Greater, Equal};
@ -56,8 +56,6 @@ lazy_static::lazy_static! {
}; };
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
InvalidMagType, InvalidMagType,
@ -509,14 +507,16 @@ impl MagAttributeOrdering {
} }
} }
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum MagModifier { pub enum MagModifier {
FeedMag{ FeedMag{
food: ItemEntityId, food: ItemEntityId,
}, },
BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags
MagCell(ItemEntityId), MagCell(ItemEntityId),
OwnerChange(CharacterClass, SectionID)
OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)]
@ -529,7 +529,7 @@ pub enum PhotonBlast {
MyllaYoulla, MyllaYoulla,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Mag { pub struct Mag {
pub mag: MagType, pub mag: MagType,
def: u16, def: u16,
@ -543,9 +543,9 @@ pub struct Mag {
//modifiers: Vec<MagModifier>, //modifiers: Vec<MagModifier>,
pub class: CharacterClass, pub class: CharacterClass,
pub id: SectionID, pub id: SectionID,
pub wrapping: Option<WrappingPaper>,
} }
impl Mag { impl Mag {
pub fn baby_mag(skin: u16) -> Mag { pub fn baby_mag(skin: u16) -> Mag {
Mag { Mag {
@ -561,6 +561,25 @@ impl Mag {
//modifiers: Vec::new(), //modifiers: Vec::new(),
class: CharacterClass::HUmar, class: CharacterClass::HUmar,
id: SectionID::Viridia, id: SectionID::Viridia,
wrapping: None,
}
}
pub fn wrapped_baby_mag(skin: u16) -> Mag {
Mag {
mag: MagType::Mag,
def: 500,
pow: 0,
dex: 0,
mnd: 0,
synchro: 20,
iq: 0,
photon_blast: [None; 3],
color: (skin % 18) as u8,
//modifiers: Vec::new(),
class: CharacterClass::HUmar,
id: SectionID::Viridia,
wrapping: WrappingPaper::from((skin % 10) as u8),
} }
} }
@ -575,6 +594,9 @@ impl Mag {
result[12] = self.synchro; result[12] = self.synchro;
result[13] = self.iq; result[13] = self.iq;
result[14] = self.photon_blast_count(); result[14] = self.photon_blast_count();
if self.wrapping.is_some() {
result[14] += 0x40;
}
result[15] = self.color; result[15] = self.color;
result result
} }
@ -648,6 +670,13 @@ impl Mag {
let sync = data[12] % 121; // TODO: handle invalid values. let sync = data[12] % 121; // TODO: handle invalid values.
let iq = data[13] % 201; // TODO: handle invalid values. let iq = data[13] % 201; // TODO: handle invalid values.
let wp = {
if data[14] & 0x40 == 0x40 {
WrappingPaper::from(data[15] % 10) // % 10 to have valid wrapping paper colour.
} else {
None
}
};
Ok(Mag{ Ok(Mag{
mag: m.unwrap(), mag: m.unwrap(),
@ -660,8 +689,9 @@ impl Mag {
photon_blast: [None, None, None], // TODO: actually get PBs from bytes photon_blast: [None, None, None], // TODO: actually get PBs from bytes
color: data[15] % 18, color: data[15] % 18,
//modifiers: Vec::new(), //modifiers: Vec::new(),
class: CharacterClass::HUmar,
id: SectionID::Viridia,
class: CharacterClass::HUmar, // TODO: determine character class
id: SectionID::Viridia, // TODO: determine section id
wrapping: wp,
}) })
} }
else { else {
@ -1097,6 +1127,14 @@ impl Mag {
MagCell::LibertaKit => MagType::Agastya, MagCell::LibertaKit => MagType::Agastya,
} }
} }
pub fn apply_modifier(&mut self, modifier: &MagModifier) {
match modifier {
MagModifier::WrapPresent => {self.wrapping = WrappingPaper::from(self.color % 10)}, // prevents mag color from crashing wrapping papers. client always shows mags in default paper colour ?
MagModifier::UnwrapPresent => {self.wrapping = None},
_ => {}, // TODO: other modifiers are already handled elsewhere. do they need to be moved here?
}
}
} }
@ -1167,6 +1205,7 @@ mod test {
color: 0, color: 0,
class: CharacterClass::FOmarl, class: CharacterClass::FOmarl,
id: SectionID::Whitill, id: SectionID::Whitill,
wrapping: None,
}); });
} }
@ -1174,4 +1213,3 @@ mod test {
fn test_mag_does_not_level_down() { fn test_mag_does_not_level_down() {
} }
} }

73
src/entity/item/mod.rs

@ -69,7 +69,6 @@ impl Meseta {
} }
} }
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum ItemType { pub enum ItemType {
Weapon(weapon::WeaponType), Weapon(weapon::WeaponType),
@ -159,6 +158,34 @@ impl ItemDetail {
_ => None, _ => None,
} }
} }
pub fn is_wrapped(&mut self) -> bool {
match self {
ItemDetail::Weapon(w) => w.wrapping.is_some(),
ItemDetail::Armor(a) => a.wrapping.is_some(),
ItemDetail::Shield(s) => s.wrapping.is_some(),
ItemDetail::Unit(u) => u.wrapping.is_some(),
ItemDetail::Tool(t) => t.wrapping.is_some(),
ItemDetail::TechniqueDisk(d) => d.wrapping.is_some(),
ItemDetail::Mag(m) => m.wrapping.is_some(),
ItemDetail::ESWeapon(e) => e.wrapping.is_some(),
_ => unreachable!(),
}
}
pub fn unwrap_present(&mut self) {
match self {
ItemDetail::Weapon(ref mut w) => w.wrapping = None,
ItemDetail::Armor(ref mut a) => a.wrapping = None,
ItemDetail::Shield(ref mut s) => s.wrapping = None,
ItemDetail::Unit(ref mut u) => u.wrapping = None,
ItemDetail::Tool(ref mut t) => t.wrapping = None,
ItemDetail::TechniqueDisk(ref mut d) => d.wrapping = None,
ItemDetail::Mag(ref mut m) => m.wrapping = None,
ItemDetail::ESWeapon(ref mut e) => e.wrapping = None,
_ => unreachable!(),
};
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
@ -200,7 +227,7 @@ impl InventoryItemEntity {
_ => self, _ => self,
} }
} }
//pub fn with_individual<T>(&self, func: fn(&ItemEntity) -> T) -> Option<T> {
pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> { pub fn with_individual<F: Fn(&ItemEntity) -> T, T>(&self, func: F) -> Option<T> {
match self { match self {
InventoryItemEntity::Individual(item) => Some(func(item)), InventoryItemEntity::Individual(item) => Some(func(item)),
@ -208,7 +235,6 @@ impl InventoryItemEntity {
} }
} }
//pub fn with_stacked<T>(&self, func: fn(&Vec<ItemEntity>) -> T) -> Option<T> {
pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> { pub fn with_stacked<F: Fn(&Vec<ItemEntity>) -> T, T>(&self, func: F) -> Option<T> {
match self { match self {
InventoryItemEntity::Stacked(items) => Some(func(items)), InventoryItemEntity::Stacked(items) => Some(func(items)),
@ -252,7 +278,6 @@ impl InventoryEntity {
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub enum BankItemEntity { pub enum BankItemEntity {
Individual(ItemEntity), Individual(ItemEntity),
@ -287,10 +312,8 @@ impl BankItemEntity {
} }
} }
#[derive(Clone, Debug, Default)] #[derive(Clone, Debug, Default)]
pub struct BankEntity { pub struct BankEntity {
//pub items: [Option<CharacterBankItem>; 30],
pub items: Vec<BankItemEntity>, pub items: Vec<BankItemEntity>,
} }
@ -301,3 +324,41 @@ impl BankEntity {
} }
} }
} }
#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum WrappingPaper {
WhitePink, // 0
YellowBlue, // 1
BlackYellow, // 2
LightBlueOrange, // 3
PinkYellowGreen, // 4
RedGreen, // 5
Magenta, // 6
Blue, // 7
Yellow, // 8
Vermillion, // 9
Green, // 10
}
impl WrappingPaper {
pub fn value(&self) -> u8 {
*self as u8
}
pub fn from(data: u8) -> Option<WrappingPaper> {
match data {
0 => Some(WrappingPaper::WhitePink),
1 => Some(WrappingPaper::YellowBlue),
2 => Some(WrappingPaper::BlackYellow),
3 => Some(WrappingPaper::LightBlueOrange),
4 => Some(WrappingPaper::PinkYellowGreen),
5 => Some(WrappingPaper::RedGreen),
6 => Some(WrappingPaper::Magenta),
7 => Some(WrappingPaper::Blue),
8 => Some(WrappingPaper::Yellow),
9 => Some(WrappingPaper::Vermillion),
10 => Some(WrappingPaper::Green),
_ => None,
}
}
}

28
src/entity/item/shield.rs

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -518,18 +519,30 @@ impl ShieldType {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum ShieldModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Shield { pub struct Shield {
pub shield: ShieldType, pub shield: ShieldType,
pub dfp: u8, pub dfp: u8,
pub evp: u8, pub evp: u8,
pub wrapping: Option<WrappingPaper>,
} }
impl Shield { impl Shield {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
result[0..3].copy_from_slice(&self.shield.value()); result[0..3].copy_from_slice(&self.shield.value());
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
result[6] = self.dfp; result[6] = self.dfp;
result[8] = self.evp; result[8] = self.evp;
result result
@ -537,15 +550,30 @@ impl Shield {
pub fn from_bytes(data: [u8; 16]) -> Result<Shield, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Shield, ItemParseError> {
let s = ShieldType::parse_type([data[0], data[1], data[2]]); let s = ShieldType::parse_type([data[0], data[1], data[2]]);
let wrapping = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
if s.is_ok() { if s.is_ok() {
Ok(Shield{ Ok(Shield{
shield: s.unwrap(), shield: s.unwrap(),
dfp: data[6], dfp: data[6],
evp: data[8], evp: data[8],
wrapping: wrapping,
}) })
} }
else { else {
Err(ItemParseError::InvalidShieldBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidShieldBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &ShieldModifier) {
match modifier {
ShieldModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)}, // TODO: client always shows shields in default paper colour ?
ShieldModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

21
src/entity/item/tech.rs

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display, strum_macros::EnumIter)] #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display, strum_macros::EnumIter)]
@ -75,10 +76,19 @@ impl Technique {
} }
} }
#[derive(Debug, Serialize)]
pub enum TechModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct TechniqueDisk { pub struct TechniqueDisk {
pub tech: Technique, pub tech: Technique,
pub level: u32, pub level: u32,
pub wrapping: Option<WrappingPaper>, // TODO: validate if this clobbers tech value?
} }
impl TechniqueDisk { impl TechniqueDisk {
@ -88,6 +98,17 @@ impl TechniqueDisk {
result[1] = 2; result[1] = 2;
result[2] = self.level as u8 - 1; result[2] = self.level as u8 - 1;
result[4] = self.tech.as_value(); result[4] = self.tech.as_value();
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
result result
} }
pub fn apply_modifier(&mut self, modifier: &TechModifier) {
match modifier {
TechModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
TechModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

26
src/entity/item/tool.rs

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -647,10 +648,18 @@ impl ToolType {
} }
} }
#[derive(Debug, Serialize)]
pub enum ToolModifier {
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)] #[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Tool { pub struct Tool {
pub tool: ToolType, pub tool: ToolType,
pub wrapping: Option<WrappingPaper>, // TODO: what happens if a present is N monomates and the inventory already has 10?
} }
impl Tool { impl Tool {
@ -668,10 +677,18 @@ impl Tool {
} }
pub fn from_bytes(data: [u8; 16]) -> Result<Tool, ItemParseError> { pub fn from_bytes(data: [u8; 16]) -> Result<Tool, ItemParseError> {
let t = ToolType::parse_type([data[0], data[1], data[2]]);
let t = ToolType::parse_type([data[0], data[1], data[2]]);
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
if t.is_ok() { if t.is_ok() {
Ok(Tool { Ok(Tool {
tool: t.unwrap(), tool: t.unwrap(),
wrapping: w,
}) })
} }
else { else {
@ -686,4 +703,11 @@ impl Tool {
pub fn max_stack(&self) -> usize { pub fn max_stack(&self) -> usize {
self.tool.max_stack() self.tool.max_stack()
} }
pub fn apply_modifier(&mut self, modifier: &ToolModifier) {
match modifier {
ToolModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
ToolModifier::UnwrapPresent => {self.wrapping = None},
}
}
} }

31
src/entity/item/unit.rs

@ -1,4 +1,5 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -329,15 +330,19 @@ pub enum UnitModifier {
Plus, Plus,
Minus, Minus,
MinusMinus, MinusMinus,
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Unit { pub struct Unit {
pub unit: UnitType, pub unit: UnitType,
pub modifier: Option<UnitModifier>, pub modifier: Option<UnitModifier>,
pub wrapping: Option<WrappingPaper>,
} }
impl Unit { impl Unit {
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16]; let mut result = [0; 16];
@ -359,8 +364,15 @@ impl Unit {
result[6] = 0xFE; result[6] = 0xFE;
result[7] = 0xFF; result[7] = 0xFF;
}, },
_ => {}, // handling wrapping here is weird?
} }
} }
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
}
result result
} }
@ -375,13 +387,30 @@ impl Unit {
_ => None, _ => None,
}; };
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
Ok(Unit{ Ok(Unit{
unit: u.unwrap(), unit: u.unwrap(),
modifier: m, modifier: m,
wrapping: w,
}) })
} }
else { else {
Err(ItemParseError::InvalidUnitBytes) // TODO: error handling if wrong bytes are given Err(ItemParseError::InvalidUnitBytes) // TODO: error handling if wrong bytes are given
} }
} }
pub fn apply_modifier(&mut self, modifier: &UnitModifier) {
match modifier {
UnitModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
UnitModifier::UnwrapPresent => {self.wrapping = None},
_ => {},
}
}
} }

48
src/entity/item/weapon.rs

@ -1,4 +1,4 @@
use crate::entity::item::ItemEntityId;
use crate::entity::item::{ItemEntityId, WrappingPaper};
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -44,7 +44,6 @@ impl WeaponAttribute {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
pub enum WeaponSpecial { pub enum WeaponSpecial {
Draw = 1, Draw = 1,
@ -1420,7 +1419,6 @@ impl WeaponType {
} }
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum TekSpecialModifier { pub enum TekSpecialModifier {
Plus, Plus,
@ -1452,18 +1450,22 @@ pub enum WeaponModifier {
percent: TekPercentModifier, percent: TekPercentModifier,
grind: i32, grind: i32,
}, },
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
} }
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Weapon { pub struct Weapon {
pub weapon: WeaponType, pub weapon: WeaponType,
pub special: Option<WeaponSpecial>, pub special: Option<WeaponSpecial>,
pub grind: u8, pub grind: u8,
pub attrs: [Option<WeaponAttribute>; 3], pub attrs: [Option<WeaponAttribute>; 3],
pub tekked: bool, pub tekked: bool,
pub wrapping: Option<WrappingPaper>,
} }
impl Weapon { impl Weapon {
pub fn new(wep: WeaponType) -> Weapon { pub fn new(wep: WeaponType) -> Weapon {
Weapon { Weapon {
@ -1472,6 +1474,7 @@ impl Weapon {
grind: 0, grind: 0,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
wrapping: None,
} }
} }
@ -1516,15 +1519,30 @@ impl Weapon {
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8; self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
self.tekked = true; self.tekked = true;
}, },
WeaponModifier::WrapPresent{paper} => {self.wrapping = Some(*paper)},
WeaponModifier::UnwrapPresent => {self.wrapping = None},
_ => {} _ => {}
} }
} }
pub fn wrap_present(&mut self, value: u8) {
self.wrapping = WrappingPaper::from(value);
}
pub fn unwrap_present(&mut self) {
self.wrapping = None;
}
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0u8; 16]; let mut result = [0u8; 16];
result[0..3].copy_from_slice(&self.weapon.value()); result[0..3].copy_from_slice(&self.weapon.value());
result[3] = self.grind; result[3] = self.grind;
result[4] = self.special.map(|s| s.value()).unwrap_or(0); result[4] = self.special.map(|s| s.value()).unwrap_or(0);
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
if self.tekked == false { if self.tekked == false {
result[4] += 0x80 result[4] += 0x80
@ -1545,17 +1563,18 @@ impl Weapon {
let mut t = true; let mut t = true;
let g = data[3]; let g = data[3];
if data[4] >= 0x81 && data[4] <= 0xA8 {
if data[4] & 0x80 == 0x80 {
s = WeaponSpecial::from(data[4] - 0x80); s = WeaponSpecial::from(data[4] - 0x80);
t = false; t = false;
} }
else if data[4] >= 0x01 && data[4] <= 0x28 {
s = WeaponSpecial::from(data[4]);
t = true;
}
// else {
// return Err(ItemParseError::InvalidSpecial)
// }
let p = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
let mut a = [ let mut a = [
None, None,
@ -1585,6 +1604,7 @@ impl Weapon {
a[2], a[2],
], ],
tekked: t, tekked: t,
wrapping: p,
}) })
} }
else { else {
@ -1592,5 +1612,3 @@ impl Weapon {
} }
} }
} }

4
src/login/character.rs

@ -220,6 +220,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
special: None, special: None,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -233,6 +234,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 0, slots: 0,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -260,6 +262,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool ( item: ItemDetail::Tool (
Tool { Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -273,6 +276,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool ( item: ItemDetail::Tool (
Tool { Tool {
tool: item::tool::ToolType::Monofluid, tool: item::tool::ToolType::Monofluid,
wrapping: None,
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,

5
src/ship/drops/generic_armor.rs

@ -107,6 +107,7 @@ impl GenericArmorTable {
dfp: dfp_modifier as u8, dfp: dfp_modifier as u8,
evp: evp_modifier as u8, evp: evp_modifier as u8,
slots: slots as u8, slots: slots as u8,
wrapping: None,
})) }))
} }
} }
@ -126,24 +127,28 @@ mod test {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 1, slots: 1,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::AbsorbArmor, armor: ArmorType::AbsorbArmor,
dfp: 1, dfp: 1,
evp: 1, evp: 1,
slots: 1, slots: 1,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::HyperFrame, armor: ArmorType::HyperFrame,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 0, slots: 0,
wrapping: None,
}))); })));
assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor { assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor {
armor: ArmorType::ImperialArmor, armor: ArmorType::ImperialArmor,
dfp: 2, dfp: 2,
evp: 1, evp: 1,
slots: 0, slots: 0,
wrapping: None,
}))); })));
} }
} }

5
src/ship/drops/generic_shield.rs

@ -86,6 +86,7 @@ impl GenericShieldTable {
shield: shield_type, shield: shield_type,
dfp: dfp_modifier as u8, dfp: dfp_modifier as u8,
evp: evp_modifier as u8, evp: evp_modifier as u8,
wrapping: None,
})) }))
} }
} }
@ -105,21 +106,25 @@ mod test {
shield: ShieldType::FreezeBarrier, shield: ShieldType::FreezeBarrier,
dfp: 4, dfp: 4,
evp: 1, evp: 1,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::PsychicBarrier, shield: ShieldType::PsychicBarrier,
dfp: 3, dfp: 3,
evp: 2, evp: 2,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::ImperialBarrier, shield: ShieldType::ImperialBarrier,
dfp: 0, dfp: 0,
evp: 4, evp: 4,
wrapping: None,
}))); })));
assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield { assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield {
shield: ShieldType::DivinityBarrier, shield: ShieldType::DivinityBarrier,
dfp: 1, dfp: 1,
evp: 0, evp: 0,
wrapping: None,
}))); })));
} }
} }

2
src/ship/drops/generic_unit.rs

@ -89,6 +89,7 @@ impl GenericUnitTable {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit_type, unit: unit_type,
modifier: unit_modifier, modifier: unit_modifier,
wrapping: None,
}) })
}) })
} }
@ -116,6 +117,7 @@ mod test {
assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit { assert!(gut.get_drop(&area, &mut rng) == Some(ItemDropType::Unit(Unit {
unit: unit, unit: unit,
modifier: umod, modifier: umod,
wrapping: None,
}))); })));
} }
} }

5
src/ship/drops/generic_weapon.rs

@ -503,6 +503,7 @@ impl GenericWeaponTable {
grind: weapon_grind as u8, grind: weapon_grind as u8,
attrs: weapon_attributes, attrs: weapon_attributes,
tekked: weapon_special.is_none(), tekked: weapon_special.is_none(),
wrapping: None,
})) }))
} }
} }
@ -524,6 +525,7 @@ mod test {
grind: 0, grind: 0,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly);
@ -533,6 +535,7 @@ mod test {
grind: 2, grind: 2,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly);
@ -542,6 +545,7 @@ mod test {
grind: 0, grind: 0,
attrs: [None, None, None], attrs: [None, None, None],
tekked: false, tekked: false,
wrapping: None,
}))); })));
let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
@ -551,6 +555,7 @@ mod test {
grind: 0, grind: 0,
attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None], attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None],
tekked: true, tekked: true,
wrapping: None,
}))); })));
} }
} }

5
src/ship/drops/rare_drop_table.rs

@ -104,6 +104,7 @@ impl RareDropTable {
grind: 0, grind: 0,
attrs: self.attribute_table.generate_rare_attributes(map_area, rng), attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
tekked: false, tekked: false,
wrapping: None,
}) })
}, },
@ -113,6 +114,7 @@ impl RareDropTable {
dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8, dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8,
evp: self.armor_stats.evp_modifier(&armor, rng) as u8, evp: self.armor_stats.evp_modifier(&armor, rng) as u8,
slots: self.armor_stats.slots(map_area, rng) as u8, slots: self.armor_stats.slots(map_area, rng) as u8,
wrapping: None,
}) })
}, },
RareDropItem::Shield(shield) => { RareDropItem::Shield(shield) => {
@ -120,17 +122,20 @@ impl RareDropTable {
shield: shield, shield: shield,
dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8, dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8,
evp: self.shield_stats.evp_modifier(&shield, rng) as u8, evp: self.shield_stats.evp_modifier(&shield, rng) as u8,
wrapping: None,
}) })
}, },
RareDropItem::Unit(unit) => { RareDropItem::Unit(unit) => {
ItemDropType::Unit(Unit { ItemDropType::Unit(Unit {
unit: unit, unit: unit,
modifier: None, modifier: None,
wrapping: None,
}) })
}, },
RareDropItem::Tool(tool) => { RareDropItem::Tool(tool) => {
ItemDropType::Tool(Tool { ItemDropType::Tool(Tool {
tool: tool, tool: tool,
wrapping: None,
}) })
}, },
RareDropItem::Mag(_mag) => { RareDropItem::Mag(_mag) => {

6
src/ship/drops/tech_table.rs

@ -103,7 +103,8 @@ impl TechniqueTable {
Some(ItemDropType::TechniqueDisk(TechniqueDisk { Some(ItemDropType::TechniqueDisk(TechniqueDisk {
tech: *tech, tech: *tech,
level: level as u32
level: level as u32,
wrapping: None,
})) }))
} }
} }
@ -127,7 +128,8 @@ mod test {
assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk( assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk(
TechniqueDisk { TechniqueDisk {
tech: tech, tech: tech,
level: level
level: level,
wrapping: None,
}))); })));
} }
} }

3
src/ship/drops/tool_table.rs

@ -158,7 +158,8 @@ impl ToolTable {
}; };
Some(ItemDropType::Tool(Tool { Some(ItemDropType::Tool(Tool {
tool: tool_type
tool: tool_type,
wrapping: None,
})) }))
} }
} }

123
src/ship/items/inventory.rs

@ -3,9 +3,14 @@ use thiserror::Error;
use libpso::character::character;//::InventoryItem; use libpso::character::character;//::InventoryItem;
use crate::entity::character::CharacterEntityId; use crate::entity::character::CharacterEntityId;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity}; use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag;
use crate::entity::item::weapon::Weapon; use crate::entity::item::weapon::Weapon;
use crate::entity::item::esweapon::ESWeapon;
use crate::entity::item::armor::Armor; // TODO: cleanup uses
use crate::entity::item::unit::Unit;
use crate::entity::item::shield::Shield;
use crate::entity::item::mag::Mag;
use crate::entity::item::tech::TechniqueDisk;
use crate::entity::item::tool::Tool;
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle}; use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
@ -38,12 +43,69 @@ impl IndividualInventoryItem {
} }
} }
pub fn weapon_mut(&mut self) -> Option<&mut Weapon> {
match self.item {
ItemDetail::Weapon(ref mut weapon) => Some(weapon),
_ => None
}
}
pub fn esweapon_mut(&mut self) -> Option<&mut ESWeapon> {
match self.item {
ItemDetail::ESWeapon(ref mut esweapon) => Some(esweapon),
_ => None
}
}
pub fn armor_mut(&mut self) -> Option<&mut Armor> {
match self.item {
ItemDetail::Armor(ref mut armor) => Some(armor),
_ => None
}
}
pub fn unit_mut(&mut self) -> Option<&mut Unit> {
match self.item {
ItemDetail::Unit(ref mut unit) => Some(unit),
_ => None
}
}
pub fn shield_mut(&mut self) -> Option<&mut Shield> {
match self.item {
ItemDetail::Shield(ref mut shield) => Some(shield),
_ => None
}
}
pub fn mag_mut(&mut self) -> Option<&mut Mag> { pub fn mag_mut(&mut self) -> Option<&mut Mag> {
match self.item { match self.item {
ItemDetail::Mag(ref mut mag) => Some(mag), ItemDetail::Mag(ref mut mag) => Some(mag),
_ => None _ => None
} }
} }
pub fn tech_mut(&mut self) -> Option<&mut TechniqueDisk> {
match self.item {
ItemDetail::TechniqueDisk(ref mut tech) => Some(tech),
_ => None
}
}
pub fn tool_mut(&mut self) -> Option<&mut Tool> {
match self.item {
ItemDetail::Tool(ref mut tool) => Some(tool),
_ => None
}
}
pub fn is_wrapped(&mut self) -> bool {
self.item.is_wrapped()
}
pub fn unwrap_present(&mut self) {
self.item.unwrap_present();
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -66,6 +128,14 @@ impl StackedInventoryItem {
None None
} }
} }
pub fn is_wrapped(&self) -> bool {
self.tool.wrapping.is_some()
}
pub fn unwrap_present(&mut self) {
self.tool.wrapping = None;
}
} }
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -127,7 +197,7 @@ impl InventoryItem {
} }
} }
// TOOD: delete?
// TODO: delete?
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -138,7 +208,7 @@ impl InventoryItem {
} }
} }
// TOOD: delete?
// TODO: delete?
pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool { pub fn can_combine_stacks(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -151,7 +221,7 @@ impl InventoryItem {
} }
// TODO: result // TODO: result
// TOOD: delete?
// TODO: delete?
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) { pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
match self { match self {
InventoryItem::Stacked(self_stacked_item) => { InventoryItem::Stacked(self_stacked_item) => {
@ -167,13 +237,13 @@ impl InventoryItem {
InventoryItem::Individual(item) => { InventoryItem::Individual(item) => {
match &item.item { match &item.item {
ItemDetail::Weapon(w) => w.as_bytes(), ItemDetail::Weapon(w) => w.as_bytes(),
ItemDetail::ESWeapon(e) => e.as_bytes(),
ItemDetail::Armor(a) => a.as_bytes(), ItemDetail::Armor(a) => a.as_bytes(),
ItemDetail::Shield(s) => s.as_bytes(),
ItemDetail::Unit(u) => u.as_bytes(), ItemDetail::Unit(u) => u.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Shield(s) => s.as_bytes(),
ItemDetail::Mag(m) => m.as_bytes(), ItemDetail::Mag(m) => m.as_bytes(),
ItemDetail::ESWeapon(e) => e.as_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
} }
}, },
InventoryItem::Stacked(item) => { InventoryItem::Stacked(item) => {
@ -219,6 +289,41 @@ impl InventoryItem {
_ => None _ => None
} }
} }
pub fn item_detail(&self) -> ItemDetail {
match self {
InventoryItem::Individual(individual_inventory_item) => individual_inventory_item.item.clone(),
InventoryItem::Stacked(stacked_inventory_item) => ItemDetail::Tool(stacked_inventory_item.tool),
}
}
pub fn as_consumed_item(&self) -> ConsumedItem {
match self {
InventoryItem::Individual(individual_inventory_item) => {ConsumedItem::Individual(IndividualConsumedItem {
entity_id: individual_inventory_item.entity_id,
item: individual_inventory_item.item.clone(),
})},
InventoryItem::Stacked(stacked_inventory_item) => {ConsumedItem::Stacked(StackedConsumedItem {
entity_ids: stacked_inventory_item.entity_ids.clone(),
tool: stacked_inventory_item.tool,
})},
}
}
// TODO: validate if wrapping stacked items & tools are valid in the client. Gallons shop allows users to wrap tools (eg techs) but then the client may not be able to unwrap it afterwards?
pub fn is_wrapped(&mut self) -> bool {
match self {
InventoryItem::Individual(i) => i.is_wrapped(),
InventoryItem::Stacked(s) => s.is_wrapped(),
}
}
pub fn unwrap_present(&mut self) {
match self {
InventoryItem::Individual(i) => i.unwrap_present(),
InventoryItem::Stacked(s) => s.unwrap_present(),
};
}
} }

256
src/ship/items/manager.rs

@ -4,10 +4,9 @@ use thiserror::Error;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel}; use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
use crate::entity::item::{ItemDetail, ItemLocation, BankName}; use crate::entity::item::{ItemDetail, ItemLocation, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity, ItemType};
use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit;
use crate::entity::item::weapon;
use crate::entity::item::{unit, weapon, armor, shield, mag, tech, tool, esweapon};
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation; use crate::ship::ship::ItemDropLocation;
use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::drops::{ItemDrop, ItemDropType};
@ -503,7 +502,6 @@ impl ItemManager {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?; let used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let consumed_item = used_item.consume(amount)?; let consumed_item = used_item.consume(amount)?;
if let ItemDetail::TechniqueDisk(tech_disk) = consumed_item.item() { if let ItemDetail::TechniqueDisk(tech_disk) = consumed_item.item() {
// TODO: validate tech level in packet is in bounds [1..30] // TODO: validate tech level in packet is in bounds [1..30]
character.techs.set_tech(tech_disk.tech, TechLevel(tech_disk.level as u8)); character.techs.set_tech(tech_disk.tech, TechLevel(tech_disk.level as u8));
@ -636,114 +634,149 @@ impl ItemManager {
} }
pub async fn use_item<EG: EntityGateway>(&mut self, pub async fn use_item<EG: EntityGateway>(&mut self,
used_item: ConsumedItem,
entity_gateway: &mut EG, entity_gateway: &mut EG,
character: &mut CharacterEntity) -> Result<(), anyhow::Error> {
character: &mut CharacterEntity,
client_item_id: ClientItemId) -> Result<(), anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
match &used_item.item() {
ItemDetail::Weapon(_w) => {
// something like when items are used to combine/transform them?
//_ => {}
},
ItemDetail::Tool(t) => {
match t.tool {
ToolType::PowerMaterial => {
use_tool::power_material(entity_gateway, character).await;
},
ToolType::MindMaterial => {
use_tool::mind_material(entity_gateway, character).await;
},
ToolType::EvadeMaterial => {
use_tool::evade_material(entity_gateway, character).await;
},
ToolType::DefMaterial => {
use_tool::def_material(entity_gateway, character).await;
},
ToolType::LuckMaterial => {
use_tool::luck_material(entity_gateway, character).await;
},
ToolType::HpMaterial => {
use_tool::hp_material(entity_gateway, character).await;
},
ToolType::TpMaterial => {
use_tool::tp_material(entity_gateway, character).await;
},
ToolType::CellOfMag502 => {
use_tool::cell_of_mag_502(entity_gateway, &used_item, inventory).await?;
},
ToolType::CellOfMag213 => {
use_tool::cell_of_mag_213(entity_gateway, &used_item, inventory).await?;
},
ToolType::PartsOfRobochao => {
use_tool::parts_of_robochao(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfOpaOpa => {
use_tool::heart_of_opaopa(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfPian => {
use_tool::heart_of_pian(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfChao=> {
use_tool::heart_of_chao(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfAngel => {
use_tool::heart_of_angel(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfHamburger => {
use_tool::kit_of_hamburger(entity_gateway, &used_item, inventory).await?;
},
ToolType::PanthersSpirit => {
use_tool::panthers_spirit(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfMark3 => {
use_tool::kit_of_mark3(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfMasterSystem=> {
use_tool::kit_of_master_system(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfGenesis => {
use_tool::kit_of_genesis(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfSegaSaturn => {
use_tool::kit_of_sega_saturn(entity_gateway, &used_item, inventory).await?;
},
ToolType::KitOfDreamcast => {
use_tool::kit_of_dreamcast(entity_gateway, &used_item, inventory).await?;
},
ToolType::Tablet => {
use_tool::tablet(entity_gateway, &used_item, inventory).await?;
},
ToolType::DragonScale => {
use_tool::dragon_scale(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeavenStrikerCoat => {
use_tool::heaven_striker_coat(entity_gateway, &used_item, inventory).await?;
},
ToolType::PioneerParts => {
use_tool::pioneer_parts(entity_gateway, &used_item, inventory).await?;
},
ToolType::AmitiesMemo => {
use_tool::amities_memo(entity_gateway, &used_item, inventory).await?;
},
ToolType::HeartOfMorolian => {
use_tool::heart_of_morolian(entity_gateway, &used_item, inventory).await?;
},
ToolType::RappysBeak => {
use_tool::rappys_beak(entity_gateway, &used_item, inventory).await?;
},
ToolType::YahoosEngine => {
use_tool::yahoos_engine(entity_gateway, &used_item, inventory).await?;
},
ToolType::DPhotonCore => {
use_tool::d_photon_core(entity_gateway, &used_item, inventory).await?;
},
ToolType::LibertaKit => {
use_tool::liberta_kit(entity_gateway, &used_item, inventory).await?;
},
_ => {}
}
let mut used_item_handle = inventory.get_item_handle_by_id(client_item_id).ok_or(ItemManagerError::ItemIdNotInInventory(client_item_id))?;
let used_item = used_item_handle.item_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
if used_item.is_wrapped() {
used_item.unwrap_present();
} else {
match used_item.item_type() {
ItemType::Weapon(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.weapon_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::ESWeapon(_) => { // TODO: wrap srank weapons
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.esweapon_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::Armor(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.armor_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::Unit(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.unit_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::Shield(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.shield_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::Mag(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.mag_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::TechniqueDisk(_) => {
let _actual_used_item = used_item.individual_mut().ok_or(ItemManagerError::CannotGetMutItem)?.tech_mut().ok_or(ItemManagerError::CannotGetMutItem)?;
// combining / unsealing?
},
ItemType::Tool(_) => {
let consumed_item = used_item.as_consumed_item();
match &used_item.item_detail() {
ItemDetail::Tool(t) => {
match t.tool {
ToolType::PowerMaterial => {
use_tool::power_material(entity_gateway, character).await;
},
ToolType::MindMaterial => {
use_tool::mind_material(entity_gateway, character).await;
},
ToolType::EvadeMaterial => {
use_tool::evade_material(entity_gateway, character).await;
},
ToolType::DefMaterial => {
use_tool::def_material(entity_gateway, character).await;
},
ToolType::LuckMaterial => {
use_tool::luck_material(entity_gateway, character).await;
},
ToolType::HpMaterial => {
use_tool::hp_material(entity_gateway, character).await;
},
ToolType::TpMaterial => {
use_tool::tp_material(entity_gateway, character).await;
},
ToolType::CellOfMag502 => {
use_tool::cell_of_mag_502(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::CellOfMag213 => {
use_tool::cell_of_mag_213(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::PartsOfRobochao => {
use_tool::parts_of_robochao(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfOpaOpa => {
use_tool::heart_of_opaopa(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfPian => {
use_tool::heart_of_pian(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfChao=> {
use_tool::heart_of_chao(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfAngel => {
use_tool::heart_of_angel(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfHamburger => {
use_tool::kit_of_hamburger(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::PanthersSpirit => {
use_tool::panthers_spirit(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfMark3 => {
use_tool::kit_of_mark3(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfMasterSystem=> {
use_tool::kit_of_master_system(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfGenesis => {
use_tool::kit_of_genesis(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfSegaSaturn => {
use_tool::kit_of_sega_saturn(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::KitOfDreamcast => {
use_tool::kit_of_dreamcast(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::Tablet => {
use_tool::tablet(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::DragonScale => {
use_tool::dragon_scale(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeavenStrikerCoat => {
use_tool::heaven_striker_coat(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::PioneerParts => {
use_tool::pioneer_parts(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::AmitiesMemo => {
use_tool::amities_memo(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::HeartOfMorolian => {
use_tool::heart_of_morolian(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::RappysBeak => {
use_tool::rappys_beak(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::YahoosEngine => {
use_tool::yahoos_engine(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::DPhotonCore => {
use_tool::d_photon_core(entity_gateway, &consumed_item, inventory).await?;
},
ToolType::LibertaKit => {
use_tool::liberta_kit(entity_gateway, &consumed_item, inventory).await?;
},
_ => {}
}
},
_ => {},
}
},
} }
_ => {}
} }
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(()) Ok(())
@ -923,4 +956,9 @@ impl ItemManager {
Ok(weapon) Ok(weapon)
} }
pub fn get_inventory_item_by_id(&mut self, character: &CharacterEntity, item_id: ClientItemId) -> Result<&InventoryItem, anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
Ok(inventory.get_item_by_id(item_id).ok_or(ItemManagerError::ItemIdNotInInventory(item_id))?)
}
} }

2
src/ship/map/area.rs

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?`
// TODO: `pub(super) for most of these?`
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use std::collections::HashMap; use std::collections::HashMap;
use thiserror::Error; use thiserror::Error;

8
src/ship/map/enemy.rs

@ -1,12 +1,9 @@
// TOOD: `pub(super) for most of these?`
// TODO: `pub(super) for most of these?`
use std::io::{Read}; use std::io::{Read};
use byteorder::{LittleEndian, ReadBytesExt}; use byteorder::{LittleEndian, ReadBytesExt};
use thiserror::Error; use thiserror::Error;
use crate::ship::monster::MonsterType; use crate::ship::monster::MonsterType;
use crate::ship::room::Episode; use crate::ship::room::Episode;
use crate::ship::map::*; use crate::ship::map::*;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -62,7 +59,6 @@ impl RawMapEnemy {
} }
} }
#[derive(Error, Debug)] #[derive(Error, Debug)]
#[error("")] #[error("")]
pub enum MapEnemyError { pub enum MapEnemyError {
@ -70,7 +66,6 @@ pub enum MapEnemyError {
MapAreaError(#[from] MapAreaError), MapAreaError(#[from] MapAreaError),
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct MapEnemy { pub struct MapEnemy {
pub monster: MonsterType, pub monster: MonsterType,
@ -194,7 +189,6 @@ impl MapEnemy {
_ => return Err(MapEnemyError::UnknownEnemyId(enemy.id)) _ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
} }
}, },
MapArea::Tower => { MapArea::Tower => {
match (enemy, episode) { match (enemy, episode) {
(RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily, (RawMapEnemy {id: 97, ..}, _) => MonsterType::DelLily,

4
src/ship/map/maps.rs

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?`
// TODO: `pub(super) for most of these?`
use std::path::PathBuf; use std::path::PathBuf;
use std::io::{Read}; use std::io::{Read};
@ -96,7 +96,7 @@ fn parse_enemy(episode: &Episode, map_area: &MapArea, raw_enemy: RawMapEnemy) ->
monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptCore, monster.map_area)));
monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::VolOptUnused, monster.map_area)));
}, },
// TOOD: this cares about difficulty (theres an ult-specific darvant?)
// TODO: this cares about difficulty (theres an ult-specific darvant?)
MonsterType::DarkFalz => { MonsterType::DarkFalz => {
for _ in 0..509 { for _ in 0..509 {
monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area))); monsters.push(Some(MapEnemy::new(MonsterType::Darvant, monster.map_area)));

2
src/ship/map/object.rs

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?`
// TODO: `pub(super) for most of these?`
use std::io::{Read}; use std::io::{Read};

2
src/ship/map/variant.rs

@ -1,4 +1,4 @@
// TOOD: `pub(super) for most of these?`
// TODO: `pub(super) for most of these?`
use rand::Rng; use rand::Rng;

2
src/ship/packet/builder/message.rs

@ -119,7 +119,7 @@ pub fn character_leveled_up(area_client: AreaClient, level: u32, before_stats: C
} }
} }
// TOOD: checksum?
// TODO: checksum?
pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList { pub fn bank_item_list(bank: &CharacterBank, char_bank_meseta: u32) -> BankItemList {
BankItemList { BankItemList {
aflag: 0, aflag: 0,

11
src/ship/packet/handler/message.rs

@ -1,12 +1,12 @@
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::item::{ItemType};
use crate::entity::item::{ItemType, ItemDetail};
use crate::common::serverstate::ClientId; use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation}; use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::items::{ItemManager, ClientItemId, InventoryItem};
use crate::ship::items::{ItemManager, ClientItemId, InventoryItem, ItemManagerError};
use crate::ship::packet::builder; use crate::ship::packet::builder;
pub async fn request_exp<EG: EntityGateway>(id: ClientId, pub async fn request_exp<EG: EntityGateway>(id: ClientId,
@ -272,9 +272,12 @@ where
EG: EntityGateway EG: EntityGateway
{ {
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let item_used_type = item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?;
item_manager.use_item(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id)).await?;
item_manager.use_item(item_used_type, entity_gateway, &mut client.character).await?;
if let ItemDetail::Tool(_t) = item_manager.get_inventory_item_by_id(&client.character, ClientItemId(player_use_tool.item_id))?.item_detail() {
item_manager.player_consumes_tool(entity_gateway, &mut client.character, ClientItemId(player_use_tool.item_id), 1).await?;
}
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }

2
src/ship/packet/handler/quest.rs

@ -7,7 +7,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::packet::builder::quest; use crate::ship::packet::builder::quest;
use libpso::util::array_to_utf8; use libpso::util::array_to_utf8;
// TOOD: enum
// TODO: enum
enum QuestFileType { enum QuestFileType {
Bin, Bin,
Dat Dat

2
src/ship/ship.rs

@ -653,7 +653,7 @@ impl<EG: EntityGateway> ServerState for ShipServerState<EG> {
handler::room::cool_62(id, cool62, &block.client_location) handler::room::cool_62(id, cool62, &block.client_location)
}, },
RecvShipPacket::ClientCharacterData(_) => { RecvShipPacket::ClientCharacterData(_) => {
// TOOD: validate this in some way?
// TODO: validate this in some way?
Box::new(None.into_iter()) Box::new(None.into_iter())
}, },
RecvShipPacket::DoneBursting(_) => { RecvShipPacket::DoneBursting(_) => {

3
src/ship/shops/armor.rs

@ -71,6 +71,7 @@ impl ShopItem for ArmorShopItem {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: *slot as u8, slots: *slot as u8,
wrapping: None,
}) })
}, },
ArmorShopItem::Barrier(barrier) => { ArmorShopItem::Barrier(barrier) => {
@ -78,12 +79,14 @@ impl ShopItem for ArmorShopItem {
shield: *barrier, shield: *barrier,
dfp: 0, dfp: 0,
evp: 0, evp: 0,
wrapping: None,
}) })
}, },
ArmorShopItem::Unit(unit) => { ArmorShopItem::Unit(unit) => {
ItemDetail::Unit(Unit { ItemDetail::Unit(Unit {
unit: *unit, unit: *unit,
modifier: None, modifier: None,
wrapping: None,
}) })
}, },
} }

15
src/ship/shops/tool.rs

@ -22,11 +22,11 @@ pub enum ToolShopItem {
impl Ord for ToolShopItem { impl Ord for ToolShopItem {
fn cmp(&self, other: &ToolShopItem) -> std::cmp::Ordering { fn cmp(&self, other: &ToolShopItem) -> std::cmp::Ordering {
let a = match self { let a = match self {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
let b = match other { let b = match other {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
@ -37,11 +37,11 @@ impl Ord for ToolShopItem {
impl PartialOrd for ToolShopItem { impl PartialOrd for ToolShopItem {
fn partial_cmp(&self, other: &ToolShopItem) -> Option<std::cmp::Ordering> { fn partial_cmp(&self, other: &ToolShopItem) -> Option<std::cmp::Ordering> {
let a = match self { let a = match self {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
let b = match other { let b = match other {
ToolShopItem::Tool(t) => Tool{tool : *t}.as_individual_bytes(),
ToolShopItem::Tool(t) => Tool{tool : *t, wrapping: None,}.as_individual_bytes(),
ToolShopItem::Tech(t) => t.as_bytes(), ToolShopItem::Tech(t) => t.as_bytes(),
}; };
@ -73,7 +73,8 @@ impl ShopItem for ToolShopItem {
match self { match self {
ToolShopItem::Tool(tool) => { ToolShopItem::Tool(tool) => {
Tool { Tool {
tool: *tool
tool: *tool,
wrapping: None,
}.as_individual_bytes()[0..12].try_into().unwrap() }.as_individual_bytes()[0..12].try_into().unwrap()
}, },
ToolShopItem::Tech(tech) => { ToolShopItem::Tech(tech) => {
@ -86,7 +87,8 @@ impl ShopItem for ToolShopItem {
match self { match self {
ToolShopItem::Tool(tool) => { ToolShopItem::Tool(tool) => {
ItemDetail::Tool(Tool { ItemDetail::Tool(Tool {
tool: *tool
tool: *tool,
wrapping: None,
}) })
}, },
ToolShopItem::Tech(tech) => { ToolShopItem::Tech(tech) => {
@ -262,6 +264,7 @@ impl<R: Rng + SeedableRng> ToolShop<R> {
ToolShopItem::Tech(TechniqueDisk { ToolShopItem::Tech(TechniqueDisk {
tech: tech, tech: tech,
level: level as u32, level: level as u32,
wrapping: None,
}) })
}) })
.collect() .collect()

1
src/ship/shops/weapon.rs

@ -142,6 +142,7 @@ impl ShopItem for WeaponShopItem {
grind: self.grind as u8, grind: self.grind as u8,
attrs: [self.attributes[0], self.attributes[1], None], attrs: [self.attributes[0], self.attributes[1], None],
tekked: true, tekked: true,
wrapping: None,
} }
) )
} }

35
tests/test_bank.rs

@ -27,6 +27,7 @@ async fn test_bank_items_sent_in_character_login() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -68,6 +69,7 @@ async fn test_request_bank_items() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -116,6 +118,7 @@ async fn test_request_stacked_bank_items() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -165,6 +168,7 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -177,6 +181,7 @@ async fn test_request_bank_items_sorted() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -193,6 +198,7 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -243,6 +249,7 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -258,6 +265,7 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -320,6 +328,7 @@ async fn test_deposit_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -384,6 +393,7 @@ async fn test_deposit_partial_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -458,6 +468,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -470,6 +481,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -535,6 +547,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -550,6 +563,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -617,6 +631,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -635,6 +650,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -695,6 +711,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -714,6 +731,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -775,6 +793,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -790,6 +809,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -810,6 +830,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -988,6 +1009,7 @@ async fn test_withdraw_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1051,6 +1073,7 @@ async fn test_withdraw_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1115,6 +1138,7 @@ async fn test_withdraw_partial_stacked_item() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1186,6 +1210,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1198,6 +1223,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1265,6 +1291,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1281,6 +1308,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1347,6 +1375,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1366,6 +1395,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1421,6 +1451,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1441,6 +1472,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1502,6 +1534,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1523,6 +1556,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1538,6 +1572,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

289
tests/test_item_modifiers.rs

@ -0,0 +1,289 @@
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket, SendShipPacket};
use libpso::packet::ship::*;
use libpso::packet::messages::*;
#[path = "common.rs"]
mod common;
use common::*;
// unwrap presents
#[async_std::test]
async fn test_unwrap_weapon() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(item::weapon::Weapon {
weapon: item::weapon::WeaponType::Saber,
special: Some(item::weapon::WeaponSpecial::Burning),
grind: 5,
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Machine, value: 20}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 30}),
None],
tekked: false,
wrapping: Some(item::WrappingPaper::RedGreen),
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x00,0x01,0x00,0x05,0xDA,0x05,0x03,0x14,0x05,0x1E,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Weapon(weapon) => {
assert!(weapon.as_bytes() == [0x00, 0x01, 0x00, 0x05, 0x9A, 0x00, 0x03, 0x14, 0x05, 0x1E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_armor() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Armor(item::armor::Armor {
armor: item::armor::ArmorType::PerfectFrame,
dfp: 3u8,
evp: 2u8,
slots: 4u8,
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
// slots should be untouched when wrapped regardless of wrapping paper colour
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x01,0x10,0x00,0x40,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Armor(armor) => {
assert!(armor.as_bytes() == [0x01,0x01,0x10,0x00,0x00,0x04,0x03,0x00,0x02,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_shield() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Shield(item::shield::Shield {
shield: item::shield::ShieldType::CoreShield,
dfp: 2u8,
evp: 3u8,
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x02,0x02,0x00,0x40,0x05,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Shield(shield) => {
assert!(shield.as_bytes() == [0x01,0x02,0x02,0x00,0x00,0x00,0x02,0x00,0x03,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
#[async_std::test]
async fn test_unwrap_unit() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Unit(item::unit::Unit {
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::MinusMinus),
wrapping: Some(item::WrappingPaper::RedGreen), // 5
}),
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x01,0x03,0x00,0x00,0x40,0x05,0xFE,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Unit(unit) => {
assert!(unit.as_bytes() == [0x01,0x03,0x00,0x00,0x00,0x00,0xFE,0xFF,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00]);
},
_ => panic!(),
}
}).unwrap();
}
// mag cells are covered in test_mags.rs
#[async_std::test]
async fn test_unwrap_mag() {
let mut entity_gateway = InMemoryGateway::new();
let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
let wrapped_item = entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Mag(item::mag::Mag::wrapped_baby_mag(12)), //turquoise
location: item::ItemLocation::Inventory{
character_id: char1.id,
}
}).await.unwrap();
assert!(wrapped_item.item.as_client_bytes() == [0x02,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x40,0x0C]); // item maker is wrong?
let mut inventory = Vec::<item::ItemEntity>::new();
inventory.push(wrapped_item.into());
entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(inventory)).await.unwrap();
let mut ship = Box::new(ShipServerState::builder()
.gateway(entity_gateway.clone())
.build());
log_in_char(&mut ship, ClientId(1), "a1", "a").await;
join_lobby(&mut ship, ClientId(1)).await;
create_room(&mut ship, ClientId(1), "room", "").await;
ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerUseItem(PlayerUseItem {
client: 0,
target: 0,
item_id: 0x10000,
})))).await.unwrap().for_each(drop);
let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap();
inventory_items.items[0].with_individual(|item| {
match &item.item {
item::ItemDetail::Mag(mag) => {
assert!(mag.as_bytes() == [0x02,0x00,0x00,0x00,0xF4,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x14,0x00,0x00,0x0C]);
},
_ => panic!(),
}
}).unwrap();
}
// TODO: implement wrapping packet (message 0xD6) (gallons shop quest)
// wrap presents
#[async_std::test]
async fn test_wrap_weapon() {}
#[async_std::test]
async fn test_wrap_armor() {}
#[async_std::test]
async fn test_wrap_shield() {}
#[async_std::test]
async fn test_wrap_unit() {}
// mag cells are covered in test_mags.rs
#[async_std::test]
async fn test_wrap_mag() {}
// item combinations?

18
tests/test_item_pickup.rs

@ -22,7 +22,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate
tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -38,7 +39,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -104,7 +106,8 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate
tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -172,6 +175,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -251,6 +255,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -264,6 +269,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -277,6 +283,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -343,6 +350,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -361,6 +369,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -476,6 +485,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -491,6 +501,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -671,6 +682,7 @@ async fn test_player_drops_partial_stack_and_other_player_picks_it_up() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

13
tests/test_item_use.rs

@ -26,7 +26,8 @@ async fn test_use_monomate() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -76,7 +77,8 @@ async fn test_use_monomate_twice() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -129,7 +131,8 @@ async fn test_use_last_monomate() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -174,6 +177,7 @@ async fn test_use_nonstackable_tool() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::MagicStoneIritista, tool: item::tool::ToolType::MagicStoneIritista,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -214,7 +218,8 @@ async fn test_use_materials() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

3
tests/test_mags.rs

@ -35,6 +35,7 @@ async fn test_mag_feed() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -180,6 +181,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool ( item: item::ItemDetail::Tool (
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate, tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::FedToMag { location: item::ItemLocation::FedToMag {
@ -193,6 +195,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502, tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

9
tests/test_item_actions.rs → tests/test_menu_actions.rs

@ -25,6 +25,7 @@ async fn test_equip_unit_from_equip_menu() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -37,6 +38,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -49,6 +51,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -111,6 +114,7 @@ async fn test_unequip_armor_with_units() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -123,6 +127,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -135,6 +140,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -188,6 +194,7 @@ async fn test_sort_items() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -200,6 +207,7 @@ async fn test_sort_items() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: None, modifier: None,
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -212,6 +220,7 @@ async fn test_sort_items() {
item::unit::Unit{ item::unit::Unit{
unit: item::unit::UnitType::KnightPower, unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus), modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,

2
tests/test_rooms.rs

@ -29,6 +29,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -48,6 +49,7 @@ async fn test_item_ids_reset_when_rejoining_rooms() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

5
tests/test_shops.rs

@ -318,7 +318,8 @@ async fn test_other_clients_see_stacked_purchase() {
item::NewItemEntity { item::NewItemEntity {
item: item::ItemDetail::Tool( item: item::ItemDetail::Tool(
item::tool::Tool { item::tool::Tool {
tool: item::tool::ToolType::Monomate
tool: item::tool::ToolType::Monomate,
wrapping: None,
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -522,7 +523,7 @@ async fn test_techs_disappear_from_shop_when_bought() {
}).unwrap(); }).unwrap();
} }
// TOOD: this is not deterministic and can randomly fail
// TODO: this is not deterministic and can randomly fail
#[async_std::test] #[async_std::test]
async fn test_units_disappear_from_shop_when_bought() { async fn test_units_disappear_from_shop_when_bought() {
let mut entity_gateway = InMemoryGateway::new(); let mut entity_gateway = InMemoryGateway::new();

Loading…
Cancel
Save