Browse Source

actually add and use AddKill modifier and remove dead code

kill_counters
andy 2 years ago
parent
commit
2d5b6c5c01
  1. 6
      src/entity/gateway/entitygateway.rs
  2. 17
      src/entity/gateway/inmemory.rs
  3. 17
      src/entity/item/mod.rs
  4. 20
      src/entity/item/unit.rs
  5. 99
      src/entity/item/weapon.rs
  6. 27
      src/ship/items/manager.rs
  7. 15
      src/ship/packet/handler/message.rs
  8. 3
      src/ship/ship.rs
  9. 49
      tests/test_unseal_items.rs

6
src/entity/gateway/entitygateway.rs

@ -85,6 +85,9 @@ pub trait EntityGateway: Send + Sync + Clone {
unimplemented!(); unimplemented!();
} }
async fn add_unit_modifier(&mut self, _item_id: &ItemEntityId, _modifier: unit::UnitModifier) -> 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> {
@ -131,7 +134,4 @@ pub trait EntityGateway: Send + Sync + Clone {
async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> { async fn set_bank_meseta(&mut self, _char_id: &CharacterEntityId, _bank: BankName, _amount: Meseta) -> Result<(), GatewayError> {
unimplemented!(); unimplemented!();
} }
async fn increment_kill_counter(&mut self, _item_entity_id: &ItemEntityId) -> Result<(), GatewayError> {
unimplemented!();
}
} }

17
src/entity/gateway/inmemory.rs

@ -21,6 +21,7 @@ pub struct InMemoryGateway {
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>>>>, weapon_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<weapon::WeaponModifier>>>>,
unit_modifiers: Arc<Mutex<BTreeMap<ItemEntityId, Vec<unit::UnitModifier>>>>,
} }
impl Default for InMemoryGateway { impl Default for InMemoryGateway {
@ -37,6 +38,7 @@ impl Default for InMemoryGateway {
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())), weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
unit_modifiers: Arc::new(Mutex::new(BTreeMap::new())),
} }
} }
} }
@ -271,6 +273,14 @@ impl EntityGateway for InMemoryGateway {
Ok(()) 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_with(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> {
let inventories = self.inventories.lock().unwrap(); let inventories = self.inventories.lock().unwrap();
Ok(inventories Ok(inventories
@ -349,11 +359,4 @@ impl EntityGateway for InMemoryGateway {
Err(GatewayError::Error) Err(GatewayError::Error)
} }
} }
async fn increment_kill_counter(&mut self, item_id: &ItemEntityId) -> Result<(), GatewayError> {
if let Some(item_entity) = self.items.lock().unwrap().get_mut(item_id) {
item_entity.item.increment_kill_counter();
}
Ok(())
}
} }

17
src/entity/item/mod.rs

@ -175,27 +175,14 @@ impl ItemDetail {
ItemDetail::Weapon(w) => w.kills.is_some(), ItemDetail::Weapon(w) => w.kills.is_some(),
ItemDetail::Armor(_a) => false, ItemDetail::Armor(_a) => false,
ItemDetail::Shield(_s) => false, ItemDetail::Shield(_s) => false,
// ItemDetail::Unit(u) => u.kills.is_some(),
ItemDetail::Unit(_u) => false,
ItemDetail::Unit(u) => u.kills.is_some(),
// ItemDetail::Unit(_u) => false,
ItemDetail::Tool(_t) => false, ItemDetail::Tool(_t) => false,
ItemDetail::TechniqueDisk(_d) => false, ItemDetail::TechniqueDisk(_d) => false,
ItemDetail::Mag(_m) => false, ItemDetail::Mag(_m) => false,
ItemDetail::ESWeapon(_e) => false, ItemDetail::ESWeapon(_e) => false,
} }
} }
pub fn increment_kill_counter(&mut self) {
match self {
ItemDetail::Weapon(w) => {w.increment_kill_counter()},
ItemDetail::Armor(_a) => {},
ItemDetail::Shield(_s) => {},
ItemDetail::Unit(u) => {u.increment_kill_counter()},
ItemDetail::Tool(_t) => {},
ItemDetail::TechniqueDisk(_d) => {},
ItemDetail::Mag(_m) => {},
ItemDetail::ESWeapon(_e) => {},
}
}
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]

