use crate::enums as twenums;
use crate::time;
use crate::SnapId;
use crate::{Events, SkinColor, Snap};
use bitflags::Flags;
use libtw2_gamenet_ddnet::enums as libenums;
use libtw2_gamenet_ddnet::snap_obj::Tick;
use libtw2_gamenet_ddnet::{snap_obj, SnapObj};
use std::iter;
use vek::Vec2;
fn i32_string<const N: usize>(s: &str) -> [i32; N] {
let padding_size = N * 4 - s.len() - 1;
let mut data = s
.bytes()
.map(|b| b.wrapping_add(128))
.chain(iter::repeat(128).take(padding_size))
.chain([0]);
let mut nums = [0; N];
for num in &mut nums {
*num = i32::from_be_bytes([
data.next().unwrap(),
data.next().unwrap(),
data.next().unwrap(),
data.next().unwrap(),
]);
}
assert_eq!(data.next(), None);
nums
}
fn u64_flags_lower<T: Flags<Bits = u64>>(flags: T) -> i32 {
(flags.bits() & 0xffff_ffff) as u32 as i32
}
fn u64_flags_upper<T: Flags<Bits = u64>>(flags: T) -> i32 {
((flags.bits() >> 32) & 0xffff_ffff) as u32 as i32
}
fn tick(t: time::Instant) -> Tick {
Tick(t.snap_tick())
}
fn duration_to_tick(t: time::Duration) -> Tick {
Tick(t.ticks())
}
fn fix<T: fixed::traits::Fixed<Bits = i32>>(x: T) -> i32 {
x.to_bits()
}
fn cmn<T: fixed::traits::Fixed<Bits = i32>>(v: Vec2<T>) -> snap_obj::Common {
snap_obj::Common {
x: fix(v.x),
y: fix(v.y),
}
}
fn skin_color(c: SkinColor) -> i32 {
i32::from_le_bytes([c.h, c.s, c.l_upper, c.a])
}
impl Snap {
pub fn to_ddnet(&self, snap: &mut Vec<(SnapObj, u16)>) -> [u8; 64] {
snap.clear();
for (id, item) in self.game_infos.iter() {
snap.push((
SnapObj::GameInfo(snap_obj::GameInfo {
game_flags: item.flags.bits(),
game_state_flags: item.game_state_flags.bits(),
round_start_tick: tick(item.round_start_tick),
warmup_timer: item.warmup_timer.ticks(),
score_limit: item.score_limit,
time_limit: item.time_limit.ticks(),
round_num: item.round_num,
round_current: item.round_current,
}),
id.ddnet_snap_id(),
));
snap.push((
SnapObj::GameInfoEx(snap_obj::GameInfoEx {
flags: u64_flags_lower(item.flags_ex),
version: 7,
flags2: u64_flags_upper(item.flags_ex),
}),
id.ddnet_snap_id(),
))
}
for (id, item) in self.lasers.iter() {
let type_ = match item.kind {
twenums::LaserType::Rifle => libenums::Lasertype::Rifle,
twenums::LaserType::Shotgun => libenums::Lasertype::Shotgun,
twenums::LaserType::Door => libenums::Lasertype::Door,
twenums::LaserType::Freeze => libenums::Lasertype::Freeze,
twenums::LaserType::Dragger(_) => libenums::Lasertype::Dragger,
twenums::LaserType::Gun(_) => libenums::Lasertype::Gun,
twenums::LaserType::Plasma => libenums::Lasertype::Plasma,
}
.to_i32();
let subtype = match item.kind {
twenums::LaserType::Dragger(sub) => sub.into(),
twenums::LaserType::Gun(sub) => sub.into(),
_ => 0,
};
snap.push((
SnapObj::DdnetLaser(snap_obj::DdnetLaser {
to_x: fix(item.to.x),
to_y: fix(item.to.y),
from_x: fix(item.from.x),
from_y: fix(item.from.y),
start_tick: tick(item.start_tick),
owner: SnapId::snap_opt_to_id(item.owner),
type_,
switch_number: item.switch_number as i32,
subtype,
}),
id.ddnet_snap_id(),
))
}
for (id, item) in self.map_projectiles.iter() {
let type_ = libenums::Weapon::from_i32(item.kind.into()).unwrap();
snap.push((
SnapObj::DdnetProjectile(snap_obj::DdnetProjectile {
x: fix(item.pos.x),
y: fix(item.pos.y),
vel_x: item.direction.x,
vel_y: item.direction.y,
type_,
start_tick: tick(item.start_tick),
owner: -1,
switch_number: item.switch_number as i32,
tune_zone: item.tune_zone as i32,
flags: item.flags.bits(),
}),
id.ddnet_snap_id(),
));
}
for (id, item) in self.pickups.iter() {
use twenums::CollectableWeapon::*;
let type_ = match item.kind {
twenums::Powerup::Health => libenums::Powerup::Health,
twenums::Powerup::Armor => libenums::Powerup::Armor,
twenums::Powerup::Weapon(_) => libenums::Powerup::Weapon,
twenums::Powerup::Shield(Shotgun) => libenums::Powerup::ArmorShotgun,
twenums::Powerup::Shield(Grenade) => libenums::Powerup::ArmorGrenade,
twenums::Powerup::Shield(Rifle) => libenums::Powerup::ArmorLaser,
twenums::Powerup::Shield(Ninja) => libenums::Powerup::ArmorNinja,
}
.to_i32();
let subtype = match item.kind {
twenums::Powerup::Weapon(weapon) => weapon.into(),
_ => 0,
};
snap.push((
SnapObj::DdnetPickup(snap_obj::DdnetPickup {
x: fix(item.pos.x),
y: fix(item.pos.y),
type_,
subtype,
switch_number: item.switch_number as i32,
}),
id.ddnet_snap_id(),
));
}
let mut teams = [0u8; 64];
for (id, item) in self.players.iter() {
let id = id.ddnet_snap_id();
teams[id as usize] = item.team.try_into().unwrap();
snap.push((
SnapObj::PlayerInfo(snap_obj::PlayerInfo {
local: item.local as i32,
client_id: id as i32,
team: libenums::Team::from_i32(item.teeworlds_team.into()).unwrap(),
score: item.score,
latency: item.latency,
}),
id,
));
snap.push((
SnapObj::ClientInfo(snap_obj::ClientInfo {
name: i32_string(&item.name),
clan: i32_string(&item.clan),
country: item.country,
skin: i32_string(&item.skin),
use_custom_color: item.use_custom_color as i32,
color_body: skin_color(item.color_body),
color_feet: skin_color(item.color_feet),
}),
id,
));
snap.push((
SnapObj::DdnetPlayer(snap_obj::DdnetPlayer {
flags: item.flags.bits(),
auth_level: item.auth_level.into(),
}),
id,
));
let tee = match &item.tee {
None => continue,
Some(tee) => tee,
};
snap.push((
SnapObj::Character(snap_obj::Character {
character_core: snap_obj::CharacterCore {
tick: tee.tick.snap_tick(),
x: fix(tee.pos.x),
y: fix(tee.pos.y),
vel_x: fix(tee.vel.x),
vel_y: fix(tee.vel.y),
angle: fix(tee.angle),
direction: tee.direction.into(),
jumped: tee.jumped.bits(),
hooked_player: SnapId::snap_opt_to_id(tee.hooked_player),
hook_state: tee.hook_state.into(),
hook_tick: duration_to_tick(tee.hook_tick),
hook_x: fix(tee.hook_pos.x),
hook_y: fix(tee.hook_pos.y),
hook_dx: fix(tee.hook_direction.x),
hook_dy: fix(tee.hook_direction.y),
},
player_flags: u64_flags_upper(tee.flags),
health: tee.health,
armor: tee.armor,
ammo_count: tee.ammo_count,
weapon: libenums::Weapon::from_i32(tee.weapon.into()).unwrap(),
emote: libenums::Emote::from_i32(tee.emote.into()).unwrap(),
attack_tick: tee.attack_tick.snap_tick(),
}),
id,
));
snap.push((
SnapObj::DdnetCharacter(snap_obj::DdnetCharacter {
flags: u64_flags_lower(tee.flags),
freeze_end: tick(tee.freeze_end),
jumps: tee.jumps,
tele_checkpoint: tee.tele_checkpoint,
strong_weak_id: tee.strong_weak_id,
jumped_total: tee.jumped_total,
ninja_activation_tick: tick(tee.ninja_activation_tick),
freeze_start: tick(tee.freeze_start),
target_x: fix(tee.target.x),
target_y: fix(tee.target.y),
}),
id,
));
}
for (id, item) in self.projectiles.iter() {
snap.push((
SnapObj::DdnetProjectile(snap_obj::DdnetProjectile {
x: fix(item.pos.x) * 100,
y: fix(item.pos.y) * 100,
vel_x: item.direction.x,
vel_y: item.direction.y,
type_: libenums::Weapon::from_i32(item.kind.into()).unwrap(),
start_tick: tick(item.start_tick),
owner: item.owner.to_id(),
switch_number: 0, tune_zone: item.tune_zone as i32,
flags: 1 << 4, }),
id.ddnet_snap_id(),
))
}
let mut damage_indicator_id = 0;
let mut death_id = 0;
let mut explosion_id = 0;
let mut hammer_hit_id = 0;
let mut sound_id = 0;
let mut sound_global_id = 0;
let mut spawn_id = 0;
for event in &self.events {
match event {
Events::DamageIndicator(event) => {
snap.push((
SnapObj::DamageInd(snap_obj::DamageInd {
common: cmn(event.pos),
angle: fix(event.angle),
}),
damage_indicator_id,
));
damage_indicator_id += 1;
}
Events::Death(event) => {
snap.push((
SnapObj::Death(snap_obj::Death {
common: cmn(event.pos),
client_id: event.player.to_id(),
}),
death_id,
));
death_id += 1;
}
Events::Explosion(event) => {
snap.push((
SnapObj::Explosion(snap_obj::Explosion {
common: cmn(event.pos),
}),
explosion_id,
));
explosion_id += 1;
}
Events::HammerHit(event) => {
snap.push((
SnapObj::HammerHit(snap_obj::HammerHit {
common: cmn(event.pos),
}),
hammer_hit_id,
));
hammer_hit_id += 1;
}
Events::Sound(event) => {
snap.push((
SnapObj::SoundWorld(snap_obj::SoundWorld {
common: cmn(event.pos),
sound_id: libenums::Sound::from_i32(event.sound.into()).unwrap(),
}),
sound_id,
));
sound_id += 1;
}
Events::SoundGlobal(event) => {
snap.push((
SnapObj::SoundGlobal(snap_obj::SoundGlobal {
common: snap_obj::Common { x: 0, y: 0 },
sound_id: libenums::Sound::from_i32(event.sound.into()).unwrap(),
}),
sound_global_id,
));
sound_global_id += 1;
}
Events::Spawn(event) => {
snap.push((
SnapObj::Spawn(snap_obj::Spawn {
common: cmn(event.pos),
}),
spawn_id,
));
spawn_id += 1;
}
}
}
teams
}
}