use std::{
io::{Cursor, ErrorKind, Read, Write},
mem::align_of,
};
use byteorder::{ReadBytesExt, WriteBytesExt, LE};
use contiguous_mem::*;
pub enum IndexOrPtr<T> {
Index(u32),
Ptr(*const T),
}
impl<T> IndexOrPtr<T> {
pub fn to_ref(&self, data: &[*const T]) -> IndexOrPtr<T> {
match self {
IndexOrPtr::Index(index) => IndexOrPtr::Ptr(data[*index as usize]),
IndexOrPtr::Ptr(ref ptr) => IndexOrPtr::Ptr(*ptr),
}
}
pub fn unwrap_ptr(&self) -> *const T {
match self {
IndexOrPtr::Index(_) => panic!("not a pointer"),
IndexOrPtr::Ptr(ptr) => *ptr,
}
}
pub fn unwrap_ref(&self) -> &'static mut T {
unsafe { &mut *(self.unwrap_ptr() as *mut T) }
}
}
pub trait Load {
unsafe fn load<R: Read>(data: R) -> Self;
}
pub trait Save {
fn save<W: Write>(&self, data: &mut W) -> Result<(), std::io::Error>;
}
pub struct Enemy {
pub max_health: u32,
pub health: u32,
pub speed: f32,
pub age: f32,
}
impl Enemy {
pub fn reset(&mut self) {
self.health = self.max_health;
self.age = 0.0;
}
}
impl Load for Enemy {
unsafe fn load<R: Read>(mut data: R) -> Enemy {
Enemy {
max_health: data.read_u32::<LE>().unwrap_unchecked(),
health: data.read_u32::<LE>().unwrap_unchecked(),
speed: data.read_f32::<LE>().unwrap_unchecked(),
age: data.read_f32::<LE>().unwrap_unchecked(),
}
}
}
impl Save for Enemy {
fn save<W: Write>(&self, data: &mut W) -> Result<(), std::io::Error> {
data.write_u32::<LE>(self.max_health)?;
data.write_u32::<LE>(self.health)?;
data.write_f32::<LE>(self.speed)?;
data.write_f32::<LE>(self.age)?;
Ok(())
}
}
pub struct Level {
pub enemies: Vec<IndexOrPtr<Enemy>>,
}
impl Load for Level {
unsafe fn load<R: Read>(mut data: R) -> Level {
let enemies_count = data.read_u32::<LE>().unwrap();
let enemies = (0..enemies_count).map(|_| {
let enemy_index = data.read_u32::<LE>().unwrap();
IndexOrPtr::Index(enemy_index)
});
Level {
enemies: enemies.collect(),
}
}
}
impl Save for Level {
fn save<W: Write>(&self, data: &mut W) -> Result<(), std::io::Error> {
data.write_u32::<LE>(self.enemies.len() as u32)?;
for enemy in self.enemies.iter() {
match enemy {
IndexOrPtr::Index(index) => data.write_u32::<LE>(*index)?,
IndexOrPtr::Ptr(_) => {
return Err(std::io::Error::new(
ErrorKind::InvalidData,
"can't save level with references",
))
}
}
}
Ok(())
}
}
fn load_game_file<T: Load>(file_name: &'static str) -> T {
let mut data = Vec::with_capacity(24);
let mut data_cursor = Cursor::new(&mut data);
match file_name {
"enemy1.dat" => Enemy {
max_health: 200,
health: 200,
speed: 2.0,
age: 0.0,
}
.save(&mut data_cursor)
.unwrap(),
"enemy2.dat" => Enemy {
max_health: 200,
health: 200,
speed: 2.0,
age: 0.0,
}
.save(&mut data_cursor)
.unwrap(),
"enemy3.dat" => Enemy {
max_health: 200,
health: 200,
speed: 2.0,
age: 0.0,
}
.save(&mut data_cursor)
.unwrap(),
"enemy4.dat" => Enemy {
max_health: 200,
health: 200,
speed: 2.0,
age: 0.0,
}
.save(&mut data_cursor)
.unwrap(),
"level1.dat" => Level {
enemies: vec![
IndexOrPtr::Index(0),
IndexOrPtr::Index(1),
IndexOrPtr::Index(1),
IndexOrPtr::Index(2),
],
}
.save(&mut data_cursor)
.unwrap(),
"level2.dat" => Level {
enemies: vec![
IndexOrPtr::Index(1),
IndexOrPtr::Index(1),
IndexOrPtr::Index(1),
IndexOrPtr::Index(2),
IndexOrPtr::Index(2),
IndexOrPtr::Index(3),
IndexOrPtr::Index(3),
],
}
.save(&mut data_cursor)
.unwrap(),
_ => unreachable!(),
};
data_cursor.set_position(0);
unsafe { T::load(data_cursor) }
}
fn main() {
let mut data = UnsafeContiguousMemory::new_aligned(112, align_of::<Level>()).unwrap();
let enemies: &[*const Enemy] = unsafe {
&[
data.push(load_game_file("enemy1.dat")).unwrap_unchecked(),
data.push(load_game_file("enemy2.dat")).unwrap_unchecked(),
data.push(load_game_file("enemy3.dat")).unwrap_unchecked(),
data.push(load_game_file("enemy4.dat")).unwrap_unchecked(),
]
};
let levels: &[*mut Level] = unsafe {
&[
data.push(load_game_file("level1.dat")).unwrap_unchecked(),
data.push(load_game_file("level2.dat")).unwrap_unchecked(),
]
};
data.forget();
levels.iter().for_each(|level| {
let level = unsafe { &mut **level };
level.enemies = level
.enemies
.iter()
.map(|enemy| enemy.to_ref(enemies))
.collect();
});
let mut time = 0.0;
let mut current_level: usize = 0;
while current_level < levels.len() {
time += 1.0;
let mut all_enemies_killed = true;
let current_lvl = unsafe { &mut *levels[current_level] };
for enemy in current_lvl.enemies.iter_mut() {
let enemy_ref = enemy.unwrap_ref();
let health_reduction = ((5.0 + time * 0.25) as u32).min(enemy_ref.health);
enemy_ref.health -= health_reduction;
enemy_ref.age += 1.0;
if enemy_ref.health > 0 {
all_enemies_killed = false;
}
}
if all_enemies_killed {
println!(
"All enemies in level {} have been killed!",
current_level + 1
);
current_level += 1;
if current_level < levels.len() {
let next_level = unsafe { &mut *levels[current_level] };
for enemy in next_level.enemies.iter_mut() {
enemy.unwrap_ref().reset();
}
}
}
}
println!(
"Congratulations! You've completed all levels in: {:.2}",
time
);
}