use crate::entities::{SpawnOrder, SpawnOrderEntity, SpawnableEntity};
use crate::ids::PlayerUid;
use crate::map::{coord, TeleTile, TuneZone};
use crate::state::GameState;
use crate::SnapOuter;
use twgame_core::twsnap::enums::ActiveWeapon;
use twgame_core::twsnap::time::{Duration, Instant};
use twgame_core::twsnap::{items, vec2_from_bits, Snap, SnapId};
use vek::Vec2;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum Kind {
Grenade,
Pistol,
}
impl From<Kind> for ActiveWeapon {
fn from(val: Kind) -> Self {
match val {
Kind::Grenade => ActiveWeapon::Grenade,
Kind::Pistol => ActiveWeapon::Pistol,
}
}
}
#[derive(Debug, Clone)]
pub struct Projectile {
snap_id: SnapId,
spawn_order: SpawnOrder,
kind: Kind,
owner: PlayerUid,
pos: Vec2<f32>,
cursor_pos: Vec2<f32>,
direction: Vec2<f32>,
start_tick: Instant,
tune_zone: TuneZone,
life_span: Duration,
speed: f32,
curvature: f32,
marked_for_destroy: bool,
}
impl Projectile {
pub fn new_grenade(
game_state: &GameState,
snap_outer: &mut SnapOuter,
owner: PlayerUid,
pos: Vec2<f32>,
cursor_pos: Vec2<f32>,
start_tick: Instant,
tune_zone: TuneZone,
) -> Projectile {
let snap_id = snap_outer.id_generator.next_projectile();
let spawn_order = SpawnOrder::new(owner, start_tick, SpawnOrderEntity::Projectile);
let tuning = game_state.map.tuning(tune_zone);
Self {
snap_id,
spawn_order,
kind: Kind::Grenade,
owner,
pos,
cursor_pos,
direction: cursor_pos.normalized(),
start_tick,
tune_zone,
life_span: Duration::from_secs_f32(tuning.grenade_lifetime),
speed: tuning.grenade_speed,
curvature: tuning.grenade_curvature,
marked_for_destroy: false,
}
}
pub fn new_pistol(
game_state: &GameState,
snap_outer: &mut SnapOuter,
owner: PlayerUid,
pos: Vec2<f32>,
cursor_pos: Vec2<f32>,
start_tick: Instant,
tune_zone: TuneZone,
) -> Self {
let snap_id = snap_outer.id_generator.next_projectile();
let spawn_order = SpawnOrder::new(owner, start_tick, SpawnOrderEntity::Projectile);
let tuning = game_state.map.tuning(tune_zone);
Self {
snap_id,
spawn_order,
kind: Kind::Pistol,
owner,
pos,
cursor_pos,
direction: cursor_pos.normalized(),
start_tick,
tune_zone,
life_span: Duration::from_secs_f32(tuning.gun_lifetime),
speed: tuning.gun_speed,
curvature: tuning.gun_curvature,
marked_for_destroy: false,
}
}
}
impl Projectile {
fn get_pos(&self, now: Instant, previous: bool) -> Vec2<f32> {
let mut time = if previous {
now.duration_since(self.start_tick)
.unwrap()
.decrement()
.map(|d| d.seconds())
.unwrap()
} else {
now.duration_since(self.start_tick).unwrap().seconds()
};
time *= self.speed;
let x = self.pos.x + self.direction.x * time;
let y = self.pos.y + self.direction.y * time + self.curvature / 10000.0 * (time * time);
Vec2::new(x, y)
}
fn get_tee_collision(
&self,
game_state: &GameState,
from: Vec2<f32>,
to: Vec2<f32>,
) -> Option<(PlayerUid, Vec2<f32>)> {
if let Some(tee) = game_state.tee_cores.get_tee(self.owner) {
if tee.is_solo() {
return None;
}
};
game_state
.tee_cores
.intersect_tees(&game_state.tee_order, Some(self.owner), from, to, 6.0)
}
}
impl SpawnableEntity for Projectile {
fn tick(&mut self, now: Instant, game_state: &mut GameState, _snap_outer: &mut SnapOuter) {
let from = self.get_pos(now, true);
let to = self.get_pos(now, false);
let mut collision = game_state.map.intersect_projectile(from, to);
if let Some((_, collision_point)) =
self.get_tee_collision(game_state, from, collision.unwrap_or(to))
{
collision = Some(collision_point);
}
if let Some(collision) = collision {
if self.kind == Kind::Grenade {
let explosion_strength = game_state.map.tuning(self.tune_zone).explosion_strength;
game_state.create_explosion(self.owner, collision, explosion_strength);
}
self.marked_for_destroy = true;
return;
}
if let Some(remaining) = self.life_span.decrement() {
self.life_span = remaining;
} else {
if self.kind == Kind::Grenade {
let explosion_strength = game_state.map.tuning(self.tune_zone).explosion_strength;
game_state.create_explosion(self.owner, to, explosion_strength);
}
self.marked_for_destroy = true;
return;
}
if let Some((TeleTile::Weapon, tele_id)) = game_state.map.get_tele_tile(coord::to_int(from))
{
if let Some(tele_out) =
game_state
.map
.select_tele_out(now, &mut game_state.prng, tele_id)
{
self.pos = tele_out;
self.start_tick = now;
}
}
}
fn snap(&self, _now: Instant, _game_state: &GameState, snapshot: &mut Snap) {
snapshot.projectiles.insert(
self.snap_id,
items::Projectile {
pos: vec2_from_bits(Vec2::new(self.pos.x, self.pos.y)),
direction: Vec2::new(
self.cursor_pos.x.round() as i32,
self.cursor_pos.y.round() as i32,
),
kind: self.kind.into(),
start_tick: self.start_tick,
owner: self.owner.snap_id(),
tune_zone: self.tune_zone.to_save(),
},
);
}
fn is_marked_for_destroy(&self) -> bool {
self.marked_for_destroy
}
fn spawn_order(&self) -> SpawnOrder {
self.spawn_order
}
fn player_uid(&self) -> PlayerUid {
self.owner
}
fn on_tee_swap(&mut self, pid1: PlayerUid, pid2: PlayerUid) {
if self.owner == pid1 {
self.owner = pid2;
} else if self.owner == pid2 {
self.owner = pid1;
}
}
}