use crate::combat;
use crate::game_data::{ActionId, EntityType, MobjFlag, StateNum};
use crate::types::Card;
use crate::types::{AmmoType, WeaponType};
use crate::map::MapData;
use crate::math::*;
use crate::rules::{GameRules, PlayerAction, TouchResult, WorldAction};
use crate::types::Button;
use crate::world::{Entity, EntityId, PeerId, World};
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub enum ClassicBehavior {
Player,
Monster,
Item,
Projectile,
Inert,
Puppet,
}
pub struct ClassicDoomRules;
impl ClassicDoomRules {
pub fn behavior_for(entity: &Entity) -> ClassicBehavior {
if entity.entity_type == EntityType(0) {
return ClassicBehavior::Player;
}
if entity.flags.contains(MobjFlag::Shootable) && entity.flags.contains(MobjFlag::CountKill) {
return ClassicBehavior::Monster;
}
if entity.flags.contains(MobjFlag::Special) {
return ClassicBehavior::Item;
}
if entity.flags.contains(MobjFlag::Missile) {
return ClassicBehavior::Projectile;
}
ClassicBehavior::Inert
}
pub fn think(behavior: ClassicBehavior, entity: &mut Entity) -> ActionId {
match behavior {
ClassicBehavior::Player | ClassicBehavior::Puppet => ActionId::NONE,
ClassicBehavior::Monster
| ClassicBehavior::Projectile
| ClassicBehavior::Item
| ClassicBehavior::Inert => combat::advance_state(entity),
}
}
pub fn on_touch(behavior: ClassicBehavior, entity: &Entity, other: &Entity) -> TouchResult {
match behavior {
ClassicBehavior::Item => {
if other.entity_type == EntityType(0) {
TouchResult::PickedUp
} else {
TouchResult::Nothing
}
}
_ => {
if entity.flags.contains(MobjFlag::Solid) {
TouchResult::Blocked
} else {
TouchResult::Nothing
}
}
}
}
pub fn on_damage(entity: &mut Entity, damage: i32) -> i32 {
if !entity.flags.contains(MobjFlag::Shootable) {
return 0;
}
entity.health -= damage;
if entity.health <= 0 {
if let Some(info) = entity.entity_type.info() {
entity.flags.remove(MobjFlag::Shootable | MobjFlag::Solid);
combat::set_entity_state(entity, info.deathstate);
}
} else if let Some(info) = entity.entity_type.info()
&& !info.painstate.is_null()
{
combat::set_entity_state(entity, info.painstate);
}
damage
}
}
impl GameRules for ClassicDoomRules {
fn tick(
&self,
world: &mut World,
map: &MapData,
player_actions: &[(PeerId, PlayerAction)],
_world_actions: &[WorldAction],
) {
for (peer, action) in player_actions {
if let Some(pid) = world.controlled_by(*peer) {
let alive = world.get(pid).is_some_and(|e| e.health > 0);
if !alive {
continue;
}
combat::apply_player_input(world, map, pid, action);
let use_pressed = action.buttons.contains(Button::Use);
let was_down = world.player_state(pid).is_none_or(|ps| ps.use_down);
if use_pressed && !was_down {
combat::use_lines(world, map, pid);
}
if let Some(ps) = world.player_state_mut(pid) {
ps.use_down = use_pressed;
}
combat::check_player_sector(world, map, pid);
combat::update_weapon_bob(world, pid);
advance_weapon(world, map, pid, action);
}
}
check_pickups(world);
combat::tick_entities(world, map, dispatch_action);
world.tick += 1;
}
fn validate_player_action(
&self,
_world: &World,
_player: PeerId,
action: &PlayerAction,
) -> bool {
action.forward_move.abs() <= 25 && action.side_move.abs() <= 20
}
fn validate_world_action(&self, _world: &World, _action: &WorldAction) -> bool {
true
}
}
fn check_pickups(world: &mut World) {
use crate::types::{doomednum as dn, AmmoType, ArmorType, Card, WeaponType};
let player_ids: alloc::vec::Vec<EntityId> = world.controlled_entities().collect();
let mut players = alloc::vec::Vec::with_capacity(player_ids.len());
for &pid in &player_ids {
if let Some(e) = world.get(pid) {
players.push((e.id, e.x, e.y, e.radius));
}
}
const MAX_PICKUPS: usize = 16;
let mut touches = [(EntityId(0), EntityType(0), EntityId(0)); MAX_PICKUPS];
let mut n_touches = 0;
for e in world.iter() {
if !e.flags.contains(MobjFlag::Special) { continue; }
for &(pid, px, py, pradius) in &players {
let dx = (e.x - px).abs();
let dy = (e.y - py).abs();
let touch_dist = pradius + e.radius;
if dx < touch_dist
&& dy < touch_dist
&& let Some(slot) = touches.get_mut(n_touches)
{
*slot = (e.id, e.entity_type, pid);
n_touches = n_touches.saturating_add(1);
break;
}
}
}
for &(item_id, item_type, player_id) in touches.iter().take(n_touches) {
let Some(info) = item_type.info() else { continue };
let dnum = info.doomednum;
let picked_up = match dnum {
dn::HEALTH_BONUS => world.get_mut(player_id).is_some_and(|e| {
e.health = (e.health + 1).min(200);
true
}),
dn::STIMPACK => match world.get(player_id) {
Some(e) if e.health < 100 => world.get_mut(player_id).is_some_and(|e| {
e.health = (e.health + 10).min(100);
true
}),
_ => false,
},
dn::MEDIKIT => match world.get(player_id) {
Some(e) if e.health < 100 => world.get_mut(player_id).is_some_and(|e| {
e.health = (e.health + 25).min(100);
true
}),
_ => false,
},
dn::SOUL_SPHERE => world.get_mut(player_id).is_some_and(|e| {
e.health = (e.health + 100).min(200);
true
}),
dn::ARMOR_BONUS => {
if let Some(ps) = world.player_state_mut(player_id) {
ps.armor_points = (ps.armor_points + 1).min(200);
if ps.armor_type == ArmorType::None { ps.armor_type = ArmorType::Green; }
true
} else { false }
}
dn::GREEN_ARMOR => {
if let Some(ps) = world.player_state_mut(player_id) {
if ps.armor_points >= 100 { false }
else { ps.armor_type = ArmorType::Green; ps.armor_points = 100; true }
} else { false }
}
dn::BLUE_ARMOR => {
if let Some(ps) = world.player_state_mut(player_id) {
if ps.armor_points >= 200 { false }
else { ps.armor_type = ArmorType::Blue; ps.armor_points = 200; true }
} else { false }
}
dn::CLIP => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Bullets, 10)),
dn::BOX_OF_AMMO => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Bullets, 50)),
dn::SHELLS => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Shells, 4)),
dn::SHELL_BOX => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Shells, 20)),
dn::ROCKET => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Rockets, 1)),
dn::ROCKET_BOX => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Rockets, 5)),
dn::CELL => world.player_state_mut(player_id).is_some_and(|ps| ps.give_ammo(AmmoType::Cells, 20)),
dn::SHOTGUN => give_weapon(world, player_id, WeaponType::Shotgun, AmmoType::Shells, 8),
dn::CHAINGUN => give_weapon(world, player_id, WeaponType::Chaingun, AmmoType::Bullets, 20),
dn::ROCKET_LAUNCHER => give_weapon(world, player_id, WeaponType::RocketLauncher, AmmoType::Rockets, 2),
dn::PLASMA_RIFLE => give_weapon(world, player_id, WeaponType::PlasmaRifle, AmmoType::Cells, 40),
dn::BFG => give_weapon(world, player_id, WeaponType::Bfg, AmmoType::Cells, 40),
dn::BACKPACK => {
if let Some(ps) = world.player_state_mut(player_id) {
ps.double_max_ammo();
ps.give_ammo(AmmoType::Bullets, 10);
ps.give_ammo(AmmoType::Shells, 4);
ps.give_ammo(AmmoType::Rockets, 1);
ps.give_ammo(AmmoType::Cells, 20);
true
} else { false }
}
dn::BLUE_CARD => grant_card(world, player_id, Card::BlueCard),
dn::YELLOW_CARD => grant_card(world, player_id, Card::YellowCard),
dn::RED_CARD => grant_card(world, player_id, Card::RedCard),
dn::BLUE_SKULL => grant_card(world, player_id, Card::BlueSkull),
dn::YELLOW_SKULL => grant_card(world, player_id, Card::YellowSkull),
dn::RED_SKULL => grant_card(world, player_id, Card::RedSkull),
_ => true,
};
if picked_up {
world.remove(item_id);
}
}
}
fn give_weapon(
world: &mut World,
player: EntityId,
weapon: WeaponType,
ammo: AmmoType,
amount: i32,
) -> bool {
if let Some(ps) = world.player_state_mut(player) {
ps.give_ammo(ammo, amount);
ps.grant_weapon(weapon);
true
} else {
false
}
}
fn grant_card(world: &mut World, player: EntityId, card: Card) -> bool {
if let Some(ps) = world.player_state_mut(player) {
ps.grant_card(card);
}
true
}
fn dispatch_action(action_id: ActionId, world: &mut World, map: &MapData, id: EntityId) {
match action_id {
ActionId::A_LOOK => combat::a_look(world, map, id),
ActionId::A_CHASE => combat::a_chase(world, map, id),
ActionId::A_FACE_TARGET => combat::a_face_target(world, id),
ActionId::A_FALL => a_fall(world, id),
ActionId::A_POS_ATTACK => a_pos_attack(world, map, id),
ActionId::A_SPOS_ATTACK => a_spos_attack(world, map, id),
ActionId::A_TROOP_ATTACK => a_troop_attack(world, id),
ActionId::A_SARG_ATTACK => a_sarg_attack(world, id),
ActionId::A_SKULL_ATTACK => a_skull_attack(world, id),
ActionId::A_EXPLODE => a_explode(world, id),
ActionId::A_BFG_SPRAY => a_bfg_spray(world, map, id),
ActionId::A_PAIN | ActionId::A_SCREAM | ActionId::NONE => {}
_ => {} }
}
fn a_fall(world: &mut World, id: EntityId) {
if let Some(e) = world.get_mut(id) {
e.flags.remove(MobjFlag::Solid | MobjFlag::Shootable);
}
}
fn a_pos_attack(world: &mut World, map: &MapData, id: EntityId) {
combat::a_face_target(world, id);
let spread = (world.p_random() - world.p_random()) << 20;
let damage = (world.p_random() % 5 + 1) * 3;
monster_hitscan(world, map, id, damage, spread);
}
fn a_spos_attack(world: &mut World, map: &MapData, id: EntityId) {
combat::a_face_target(world, id);
for _ in 0..3 {
let spread = (world.p_random() - world.p_random()) << 20;
let damage = (world.p_random() % 5 + 1) * 3;
monster_hitscan(world, map, id, damage, spread);
}
}
fn a_troop_attack(world: &mut World, id: EntityId) {
let Some(entity) = world.get(id) else { return };
let Some(target_id) = entity.target else { return };
combat::a_face_target(world, id);
if combat::check_melee_range(world, id, target_id) {
let damage = (world.p_random() % 8 + 1) * 3;
combat::apply_damage_to(world, target_id, damage, Some(id));
} else {
combat::spawn_projectile(world, id, target_id, crate::game_data::MT_TROOPSHOT);
}
}
fn a_sarg_attack(world: &mut World, id: EntityId) {
let Some(entity) = world.get(id) else { return };
let Some(target_id) = entity.target else { return };
combat::a_face_target(world, id);
if combat::check_melee_range(world, id, target_id) {
let damage = (world.p_random() % 10 + 1) * 4;
combat::apply_damage_to(world, target_id, damage, Some(id));
}
}
fn a_skull_attack(world: &mut World, id: EntityId) {
let Some(entity) = world.get(id) else { return };
let Some(target_id) = entity.target else { return };
combat::a_face_target(world, id);
let Some(target) = world.get(target_id) else { return };
let Some(entity) = world.get(id) else { return };
let dx = target.x - entity.x;
let dy = target.y - entity.y;
let dist = ((dx.abs() as i64 + dy.abs() as i64) >> FRACBITS).max(1) as i32;
let speed = 20 * FRACUNIT;
let momx = fixed_div(fixed_mul(dx >> FRACBITS, speed), dist);
let momy = fixed_div(fixed_mul(dy >> FRACBITS, speed), dist);
if let Some(e) = world.get_mut(id) {
e.momx = momx;
e.momy = momy;
}
}
fn a_explode(world: &mut World, id: EntityId) {
combat::radius_damage(world, id, 128 * FRACUNIT, 128);
}
fn a_bfg_spray(world: &mut World, map: &MapData, id: EntityId) {
let (ball_angle, source_id) = match world.get(id) {
Some(e) => (e.angle, e.target),
None => return,
};
let Some(source_id) = source_id else { return };
let (sx, sy, sz) = match world.get(source_id) {
Some(s) => (s.x, s.y, s.z + s.height - (s.height >> 2)),
None => return,
};
const STEPS: u32 = 40;
const BFG_RANGE: Fixed = 16 * 64 * FRACUNIT; const STEP_ANGLE: u32 = ANG90 / STEPS;
let fan_start = ball_angle.wrapping_sub(ANG90 / 2);
for i in 0..STEPS {
let ray_angle = fan_start.wrapping_add(STEP_ANGLE.wrapping_mul(i));
let tolerance = ANG90 / 18;
let mut best_dist = i64::MAX;
let mut best_id = None;
for e in world.iter() {
if e.id == source_id || e.id == id || !e.flags.contains(MobjFlag::Shootable) {
continue;
}
let dx = e.x - sx;
let dy = e.y - sy;
let dist = dx.abs() as i64 + dy.abs() as i64;
if dist <= 0 || dist > BFG_RANGE as i64 {
continue;
}
let to_angle = crate::map::point_to_angle(dx, dy);
let diff = to_angle.wrapping_sub(ray_angle);
let within = diff <= tolerance || diff >= 0u32.wrapping_sub(tolerance);
if !within {
continue;
}
if dist < best_dist
&& crate::physics::check_sight(map, sx, sy, sz, e.x, e.y, e.z, e.height)
{
best_dist = dist;
best_id = Some(e.id);
}
}
let Some(hit_id) = best_id else { continue };
let mut damage = 0;
for _ in 0..15 {
damage += (world.p_random() & 7) + 1;
}
combat::apply_damage_to(world, hit_id, damage, Some(source_id));
}
}
fn monster_hitscan(world: &mut World, map: &MapData, id: EntityId, damage: i32, spread: i32) {
let (sx, sy, sz, s_height, aim_angle) = {
let Some(entity) = world.get(id) else { return };
(
entity.x,
entity.y,
entity.z,
entity.height,
entity.angle.wrapping_add(spread as u32),
)
};
let Some(target_id) = world.get(id).and_then(|e| e.target) else { return };
let (tx, ty, tz, th) = {
let Some(t) = world.get(target_id) else { return };
(t.x, t.y, t.z, t.height)
};
let to_angle = crate::map::point_to_angle(tx - sx, ty - sy);
let tolerance = crate::math::ANG90 / 18; let diff = to_angle.wrapping_sub(aim_angle);
let within = diff <= tolerance || diff >= 0u32.wrapping_sub(tolerance);
if !within {
return; }
let eye_z = sz + s_height - (s_height >> 2);
if !crate::physics::check_sight(map, sx, sy, eye_z, tx, ty, tz, th) {
return;
}
combat::apply_damage_to(world, target_id, damage, Some(id));
}
fn advance_weapon(world: &mut World, map: &MapData, pid: EntityId, action: &PlayerAction) {
let action_id = {
let Some(ps) = world.player_state_mut(pid) else { return };
if ps.psp_tics != -1 {
ps.psp_tics -= 1;
if ps.psp_tics <= 0 {
let cur = ps.psp_state;
let Some(st) = cur.get() else { return };
let next = st.next_state;
let Some(nst) = next.get() else { return };
ps.psp_state = next;
ps.psp_tics = nst.tics;
nst.action
} else {
ActionId::NONE
}
} else {
ActionId::NONE
}
};
let mut cur_action = action_id;
let mut chain = 0;
while cur_action != ActionId::NONE && chain < 8 {
let state_before = world.player_state(pid).map(|ps| ps.psp_state);
psp_dispatch(cur_action, world, map, pid, action);
let state_after = world.player_state(pid).map(|ps| ps.psp_state);
cur_action = if state_after != state_before {
state_after
.and_then(|s| s.get())
.map_or(ActionId::NONE, |st| st.action)
} else {
ActionId::NONE
};
chain += 1;
}
if let Some(ps) = world.player_state_mut(pid)
&& !ps.flash_state.is_null() && ps.flash_tics > 0
{
ps.flash_tics -= 1;
if ps.flash_tics <= 0 {
let cur = ps.flash_state;
if let Some(st) = cur.get() {
let next = st.next_state;
if next.is_null() || next == crate::game_data::StateNum(1) {
ps.flash_state = crate::game_data::StateNum::NULL;
} else if let Some(nst) = next.get() {
ps.flash_state = next;
ps.flash_tics = nst.tics;
}
}
}
}
let tick = world.tick;
let Some(ps) = world.player_state(pid) else { return };
let cur_state = ps.psp_state;
if let Some(st) = cur_state.get()
&& st.action == ActionId::A_WEAPON_READY
{
let bob = ps.bob;
let angle = ((128u32.wrapping_mul(tick)) as usize) & FINEMASK;
let sx = FRACUNIT + fixed_mul(bob, finecosine(angle));
let sy = combat::WEAPONTOP + fixed_mul(bob, finesine(angle & (FINEANGLES / 2 - 1)));
if let Some(ps) = world.player_state_mut(pid) {
ps.psp_sx = sx;
ps.psp_sy = sy;
}
}
}
fn weapon_states(weapon: WeaponType) -> (StateNum, StateNum, StateNum, StateNum, StateNum) {
use crate::game_data::*;
match weapon {
WeaponType::Fist => (S_PUNCH, StateNum(3), StateNum(4), StateNum(5), StateNum::NULL),
WeaponType::Pistol => (S_PISTOL, StateNum(11), StateNum(12), StateNum(13), S_PISTOLFLASH),
WeaponType::Shotgun => (S_SGUN, StateNum(19), StateNum(20), StateNum(21), StateNum(30)),
WeaponType::Chaingun => (StateNum(49), StateNum(50), StateNum(51), StateNum(52), StateNum(55)),
WeaponType::RocketLauncher => (StateNum(57), StateNum(58), StateNum(59), StateNum(60), StateNum(63)),
WeaponType::PlasmaRifle => (StateNum(74), StateNum(75), StateNum(76), StateNum(77), StateNum(79)),
WeaponType::Bfg => (StateNum(81), StateNum(82), StateNum(83), StateNum(84), StateNum(88)),
WeaponType::Chainsaw => (StateNum(67), StateNum(69), StateNum(70), StateNum(71), StateNum::NULL),
WeaponType::SuperShotgun => (S_SGUN, StateNum(19), StateNum(20), StateNum(21), StateNum(30)), }
}
fn psp_dispatch(
action_id: ActionId,
world: &mut World,
map: &MapData,
pid: EntityId,
action: &PlayerAction,
) {
match action_id {
ActionId::A_WEAPON_READY => {
let want = action.weapon_select;
let new_weapon = match want {
1 => Some(WeaponType::Fist),
2 => Some(WeaponType::Pistol),
3 => Some(WeaponType::Shotgun),
4 => Some(WeaponType::Chaingun),
5 => Some(WeaponType::RocketLauncher),
6 => Some(WeaponType::PlasmaRifle),
7 => Some(WeaponType::Bfg),
8 => Some(WeaponType::Chainsaw),
_ => None,
};
if let Some(new_weapon) = new_weapon {
let owns = world.player_state(pid).is_some_and(|ps| {
ps.weapon_owned
.get(new_weapon as usize)
.copied()
.unwrap_or(false)
});
let cur = world.player_state(pid).map(|ps| ps.ready_weapon);
if owns && cur != Some(new_weapon) {
if let Some(ps) = world.player_state_mut(pid) {
ps.pending_weapon = new_weapon;
}
let cur_weapon = world.player_state(pid).map_or(WeaponType::Pistol, |ps| ps.ready_weapon);
let (_, down, _, _, _) = weapon_states(cur_weapon);
combat::psp_set_state(world, pid, down);
return;
}
}
if action.buttons.contains(Button::Attack) {
psp_fire_weapon(world, pid);
}
}
ActionId::A_PUNCH => {
let damage = (world.p_random() % 10 + 1) * 2;
let _spread = (world.p_random() - world.p_random()) << 18;
combat::melee_attack(world, pid, damage, 64 * FRACUNIT);
}
ActionId::A_SAW => {
let damage = (world.p_random() % 10 + 1) * 2;
let _spread = (world.p_random() - world.p_random()) << 18;
combat::melee_attack(world, pid, damage, 64 * FRACUNIT + 16 * FRACUNIT);
}
ActionId::A_FIRE_PISTOL => {
if !combat::use_ammo(world, pid, AmmoType::Bullets, 1) { return; }
let damage = 5 * (world.p_random() % 3 + 1);
let refire = world.player_state(pid).map_or(0, |ps| ps.refire_count);
let spread = if refire > 0 { (world.p_random() - world.p_random()) << 18 } else { 0 };
combat::hitscan_attack(world, map, pid, damage, spread, crate::game_data::MT_PUFF);
psp_set_flash(world, pid);
}
ActionId::A_FIRE_SHOTGUN => {
if !combat::use_ammo(world, pid, AmmoType::Shells, 1) { return; }
for _ in 0..7 {
let damage = 5 * (world.p_random() % 3 + 1);
let spread = (world.p_random() - world.p_random()) << 18;
combat::hitscan_attack(world, map, pid, damage, spread, crate::game_data::MT_PUFF);
}
psp_set_flash(world, pid);
}
ActionId::A_FIRE_CGUN => {
if !combat::use_ammo(world, pid, AmmoType::Bullets, 1) { return; }
let damage = 5 * (world.p_random() % 3 + 1);
let refire = world.player_state(pid).map_or(0, |ps| ps.refire_count);
let spread = if refire > 0 { (world.p_random() - world.p_random()) << 18 } else { 0 };
combat::hitscan_attack(world, map, pid, damage, spread, crate::game_data::MT_PUFF);
psp_set_flash(world, pid);
}
ActionId::A_FIRE_MISSILE => {
if !combat::use_ammo(world, pid, AmmoType::Rockets, 1) { return; }
combat::fire_player_projectile(world, pid, crate::game_data::MT_ROCKET);
psp_set_flash(world, pid);
}
ActionId::A_FIRE_PLASMA => {
if !combat::use_ammo(world, pid, AmmoType::Cells, 1) { return; }
combat::fire_player_projectile(world, pid, crate::game_data::MT_PLASMA);
psp_set_flash(world, pid);
}
ActionId::A_BFG_SOUND => {
}
ActionId::A_CHECK_RELOAD => {
}
ActionId::A_REFIRE => {
if action.buttons.contains(Button::Attack) {
if let Some(ps) = world.player_state_mut(pid) {
ps.refire_count += 1;
}
psp_fire_weapon(world, pid);
} else if let Some(ps) = world.player_state_mut(pid) {
ps.refire_count = 0;
}
}
ActionId::A_LOWER => {
if let Some(ps) = world.player_state_mut(pid) {
ps.psp_sy += combat::RAISESPEED;
if ps.psp_sy >= combat::WEAPONBOTTOM {
let new_weapon = ps.pending_weapon;
ps.ready_weapon = new_weapon;
let (_, _, up, _, _) = weapon_states(new_weapon);
ps.psp_state = up;
if let Some(st) = up.get() {
ps.psp_tics = st.tics;
}
}
}
}
ActionId::A_RAISE => {
if let Some(ps) = world.player_state_mut(pid) {
ps.psp_sy -= combat::RAISESPEED;
if ps.psp_sy <= combat::WEAPONTOP {
ps.psp_sy = combat::WEAPONTOP;
let weapon = ps.ready_weapon;
let (ready, _, _, _, _) = weapon_states(weapon);
ps.psp_state = ready;
if let Some(st) = ready.get() {
ps.psp_tics = st.tics;
}
}
}
}
ActionId::A_LIGHT0 | ActionId::A_LIGHT1 | ActionId::A_LIGHT2 => {}
ActionId::NONE => {}
_ => {}
}
}
fn psp_fire_weapon(world: &mut World, pid: EntityId) {
let weapon = world.player_state(pid).map_or(WeaponType::Pistol, |ps| ps.ready_weapon);
let (_, _, _, fire, _) = weapon_states(weapon);
combat::psp_set_state(world, pid, fire);
}
fn psp_set_flash(world: &mut World, pid: EntityId) {
let weapon = world.player_state(pid).map_or(WeaponType::Pistol, |ps| ps.ready_weapon);
let (_, _, _, _, flash) = weapon_states(weapon);
if !flash.is_null()
&& let Some(ps) = world.player_state_mut(pid)
{
ps.flash_state = flash;
if let Some(st) = flash.get() {
ps.flash_tics = st.tics;
}
}
}