Browse Source

killcounters, redbox mode, failing unseal test, and random debug strings

kill_counters
andy 2 years ago
parent
commit
117a2daa31
  1. 11
      src/ship/drops/mod.rs
  2. 10
      src/ship/drops/rare_drop_table.rs
  3. 10
      src/ship/map/maps.rs
  4. 16
      src/ship/packet/handler/direct_message.rs
  5. 6
      src/ship/room.rs
  6. 1
      src/ship/ship.rs
  7. 37
      tests/common.rs
  8. 6
      tests/test_item_actions.rs
  9. 131
      tests/test_unseal_items.rs

11
src/ship/drops/mod.rs

@ -186,6 +186,10 @@ impl<R: Rng + SeedableRng> DropTable<R> {
pub fn get_box_drop(&mut self, map_area: &MapArea, object: &MapObject) -> Option<ItemDropType> {
self.box_table.get_drop(map_area, object, &mut self.rng)
}
pub fn get_rare_drop(&mut self, map_area: &MapArea, monster: &MonsterType) -> Option<ItemDropType> {
self.rare_table.get_rare_drop(map_area, monster, &mut self.rng)
}
}
@ -205,4 +209,11 @@ mod test {
.into_iter().choose(&mut rng).unwrap();
DropTable::<rand_chacha::ChaCha20Rng>::new(episode, difficulty, section_id);
}
#[test]
fn test_sjs_drop() {
let mut drop_table = DropTable::<rand_chacha::ChaCha20Rng>::new(Episode::Two, Difficulty::Ultimate, SectionID::Skyly);
let drop = drop_table.get_rare_drop(&MapArea::Seaside, &MonsterType::GiGue);
println!("drop: {:?}", drop);
}
}

10
src/ship/drops/rare_drop_table.rs

@ -167,4 +167,14 @@ impl RareDropTable {
}).next()
})
}
pub fn get_rare_drop<R: Rng>(&self, map_area: &MapArea, monster: &MonsterType, rng: &mut R) -> Option<ItemDropType> {
self.rates.get(monster)
.and_then(|drop_rates| {
drop_rates.iter()
.filter_map(|drop_rate| {
Some(self.apply_item_stats(map_area, drop_rate.item, rng))
}).next()
})
}
}

10
src/ship/map/maps.rs

@ -320,6 +320,16 @@ impl Maps {
self.object_data = objects;
}
pub fn get_enemy_id_by_monster_type(&self, monster: MonsterType) -> Option<u16> {
let (id, _) = self.enemy_data
.iter()
.enumerate()
.filter(|(_i, &m)| m.is_some())
.find(|(_i, &m)| m.unwrap().monster == monster)?;
Some(id as u16)
}
pub fn get_rare_monster_list(&self) -> Vec<u16> {
let mut rare_monsters = vec![0xFFFF; 16];
let shiny: Vec<(usize, &Option<MapEnemy>)> = self.enemy_data.iter()

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

@ -79,6 +79,7 @@ pub async fn request_item<EG>(id: ClientId,
where
EG: EntityGateway
{
println!("src::ship::packet::handler::request_item() - requesting an item!");
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))?
@ -86,7 +87,9 @@ where
.ok_or(ShipError::InvalidRoom(room_id.0 as u32))?;
let monster = room.maps.enemy_by_id(request_item.enemy_id as usize)?;
println!("room id: {:?}, monster: {:?}", room_id, monster);
if monster.dropped_item {
println!("monster {:?} already dropped an item!", monster);
return Err(ShipError::MonsterAlreadyDroppedItem(id, request_item.enemy_id).into())
}
@ -94,9 +97,16 @@ where
let client_and_drop = clients_in_area.into_iter()
.filter_map(|area_client| {
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
(area_client, item_drop_type)
})
if room.redbox {
println!("red box mode is currently enabled. dropping red box!");
room.drop_table.get_rare_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
(area_client, item_drop_type)
})
} else {
room.drop_table.get_drop(&monster.map_area, &monster.monster).map(|item_drop_type| {
(area_client, item_drop_type)
})
}
});
let mut item_drop_packets = Vec::new();

