expsteal #107
| @ -137,9 +137,9 @@ fn main() { | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Vjaya, | ||||
|                             weapon: item::weapon::WeaponType::Ripper, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             special: Some(item::weapon::WeaponSpecial::Kings), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                                     None,], | ||||
| @ -153,7 +153,7 @@ fn main() { | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Vulcan, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             special: Some(item::weapon::WeaponSpecial::Kings), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                                     None,], | ||||
|  | ||||
| @ -1421,6 +1421,80 @@ impl WeaponType { | ||||
|            _ => Err(ItemParseError::InvalidWeaponType), | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn special_penalty(&self) -> f32 { | ||||
|         match(self) { | ||||
|             WeaponType::Saber => 0.0, | ||||
|             WeaponType::Brand => 0.0, | ||||
|             WeaponType::Buster => 0.0, | ||||
|             WeaponType::Pallasch => 0.0, | ||||
|             WeaponType::Gladius => 0.0, | ||||
|             WeaponType::Handgun => 0.0, | ||||
|             WeaponType::Autogun => 0.0, | ||||
|             WeaponType::Lockgun => 0.0, | ||||
|             WeaponType::Railgun => 0.0, | ||||
|             WeaponType::Raygun => 0.0, | ||||
|             WeaponType::Rifle => 0.0, | ||||
|             WeaponType::Sniper => 0.0, | ||||
|             WeaponType::Blaster => 0.0, | ||||
|             WeaponType::Beam => 0.0, | ||||
|             WeaponType::Laser => 0.0, | ||||
|             WeaponType::Cane => 0.0, | ||||
|             WeaponType::Stick => 0.0, | ||||
|             WeaponType::Mace => 0.0, | ||||
|             WeaponType::Club => 0.0, | ||||
|             WeaponType::Rod => 0.0, | ||||
|             WeaponType::Pole => 0.0, | ||||
|             WeaponType::Pillar => 0.0, | ||||
|             WeaponType::Striker => 0.0, | ||||
|             WeaponType::Wand => 0.0, | ||||
|             WeaponType::Staff => 0.0, | ||||
|             WeaponType::Baton => 0.0, | ||||
|             WeaponType::Scepter => 0.0, | ||||
| 
 | ||||
|             WeaponType::Sword => 0.5, | ||||
|             WeaponType::Gigush => 0.5, | ||||
|             WeaponType::Breaker => 0.5, | ||||
|             WeaponType::Claymore => 0.5, | ||||
|             WeaponType::Calibur => 0.5, | ||||
|             WeaponType::FlowensSword => 0.5, | ||||
|             WeaponType::LastSurvivor => 0.5, | ||||
|             WeaponType::DragonSlayer => 0.5, | ||||
|             WeaponType::Dagger => 0.5, | ||||
|             WeaponType::Knife => 0.5, | ||||
|             WeaponType::Blade => 0.5, | ||||
|             WeaponType::Edge => 0.5, | ||||
|             WeaponType::Ripper => 0.5, | ||||
|             WeaponType::BladeDance => 0.5, | ||||
|             WeaponType::BloodyArt => 0.5, | ||||
|             WeaponType::CrossScar => 0.5, | ||||
|             WeaponType::ZeroDivide => 0.5, | ||||
|             WeaponType::TwoKamui => 0.5, | ||||
|             WeaponType::Partisan => 0.5, | ||||
|             WeaponType::Halbert => 0.5, | ||||
|             WeaponType::Glaive => 0.5, | ||||
|             WeaponType::Berdys => 0.5, | ||||
|             WeaponType::Gungnir => 0.5, | ||||
| 
 | ||||
|             WeaponType::Slicer => 0.6666, | ||||
|             WeaponType::Spinner => 0.6666, | ||||
|             WeaponType::Cutter => 0.6666, | ||||
|             WeaponType::Sawcer => 0.6666, | ||||
|             WeaponType::Diska => 0.6666, | ||||
|             WeaponType::Mechgun => 0.6666, | ||||
|             WeaponType::Assault => 0.6666, | ||||
|             WeaponType::Repeater => 0.6666, | ||||
|             WeaponType::Gatling => 0.6666, | ||||
|             WeaponType::Vulcan => 0.6666, | ||||
|             WeaponType::Shot => 0.6666, | ||||
|             WeaponType::Spread => 0.6666, | ||||
|             WeaponType::Cannon => 0.6666, | ||||
|             WeaponType::Launcher => 0.6666, | ||||
|             WeaponType::Arms => 0.6666, | ||||
| 
 | ||||
|             _ => 1.0, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -148,6 +148,44 @@ pub enum MonsterType { | ||||
|     Kondrieu, | ||||
| } | ||||
| 
 | ||||
| impl MonsterType { | ||||
|     pub fn is_boss(&self) -> bool { | ||||
|         matches!(self, | ||||
|             MonsterType::Dragon | | ||||
|             MonsterType::DeRolLe | | ||||
|             MonsterType::DeRolLeBody | | ||||
|             MonsterType::DeRolLeMine | | ||||
|             MonsterType::VolOptPartA | | ||||
|             MonsterType::VolOptPillar | | ||||
|             MonsterType::VolOptMonitor | | ||||
|             MonsterType::VolOptAmp | | ||||
|             MonsterType::VolOptCore | | ||||
|             MonsterType::VolOptUnused | | ||||
|             MonsterType::VolOpt | | ||||
|             MonsterType::VolOptTrap | | ||||
|             MonsterType::DarkFalz | | ||||
|             MonsterType::DarkFalz1 | | ||||
|             MonsterType::DarkFalz2 | | ||||
|             MonsterType::DarkFalz3 | | ||||
|             MonsterType::Darvant | | ||||
|             MonsterType::UltDarvant | | ||||
|             MonsterType::Epsiguard | // TODO: is epsilon core a boss?
 | ||||
|             MonsterType::BarbaRay | | ||||
|             MonsterType::PigRay | | ||||
|             MonsterType::GolDragon | | ||||
|             MonsterType::GalGryphon | | ||||
|             MonsterType::OlgaFlow | | ||||
|             MonsterType::OlgaFlow1 | | ||||
|             MonsterType::OlgaFlow2 | | ||||
|             MonsterType::Gael | | ||||
|             MonsterType::Giel | | ||||
|             MonsterType::SaintMillion | | ||||
|             MonsterType::Shambertin | | ||||
|             MonsterType::Kondrieu | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(serde::Deserialize, Debug)] | ||||
| pub struct MonsterStats { | ||||
|  | ||||
| @ -442,19 +442,18 @@ where | ||||
|         0.0 | ||||
|     }; | ||||
| 
 | ||||
|      
 | ||||
|     let weapon_exp_ratio: f32 = { | ||||
|         let equipped_weapon_handle = item_manager | ||||
|                                                                 .get_character_inventory_mut(&client.character)? | ||||
|                                                                 .get_equipped_weapon_handle() | ||||
|                                                                 .ok_or(ItemManagerError::CannotGetIndividualItem)?; 
 | ||||
|     
 | ||||
|         let equipped_weapon = &equipped_weapon_handle | ||||
|                                                     .item() | ||||
|                                                     .ok_or(ItemManagerError::Idunnoman)? | ||||
|                                                     .individual() | ||||
|                                                     .ok_or(ItemManagerError::Idunnoman)? | ||||
|                                                     .item; | ||||
|     let equipped_weapon_handle = item_manager | ||||
|         .get_character_inventory_mut(&client.character)? | ||||
|         .get_equipped_weapon_handle() | ||||
|         .ok_or(ItemManagerError::CannotGetIndividualItem)?; 
 | ||||
| 
 | ||||
|     let equipped_weapon = &equipped_weapon_handle | ||||
|         .item() | ||||
|         .ok_or(ItemManagerError::Idunnoman)? | ||||
|         .individual() | ||||
|         .ok_or(ItemManagerError::Idunnoman)?.item; | ||||
| 
 | ||||
|     let special_exp_ratio: f32 = { | ||||
|         match equipped_weapon { | ||||
|             ItemDetail::Weapon(weapon) => match weapon.special { | ||||
|                 Some(WeaponSpecial::Masters) => 0.08, | ||||
| @ -470,7 +469,15 @@ where | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let exp_gain = (monster_stats.exp as f32 * (char_special_modifier + weapon_exp_ratio)).clamp(1.0, 80.0) as u32; | ||||
|     let weapon_special_reduction: f32 = { | ||||
|         match equipped_weapon { | ||||
|             ItemDetail::Weapon(weapon) => weapon.weapon.special_penalty(), | ||||
|             ItemDetail::ESWeapon(_esweapon) => 0.0, | ||||
|             _ => 0.0, | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     let exp_gain = ((monster_stats.exp as f32 * (char_special_modifier + special_exp_ratio)).clamp(1.0, 80.0) * (1.0 - weapon_special_reduction)) as u32; | ||||
| 
 | ||||
|     let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; | ||||
|     let gain_exp_pkt = builder::message::character_gained_exp(area_client, exp_gain); | ||||
|  | ||||
| @ -561,18 +561,118 @@ async fn test_exp_steal_no_android_boost_in_vhard() { | ||||
|     assert!(c2.character.exp == 80000010); | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| async fn test_exp_steal_multihit_penalty() { | ||||
|     let mut entity_gateway = InMemoryGateway::default(); | ||||
| 
 | ||||
|     let (_user1, mut char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; | ||||
|     char1.exp = 80000000; | ||||
|     char1.char_class = CharacterClass::HUcast; | ||||
|     entity_gateway.save_character(&char1).await.unwrap(); | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         item::NewItemEntity { | ||||
|             item: item::ItemDetail::Weapon( | ||||
|                 item::weapon::Weapon { | ||||
|                     weapon: item::weapon::WeaponType::Dagger, | ||||
|                     grind: 5, | ||||
|                     special: Some(item::weapon::WeaponSpecial::Kings), | ||||
|                     attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                             Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), | ||||
|                             None,], | ||||
|                     tekked: true, | ||||
|                 } | ||||
|             ), | ||||
|         }).await.unwrap()); | ||||
|     
 | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         item::NewItemEntity { | ||||
|             item: item::ItemDetail::Weapon( | ||||
|                 item::weapon::Weapon { | ||||
|                     weapon: item::weapon::WeaponType::Mechgun, | ||||
|                     grind: 5, | ||||
|                     special: Some(item::weapon::WeaponSpecial::Kings), | ||||
|                     attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                             Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), | ||||
|                             None,], | ||||
|                     tekked: true, | ||||
|                 } | ||||
|             ), | ||||
|         }).await.unwrap()); | ||||
|     
 | ||||
|     let equipped = item::EquippedEntity { | ||||
|         weapon: Some(p1_inv[0].id), | ||||
|         armor: None, | ||||
|         shield: None, | ||||
|         unit: [None; 4], | ||||
|         mag: None, | ||||
|     }; | ||||
|     entity_gateway.set_character_equips(&char1.id, &equipped).await.unwrap(); | ||||
|     entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(p1_inv)).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_with_difficulty(&mut ship, ClientId(1), "room", "", Difficulty::Ultimate).await; | ||||
| 
 | ||||
|     let enemy_id = { | ||||
|         let room = ship.blocks.0[0].rooms[0].as_ref().unwrap(); | ||||
|         let enemy_id = (0..).filter_map(|i| { | ||||
|             room.maps.enemy_by_id(i).ok().and_then(|enemy| { | ||||
|                 if enemy.monster == MonsterType::Booma { | ||||
|                     Some(i) | ||||
|                 } | ||||
|                 else { | ||||
|                     None | ||||
|                 } | ||||
|             }) | ||||
|         }).next().unwrap(); | ||||
|         enemy_id | ||||
|     }; | ||||
| 
 | ||||
|     ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::ExperienceSteal(ExperienceSteal{ | ||||
|         client: 0, | ||||
|         target: 0, | ||||
|         client2: enemy_id as u8, | ||||
|         target2: 16, | ||||
|         enemy_id: enemy_id as u16, | ||||
|     })))).await.unwrap().for_each(drop); | ||||
| 
 | ||||
|     let c1 = ship.clients.get(&ClientId(1)).unwrap(); | ||||
|     assert!(c1.character.exp == 80000040); | ||||
| 
 | ||||
|     // change equipped item
 | ||||
|     ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::PlayerEquipItem(PlayerEquipItem { | ||||
|         client: 0, | ||||
|         target: 0, | ||||
|         item_id: 0x10001, | ||||
|         sub_menu: 9, | ||||
|         unknown1: 0, | ||||
|     })))).await.unwrap().for_each(drop); | ||||
| 
 | ||||
|     ship.handle(ClientId(1), &RecvShipPacket::Message(Message::new(GameMessage::ExperienceSteal(ExperienceSteal{ | ||||
|         client: 0, | ||||
|         target: 0, | ||||
|         client2: enemy_id as u8, | ||||
|         target2: 16, | ||||
|         enemy_id: enemy_id as u16, | ||||
|     })))).await.unwrap().for_each(drop); | ||||
| 
 | ||||
|     let c1 = ship.clients.get(&ClientId(1)).unwrap(); | ||||
|     assert!(c1.character.exp == 80000066); | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| async fn test_exp_steal_doesnt_exceed_100p() { | ||||
|     assert!(false) | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| async fn test_exp_steal_multihit_penalty() { | ||||
|     assert!(false) | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| async fn test_each_client_can_steal_exp_from_same_enemy() { | ||||
| async fn test_each_client_can_steal_full_exp_from_same_enemy() { | ||||
|     assert!(false) | ||||
| } | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user