From 23e23ad1a21869ce0f1dde97cb17d45c54c442f2 Mon Sep 17 00:00:00 2001 From: Rusty Striker Date: Fri, 18 Jul 2025 14:51:26 +0300 Subject: [PATCH] allow invisible tokens --- src/api/mod.rs | 1 + src/game/mod.rs | 16 ++++++++++++++-- src/game/scene.rs | 2 ++ src/lib.rs | 48 ++++++++++++++++++++++++++++++----------------- 4 files changed, 48 insertions(+), 19 deletions(-) diff --git a/src/api/mod.rs b/src/api/mod.rs index 1dffc46..b36fcc4 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -70,6 +70,7 @@ pub enum Request { SpawnToken { map_id: usize, character: String, + visible_to_players: bool, x: f32, y: f32, img_path: String, diff --git a/src/game/mod.rs b/src/game/mod.rs index a3b0092..7860a2c 100644 --- a/src/game/mod.rs +++ b/src/game/mod.rs @@ -26,6 +26,7 @@ impl<'a, C: Character + Serialize + Deserialize<'a>, A: entry::GameEntry + Se img_source: "assets/pf2r/tokens/adams.png".to_string(), x: 2.0, y: 2.0, + visible_to_players: true, }, ); tokens.insert( @@ -35,6 +36,7 @@ impl<'a, C: Character + Serialize + Deserialize<'a>, A: entry::GameEntry + Se img_source: "assets/pf2r/tokens/tim.png".to_string(), x: 0.0, y: 2.0, + visible_to_players: true, }, ); let mut scenes = HashMap::new(); @@ -103,9 +105,10 @@ impl<'a, C: Character + Serialize + Deserialize<'a>, A: entry::GameEntry + Se if let Some(ti) = token { Some(ti) } else { None } } - pub fn available_tokens(&self, scene: usize) -> Vec { + pub fn available_tokens(&self, scene: usize, admin: bool) -> Vec { self.scenes .get(&scene) + .filter(|&s| s.visible_to_users || admin) .map(|s| s.map.as_ref()) .flatten() // this map feels stupid but keys() turns into a &usize iterator so :shrug: @@ -113,7 +116,15 @@ impl<'a, C: Character + Serialize + Deserialize<'a>, A: entry::GameEntry + Se .unwrap_or(Vec::new()) } - pub fn create_token(&mut self, scene: usize, character: String, img_source: String, x: f32, y: f32) -> usize { + pub fn create_token( + &mut self, + scene: usize, + character: String, + img_source: String, + x: f32, + y: f32, + visible_to_players: bool, + ) -> usize { let mut id = 0; let tokens = self .scenes @@ -132,6 +143,7 @@ impl<'a, C: Character + Serialize + Deserialize<'a>, A: entry::GameEntry + Se img_source, x, y, + visible_to_players, }, ); id diff --git a/src/game/scene.rs b/src/game/scene.rs index f8c5cee..25f5b53 100644 --- a/src/game/scene.rs +++ b/src/game/scene.rs @@ -35,6 +35,8 @@ pub struct TokenInfo { pub x: f32, /// Y position, in grid slots units (integers are grid aligned) pub y: f32, + /// Is the token visible to players or only to admins + pub visible_to_players: bool, } // for now this is defined so i could easily modify this later if needed (and only change initializations) diff --git a/src/lib.rs b/src/lib.rs index d2bf8f7..48a0e78 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,7 +120,7 @@ impl GameServer { api::Request::Message(mut msg) => { if msg.id == 0 || msg.id > self.chat.len() { msg.id = self.chat.len() + 1; // set the message id, 0 is invalid - } else if id == self.chat[msg.id - 1].0 || *self.users.get(&id).unwrap_or(&false) { + } else if id == self.chat[msg.id - 1].0 || self.is_admin(&id) { self.chat[msg.id - 1] = (id.clone(), msg.clone()); } else { // if its an edit message and editor is not the owner, skip @@ -142,8 +142,10 @@ impl GameServer { _ = broadcast.send((Some(id), self.get_last_messages(amount))); } api::Request::GetTokens { scene } => { - for token_id in self.game.available_tokens(scene) { - if let Some(ti) = self.game.token_info(0, token_id) { + for token_id in self.game.available_tokens(scene, self.is_admin(&id)) { + if let Some(ti) = self.game.token_info(0, token_id) && + (ti.visible_to_players) + { _ = broadcast.send(( Some(id.clone()), api::Response::SpawnToken(SpawnToken { @@ -162,10 +164,9 @@ impl GameServer { } } api::Request::CreateScene { title } => { - if *self.users.get(&id).unwrap_or(&false) { + if self.is_admin(&id) { self.game.create_scene(title); - let admin = *self.users.get(&id).unwrap_or(&false); - let scenes = self.get_scene_list(admin); + let scenes = self.get_scene_list(true); _ = broadcast.send((Some(id), scenes)); } } @@ -174,7 +175,7 @@ impl GameServer { grid_offset, scene, } => { - if *self.users.get(&id).unwrap_or(&false) { + if self.is_admin(&id) { if let Some(s) = self.game.get_scene_mut(scene) { if let Some(map) = s.map.as_mut() { map.grid_cell_size = grid_cell_size; @@ -188,15 +189,14 @@ impl GameServer { } } api::Request::SceneSetVisible { scene, visible } => { - if *self.users.get(&id).unwrap_or(&false) { + if self.is_admin(&id) { self.game.scene_visible(scene, visible); _ = broadcast.send((None, self.get_scene_list(false))); _ = broadcast.send((Some(id), self.get_scene_list(true))); } } api::Request::GetSceneList => { - let admin = *self.users.get(&id).unwrap_or(&false); - _ = broadcast.send((Some(id.clone()), self.get_scene_list(admin))); + _ = broadcast.send((Some(id.clone()), self.get_scene_list(self.is_admin(&id)))); } api::Request::SpawnToken { map_id, @@ -204,11 +204,19 @@ impl GameServer { x, y, img_path, + visible_to_players, } => { - if *self.users.get(&id).unwrap_or(&false) { - let token_id = self.game.create_token(map_id, character, img_path.clone(), x, y); + if self.is_admin(&id) { + let scene_visible = self.game.get_scene(map_id).map(|s| s.visible_to_users).unwrap_or(false); + let token_id = + self.game + .create_token(map_id, character, img_path.clone(), x, y, visible_to_players); _ = broadcast.send(( - None, // TODO: add the option to spawn the token hidden + if visible_to_players && scene_visible { + None + } else { + Some(id) + }, api::Response::SpawnToken(SpawnToken { token_id, x, @@ -222,6 +230,7 @@ impl GameServer { // TODO: add check to make sure the actor is authorized to move the token if self.game.move_token(0, token_id, x, y) { // TODO: maybe chage move_token to return optional x,y values if succeeded to make sure the token is where it was going to be + // TODO: Check if the token is visible to players and if not, only update it's position to admins or something _ = broadcast.send((None, api::Response::MoveToken { token_id, x, y })); } } @@ -244,7 +253,7 @@ impl GameServer { } api::Request::CreateCharacter => { // check if user is admin - if self.users.get(&id).map(|a| *a).unwrap_or(false) { + if self.is_admin(&id) { let new_id = self.game.create_character(); // return the new id with the character i think _ = broadcast.send((Some(id), api::Response::CharacterCreated(new_id))); @@ -263,7 +272,7 @@ impl GameServer { _ = broadcast.send((Some(id), api::Response::Shutdown)); } api::Request::Shutdown => { - if *self.users.get(&id).unwrap_or(&false) { + if self.is_admin(&id) { break; } } @@ -322,16 +331,17 @@ impl GameServer { } fn get_scene(&self, user_id: &str, scene_id: usize) -> Option { + let admin = self.is_admin(user_id); if self .game .get_scene(scene_id) .map(|s| s.visible_to_users) .unwrap_or(false) || - *self.users.get(user_id).unwrap_or(&false) + admin { let scene_tokens = self .game - .available_tokens(scene_id) + .available_tokens(scene_id, admin) .iter() .map(|&id| self.game.token_info(scene_id, id).map(|info| (id, info))) .flatten() @@ -354,4 +364,8 @@ impl GameServer { None } } + + fn is_admin(&self, id: &str) -> bool { + self.users.get(id).map(|a| *a).unwrap_or(false) + } }