Browse Source

Merge pull request 'tekking!' (#271) from andy_is_unable_to_tek_properly into master

pbs
jake 4 years ago
parent
commit
03aa41ef6a
  1. 10
      src/bin/main.rs
  2. 10
      src/entity/gateway/entitygateway.rs
  3. 29
      src/entity/gateway/inmemory.rs
  4. 8
      src/entity/gateway/postgres/models.rs
  5. 34
      src/entity/gateway/postgres/postgres.rs
  6. 2
      src/entity/item/armor.rs
  7. 140
      src/entity/item/weapon.rs
  8. 2
      src/login/character.rs
  9. 5
      src/ship/drops/generic_armor.rs
  10. 5
      src/ship/drops/generic_weapon.rs
  11. 2
      src/ship/drops/rare_drop_table.rs
  12. 25
      src/ship/items/inventory.rs
  13. 37
      src/ship/items/manager.rs
  14. 2
      src/ship/items/use_tool.rs
  15. 16
      src/ship/packet/builder/message.rs
  16. 109
      src/ship/packet/handler/direct_message.rs
  17. 17
      src/ship/packet/handler/message.rs
  18. 21
      src/ship/ship.rs
  19. 1
      src/ship/shops/armor.rs
  20. 1
      src/ship/shops/weapon.rs
  21. 15
      tests/test_bank.rs
  22. 3
      tests/test_item_actions.rs
  23. 4
      tests/test_item_pickup.rs
  24. 2
      tests/test_rooms.rs

10
src/bin/main.rs

@ -84,7 +84,6 @@ fn main() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -119,8 +118,7 @@ fn main() {
attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: true,
modifiers: Vec::new(),
tekked: false,
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -138,7 +136,6 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}),
None,], None,],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -156,7 +153,6 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -174,7 +170,6 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
None,], None,],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -192,7 +187,6 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}),
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
@ -257,7 +251,6 @@ fn main() {
Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}),
None,], None,],
tekked: false, tekked: false,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Bank { location: ItemLocation::Bank {
@ -273,7 +266,6 @@ fn main() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
modifiers: Vec::new(),
} }
), ),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {

10
src/entity/gateway/entitygateway.rs

@ -65,11 +65,6 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
// TODO: remove
async fn change_item(&mut self, _id: &ItemEntityId, _item: &ItemDetail) -> Result<(), GatewayError> {
unimplemented!();
}
async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) -> Result<(), GatewayError> { async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) -> Result<(), GatewayError> {
unimplemented!(); unimplemented!();
} }
@ -86,6 +81,11 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn add_weapon_modifier(&mut self, _item_id: &ItemEntityId, _modifier: weapon::WeaponModifier) -> Result<(), GatewayError> {
unimplemented!();
}
/* /*
async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
unimplemented!(); unimplemented!();

29
src/entity/gateway/inmemory.rs

@ -18,6 +18,7 @@ pub struct InMemoryGateway {
banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>, banks: Arc<Mutex<BTreeMap<CharacterEntityId, BankEntity>>>,
equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>, equips: Arc<Mutex<BTreeMap<CharacterEntityId, EquippedEntity>>>,
mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>, mag_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<mag::MagModifier>>>>,
weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
} }
impl InMemoryGateway { impl InMemoryGateway {
@ -31,6 +32,7 @@ impl InMemoryGateway {
banks: Arc::new(Mutex::new(BTreeMap::new())), banks: Arc::new(Mutex::new(BTreeMap::new())),
equips: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())),
mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -42,9 +44,16 @@ impl InMemoryGateway {
.map(|item| { .map(|item| {
item.map_individual(|mut item| { item.map_individual(|mut item| {
item.item = match item.item { item.item = match item.item {
ItemDetail::Weapon(mut weapon) => {
if let Some(weapon_modifiers) = self.weapon_modifiers.lock().unwrap().get(&item.id) {
for weapon_modifier in weapon_modifiers.iter() {
weapon.apply_modifier(&weapon_modifier);
}
}
ItemDetail::Weapon(weapon)
},
ItemDetail::Mag(mag) => { ItemDetail::Mag(mag) => {
let mut mag = mag::Mag::baby_mag(mag.color as u16); let mut mag = mag::Mag::baby_mag(mag.color as u16);
println!("mag! {:?}", mag);
if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) { if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) {
for mag_modifier in mag_modifiers.iter() { for mag_modifier in mag_modifiers.iter() {
match mag_modifier { match mag_modifier {
@ -69,7 +78,6 @@ impl InMemoryGateway {
}, },
_ => {} _ => {}
} }
println!("{:?} -> {:?}", mag_modifier, mag);
} }
} }
ItemDetail::Mag(mag) ItemDetail::Mag(mag)
@ -223,15 +231,6 @@ impl EntityGateway for InMemoryGateway {
Ok(new_item) Ok(new_item)
} }
async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> {
let mut items = self.items.lock().unwrap();
if let Some((_, ref mut old_item)) = items.iter_mut().find(|(existing_id, _)| **existing_id == *id) {
old_item.item = item.clone();
}
Ok(())
}
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
self.items.lock().unwrap().get_mut(&item_id) self.items.lock().unwrap().get_mut(&item_id)
.map(|item_entity| { .map(|item_entity| {
@ -266,6 +265,14 @@ impl EntityGateway for InMemoryGateway {
Ok(()) Ok(())
} }
async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> {
self.weapon_modifiers.lock().unwrap()
.entry(*item_id)
.or_insert(Vec::new())
.push(modifier);
Ok(())
}
async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> { async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result<InventoryEntity, GatewayError> {
println!("getting inv"); println!("getting inv");
let inventories = self.inventories.lock().unwrap(); let inventories = self.inventories.lock().unwrap();

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

@ -316,11 +316,16 @@ impl Into<weapon::Weapon> for PgWeapon {
grind: self.grind, grind: self.grind,
attrs: attrs, attrs: attrs,
tekked: self.tekked, tekked: self.tekked,
modifiers: Vec::new(),
} }
} }
} }
#[derive(Debug, sqlx::FromRow)]
pub struct PgWeaponModifier {
pub weapon: i32,
pub modifier: sqlx::types::Json<weapon::WeaponModifier>,
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub struct PgArmor { pub struct PgArmor {
armor: armor::ArmorType, armor: armor::ArmorType,
@ -347,7 +352,6 @@ impl Into<armor::Armor> for PgArmor {
dfp: self.dfp, dfp: self.dfp,
evp: self.evp, evp: self.evp,
slots: self.slots, slots: self.slots,
modifiers: Vec::new(),
} }
} }
} }

34
src/entity/gateway/postgres/postgres.rs

@ -48,6 +48,23 @@ impl PostgresGateway {
let ItemEntity {id, item, location} = item; let ItemEntity {id, item, location} = item;
let item = match item { let item = match item {
ItemDetail::Weapon(mut weapon) => {
let q = r#"select weapon, modifier
from weapon_modifier
where weapon = $1
order by created_at"#;
let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q)
.bind(id.0 as i32)
.fetch(&self.pool);
weapon_modifiers.for_each(|modifier| {
if let Ok(modifier) = modifier {
weapon.apply_modifier(&modifier.modifier);
}
}).await;
ItemDetail::Weapon(weapon)
},
ItemDetail::Mag(mut mag) => { ItemDetail::Mag(mut mag) => {
let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell
from mag_modifier from mag_modifier
@ -331,14 +348,6 @@ impl EntityGateway for PostgresGateway {
*/ */
} }
async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> {
sqlx::query("update item set item = $1 where id = $2")
.bind(sqlx::types::Json(PgItemDetail::from(item.clone())))
.bind(id.0)
.execute(&self.pool).await?;
Ok(())
}
async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> {
sqlx::query("insert into item_location (item, location) values ($1, $2)") sqlx::query("insert into item_location (item, location) values ($1, $2)")
.bind(item_id.0) .bind(item_id.0)
@ -398,6 +407,15 @@ impl EntityGateway for PostgresGateway {
.execute(&self.pool).await?; .execute(&self.pool).await?;
Ok(()) Ok(())
} }
async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> {
sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);")
.bind(item_id.0)
.bind(sqlx::types::Json(modifier))
.execute(&self.pool).await?;
Ok(())
}
/* /*
async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> { async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result<Vec<ItemEntity>, GatewayError> {
let q = r#"select * from ( let q = r#"select * from (

2
src/entity/item/armor.rs

@ -303,7 +303,6 @@ pub struct Armor {
pub dfp: u8, pub dfp: u8,
pub evp: u8, pub evp: u8,
pub slots: u8, pub slots: u8,
pub modifiers: Vec<ArmorModifier>
} }
impl Armor { impl Armor {
@ -324,7 +323,6 @@ impl Armor {
dfp: data[6], dfp: data[6],
evp: data[8], evp: data[8],
slots: data[5], slots: data[5],
modifiers: Vec::new(),
}) })
} }
else { else {

140
src/entity/item/weapon.rs

@ -93,6 +93,95 @@ impl WeaponSpecial {
pub fn value(&self) -> u8 { pub fn value(&self) -> u8 {
*self as u8 *self as u8
} }
pub fn rank_up(&self) -> WeaponSpecial {
match self {
WeaponSpecial::Draw => WeaponSpecial::Drain,
WeaponSpecial::Drain => WeaponSpecial::Fill,
WeaponSpecial::Fill => WeaponSpecial::Gush,
WeaponSpecial::Gush => WeaponSpecial::Gush,
WeaponSpecial::Heart => WeaponSpecial::Mind,
WeaponSpecial::Mind => WeaponSpecial::Soul,
WeaponSpecial::Soul => WeaponSpecial::Geist,
WeaponSpecial::Geist => WeaponSpecial::Geist,
WeaponSpecial::Masters => WeaponSpecial::Lords,
WeaponSpecial::Lords => WeaponSpecial::Kings,
WeaponSpecial::Kings => WeaponSpecial::Kings,
WeaponSpecial::Charge => WeaponSpecial::Charge,
WeaponSpecial::Spirit => WeaponSpecial::Spirit,
WeaponSpecial::Berserk => WeaponSpecial::Berserk,
WeaponSpecial::Ice => WeaponSpecial::Frost,
WeaponSpecial::Frost => WeaponSpecial::Freeze,
WeaponSpecial::Freeze => WeaponSpecial::Blizzard,
WeaponSpecial::Blizzard => WeaponSpecial::Blizzard,
WeaponSpecial::Bind => WeaponSpecial::Hold,
WeaponSpecial::Hold => WeaponSpecial::Seize,
WeaponSpecial::Seize => WeaponSpecial::Arrest,
WeaponSpecial::Arrest => WeaponSpecial::Arrest,
WeaponSpecial::Heat => WeaponSpecial::Fire,
WeaponSpecial::Fire => WeaponSpecial::Flame,
WeaponSpecial::Flame => WeaponSpecial::Burning,
WeaponSpecial::Burning => WeaponSpecial::Burning,
WeaponSpecial::Shock => WeaponSpecial::Thunder,
WeaponSpecial::Thunder => WeaponSpecial::Storm,
WeaponSpecial::Storm => WeaponSpecial::Tempest,
WeaponSpecial::Tempest => WeaponSpecial::Tempest,
WeaponSpecial::Dim => WeaponSpecial::Shadow,
WeaponSpecial::Shadow => WeaponSpecial::Dark,
WeaponSpecial::Dark => WeaponSpecial::Hell,
WeaponSpecial::Hell => WeaponSpecial::Hell,
WeaponSpecial::Panic => WeaponSpecial::Riot,
WeaponSpecial::Riot => WeaponSpecial::Havoc,
WeaponSpecial::Havoc => WeaponSpecial::Chaos,
WeaponSpecial::Chaos => WeaponSpecial::Chaos,
WeaponSpecial::Devils => WeaponSpecial::Demons,
WeaponSpecial::Demons => WeaponSpecial::Demons,
}
}
pub fn rank_down(&self) -> WeaponSpecial {
match self {
WeaponSpecial::Draw => WeaponSpecial::Draw,
WeaponSpecial::Drain => WeaponSpecial::Draw,
WeaponSpecial::Fill => WeaponSpecial::Drain,
WeaponSpecial::Gush => WeaponSpecial::Fill,
WeaponSpecial::Heart => WeaponSpecial::Heart,
WeaponSpecial::Mind => WeaponSpecial::Heart,
WeaponSpecial::Soul => WeaponSpecial::Mind,
WeaponSpecial::Geist => WeaponSpecial::Soul,
WeaponSpecial::Masters => WeaponSpecial::Masters,
WeaponSpecial::Lords => WeaponSpecial::Masters,
WeaponSpecial::Kings => WeaponSpecial::Lords,
WeaponSpecial::Charge => WeaponSpecial::Charge,
WeaponSpecial::Spirit => WeaponSpecial::Spirit,
WeaponSpecial::Berserk => WeaponSpecial::Berserk,
WeaponSpecial::Ice => WeaponSpecial::Ice,
WeaponSpecial::Frost => WeaponSpecial::Ice,
WeaponSpecial::Freeze => WeaponSpecial::Frost,
WeaponSpecial::Blizzard => WeaponSpecial::Freeze,
WeaponSpecial::Bind => WeaponSpecial::Bind,
WeaponSpecial::Hold => WeaponSpecial::Bind,
WeaponSpecial::Seize => WeaponSpecial::Hold,
WeaponSpecial::Arrest => WeaponSpecial::Seize,
WeaponSpecial::Heat => WeaponSpecial::Heat,
WeaponSpecial::Fire => WeaponSpecial::Heat,
WeaponSpecial::Flame => WeaponSpecial::Fire,
WeaponSpecial::Burning => WeaponSpecial::Flame,
WeaponSpecial::Shock => WeaponSpecial::Shock,
WeaponSpecial::Thunder => WeaponSpecial::Shock,
WeaponSpecial::Storm => WeaponSpecial::Thunder,
WeaponSpecial::Tempest => WeaponSpecial::Storm,
WeaponSpecial::Dim => WeaponSpecial::Dim,
WeaponSpecial::Shadow => WeaponSpecial::Dim,
WeaponSpecial::Dark => WeaponSpecial::Shadow,
WeaponSpecial::Hell => WeaponSpecial::Dark,
WeaponSpecial::Panic => WeaponSpecial::Panic,
WeaponSpecial::Riot => WeaponSpecial::Panic,
WeaponSpecial::Havoc => WeaponSpecial::Riot,
WeaponSpecial::Chaos => WeaponSpecial::Havoc,
WeaponSpecial::Devils => WeaponSpecial::Devils,
WeaponSpecial::Demons => WeaponSpecial::Devils,
}
}
pub fn from(data: u8) -> Option<WeaponSpecial> { pub fn from(data: u8) -> Option<WeaponSpecial> {
match data { match data {
@ -1360,7 +1449,8 @@ pub enum WeaponModifier {
}, },
Tekked { Tekked {
special: TekSpecialModifier, special: TekSpecialModifier,
percents: TekPercentModifier,
percent: TekPercentModifier,
grind: i32,
}, },
} }
@ -1371,7 +1461,6 @@ pub struct Weapon {
pub grind: u8, pub grind: u8,
pub attrs: [Option<WeaponAttribute>; 3], pub attrs: [Option<WeaponAttribute>; 3],
pub tekked: bool, pub tekked: bool,
pub modifiers: Vec<WeaponModifier>
} }
@ -1383,7 +1472,51 @@ impl Weapon {
grind: 0, grind: 0,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
modifiers: Vec::new(),
}
}
pub fn apply_modifier(&mut self, modifier: &WeaponModifier) {
match modifier {
WeaponModifier::Tekked{special, percent, grind} => {
match special {
TekSpecialModifier::Plus => {
self.special = self.special.map(|special| {
special.rank_up()
});
},
TekSpecialModifier::Minus => {
self.special = self.special.map(|special| {
special.rank_down()
});
},
TekSpecialModifier::Neutral => {
},
}
for i in 0..3 {
self.attrs[i] = self.attrs[i].map(|mut attr| {
match percent {
TekPercentModifier::PlusPlus => {
attr.value += 10;
},
TekPercentModifier::Plus => {
attr.value += 5;
},
TekPercentModifier::MinusMinus => {
attr.value -= 10;
},
TekPercentModifier::Minus => {
attr.value -= 5;
},
TekPercentModifier::Neutral => {
}
}
attr
});
}
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
self.tekked = true;
},
_ => {}
} }
} }
@ -1452,7 +1585,6 @@ impl Weapon {
a[2], a[2],
], ],
tekked: t, tekked: t,
modifiers: Vec::new(),
}) })
} }
else { else {

2
src/login/character.rs

@ -220,7 +220,6 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
special: None, special: None,
attrs: [None; 3], attrs: [None; 3],
tekked: true, tekked: true,
modifiers: Vec::new(),
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,
@ -234,7 +233,6 @@ async fn new_character<EG: EntityGateway>(entity_gateway: &mut EG, user: &UserAc
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 0, slots: 0,
modifiers: Vec::new(),
}), }),
location: ItemLocation::Inventory { location: ItemLocation::Inventory {
character_id: character.id, character_id: character.id,

5
src/ship/drops/generic_armor.rs

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

5
src/ship/drops/generic_weapon.rs

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

2
src/ship/drops/rare_drop_table.rs

@ -104,7 +104,6 @@ impl RareDropTable {
grind: 0, grind: 0,
attrs: self.attribute_table.generate_rare_attributes(map_area, rng), attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
tekked: false, tekked: false,
modifiers: Vec::new(),
}) })
}, },
@ -114,7 +113,6 @@ impl RareDropTable {
dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8, dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8,
evp: self.armor_stats.evp_modifier(&armor, rng) as u8, evp: self.armor_stats.evp_modifier(&armor, rng) as u8,
slots: self.armor_stats.slots(map_area, rng) as u8, slots: self.armor_stats.slots(map_area, rng) as u8,
modifiers: Vec::new(),
}) })
}, },
RareDropItem::Shield(shield) => { RareDropItem::Shield(shield) => {

25
src/ship/items/inventory.rs

@ -5,6 +5,7 @@ use crate::entity::character::CharacterEntityId;
use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity}; use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity};
use crate::entity::item::tool::Tool; use crate::entity::item::tool::Tool;
use crate::entity::item::mag::Mag; use crate::entity::item::mag::Mag;
use crate::entity::item::weapon::Weapon;
use crate::ship::items::{ClientItemId, BankItem, BankItemHandle}; use crate::ship::items::{ClientItemId, BankItem, BankItemHandle};
use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem};
@ -30,6 +31,13 @@ impl IndividualInventoryItem {
} }
} }
pub fn weapon(&self) -> Option<&Weapon> {
match self.item {
ItemDetail::Weapon(ref weapon) => Some(weapon),
_ => None
}
}
pub fn mag_mut(&mut self) -> Option<&mut Mag> { pub fn mag_mut(&mut self) -> Option<&mut Mag> {
match self.item { match self.item {
ItemDetail::Mag(ref mut mag) => Some(mag), ItemDetail::Mag(ref mut mag) => Some(mag),
@ -198,7 +206,14 @@ impl InventoryItem {
Ok(()) Ok(())
} }
pub fn individual(&mut self) -> Option<&mut IndividualInventoryItem> {
pub fn individual(&self) -> Option<&IndividualInventoryItem> {
match self {
InventoryItem::Individual(ref individual_inventory_item) => Some(individual_inventory_item),
_ => None
}
}
pub fn individual_mut(&mut self) -> Option<&mut IndividualInventoryItem> {
match self { match self {
InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item), InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item),
_ => None _ => None
@ -640,6 +655,14 @@ impl CharacterInventory {
self.items = sorted_items; self.items = sorted_items;
} }
pub fn remove_by_id(&mut self, id: ClientItemId) -> Option<InventoryItem> {
self.items.iter()
.position(|i| i.item_id() == id)
.map(|position| {
self.items.remove(position)
})
}
pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) { pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) {
for item in &self.items { for item in &self.items {
if let InventoryItem::Individual(inventory_item) = item { if let InventoryItem::Individual(inventory_item) = item {

37
src/ship/items/manager.rs

@ -7,6 +7,7 @@ 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};
use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::unit; use crate::entity::item::unit;
use crate::entity::item::weapon;
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation; use crate::ship::ship::ItemDropLocation;
use crate::ship::drops::{ItemDrop, ItemDropType}; use crate::ship::drops::{ItemDrop, ItemDropType};
@ -611,7 +612,7 @@ impl ItemManager {
let individual_item = mag_handle.item_mut() let individual_item = mag_handle.item_mut()
.ok_or(ItemManagerError::NoSuchItemId(mag_id))? .ok_or(ItemManagerError::NoSuchItemId(mag_id))?
.individual()
.individual_mut()
.ok_or(ItemManagerError::WrongItemType(mag_id))?; .ok_or(ItemManagerError::WrongItemType(mag_id))?;
let mag = individual_item let mag = individual_item
.mag_mut() .mag_mut()
@ -888,4 +889,38 @@ impl ItemManager {
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(()) Ok(())
} }
pub async fn replace_item_with_tekked<EG: EntityGateway>(&mut self,
entity_gateway: &mut EG,
character: &CharacterEntity,
item_id: ClientItemId,
tek: weapon::WeaponModifier)
-> Result<weapon::Weapon, anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let item = inventory.remove_by_id(item_id)
.ok_or(ItemManagerError::NoSuchItemId(item_id))?;
let individual = item
.individual()
.ok_or(ItemManagerError::WrongItemType(item_id))?;
let entity_id = individual.entity_id;
let mut weapon = individual
.weapon()
.ok_or(ItemManagerError::WrongItemType(item_id))?
.clone();
weapon.apply_modifier(&tek);
entity_gateway.add_weapon_modifier(&entity_id, tek).await?;
inventory.add_item(InventoryItem::Individual(IndividualInventoryItem {
entity_id: entity_id,
item_id: item_id,
item: ItemDetail::Weapon(weapon.clone()),
}));
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(weapon)
}
} }

2
src/ship/items/use_tool.rs

@ -52,7 +52,7 @@ async fn mag_cell<EG: EntityGateway>(entity_gateway: &mut EG, used_cell: &Consum
let mag_item = mag_handle.item_mut() let mag_item = mag_handle.item_mut()
.ok_or(UseItemError::InvalidItem)?; .ok_or(UseItemError::InvalidItem)?;
let actual_mag = mag_item let actual_mag = mag_item
.individual()
.individual_mut()
.ok_or(UseItemError::InvalidItem)? .ok_or(UseItemError::InvalidItem)?
.mag_mut() .mag_mut()
.ok_or(UseItemError::InvalidItem)?; .ok_or(UseItemError::InvalidItem)?;

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

@ -1,5 +1,6 @@
use libpso::packet::messages::*; use libpso::packet::messages::*;
use libpso::packet::ship::*; use libpso::packet::ship::*;
use crate::entity::item;
use crate::common::leveltable::CharacterStats; use crate::common::leveltable::CharacterStats;
use crate::ship::ship::{ShipError}; use crate::ship::ship::{ShipError};
use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank};
@ -26,13 +27,13 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result<ItemDr
}) })
} }
pub fn create_item(area_client: AreaClient, item: &FloorItem) -> Result<CreateItem, ShipError> {
pub fn create_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result<CreateItem, ShipError> {
let bytes = item.as_client_bytes(); let bytes = item.as_client_bytes();
Ok(CreateItem { Ok(CreateItem {
client: area_client.local_client.id(), client: area_client.local_client.id(),
target: 0, target: 0,
item_data: bytes[0..12].try_into()?, item_data: bytes[0..12].try_into()?,
item_id: item.item_id().0,
item_id: item_id.0,
item_data2: bytes[12..16].try_into()?, item_data2: bytes[12..16].try_into()?,
unknown: 0, unknown: 0,
}) })
@ -162,3 +163,14 @@ pub fn shop_list<I: ShopItem>(shop_type: u8, items: &Vec<I>) -> ShopList {
items: items, items: items,
} }
} }
pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> Result<TekPreview, ShipError> {
let bytes = weapon.as_bytes();
Ok(TekPreview {
client: 0x79,
target: 0,
item_bytes: bytes[0..12].try_into()?,
item_id: id.0,
item_bytes2: bytes[12..16].try_into()?,
})
}

109
src/ship/packet/handler/direct_message.rs

@ -1,4 +1,6 @@
use log::warn; use log::warn;
use rand::Rng;
use rand::seq::SliceRandom;
use libpso::packet::ship::*; use libpso::packet::ship::*;
use libpso::packet::messages::*; use libpso::packet::messages::*;
use crate::common::leveltable::CharacterLevelTable; use crate::common::leveltable::CharacterLevelTable;
@ -6,8 +8,9 @@ use crate::common::serverstate::ClientId;
use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops};
use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::location::{ClientLocation, ClientLocationError};
use crate::ship::drops::ItemDrop; use crate::ship::drops::ItemDrop;
use crate::ship::items::{ItemManager, ClientItemId, TriggerCreateItem, FloorItem, FloorType};
use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType};
use crate::entity::gateway::EntityGateway; use crate::entity::gateway::EntityGateway;
use crate::entity::item;
use libpso::utf8_to_utf16_array; use libpso::utf8_to_utf16_array;
use crate::ship::packet::builder; use crate::ship::packet::builder;
use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem};
@ -24,6 +27,13 @@ const BANK_MESETA_CAPACITY: u32 = 999999;
//const BANK_ACTION_: u8 = 1; //const BANK_ACTION_: u8 = 1;
#[derive(thiserror::Error, Debug)]
#[error("")]
pub enum MessageError {
InvalidTek(ClientItemId),
MismatchedTekIds(ClientItemId, ClientItemId),
}
fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation) fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation)
-> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> { -> Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send> {
Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter()
@ -127,8 +137,10 @@ where
let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?; let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?;
let remove_item = builder::message::remove_item_from_floor(area_client, &item)?; let remove_item = builder::message::remove_item_from_floor(area_client, &item)?;
let create_item = match item { let create_item = match item {
FloorItem::Individual(individual_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &individual_floor_item.item)?),
FloorItem::Stacked(stacked_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &item::ItemDetail::Tool(stacked_floor_item.tool))?),
FloorItem::Meseta(_) => None, FloorItem::Meseta(_) => None,
_ => Some(builder::message::create_item(area_client, &item)?),
//_ => Some(builder::message::create_item(area_client, &item)?),
}; };
match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await { match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await {
@ -393,3 +405,96 @@ where
}))) })))
} }
const TEK_SPECIAL_MODIFIER: [item::weapon::TekSpecialModifier; 3] = [item::weapon::TekSpecialModifier::Plus,
item::weapon::TekSpecialModifier::Neutral,
item::weapon::TekSpecialModifier::Minus];
const TEK_PERCENT_MODIFIER: [item::weapon::TekPercentModifier; 5] = [item::weapon::TekPercentModifier::PlusPlus,
item::weapon::TekPercentModifier::Plus,
item::weapon::TekPercentModifier::Neutral,
item::weapon::TekPercentModifier::Minus,
item::weapon::TekPercentModifier::MinusMinus];
pub async fn request_tek_item<EG>(id: ClientId,
tek_request: &TekRequest,
entity_gateway: &mut EG,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where
EG: EntityGateway
{
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let (grind_mod, special_mod, percent_mod) = {
let mut rng = rand::thread_rng();
let grind_mod = rng.gen_range(-4, 4);
let special_mod = TEK_SPECIAL_MODIFIER.choose(&mut rng).cloned().unwrap();
let percent_mod = TEK_PERCENT_MODIFIER.choose(&mut rng).cloned().unwrap();
(grind_mod, special_mod, percent_mod)
};
client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod));
let inventory = item_manager.get_character_inventory(&client.character)?;
let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id))
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?;
let mut weapon = item.individual()
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
.weapon()
.ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?
.clone();
weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked {
special: special_mod,
percent: percent_mod,
grind: grind_mod,
});
client.character.meseta -= 100;
entity_gateway.save_character(&client.character).await?;
let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?;
Ok(Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))].into_iter()))
}
pub async fn accept_tek_item<EG>(id: ClientId,
tek_accept: &TekAccept,
entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &mut Clients,
item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where
EG: EntityGateway
{
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek {
if item_id.0 != tek_accept.item_id {
return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into());
}
let modifier = item::weapon::WeaponModifier::Tekked {
special: special_mod,
percent: percent_mod,
grind: grind_mod,
};
let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?;
let create_item_pkt = builder::message::create_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?;
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
Ok(Box::new(neighbors.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone()))))
})))
}
else {
Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into())
}
}

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

@ -114,7 +114,7 @@ pub fn drop_coordinates(id: ClientId,
Ok(Box::new(None.into_iter())) Ok(Box::new(None.into_iter()))
} }
pub async fn split_item_stack<EG>(id: ClientId,
pub async fn no_longer_has_item<EG>(id: ClientId,
no_longer_has_item: &PlayerNoLongerHasItem, no_longer_has_item: &PlayerNoLongerHasItem,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation, client_location: &ClientLocation,
@ -127,8 +127,7 @@ where
let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?;
let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?;
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
let drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?;
if let Some(drop_location) = client.item_drop_location {
if drop_location.item_id.0 != no_longer_has_item.item_id { if drop_location.item_id.0 != no_longer_has_item.item_id {
return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into()); return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into());
} }
@ -157,6 +156,18 @@ where
(c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone())))) (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone()))))
}))) })))
} }
}
else if let Some(_tek) = client.tek {
let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?;
let no_longer_has_item = no_longer_has_item.clone();
Ok(Box::new(neighbors.into_iter()
.map(move |c| {
(c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_item.clone()))))
})))
}
else {
Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into())
}
} }
pub fn update_player_position(id: ClientId, pub fn update_player_position(id: ClientId,

21
src/ship/ship.rs

@ -23,6 +23,7 @@ use crate::login::character::SHIP_MENU_ID;
use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::gateway::{EntityGateway, GatewayError};
use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity};
use crate::entity::character::{CharacterEntity, SectionID}; use crate::entity::character::{CharacterEntity, SectionID};
use crate::entity::item;
use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError}; use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError};
@ -71,6 +72,7 @@ pub enum ShipError {
UnknownMonster(crate::ship::monster::MonsterType), UnknownMonster(crate::ship::monster::MonsterType),
InvalidShip(usize), InvalidShip(usize),
InvalidBlock(usize), InvalidBlock(usize),
InvalidItem(items::ClientItemId),
} }
#[derive(Debug)] #[derive(Debug)]
@ -254,6 +256,7 @@ pub struct ClientState {
pub weapon_shop: Vec<WeaponShopItem>, pub weapon_shop: Vec<WeaponShopItem>,
pub tool_shop: Vec<ToolShopItem>, pub tool_shop: Vec<ToolShopItem>,
pub armor_shop: Vec<ArmorShopItem>, pub armor_shop: Vec<ArmorShopItem>,
pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>,
} }
impl ClientState { impl ClientState {
@ -273,6 +276,7 @@ impl ClientState {
weapon_shop: Vec::new(), weapon_shop: Vec::new(),
tool_shop: Vec::new(), tool_shop: Vec::new(),
armor_shop: Vec::new(), armor_shop: Vec::new(),
tek: None,
} }
} }
} }
@ -445,7 +449,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
}, },
GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => {
let block = self.blocks.with_client(id, &self.clients)?; let block = self.blocks.with_client(id, &self.clients)?;
handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) |
GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) |
@ -490,41 +494,40 @@ impl<EG: EntityGateway> ShipServerState<EG> {
async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> { async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> {
let target = msg.flag; let target = msg.flag;
let block = self.blocks.with_client(id, &self.clients)?;
Ok(match &msg.msg { Ok(match &msg.msg {
GameMessage::GuildcardSend(guildcard_send) => { GameMessage::GuildcardSend(guildcard_send) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients) handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
}, },
GameMessage::RequestItem(request_item) => { GameMessage::RequestItem(request_item) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
GameMessage::PickupItem(pickup_item) => { GameMessage::PickupItem(pickup_item) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::BoxDropRequest(box_drop_request) => { GameMessage::BoxDropRequest(box_drop_request) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
GameMessage::BankRequest(_bank_request) => { GameMessage::BankRequest(_bank_request) => {
handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await? handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await?
}, },
GameMessage::BankInteraction(bank_interaction) => { GameMessage::BankInteraction(bank_interaction) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::ShopRequest(shop_request) => { GameMessage::ShopRequest(shop_request) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await?
}, },
GameMessage::BuyItem(buy_item) => { GameMessage::BuyItem(buy_item) => {
let block = self.blocks.with_client(id, &self.clients)?;
handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::TekRequest(tek_request) => {
handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
},
GameMessage::TekAccept(tek_accept) => {
handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await?
},
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();
let block = self.blocks.with_client(id, &self.clients)?;
Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter() Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()
.filter(move |client| client.local_client.id() == target as u8) .filter(move |client| client.local_client.id() == target as u8)
.map(move |client| { .map(move |client| {

1
src/ship/shops/armor.rs

@ -71,7 +71,6 @@ impl ShopItem for ArmorShopItem {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: *slot as u8, slots: *slot as u8,
modifiers: Vec::new()
}) })
}, },
ArmorShopItem::Barrier(barrier) => { ArmorShopItem::Barrier(barrier) => {

1
src/ship/shops/weapon.rs

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

15
tests/test_bank.rs

@ -27,7 +27,6 @@ async fn test_bank_items_sent_in_character_login() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -69,7 +68,6 @@ async fn test_request_bank_items() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -167,7 +165,6 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -196,7 +193,6 @@ async fn test_request_bank_items_sorted() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -247,7 +243,6 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -263,7 +258,6 @@ async fn test_deposit_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -623,7 +617,6 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -642,7 +635,6 @@ async fn test_deposit_individual_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -722,7 +714,6 @@ async fn test_deposit_stacked_item_in_full_bank() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -819,7 +810,6 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -998,7 +988,6 @@ async fn test_withdraw_individual_item() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1358,7 +1347,6 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Bank { location: item::ItemLocation::Bank {
@ -1378,7 +1366,6 @@ async fn test_withdraw_individual_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1454,7 +1441,6 @@ async fn test_withdraw_stacked_item_in_full_inventory() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -1537,7 +1523,6 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

3
tests/test_item_actions.rs

@ -25,7 +25,6 @@ async fn test_equip_unit_from_equip_menu() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
modifiers: Vec::new(),
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -112,7 +111,6 @@ async fn test_unequip_armor_with_units() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
modifiers: Vec::new(),
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,
@ -190,7 +188,6 @@ async fn test_sort_items() {
dfp: 0, dfp: 0,
evp: 0, evp: 0,
slots: 4, slots: 4,
modifiers: Vec::new(),
}), }),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
character_id: char1.id, character_id: char1.id,

4
tests/test_item_pickup.rs

@ -172,7 +172,6 @@ async fn test_pick_up_meseta_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -252,7 +251,6 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -345,7 +343,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {
@ -364,7 +361,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() {
special: None, special: None,
attrs: [None, None, None], attrs: [None, None, None],
tekked: true, tekked: true,
modifiers: Vec::new(),
} }
), ),
location: item::ItemLocation::Inventory { location: item::ItemLocation::Inventory {

2
tests/test_rooms.rs

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

Loading…
Cancel
Save