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]
list_order = 1
description = "I am a description"
description = "kill some shit"
[[Extermination.quests]]
bin = "q058-ret-bb.bin"
@ -32,4 +32,13 @@ dat = "q233-ext-bb.dat"
[[Retrieval.quests]]
bin = "q236-ext-bb.bin"
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;
let character = entity_gateway.create_character(character).await.unwrap();
for _ in 0..3 {
for _ in 0..3usize {
entity_gateway.create_item(
item::NewItemEntity {
item: item::ItemDetail::Weapon(
@ -84,6 +84,7 @@ fn main() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -93,12 +94,13 @@ fn main() {
}).await.unwrap();
}
for _ in 0..8 {
for _ in 0..8usize {
entity_gateway.create_item(
NewItemEntity {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -119,6 +121,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,],
tekked: false,
wrapping: Some(item::WrappingPaper::BlackYellow),
}
),
location: ItemLocation::Inventory {
@ -136,6 +139,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,],
tekked: true,
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -153,6 +157,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,],
tekked: true,
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -170,6 +175,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,],
tekked: true,
wrapping: None,
}
),
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::Native, value: 100}),],
tekked: true,
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -208,6 +215,7 @@ fn main() {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::FedToMag {
@ -223,6 +231,7 @@ fn main() {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -234,6 +243,8 @@ fn main() {
item: ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502,
// wrapping: None,
wrapping: Some(item::WrappingPaper::PinkYellowGreen),
}
),
location: item::ItemLocation::Consumed,
@ -251,6 +262,7 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
None,],
tekked: false,
wrapping: None,
}
),
location: ItemLocation::Bank {
@ -266,6 +278,7 @@ fn main() {
dfp: 0,
evp: 0,
slots: 4,
wrapping: Some(item::WrappingPaper::Green),
}
),
location: ItemLocation::Inventory {
@ -280,6 +293,7 @@ fn main() {
shield: item::shield::ShieldType::Barrier,
dfp: 0,
evp: 0,
wrapping: Some(item::WrappingPaper::Green),
}
),
location: ItemLocation::Inventory {
@ -293,6 +307,7 @@ fn main() {
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -306,6 +321,8 @@ fn main() {
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
// wrapping: None,
wrapping: Some(item::WrappingPaper::YellowBlue),
}
),
location: ItemLocation::Inventory {
@ -319,6 +336,7 @@ fn main() {
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -332,6 +350,7 @@ fn main() {
item::unit::Unit {
unit: item::unit::UnitType::PriestMind,
modifier: Some(item::unit::UnitModifier::Minus),
wrapping: None,
}
),
location: ItemLocation::Inventory {
@ -341,8 +360,56 @@ fn main() {
).await.unwrap();
let item13 = entity_gateway.create_item(
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 {
character_id: character.id,
@ -359,7 +426,7 @@ fn main() {
};
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_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!();
}
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> {

136
src/entity/gateway/inmemory.rs

@ -17,8 +17,14 @@ pub struct InMemoryGateway {
inventories: Arc<Mutex<BTreeMap<CharacterEntityId, InventoryEntity>>>,
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
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 {
@ -31,8 +37,14 @@ impl InMemoryGateway {
inventories: Arc::new(Mutex::new(BTreeMap::new())),
banks: 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())),
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::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) => {
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) {
for mag_modifier in mag_modifiers.iter() {
match mag_modifier {
@ -81,12 +131,24 @@ impl InMemoryGateway {
}
}
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
})
})
@ -273,6 +335,62 @@ impl EntityGateway for InMemoryGateway {
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> {
println!("getting inv");
let inventories = self.inventories.lock().unwrap();
@ -299,7 +417,7 @@ impl EntityGateway for InMemoryGateway {
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> {
let mut banks = self.banks.lock().unwrap();
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,
techs bytea not null,
config bytea not null,
infoboard 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
);
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,
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,
created_at timestamptz default current_timestamp not null
);
@ -111,8 +111,8 @@ create table unit_modifier (
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,
created_at timestamptz default current_timestamp not null
);
@ -122,3 +122,15 @@ create table mag_modifier (
modifier jsonb 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)]
pub struct PgCharacter {
pub id: i32,
@ -277,8 +276,6 @@ impl Into<CharacterEntity> for PgCharacter {
pub struct PgGuildCard {
}
#[derive(Debug, Serialize, Deserialize)]
pub struct PgWeapon {
weapon: weapon::WeaponType,
@ -286,6 +283,7 @@ pub struct PgWeapon {
grind: u8,
attrs: HashMap<weapon::Attribute, i8>,
tekked: bool,
wrapping: Option<WrappingPaper>,
}
impl From<weapon::Weapon> for PgWeapon {
@ -296,6 +294,7 @@ impl From<weapon::Weapon> for PgWeapon {
grind: other.grind,
attrs: other.attrs.iter().flatten().map(|attr| (attr.attr, attr.value)).collect(),
tekked: other.tekked,
wrapping: other.wrapping,
}
}
}
@ -316,6 +315,7 @@ impl Into<weapon::Weapon> for PgWeapon {
grind: self.grind,
attrs: attrs,
tekked: self.tekked,
wrapping: self.wrapping,
}
}
}
@ -326,12 +326,52 @@ pub struct PgWeaponModifier {
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)]
pub struct PgArmor {
armor: armor::ArmorType,
dfp: u8,
evp: u8,
slots: u8,
wrapping: Option<WrappingPaper>, // TODO: check if this clobbers slots
}
impl From<armor::Armor> for PgArmor {
@ -341,6 +381,7 @@ impl From<armor::Armor> for PgArmor {
dfp: other.dfp,
evp: other.evp,
slots: other.slots,
wrapping: other.wrapping,
}
}
}
@ -352,41 +393,22 @@ impl Into<armor::Armor> for PgArmor {
dfp: self.dfp,
evp: self.evp,
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)]
pub struct PgUnit {
unit: unit::UnitType,
modifier: Option<unit::UnitModifier>,
wrapping: Option<WrappingPaper>,
}
impl From<unit::Unit> for PgUnit {
@ -394,6 +416,7 @@ impl From<unit::Unit> for PgUnit {
PgUnit {
unit: other.unit,
modifier: other.modifier,
wrapping: other.wrapping,
}
}
}
@ -403,53 +426,51 @@ impl Into<unit::Unit> for PgUnit {
unit::Unit {
unit: self.unit,
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)]
@ -457,6 +478,7 @@ pub struct PgMag {
mag: mag::MagType,
synchro: u8,
color: u8,
wrapping: Option<WrappingPaper>,
}
impl From<mag::Mag> for PgMag {
@ -465,85 +487,107 @@ impl From<mag::Mag> for PgMag {
mag: other.mag,
synchro: other.synchro,
color: other.color,
wrapping: other.wrapping,
}
}
}
impl Into<mag::Mag> for PgMag {
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);
mag.mag = self.mag;
mag.synchro = self.synchro;
mag.wrapping = self.wrapping;
mag
}
}
#[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)]
pub enum PgItemDetail {
Weapon(PgWeapon),
ESWeapon(PgESWeapon),
Armor(PgArmor),
Shield(PgShield),
Unit(PgUnit),
Tool(PgTool),
TechDisk(PgTechDisk),
Shield(PgShield),
Mag(PgMag),
ESWeapon(PgESWeapon),
TechDisk(PgTechDisk),
Tool(PgTool),
}
impl From<ItemDetail> for PgItemDetail {
fn from(other: ItemDetail) -> PgItemDetail {
match other {
ItemDetail::Weapon(weapon) => PgItemDetail::Weapon(weapon.into()),
ItemDetail::ESWeapon(esweapon) => PgItemDetail::ESWeapon(esweapon.into()),
ItemDetail::Armor(armor) => PgItemDetail::Armor(armor.into()),
ItemDetail::Shield(shield) => PgItemDetail::Shield(shield.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::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 {
match self {
PgItemDetail::Weapon(weapon) => ItemDetail::Weapon(weapon.into()),
PgItemDetail::ESWeapon(esweapon) => ItemDetail::ESWeapon(esweapon.into()),
PgItemDetail::Armor(armor) => ItemDetail::Armor(armor.into()),
PgItemDetail::Shield(shield) => ItemDetail::Shield(shield.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::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>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum PgItemLocationDetail {
Inventory {
@ -627,7 +670,6 @@ impl Into<ItemLocation> for PgItemLocationDetail {
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgItemLocation {
//pub id: i32,
@ -635,13 +677,14 @@ pub struct PgItemLocation {
created_at: chrono::DateTime<chrono::Utc>,
}
#[derive(Debug, Serialize, Deserialize)]
pub enum PgMagModifierDetail {
FeedMag(i32),
BankMag,
MagCell(i32),
OwnerChange(CharacterClass, SectionID)
OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
}
impl From<mag::MagModifier> for PgMagModifierDetail {
@ -651,6 +694,8 @@ impl From<mag::MagModifier> for PgMagModifierDetail {
mag::MagModifier::BankMag => PgMagModifierDetail::BankMag,
mag::MagModifier::MagCell(cell) => PgMagModifierDetail::MagCell(cell.0 as i32),
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::MagCell(cell) => mag::MagModifier::MagCell(ItemEntityId(cell as u32)),
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>,
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgItemEntity {
pub id: i32,
@ -697,7 +743,6 @@ impl Into<ItemEntity> for PgItemWithLocation {
}
}
#[derive(Debug, sqlx::FromRow)]
pub struct PgMagModifierWithParameters {
pub mag: i32,
@ -706,7 +751,6 @@ pub struct PgMagModifierWithParameters {
pub cell: Option<sqlx::types::Json<PgTool>>,
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PgInventoryItemEntity {
@ -732,11 +776,11 @@ pub struct PgEquipped {
pchar: i32,
weapon: Option<i32>,
armor: Option<i32>,
shield: Option<i32>,
unit0: Option<i32>,
unit1: Option<i32>,
unit2: Option<i32>,
unit3: Option<i32>,
shield: Option<i32>,
mag: Option<i32>,
}
@ -745,12 +789,12 @@ impl Into<EquippedEntity> for PgEquipped {
EquippedEntity {
weapon: self.weapon.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)),
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)),
}
}
@ -762,13 +806,12 @@ impl From<(CharacterEntityId, EquippedEntity)> for PgEquipped {
pchar: char_equips.0.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),
shield: char_equips.1.shield.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),
unit2: char_equips.1.unit[2].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),
}
}
}

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

@ -65,6 +65,74 @@ impl PostgresGateway {
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) => {
let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell
from mag_modifier
@ -91,6 +159,8 @@ impl PostgresGateway {
mag::MagModifier::OwnerChange(class, section_id) => {
mag.change_owner(class, section_id)
},
mag::MagModifier::WrapPresent => {mag.apply_modifier(&modifier)},
mag::MagModifier::UnwrapPresent => {mag.apply_modifier(&modifier)},
}
}).await;
@ -416,6 +486,62 @@ impl EntityGateway for PostgresGateway {
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> {
let q = r#"select * from (
@ -540,7 +666,6 @@ impl EntityGateway for PostgresGateway {
}
})
.collect::<Vec<_>>();
sqlx::query("insert into inventory (pchar, items) values ($1, $2) on conflict (pchar) do update set items = $2")
.bind(char_id.0)
.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> {
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"#)
.bind(char_id.0)
.bind(equips.weapon.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[1].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.shield.map(|i| i.0 as i32))
.bind(equips.mag.map(|i| i.0 as i32))
.execute(&self.pool)
.await?;

30
src/entity/item/armor.rs

@ -1,5 +1,5 @@
use serde::{Serialize, Deserialize};
use crate::entity::item::ItemEntityId;
use crate::entity::item::{ItemEntityId, WrappingPaper};
#[derive(Debug, Copy, Clone)]
pub enum ItemParseError {
@ -288,27 +288,31 @@ impl ArmorType {
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ArmorModifier {
AddSlot {
addslot: ItemEntityId,
}
},
WrapPresent,
UnwrapPresent,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Armor {
pub armor: ArmorType,
pub dfp: u8,
pub evp: u8,
pub slots: u8,
pub wrapping: Option<WrappingPaper>,
}
impl Armor {
pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16];
result[0..3].copy_from_slice(&self.armor.value());
if self.wrapping.is_some() {
result[4] += 0x40;
}
result[5] = self.slots;
result[6] = self.dfp;
result[8] = self.evp;
@ -317,16 +321,32 @@ impl Armor {
pub fn from_bytes(data: [u8; 16]) -> Result<Armor, ItemParseError> {
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() {
Ok(Armor {
armor: a.unwrap(),
dfp: data[6],
evp: data[8],
slots: data[5],
wrapping: w,
})
}
else {
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 crate::entity::item::{WrappingPaper};
// TODO: actually use this
#[derive(Debug)]
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)]
pub struct ESWeapon {
pub esweapon: ESWeaponType,
pub special: Option<ESWeaponSpecial>,
pub name: String,
pub grind: u8,
pub wrapping: Option<WrappingPaper>
}
impl ESWeapon {
@ -184,6 +194,7 @@ impl ESWeapon {
special: None,
name: "".to_owned(),
grind: 0,
wrapping: None,
}
}
@ -241,7 +252,10 @@ impl ESWeapon {
result[1] = 0x70 + self.esweapon.to_value();
result[2] = self.special.map(|s| s.to_value()).unwrap_or(0);
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
}
@ -252,12 +266,28 @@ impl ESWeapon {
let special = ESWeaponSpecial::from_value(bytes[2]);
let grind = bytes[3];
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,
special: special.ok(),
grind: grind,
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),
grind: 137u8,
name: "JAKESERV".to_owned(),
wrapping: None,
});
}
@ -287,6 +318,7 @@ mod test {
special: Some(ESWeaponSpecial::Chaos),
grind: 72u8,
name: "PSYCHO".to_owned(),
wrapping: None,
};
let bytes = testweapon.as_bytes();
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),
grind: 105u8,
name: "YUGIOH".to_owned(),
wrapping: None,
};
let bytes = testweapon.as_bytes();
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 crate::entity::item::tool::ToolType;
use crate::entity::character::{CharacterClass, SectionID};
use crate::entity::item::ItemEntityId;
use crate::entity::item::{ItemEntityId, WrappingPaper};
use std::io::Read;
use std::cmp::Ordering::{Less, Greater, Equal};
@ -56,8 +56,6 @@ lazy_static::lazy_static! {
};
}
#[derive(Debug, Copy, Clone)]
pub enum ItemParseError {
InvalidMagType,
@ -509,14 +507,16 @@ impl MagAttributeOrdering {
}
}
#[derive(Debug, Clone, PartialEq)]
#[derive(Debug, Clone, PartialEq, Serialize)]
pub enum MagModifier {
FeedMag{
food: ItemEntityId,
},
BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags
MagCell(ItemEntityId),
OwnerChange(CharacterClass, SectionID)
OwnerChange(CharacterClass, SectionID),
WrapPresent,
UnwrapPresent,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, enum_utils::FromStr)]
@ -529,7 +529,7 @@ pub enum PhotonBlast {
MyllaYoulla,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Mag {
pub mag: MagType,
def: u16,
@ -543,9 +543,9 @@ pub struct Mag {
//modifiers: Vec<MagModifier>,
pub class: CharacterClass,
pub id: SectionID,
pub wrapping: Option<WrappingPaper>,
}
impl Mag {
pub fn baby_mag(skin: u16) -> Mag {
Mag {
@ -561,6 +561,25 @@ impl Mag {
//modifiers: Vec::new(),
class: CharacterClass::HUmar,
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[13] = self.iq;
result[14] = self.photon_blast_count();
if self.wrapping.is_some() {
result[14] += 0x40;
}
result[15] = self.color;
result
}
@ -648,6 +670,13 @@ impl Mag {
let sync = data[12] % 121; // 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{
mag: m.unwrap(),
@ -660,8 +689,9 @@ impl Mag {
photon_blast: [None, None, None], // TODO: actually get PBs from bytes
color: data[15] % 18,
//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 {
@ -1097,6 +1127,14 @@ impl Mag {
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,
class: CharacterClass::FOmarl,
id: SectionID::Whitill,
wrapping: None,
});
}
@ -1174,4 +1213,3 @@ mod test {
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)]
pub enum ItemType {
Weapon(weapon::WeaponType),
@ -159,6 +158,34 @@ impl ItemDetail {
_ => 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)]
@ -200,7 +227,7 @@ impl InventoryItemEntity {
_ => 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> {
match self {
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> {
match self {
InventoryItemEntity::Stacked(items) => Some(func(items)),
@ -252,7 +278,6 @@ impl InventoryEntity {
}
}
#[derive(Clone, Debug)]
pub enum BankItemEntity {
Individual(ItemEntity),
@ -287,10 +312,8 @@ impl BankItemEntity {
}
}
#[derive(Clone, Debug, Default)]
pub struct BankEntity {
//pub items: [Option<CharacterBankItem>; 30],
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 crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)]
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)]
pub struct Shield {
pub shield: ShieldType,
pub dfp: u8,
pub evp: u8,
pub wrapping: Option<WrappingPaper>,
}
impl Shield {
pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16];
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[8] = self.evp;
result
@ -537,15 +550,30 @@ impl Shield {
pub fn from_bytes(data: [u8; 16]) -> Result<Shield, ItemParseError> {
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() {
Ok(Shield{
shield: s.unwrap(),
dfp: data[6],
evp: data[8],
wrapping: wrapping,
})
}
else {
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 crate::entity::item::{WrappingPaper};
#[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)]
pub struct TechniqueDisk {
pub tech: Technique,
pub level: u32,
pub wrapping: Option<WrappingPaper>, // TODO: validate if this clobbers tech value?
}
impl TechniqueDisk {
@ -88,6 +98,17 @@ impl TechniqueDisk {
result[1] = 2;
result[2] = self.level as u8 - 1;
result[4] = self.tech.as_value();
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
};
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 crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)]
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)]
pub struct Tool {
pub tool: ToolType,
pub wrapping: Option<WrappingPaper>, // TODO: what happens if a present is N monomates and the inventory already has 10?
}
impl Tool {
@ -668,10 +677,18 @@ impl Tool {
}
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() {
Ok(Tool {
tool: t.unwrap(),
wrapping: w,
})
}
else {
@ -686,4 +703,11 @@ impl Tool {
pub fn max_stack(&self) -> usize {
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 crate::entity::item::{WrappingPaper};
#[derive(Debug, Copy, Clone)]
pub enum ItemParseError {
@ -329,15 +330,19 @@ pub enum UnitModifier {
Plus,
Minus,
MinusMinus,
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub struct Unit {
pub unit: UnitType,
pub modifier: Option<UnitModifier>,
pub wrapping: Option<WrappingPaper>,
}
impl Unit {
pub fn as_bytes(&self) -> [u8; 16] {
let mut result = [0; 16];
@ -359,8 +364,15 @@ impl Unit {
result[6] = 0xFE;
result[7] = 0xFF;
},
_ => {}, // handling wrapping here is weird?
}
}
if self.wrapping.is_some() {
result[4] += 0x40;
result[5] = self.wrapping.unwrap().value();
}
result
}
@ -375,13 +387,30 @@ impl Unit {
_ => None,
};
let w = {
if data[4] & 0x40 == 0x40 {
WrappingPaper::from(data[5])
} else {
None
}
};
Ok(Unit{
unit: u.unwrap(),
modifier: m,
wrapping: w,
})
}
else {
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};
#[derive(Debug, Copy, Clone)]
@ -44,7 +44,6 @@ impl WeaponAttribute {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize, strum_macros::EnumIter)]
pub enum WeaponSpecial {
Draw = 1,
@ -1420,7 +1419,6 @@ impl WeaponType {
}
}
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
pub enum TekSpecialModifier {
Plus,
@ -1452,18 +1450,22 @@ pub enum WeaponModifier {
percent: TekPercentModifier,
grind: i32,
},
WrapPresent {
paper: WrappingPaper,
},
UnwrapPresent,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
pub struct Weapon {
pub weapon: WeaponType,
pub special: Option<WeaponSpecial>,
pub grind: u8,
pub attrs: [Option<WeaponAttribute>; 3],
pub tekked: bool,
pub wrapping: Option<WrappingPaper>,
}
impl Weapon {
pub fn new(wep: WeaponType) -> Weapon {
Weapon {
@ -1472,6 +1474,7 @@ impl Weapon {
grind: 0,
attrs: [None; 3],
tekked: true,
wrapping: None,
}
}
@ -1516,15 +1519,30 @@ impl Weapon {
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
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] {
let mut result = [0u8; 16];
result[0..3].copy_from_slice(&self.weapon.value());
result[3] = self.grind;
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 {
result[4] += 0x80
@ -1545,17 +1563,18 @@ impl Weapon {
let mut t = true;
let g = data[3];
if data[4] >= 0x81 && data[4] <= 0xA8 {
if data[4] & 0x80 == 0x80 {
s = WeaponSpecial::from(data[4] - 0x80);
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 = [
None,
@ -1585,6 +1604,7 @@ impl Weapon {
a[2],
],
tekked: t,
wrapping: p,
})
}
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,
attrs: [None; 3],
tekked: true,
wrapping: None,
}),
location: ItemLocation::Inventory {
character_id: character.id,
@ -233,6 +234,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
dfp: 0,
evp: 0,
slots: 0,
wrapping: None,
}),
location: ItemLocation::Inventory {
character_id: character.id,
@ -260,6 +262,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool (
Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}),
location: ItemLocation::Inventory {
character_id: character.id,
@ -273,6 +276,7 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
item: ItemDetail::Tool (
Tool {
tool: item::tool::ToolType::Monofluid,
wrapping: None,
}),
location: ItemLocation::Inventory {
character_id: character.id,

5
src/ship/drops/generic_armor.rs

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

5
src/ship/drops/generic_shield.rs

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

2
src/ship/drops/generic_unit.rs

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

5
src/ship/drops/generic_weapon.rs

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

5
src/ship/drops/rare_drop_table.rs

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

6
src/ship/drops/tech_table.rs

@ -103,7 +103,8 @@ impl TechniqueTable {
Some(ItemDropType::TechniqueDisk(TechniqueDisk {
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(
TechniqueDisk {
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 {
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 crate::entity::character::CharacterEntityId;
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::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::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> {
match self.item {
ItemDetail::Mag(ref mut mag) => Some(mag),
_ => 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)]
@ -66,6 +128,14 @@ impl StackedInventoryItem {
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)]
@ -127,7 +197,7 @@ impl InventoryItem {
}
}
// TOOD: delete?
// TODO: delete?
pub fn are_same_stackable_tool(&self, other_stacked_item: &StackedFloorItem) -> bool {
match self {
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 {
match self {
InventoryItem::Stacked(self_stacked_item) => {
@ -151,7 +221,7 @@ impl InventoryItem {
}
// TODO: result
// TOOD: delete?
// TODO: delete?
pub fn combine_stacks(&mut self, other_stacked_item: &mut StackedFloorItem) {
match self {
InventoryItem::Stacked(self_stacked_item) => {
@ -167,13 +237,13 @@ impl InventoryItem {
InventoryItem::Individual(item) => {
match &item.item {
ItemDetail::Weapon(w) => w.as_bytes(),
ItemDetail::ESWeapon(e) => e.as_bytes(),
ItemDetail::Armor(a) => a.as_bytes(),
ItemDetail::Shield(s) => s.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::ESWeapon(e) => e.as_bytes(),
ItemDetail::TechniqueDisk(d) => d.as_bytes(),
ItemDetail::Tool(t) => t.as_individual_bytes(),
}
},
InventoryItem::Stacked(item) => {
@ -219,6 +289,41 @@ impl InventoryItem {
_ => 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::character::{CharacterEntity, CharacterEntityId, TechLevel};
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::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::ship::ItemDropLocation;
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 used_item = inventory.get_item_handle_by_id(item_id).ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let consumed_item = used_item.consume(amount)?;
if let ItemDetail::TechniqueDisk(tech_disk) = consumed_item.item() {
// TODO: validate tech level in packet is in bounds [1..30]
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,
used_item: ConsumedItem,
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))?;
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?;
Ok(())
@ -923,4 +956,9 @@ impl ItemManager {
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 std::collections::HashMap;
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 byteorder::{LittleEndian, ReadBytesExt};
use thiserror::Error;
use crate::ship::monster::MonsterType;
use crate::ship::room::Episode;
use crate::ship::map::*;
#[derive(Debug, Copy, Clone)]
@ -62,7 +59,6 @@ impl RawMapEnemy {
}
}
#[derive(Error, Debug)]
#[error("")]
pub enum MapEnemyError {
@ -70,7 +66,6 @@ pub enum MapEnemyError {
MapAreaError(#[from] MapAreaError),
}
#[derive(Debug, Copy, Clone)]
pub struct MapEnemy {
pub monster: MonsterType,
@ -194,7 +189,6 @@ impl MapEnemy {
_ => return Err(MapEnemyError::UnknownEnemyId(enemy.id))
}
},
MapArea::Tower => {
match (enemy, episode) {
(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::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::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 => {
for _ in 0..509 {
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};

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;

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 {
BankItemList {
aflag: 0,

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

@ -1,12 +1,12 @@
use libpso::packet::ship::*;
use libpso::packet::messages::*;
use crate::entity::gateway::EntityGateway;
use crate::entity::item::{ItemType};
use crate::entity::item::{ItemType, ItemDetail};
use crate::common::serverstate::ClientId;
use crate::common::leveltable::CharacterLevelTable;
use crate::ship::ship::{SendShipPacket, ShipError, Rooms, Clients, ItemDropLocation};
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;
pub async fn request_exp<EG: EntityGateway>(id: ClientId,
@ -272,9 +272,12 @@ where
EG: EntityGateway
{
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()))
}

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

@ -7,7 +7,7 @@ use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::packet::builder::quest;
use libpso::util::array_to_utf8;
// TOOD: enum
// TODO: enum
enum QuestFileType {
Bin,
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)
},
RecvShipPacket::ClientCharacterData(_) => {
// TOOD: validate this in some way?
// TODO: validate this in some way?
Box::new(None.into_iter())
},
RecvShipPacket::DoneBursting(_) => {

3
src/ship/shops/armor.rs

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

15
src/ship/shops/tool.rs

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

1
src/ship/shops/weapon.rs

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

35
tests/test_bank.rs

@ -27,6 +27,7 @@ async fn test_bank_items_sent_in_character_login() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -68,6 +69,7 @@ async fn test_request_bank_items() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -116,6 +118,7 @@ async fn test_request_stacked_bank_items() {
item: item::ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -165,6 +168,7 @@ async fn test_request_bank_items_sorted() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -177,6 +181,7 @@ async fn test_request_bank_items_sorted() {
item: item::ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -193,6 +198,7 @@ async fn test_request_bank_items_sorted() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -243,6 +249,7 @@ async fn test_deposit_individual_item() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -258,6 +265,7 @@ async fn test_deposit_individual_item() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -320,6 +328,7 @@ async fn test_deposit_stacked_item() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -384,6 +393,7 @@ async fn test_deposit_partial_stacked_item() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -458,6 +468,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -470,6 +481,7 @@ async fn test_deposit_stacked_item_with_stack_already_in_bank() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -535,6 +547,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -550,6 +563,7 @@ async fn test_deposit_stacked_item_with_full_stack_in_bank() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -617,6 +631,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -635,6 +650,7 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -695,6 +711,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -714,6 +731,7 @@ async fn test_deposit_stacked_item_in_full_bank() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -810,6 +830,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -988,6 +1009,7 @@ async fn test_withdraw_individual_item() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1051,6 +1073,7 @@ async fn test_withdraw_stacked_item() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1115,6 +1138,7 @@ async fn test_withdraw_partial_stacked_item() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1186,6 +1210,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -1198,6 +1223,7 @@ async fn test_withdraw_stacked_item_with_stack_already_in_inventory() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1265,6 +1291,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1281,6 +1308,7 @@ async fn test_withdraw_stacked_item_with_full_stack_in_inventory() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -1347,6 +1375,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1366,6 +1395,7 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -1421,6 +1451,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1441,6 +1472,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Bank {
@ -1523,6 +1556,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
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: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -38,7 +39,8 @@ async fn test_pick_up_item_stack_of_items_already_in_inventory() {
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: tool
tool: tool,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -104,7 +106,8 @@ async fn test_pick_up_item_stack_of_items_not_already_held() {
item::NewItemEntity {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -172,6 +175,7 @@ async fn test_pick_up_meseta_when_inventory_full() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -251,6 +255,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -343,6 +350,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -361,6 +369,7 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None,
attrs: [None, None, None],
tekked: true,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -476,6 +485,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -491,6 +501,7 @@ async fn test_pick_up_stack_that_would_exceed_stack_limit() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
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::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {

13
tests/test_item_use.rs

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

3
tests/test_mags.rs

@ -35,6 +35,7 @@ async fn test_mag_feed() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::Inventory {
@ -180,6 +181,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool (
item::tool::Tool {
tool: item::tool::ToolType::Monomate,
wrapping: None,
}
),
location: item::ItemLocation::FedToMag {
@ -193,6 +195,7 @@ async fn test_mag_cell() {
item: item::ItemDetail::Tool(
item::tool::Tool {
tool: item::tool::ToolType::CellOfMag502,
wrapping: None,
}
),
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,
evp: 0,
slots: 4,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -37,6 +38,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -49,6 +51,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -111,6 +114,7 @@ async fn test_unequip_armor_with_units() {
dfp: 0,
evp: 0,
slots: 4,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -123,6 +127,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -135,6 +140,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -188,6 +194,7 @@ async fn test_sort_items() {
dfp: 0,
evp: 0,
slots: 4,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -200,6 +207,7 @@ async fn test_sort_items() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,
@ -212,6 +220,7 @@ async fn test_sort_items() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
wrapping: None,
}),
location: item::ItemLocation::Inventory {
character_id: char1.id,

2
tests/test_rooms.rs

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

5
tests/test_shops.rs

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

Loading…
Cancel
Save