20
src/entity/item/unit.rs

@ -1,4 +1,6 @@
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
use crate::ship::monster::MonsterType;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub enum ItemParseError { pub enum ItemParseError {
@ -333,6 +335,10 @@ pub enum UnitModifier {
Plus, Plus,
Minus, Minus,
MinusMinus, MinusMinus,
AddKill {
enemy: MonsterType,
// attack: u32, // maybe one day for TURBO logging?
},
} }
#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
@ -364,6 +370,7 @@ impl Unit {
result[6] = 0xFE; result[6] = 0xFE;
result[7] = 0xFF; result[7] = 0xFF;
}, },
_ => {},
} }
} }
if self.unit.has_counter() { if self.unit.has_counter() {
@ -472,9 +479,14 @@ impl Unit {
} }
} }
pub fn increment_kill_counter(&mut self) {
if let Some(kills) = self.kills {
self.kills = Some(kills + 1);
}
pub fn apply_modifier(&mut self, modifier: &UnitModifier) {
match modifier {
UnitModifier::AddKill{enemy: _} => {
if let Some(kills) = self.kills {
self.kills = Some(kills + 1);
}
},
_ => {},
};
} }
} }

99
src/entity/item/weapon.rs

@ -1,4 +1,5 @@
use crate::entity::item::ItemEntityId; use crate::entity::item::ItemEntityId;
use crate::ship::monster::MonsterType;
use serde::{Serialize, Deserialize}; use serde::{Serialize, Deserialize};
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -1459,6 +1460,10 @@ pub enum WeaponModifier {
percent: TekPercentModifier, percent: TekPercentModifier,
grind: i32, grind: i32,
}, },
AddKill {
enemy: MonsterType,
// attack: u32, // maybe one day for TURBO logging?
},
} }
#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize)]
@ -1484,46 +1489,58 @@ impl Weapon {
} }
} }
// TODO: apply other modifiers
pub fn apply_modifier(&mut self, modifier: &WeaponModifier) { pub fn apply_modifier(&mut self, modifier: &WeaponModifier) {
if let WeaponModifier::Tekked{special, percent, grind} = modifier {
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 => {
match modifier {
WeaponModifier::AddPercents{attr: _, pds: _} => {},
WeaponModifier::AddGrind{amount: _, grinder: _} => {},
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;
}
attr
});
}
self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8;
self.tekked = true;
},
WeaponModifier::AddKill{enemy: _} => {
if let Some(kills) = self.kills {
println!("{:?} currently has {:?} kills", self.weapon, self.kills);
self.kills = Some(kills + 1);
println!("now {:?} has {:?} kills", self.weapon, self.kills);
}
},
};
} }
pub fn as_bytes(&self) -> [u8; 16] { pub fn as_bytes(&self) -> [u8; 16] {
@ -1671,10 +1688,4 @@ impl Weapon {
| WeaponType::Scepter | WeaponType::Scepter
) )
} }
pub fn increment_kill_counter(&mut self) {
if let Some(kills) = self.kills {
self.kills = Some(kills + 1);
}
}
} }

27
src/ship/items/manager.rs

