From ce283e50631db36287d7bc51b529105792dcaadc Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Thu, 1 Feb 2024 00:04:07 -0800
Subject: [PATCH] the work

---
 src/gameplay_attributes.rs |   3 +-
 src/gameplay_modes.rs      | 168 +++++++++++++++++++++++++++++++++----
 src/lib.rs                 |   2 +
 src/map.rs                 |  11 +++
 src/model.rs               |  26 +++++-
 src/updatable.rs           |  62 ++++++++++++++
 6 files changed, 253 insertions(+), 19 deletions(-)
 create mode 100644 src/map.rs
 create mode 100644 src/updatable.rs

diff --git a/src/gameplay_attributes.rs b/src/gameplay_attributes.rs
index 6756e45..75e9024 100644
--- a/src/gameplay_attributes.rs
+++ b/src/gameplay_attributes.rs
@@ -8,8 +8,9 @@ pub struct ContactingLadder{
 #[derive(Clone,Hash,Eq,PartialEq)]
 pub enum ContactingBehaviour{
 	Surf,
-	Cling,//usable as a zipline, or other weird and wonderful things
 	Ladder(ContactingLadder),
+	NoJump,
+	Cling,//usable as a zipline, or other weird and wonderful things
 	Elastic(u32),//[1/2^32,1] 0=None (elasticity+1)/2^32
 }
 //you have this effect while intersecting
diff --git a/src/gameplay_modes.rs b/src/gameplay_modes.rs
index a06fbb2..66ed787 100644
--- a/src/gameplay_modes.rs
+++ b/src/gameplay_modes.rs
@@ -1,12 +1,23 @@
 use std::collections::{HashSet,HashMap};
 use crate::model::ModelId;
 use crate::gameplay_style;
+use crate::updatable::Updatable;
 
+#[derive(Clone)]
 pub struct StageElement{
 	stage:StageId,//which stage spawn to send to
 	force:bool,//allow setting to lower spawn id i.e. 7->3
 	behaviour:StageElementBehaviour
 }
+impl StageElement{
+	pub fn new(stage_id:u32,force:bool,behaviour:StageElementBehaviour)->Self{
+		Self{
+			stage:StageId(stage_id),
+			force,
+			behaviour,
+		}
+	}
+}
 
 #[derive(Clone,Hash,Eq,PartialEq)]
 pub enum StageElementBehaviour{
@@ -20,25 +31,63 @@ pub enum StageElementBehaviour{
 	Checkpoint,//this is a combined behaviour for Ordered & Unordered in case a model is used multiple times or for both.
 }
 
+#[derive(Clone,Copy,Hash,Eq,PartialEq)]
+pub struct CheckpointId(usize);
+#[derive(Clone,Hash,Eq,PartialEq)]
 pub struct StageId(u32);
 pub struct Stage{
 	spawn:ModelId,
+	//open world support lol
+	ordered_checkpoints_count:u32,
 	//other behaviour models of this stage can have
-	ordered_checkpoints:Vec<ModelId>,
+	ordered_checkpoints:HashMap<CheckpointId,ModelId>,
 	unordered_checkpoints:HashSet<ModelId>,
 }
-
-#[derive(Clone,Hash,Eq,PartialEq)]
-pub enum ZoneBehaviour{
-	Finish,
-	Anitcheat,
+#[derive(Default)]
+pub struct StageUpdate{
+	//other behaviour models of this stage can have
+	ordered_checkpoints:HashMap<CheckpointId,ModelId>,
+	unordered_checkpoints:HashSet<ModelId>,
 }
+impl Updatable<&StageUpdate> for Stage{
+	fn insert(&mut self,update:&StageUpdate){
+		for (&checkpoint,&model) in &update.ordered_checkpoints{
+			self.ordered_checkpoints.insert(checkpoint,model);
+		}
+		for &checkpoint in &update.unordered_checkpoints{
+			self.unordered_checkpoints.insert(checkpoint);
+		}
+	}
+	fn remove(&mut self,update:&StageUpdate){
+		for (checkpoint,_) in &update.ordered_checkpoints{
+			self.ordered_checkpoints.remove(checkpoint);
+		}
+		for model in &update.unordered_checkpoints{
+			self.unordered_checkpoints.remove(model);
+		}
+	}
+}
+
+#[derive(Clone,Copy,Hash,Eq,PartialEq)]
+pub enum Zone{
+	Start,
+	Finish,
+	Anticheat,
+}
+#[derive(Clone,Hash,Eq,PartialEq)]
 pub struct ModeId(u32);
+impl ModeId{
+	pub const MAIN:Self=Self(0);
+	pub const BONUS:Self=Self(1);
+	pub const fn mode(mode_id:u32)->Self{
+		Self(mode_id)
+	}
+}
 pub struct Mode{
 	style:gameplay_style::StyleModifiers,
-	start:ModelId,
-	zones:HashMap<ModelId,ZoneBehaviour>,
-	stages:Vec<Stage>,
+	start:ModelId,//when you press reset you go here
+	zones:HashMap<ModelId,Zone>,
+	stages:Vec<Stage>,//when you load the map you go to stages[0].spawn
 	//mutually exlusive stage element behaviour
 	elements:HashMap<ModelId,StageElement>,
 	jump_limit:HashMap<ModelId,u32>,
@@ -47,23 +96,25 @@ impl Mode{
 	pub fn get_spawn_model_id(&self,stage:StageId)->Option<ModelId>{
 		self.stages.get(stage.0 as usize).map(|s|s.spawn)
 	}
+	//TODO: put this in the SNF
 	pub fn denormalize_data(&mut self){
 		//expand and index normalized data
+		self.zones.insert(self.start,Zone::Start);
 		for (stage_id,stage) in self.stages.iter().enumerate(){
 			self.elements.insert(stage.spawn,StageElement{
 				stage:StageId(stage_id as u32),
 				force:false,
 				behaviour:StageElementBehaviour::SpawnAt,
 			});
-			for &model_id in &stage.ordered_checkpoints{
-				self.elements.insert(model_id,StageElement{
+			for (_,&model) in &stage.ordered_checkpoints{
+				self.elements.insert(model,StageElement{
 					stage:StageId(stage_id as u32),
 					force:false,
 					behaviour:StageElementBehaviour::Checkpoint,
 				});
 			}
-			for &model_id in &stage.unordered_checkpoints{
-				self.elements.insert(model_id,StageElement{
+			for &model in &stage.unordered_checkpoints{
+				self.elements.insert(model,StageElement{
 					stage:StageId(stage_id as u32),
 					force:false,
 					behaviour:StageElementBehaviour::Checkpoint,
@@ -72,19 +123,102 @@ impl Mode{
 		}
 	}
 }
+//this would be nice as a macro
+#[derive(Default)]
+pub struct ModeUpdate{
+	zones:HashMap<ModelId,Zone>,
+	stages:HashMap<StageId,StageUpdate>,
+	//mutually exlusive stage element behaviour
+	elements:HashMap<ModelId,StageElement>,
+	jump_limit:HashMap<ModelId,u32>,
+}
+impl Updatable<&ModeUpdate> for Mode{
+	fn insert(&mut self,update:&ModeUpdate){
+		for (&model,&zone) in &update.zones{
+			self.zones.insert(model,zone);
+		}
+		for (stage,stage_update) in &update.stages{
+			if let Some(stage)=self.stages.get_mut(stage.0 as usize){
+				stage.insert(stage_update);
+			}
+		}
+		for (&model,stage_element) in &update.elements{
+			self.elements.insert(model,stage_element.clone());
+		}
+		for (&model,&limit) in &update.jump_limit{
+			self.jump_limit.insert(model,limit);
+		}
+	}
+	fn remove(&mut self,update:&ModeUpdate){
+		for (model,_) in &update.zones{
+			self.zones.remove(model);
+		}
+		for (stage,stage_update) in &update.stages{
+			if let Some(stage)=self.stages.get_mut(stage.0 as usize){
+				stage.remove(stage_update);
+			}
+		}
+		for (model,_) in &update.elements{
+			self.elements.remove(model);
+		}
+		for (model,_) in &update.jump_limit{
+			self.jump_limit.remove(model);
+		}
+	}
+}
+impl ModeUpdate{
+	pub fn zone(model_id:ModelId,zone:Zone)->Self{
+		let mut mu=Self::default();
+		mu.zones.insert(model_id,zone);
+		mu
+	}
+	pub fn stage(stage_id:StageId,stage_update:StageUpdate)->Self{
+		let mut mu=Self::default();
+		mu.stages.insert(stage_id,stage_update);
+		mu
+	}
+	pub fn element(model_id:ModelId,element:StageElement)->Self{
+		let mut mu=Self::default();
+		mu.elements.insert(model_id,element);
+		mu
+	}
+	pub fn jump_limit(model_id:ModelId,jump_limit:u32)->Self{
+		let mut mu=Self::default();
+		mu.jump_limit.insert(model_id,jump_limit);
+		mu
+	}
+}
 
 #[derive(Default)]
 pub struct Modes{
 	modes:Vec<Mode>,
 }
 impl Modes{
-	pub fn clear(&mut self){
-		self.modes.clear();
+	pub fn new(modes:Vec<Mode>)->Self{
+		Self{
+			modes,
+		}
 	}
 	pub fn get_mode(&self,mode:ModeId)->Option<&Mode>{
 		self.modes.get(mode.0 as usize)
 	}
-	pub fn insert(&mut self,mode:Mode){
-		self.modes.push(mode);
+}
+pub struct ModesUpdate{
+	modes:HashMap<ModeId,ModeUpdate>,
+}
+impl Updatable<&ModesUpdate> for Modes{
+	fn insert(&mut self,update:&ModesUpdate){
+		for (mode,mode_update) in &update.modes{
+			if let Some(mode)=self.modes.get_mut(mode.0 as usize){
+				mode.insert(mode_update);
+			}
+		}
+	}
+	fn remove(&mut self,update:&ModesUpdate){
+		for (mode,mode_update) in &update.modes{
+			if let Some(mode)=self.modes.get_mut(mode.0 as usize){
+				mode.remove(mode_update);
+			}
+		}
 	}
 }
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index 99b060b..b567481 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,8 +1,10 @@
 pub mod bvh;
+pub mod map;
 pub mod aabb;
 pub mod model;
 pub mod zeroes;
 pub mod integer;
+pub mod updatable;
 pub mod instruction;
 pub mod gameplay_modes;
 pub mod gameplay_style;
diff --git a/src/map.rs b/src/map.rs
new file mode 100644
index 0000000..d85c28b
--- /dev/null
+++ b/src/map.rs
@@ -0,0 +1,11 @@
+use std::collections::HashMap;
+
+use crate::model;
+use crate::gameplay_modes;
+//this is the current map data loaded in memory
+pub struct Map{
+	modes:gameplay_modes::Modes,
+	models:model::Models,
+	//RenderPattern
+	textures:HashMap<u32,Vec<u8>>,
+}
diff --git a/src/model.rs b/src/model.rs
index cbf7370..ebbfabe 100644
--- a/src/model.rs
+++ b/src/model.rs
@@ -1,5 +1,7 @@
+use std::collections::HashMap;
 use crate::integer::{Planar64Vec3,Planar64Affine3};
 use crate::gameplay_attributes;
+use crate::updatable::Updatable;
 
 pub type TextureCoordinate=glam::Vec2;
 pub type Color4=glam::Vec4;
@@ -30,6 +32,8 @@ pub struct IndexedPhysicsGroup{
 	pub groups:Vec<GroupId>,
 }
 //This is a superset of PhysicsModel and GraphicsModel
+#[derive(Clone,Copy,Hash,Eq,PartialEq)]
+pub struct IndexedModelId(u32);
 pub struct IndexedModel{
 	pub unique_pos:Vec<Planar64Vec3>,//Unit32Vec3
 	pub unique_normal:Vec<Planar64Vec3>,//Unit32Vec3
@@ -47,8 +51,28 @@ pub struct IndexedModel{
 #[derive(Clone,Copy,Hash,Eq,PartialEq)]
 pub struct ModelId(u32);
 pub struct Model{
-	pub model:ModelId,
+	pub model:IndexedModelId,
 	pub attributes:gameplay_attributes::CollisionAttributesId,
 	pub color:Color4,//transparency is in here
 	pub transform:Planar64Affine3,
 }
+
+pub struct Models{
+	indexed_models:HashMap<IndexedModelId,IndexedModel>,
+	models:HashMap<ModelId,Model>,
+}
+impl Updatable<Models> for Models{
+	fn insert(&mut self,update:Models){
+		self.indexed_models.extend(update.indexed_models);
+		self.models.extend(update.models);
+	}
+	fn remove(&mut self,update:Models){
+		for (indexed_model_id,_) in &update.indexed_models{
+			self.indexed_models.remove(indexed_model_id);
+		}
+		for (model_id,_) in &update.models{
+			self.models.remove(model_id);
+		}
+		todo!("stop cloning models for remove");
+	}
+}
\ No newline at end of file
diff --git a/src/updatable.rs b/src/updatable.rs
new file mode 100644
index 0000000..f3d6b4d
--- /dev/null
+++ b/src/updatable.rs
@@ -0,0 +1,62 @@
+pub trait Updatable<Updater>{
+	fn insert(&mut self,update:Updater);
+	fn remove(&mut self,update:Updater);
+}
+//what if do like this
+//*
+pub trait Updatable2<Updater>{
+	fn update(&mut self,update:Updater);
+}
+#[derive(Clone,Copy,Hash,Eq,PartialEq)]
+struct InnerId(u32);
+#[derive(Clone)]
+struct Inner{
+	id:InnerId,
+	enabled:bool,
+}
+#[derive(Clone,Copy,Hash,Eq,PartialEq)]
+struct OuterId(u32);
+struct Outer{
+	id:OuterId,
+	inners:std::collections::HashMap<InnerId,Inner>,
+}
+
+enum Update<I,U>{
+	Insert(I),
+	Update(U),
+	Remove
+}
+
+struct InnerUpdate{
+	//#[updatable(Update)]
+	enabled:Option<bool>,
+}
+struct OuterUpdate{
+	//#[updatable(Insert,Update,Remove)]
+	inners:std::collections::HashMap<InnerId,Update<Inner,InnerUpdate>>,
+	//#[updatable(Update)]
+	//inners:std::collections::HashMap<InnerId,InnerUpdate>,
+}
+impl Updatable2<InnerUpdate> for Inner{
+	fn update(&mut self,update:InnerUpdate){
+		if let Some(enabled)=update.enabled{
+			self.enabled=enabled;
+		}
+	}
+}
+impl Updatable2<OuterUpdate> for Outer{
+	fn update(&mut self,update:OuterUpdate){
+		for (id,up) in update.inners{
+			match up{
+				Update::Insert(new_inner)=>self.inners.insert(id,new_inner),
+				Update::Update(inner_update)=>self.inners.get_mut(&id).map(|inner|{
+					let old=inner.clone();
+					inner.update(inner_update);
+					old
+				}),
+				Update::Remove=>self.inners.remove(&id),
+			};
+		}
+	}
+}
+//*/
\ No newline at end of file