6
src/ship/room.rs

@ -203,6 +203,7 @@ pub struct RoomState {
pub rare_monster_table: Box<RareMonsterAppearTable>,
pub quest_group: QuestCategoryType,
pub quests: Vec<quests::QuestList>,
pub redbox: bool,
// items on ground
// enemy info
}
@ -316,6 +317,11 @@ impl RoomState {
map_areas: MapAreaLookup::new(&room_mode.episode()),
quest_group: QuestCategoryType::Standard,
quests: room_quests,
redbox: false,
})
}
pub fn toggle_redbox_mode(&mut self) {
self.redbox = !self.redbox;
}
}

1
src/ship/ship.rs

@ -542,6 +542,7 @@ impl<EG: EntityGateway> ShipServerState<EG> {
handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients)
},
GameMessage::RequestItem(request_item) => {
println!("someone requested an item from the ship!");
handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await?
},
GameMessage::PickupItem(pickup_item) => {

37
tests/common.rs

@ -3,8 +3,8 @@
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::EntityGateway;
use elseware::entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity};
use elseware::entity::character::{CharacterEntity, NewCharacterEntity};
use elseware::entity::item::{Meseta, BankName};
use elseware::entity::character::{CharacterEntity, NewCharacterEntity, SectionID};
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use elseware::ship::room::Difficulty;
@ -34,6 +34,26 @@ pub async fn new_user_character<EG: EntityGateway>(entity_gateway: &mut EG, user
(user, character)
}
pub async fn new_user_character_with_sid<EG: EntityGateway>(entity_gateway: &mut EG, username: &str, password: &str, sid: SectionID) -> (UserAccountEntity, CharacterEntity) {
let new_user = NewUserAccountEntity {
email: format!("{}@pso.com", username),
username: username.into(),
password: bcrypt::hash(password, 5).unwrap(),
guildcard: 1,
activated: true,
..NewUserAccountEntity::default()
};
let user = entity_gateway.create_user(new_user).await.unwrap();
let new_settings = NewUserSettingsEntity::new(user.id);
let _settings = entity_gateway.create_user_settings(new_settings).await.unwrap();
let mut new_character = NewCharacterEntity::new(user.id);
new_character.section_id = sid;
let character = entity_gateway.create_character(new_character).await.unwrap();
(user, character)
}
pub async fn log_in_char<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, username: &str, password: &str) {
let username = username.to_string();
let password = password.to_string();
@ -84,6 +104,21 @@ pub async fn create_room_with_difficulty<EG: EntityGateway>(ship: &mut ShipServe
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
}
pub async fn create_ep2_room_with_difficulty<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, name: &str, password: &str, difficulty: Difficulty) {
ship.handle(id, &RecvShipPacket::CreateRoom(CreateRoom {
unknown: [0; 2],
name: utf8_to_utf16_array!(name, 16),
password: utf8_to_utf16_array!(password, 16),
difficulty: difficulty.into(),
battle: 0,
challenge: 0,
episode: 2,
single_player: 0,
padding: [0; 3],
})).await.unwrap().for_each(drop);
ship.handle(id, &RecvShipPacket::DoneBursting(DoneBursting {})).await.unwrap().for_each(drop);
}
pub async fn join_room<EG: EntityGateway>(ship: &mut ShipServerState<EG>, id: ClientId, room_id: u32) {
ship.handle(id, &RecvShipPacket::MenuSelect(MenuSelect {
menu: ROOM_MENU_ID,

6
tests/test_item_actions.rs

@ -34,6 +34,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
kills: None,
}),
}).await.unwrap());
@ -43,6 +44,7 @@ async fn test_equip_unit_from_equip_menu() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
kills: None,
}),
}).await.unwrap());
@ -111,6 +113,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
kills: None,
}),
}).await.unwrap());
@ -120,6 +123,7 @@ async fn test_unequip_armor_with_units() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
kills: None,
}),
}).await.unwrap());
@ -179,6 +183,7 @@ async fn test_sort_items() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: None,
kills: None,
}),
}).await.unwrap());
@ -188,6 +193,7 @@ async fn test_sort_items() {
item::unit::Unit{
unit: item::unit::UnitType::KnightPower,
modifier: Some(item::unit::UnitModifier::Plus),
kills: None,
}),
}).await.unwrap());