@ -8,7 +8,8 @@ use crate::entity::character::{CharacterEntity, CharacterEntityId, TechLevel};
use crate::entity::item::{ItemDetail, ItemNote, BankName}; use crate::entity::item::{ItemDetail, ItemNote, BankName};
use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity, EquippedEntity, ItemEntityId}; use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, BankItemEntity, EquippedEntity, ItemEntityId};
use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::tool::{Tool, ToolType};
use crate::entity::item::weapon;
use crate::entity::item::weapon::{Weapon, WeaponModifier};
use crate::entity::item::unit::UnitModifier;
use crate::ship::map::MapArea; use crate::ship::map::MapArea;
use crate::ship::ship::ItemDropLocation; use crate::ship::ship::ItemDropLocation;
use crate::ship::trade::TradeItem; use crate::ship::trade::TradeItem;
@ -22,6 +23,7 @@ use crate::ship::items::floor::*;
use crate::ship::items::inventory::*; use crate::ship::items::inventory::*;
use crate::ship::items::use_tool; use crate::ship::items::use_tool;
use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError}; use crate::ship::items::transaction::{ItemTransaction, ItemAction, TransactionError, TransactionCommitError};
use crate::ship::monster::MonsterType;
#[derive(PartialEq, Eq)] #[derive(PartialEq, Eq)]
pub enum FloorType { pub enum FloorType {
@ -984,8 +986,8 @@ impl ItemManager {
entity_gateway: &mut EG, entity_gateway: &mut EG,
character: &CharacterEntity, character: &CharacterEntity,
item_id: ClientItemId, item_id: ClientItemId,
tek: weapon::WeaponModifier)
-> Result<weapon::Weapon, anyhow::Error> {
tek: WeaponModifier)
-> Result<Weapon, anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
let item = inventory.remove_by_id(item_id) let item = inventory.remove_by_id(item_id)
@ -1194,20 +1196,11 @@ impl ItemManager {
.map_err(|err| err.into()) .map_err(|err| err.into())
} }
pub async fn increase_kill_counters<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, equipped_items: &EquippedEntity) -> Result<(), anyhow::Error> {
pub async fn increase_kill_counters<EG: EntityGateway>(&mut self, entity_gateway: &mut EG, character: &CharacterEntity, equipped_items: &EquippedEntity, monstertype: MonsterType) -> Result<(), anyhow::Error> {
let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?;
if let Some(weapon_entity) = equipped_items.weapon { if let Some(weapon_entity) = equipped_items.weapon {
let weapon_id = inventory.get_item_by_entity_id(weapon_entity).ok_or(ItemManagerError::EntityIdNotInInventory(weapon_entity))?.item_id();
let mut weapon_handle = inventory.get_item_handle_by_id(weapon_id).ok_or(ItemManagerError::NoSuchItemId(weapon_id))?;
let individual_item_w = weapon_handle.item_mut()
.ok_or(ItemManagerError::NoSuchItemId(weapon_id))?
.individual_mut()
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
let weapon = individual_item_w
.weapon_mut()
.ok_or(ItemManagerError::WrongItemType(weapon_id))?;
weapon.increment_kill_counter();
let wmodifier = WeaponModifier::AddKill { enemy: monstertype };
entity_gateway.add_weapon_modifier(&weapon_entity, wmodifier).await?;
} }
for units in equipped_items.unit.iter().flatten() { for units in equipped_items.unit.iter().flatten() {
let unit_id = inventory.get_item_by_entity_id(*units).ok_or(ItemManagerError::EntityIdNotInInventory(*units))?.item_id(); let unit_id = inventory.get_item_by_entity_id(*units).ok_or(ItemManagerError::EntityIdNotInInventory(*units))?.item_id();
@ -1220,7 +1213,9 @@ impl ItemManager {
.unit_mut() .unit_mut()
.ok_or(ItemManagerError::WrongItemType(unit_id))?; .ok_or(ItemManagerError::WrongItemType(unit_id))?;
unit.increment_kill_counter();
let umodifier = UnitModifier::AddKill { enemy: monstertype };
unit.apply_modifier(&umodifier);
entity_gateway.add_unit_modifier(units, umodifier).await?;
} }
entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?;
Ok(()) Ok(())

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

@ -399,9 +399,11 @@ where
} }
pub async fn player_killed_monster<EG>( id: ClientId, pub async fn player_killed_monster<EG>( id: ClientId,
_pkt: &KillMonster, // use this later for turbo logging?
pkt: &KillMonster,
entity_gateway: &mut EG, entity_gateway: &mut EG,
client_location: &ClientLocation,
clients: &Clients, clients: &Clients,
rooms: &mut Rooms,
item_manager: &mut ItemManager) item_manager: &mut ItemManager)
-> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error> -> Result<Box<dyn Iterator<Item = (ClientId, SendShipPacket)> + Send>, anyhow::Error>
where where
@ -409,6 +411,13 @@ where
{ {
let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?; let client = clients.get(&id).ok_or(ShipError::ClientNotFound(id))?;
let equipped_items = entity_gateway.get_character_equips(&client.character.id).await?; let equipped_items = entity_gateway.get_character_equips(&client.character.id).await?;
item_manager.increase_kill_counters(entity_gateway, &client.character, &equipped_items).await?;
Ok(Box::new(None.into_iter()))
let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?;
let room = rooms.get_mut(room_id.0)
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?
.as_mut()
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
let enemy_id = u16::from_le_bytes([pkt.client, pkt.target]) & 0x0FFF;
let monstertype = room.maps.enemy_by_id(enemy_id as usize)?.monster;
item_manager.increase_kill_counters(entity_gateway, &client.character, &equipped_items, monstertype).await?;
Ok(Box::new(None.into_iter())) // TODO: forward to other clients in the room
} }

3
src/ship/ship.rs

@ -521,7 +521,8 @@ impl<EG: EntityGateway> ShipServerState<EG> {
handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? handler::message::player_sells_item(id, player_sold_item, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await?
}, },
GameMessage::KillMonster(kill_monster) => { GameMessage::KillMonster(kill_monster) => {
handler::message::player_killed_monster(id, kill_monster, &mut self.entity_gateway, &self.clients, &mut self.item_manager).await?
let block = self.blocks.with_client(id, &self.clients)?;
handler::message::player_killed_monster(id, kill_monster, &mut self.entity_gateway, &block.client_location, &self.clients, &mut block.rooms, &mut self.item_manager).await?
}, },
_ => { _ => {
let cmsg = msg.clone(); let cmsg = msg.clone();

49
tests/test_unseal_items.rs

@ -1,7 +1,4 @@
/* TODO: /* TODO:
1. test to check if sjs/lame/limiter drop with Some() kill counter enabled
2. test to make sure other items drop with None kill counter
3. test kill counters get incremented per kill
4. test unsealing item: 4. test unsealing item:
- client item id does not change - client item id does not change
- unsealed item no longer has kill counter - unsealed item no longer has kill counter
@ -24,7 +21,7 @@ mod common;
use common::*; use common::*;
#[async_std::test] #[async_std::test]
async fn test_item_drops_with_kill_counter() {
async fn test_sjs_drops_with_kill_counter() {
let mut entity_gateway = InMemoryGateway::default(); let mut entity_gateway = InMemoryGateway::default();
let (_user1, mut char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await; let (_user1, mut char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await;
@ -41,7 +38,7 @@ async fn test_item_drops_with_kill_counter() {
create_ep2_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; create_ep2_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
let room = ship.blocks.0[0].rooms[0].as_mut().unwrap(); let room = ship.blocks.0[0].rooms[0].as_mut().unwrap();
room.toggle_redbox_mode(); // enable redbox mode for sjs
room.toggle_redbox_mode(); // enable redbox mode
let gigue_id = room.maps.get_enemy_id_by_monster_type(MonsterType::GiGue).unwrap(); let gigue_id = room.maps.get_enemy_id_by_monster_type(MonsterType::GiGue).unwrap();
@ -65,6 +62,48 @@ async fn test_item_drops_with_kill_counter() {
} }
} }
#[async_std::test]
async fn test_other_weapons_drop_without_kill_counter() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, mut char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await;
char1.exp = 80000000;
entity_gateway.save_character(&char1).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_ep2_room_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await;
let room = ship.blocks.0[0].rooms[0].as_mut().unwrap();
room.toggle_redbox_mode(); // enable redbox mode
let enemy_id = room.maps.get_enemy_id_by_monster_type(MonsterType::Hildebear).unwrap();
let packets = ship.handle(ClientId(1), &RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem {
client: 0,
target: 0,
map_area: 1, // temple alpha
pt_index: 0, // TODO: this is going to break if pt_index ever gets properly used
enemy_id: enemy_id,
x: 0.0,
y: 0.0,
z: 0.0,
})))).await.unwrap().collect::<Vec<_>>();
assert!(packets.len() == 1);
match &packets[0].1 {
SendShipPacket::Message(Message {msg: GameMessage::ItemDrop(item_drop)}) => {
assert_ne!(item_drop.item_bytes[10], 0x80)
}
_ => panic!("Weapon didn't drop with the expected value! attr[2] should be less than 0x80 (128) because it shouldn't have a kill counter!")
}
}
#[async_std::test] #[async_std::test]
async fn test_all_equipped_kill_counters_increase_per_kill() { async fn test_all_equipped_kill_counters_increase_per_kill() {
let mut entity_gateway = InMemoryGateway::default(); let mut entity_gateway = InMemoryGateway::default();

Loading…
Cancel
Save