131
tests/test_unseal_items.rs

@ -0,0 +1,131 @@
/* 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:
- client item id does not change
- unsealed item no longer has kill counter
5. test reject unsealing item if not enough kills (can this even happen?)
*/
use elseware::common::serverstate::{ClientId, ServerState};
use elseware::entity::gateway::{EntityGateway, InMemoryGateway};
use elseware::entity::item;
use elseware::ship::ship::{ShipServerState, RecvShipPacket};
use elseware::entity::character::{SectionID};
use elseware::ship::room::{Difficulty};
use elseware::ship::map::area::{MapArea};
use elseware::ship::monster::{MonsterType};
use libpso::packet::ship::*;
use libpso::packet::messages::*;
#[path = "common.rs"]
mod common;
use common::*;
#[async_std::test]
async fn test_item_drops_with_kill_counter() {
let mut entity_gateway = InMemoryGateway::default();
let (_user1, _char1) = new_user_character_with_sid(&mut entity_gateway, "a1", "a", SectionID::Skyly).await;
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;
// ship.handle(ClientId(1), &RecvShipPacket()).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerWarpingToFloor(PlayerWarpingToFloor{
// client: 0,
// target: 0,
// area: 9, // seaside
// data: 0,
// })))).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerWarping(PlayerWarping{
// client: 0,
// target: 0,
// })))).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerLoadedIn(PlayerLoadedIn{
// client: 0,
// target: 0,
// unknown1: [0,0],
// rotation: 0,
// area: 9,
// room: 1,
// x: 100.0,
// y: 10.0,
// z: -20.0,
// })))).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerChangedMap2(PlayerChangedMap2{
// client: 0,
// target: 0,
// map_area: 9,
// _unknown1: 0,
// })))).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerSpawnedIntoArea(PlayerSpawnedIntoArea{
// client: 0,
// target: 0,
// })))).await.unwrap().for_each(drop);
// ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerDoneChangingMap(PlayerDoneChangingMap{
// client: 0,
// target: 0,
// })))).await.unwrap().for_each(drop);
let room = ship.blocks.0[0].rooms[0].as_mut().unwrap();
room.toggle_redbox_mode(); // enable redbox mode for sjs
println!("room redbox mode: {:?}", room.redbox);
println!("room.mode: {:?}", room.mode);
println!("killing gigue for sjs!");
let gigue_id = room.maps.get_enemy_id_by_monster_type(MonsterType::GiGue).unwrap();
println!("found gigue id: {:?}!", gigue_id);
let pkts = ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::RequestItem(RequestItem {
client: 0,
target: 0,
map_area: 9, // seaside
pt_index: 55, // gigue ?
enemy_id: gigue_id,
x: 0.0,
y: 0.0,
z: 0.0,
}))))
.await
.unwrap()
.collect::<Vec<_>>(); // this should return 1 packet (ItemDrop)?
println!("packets returned: {:?}", pkts);
assert!(false);
}
// #[async_std::test]
// async fn test_all_equipped_kill_counters_increase_per_kill() {
// let mut entity_gateway = InMemoryGateway::default();
// let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
// }
// #[async_std::test]
// async fn test_non_equipped_kill_counter_does_not_increase() {
// let mut entity_gateway = InMemoryGateway::default();
// let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
// }
// #[async_std::test]
// async fn test_kill_counter_increase_only_for_final_hit() { // don't share kills among players
// let mut entity_gateway = InMemoryGateway::default();
// let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a").await;
// }
Loading…
Cancel
Save