support multiple scenes and passing between them
This commit is contained in:
parent
2d46cbb4d0
commit
838c89ac73
8 changed files with 124 additions and 67 deletions
|
@ -111,8 +111,11 @@ tavern.onlogin = (s) => {
|
|||
login.style.display = 'none';
|
||||
game.style.display = 'flex';
|
||||
// get last 50 msgs (i think that is enough for now) when we get in
|
||||
// TODO: Maybe move this into the server itself? that is a lot of stuff that we know are gonna happen...
|
||||
// For now i'll keep it like that tho
|
||||
tavern.get_last_msgs(50);
|
||||
tavern.get_current_scene();
|
||||
// TODO: Perhaps figure out a way to show a certain scene? maybe on the server it would make more sense
|
||||
tavern.get_scene_list();
|
||||
}
|
||||
else {
|
||||
alert("Invalid username or password!");
|
||||
|
@ -210,6 +213,14 @@ tavern.onshowscene = (show) => {
|
|||
tavern.onspawntoken(token);
|
||||
}
|
||||
}
|
||||
tavern.onscenelist = (list) => {
|
||||
console.log(list);
|
||||
let div = document.getElementById('scene-list');
|
||||
div.innerHTML = '';
|
||||
for (let scene of list.scenes) {
|
||||
div.innerHTML += `<button onclick='tavern.get_scene(${scene[0]});'>${scene[1]}</button>`;
|
||||
}
|
||||
}
|
||||
function onLoginClick() {
|
||||
let username = document.getElementById('login-username').value;
|
||||
let pass = document.getElementById('login-pass').value;
|
||||
|
|
|
@ -36,8 +36,7 @@
|
|||
</div>
|
||||
<div id="game-view"
|
||||
style="display: flex; overflow: hidden; position: relative; top: 0px; left: 0px; flex-grow: 1;">
|
||||
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 5;">
|
||||
floating<br>stuff
|
||||
<div id="scene-list">
|
||||
</div>
|
||||
<div id="dice-roll-popup"
|
||||
style="display:none; justify-content: center; width: 100%; height: 100%; background-color: transparent; position: absolute; z-index: 10; top: 0px; left: 0px">
|
||||
|
|
|
@ -93,3 +93,22 @@ body {
|
|||
.dice-roll-row p {
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#scene-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 5px;
|
||||
z-index: 5;
|
||||
border-color: black;
|
||||
border-width: 2px;
|
||||
border-style: solid;
|
||||
background-color: black;
|
||||
}
|
||||
|
||||
#scene-list button {
|
||||
background-color: wheat;
|
||||
border-width: 0px;
|
||||
}
|
|
@ -3,7 +3,6 @@ const tavern = {
|
|||
msgs: [],
|
||||
connected: false,
|
||||
loggedIn: false,
|
||||
currentScene: 0,
|
||||
call: (f, ...args) => {
|
||||
if (typeof (f) == "function") {
|
||||
f(...args);
|
||||
|
@ -57,9 +56,11 @@ tavern.socket.onmessage = (m) => {
|
|||
tavern.call(tavern.onmovetoken, m.move_token);
|
||||
}
|
||||
if (m.show_scene) {
|
||||
tavern.currentScene = m.show_scene.scene;
|
||||
tavern.call(tavern.onshowscene, m.show_scene);
|
||||
}
|
||||
if (m.scene_list) {
|
||||
tavern.call(tavern.onscenelist, m.scene_list);
|
||||
}
|
||||
}
|
||||
tavern.login = (username, password) => {
|
||||
if (!tavern.connected || tavern.loggedIn) { return false; }
|
||||
|
@ -96,7 +97,11 @@ tavern.action_result = (name, source, targets, results) => {
|
|||
if (!tavern.connected || tavern.loggedIn) { return false; }
|
||||
tavern.socket.send(JSON.stringify({ action_result: { name: name, source: source ?? '', targets: targets ?? [], results: results } }));
|
||||
}
|
||||
tavern.get_current_scene = () => {
|
||||
tavern.get_scene = (id) => {
|
||||
if (!tavern.connected || tavern.loggedIn) { return; }
|
||||
tavern.socket.send(JSON.stringify('get_current_scene'))
|
||||
tavern.socket.send(JSON.stringify({ get_scene: { id: id } }))
|
||||
}
|
||||
tavern.get_scene_list = () => {
|
||||
if (!tavern.connected || tavern.loggedIn) { return; }
|
||||
tavern.socket.send(JSON.stringify('get_scene_list'))
|
||||
}
|
|
@ -48,7 +48,10 @@ pub enum Request {
|
|||
amount: usize,
|
||||
},
|
||||
// Map requests
|
||||
GetCurrentScene,
|
||||
GetScene {
|
||||
id: usize,
|
||||
},
|
||||
GetSceneList,
|
||||
GetTokens {
|
||||
scene: usize,
|
||||
},
|
||||
|
@ -82,6 +85,9 @@ pub enum Response {
|
|||
grid_cell_size: Option<f32>,
|
||||
grid_offset: Option<[f32; 2]>,
|
||||
},
|
||||
SceneList {
|
||||
scenes: Vec<(usize, String)>,
|
||||
},
|
||||
MoveToken {
|
||||
token_id: usize,
|
||||
x: f32,
|
||||
|
|
|
@ -5,8 +5,6 @@ use character_sheet::{AccessLevel, Character, CharacterShort, EntryType};
|
|||
use scene::{Party, Scene, TokenInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::game::scene::Map;
|
||||
|
||||
pub mod character_sheet;
|
||||
pub mod chat_message;
|
||||
pub mod entry;
|
||||
|
@ -26,10 +24,7 @@ pub trait GameImpl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::
|
|||
// Scenes
|
||||
/// the list of available scenes
|
||||
fn scenes(&self) -> Vec<usize>;
|
||||
fn current_scene(&self) -> usize;
|
||||
/// Gets the map background (file path)
|
||||
fn scene_characters(&self, scene: usize, character_id: usize) -> Option<Vec<CharacterShort>>;
|
||||
fn scene_map(&self, scene_id: usize) -> Option<&Map>;
|
||||
fn get_scene(&self, id: usize) -> Option<&Scene>;
|
||||
fn create_token(&mut self, scene_id: usize, character: String, img_source: String, x: f32, y: f32) -> usize;
|
||||
fn move_token(&mut self, scene_id: usize, token_id: usize, x: f32, y: f32) -> bool;
|
||||
fn token_info(&self, scene: usize, token_id: usize) -> Option<&TokenInfo>;
|
||||
|
@ -41,7 +36,6 @@ pub struct Game<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
|||
_a: PhantomData<A>,
|
||||
characters: Vec<(C, CharacterInfo)>,
|
||||
scenes: HashMap<usize, Scene>,
|
||||
current_scene: usize,
|
||||
}
|
||||
impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Serialize + Deserialize<'a>>
|
||||
GameImpl<'a, C, A> for Game<C, A>
|
||||
|
@ -70,6 +64,8 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
scenes.insert(
|
||||
0,
|
||||
Scene {
|
||||
title: "Dungeon".to_string(),
|
||||
visible_to_users: true,
|
||||
map: Some(scene::Map {
|
||||
background: "assets/pf2r/maps/testmap.jpg".to_string(),
|
||||
grid_cell_size: 150.0,
|
||||
|
@ -79,11 +75,24 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
characters: vec![(0, Party(true))],
|
||||
},
|
||||
);
|
||||
scenes.insert(
|
||||
1,
|
||||
Scene {
|
||||
title: "Basement".to_string(),
|
||||
visible_to_users: false,
|
||||
map: Some(scene::Map {
|
||||
background: "assets/pf2r/maps/testmap.jpg".to_string(),
|
||||
grid_cell_size: 300.0,
|
||||
grid_offset: [80.0, 35.0],
|
||||
tokens: HashMap::new(),
|
||||
}),
|
||||
characters: vec![(0, Party(true))],
|
||||
},
|
||||
);
|
||||
Self {
|
||||
_a: PhantomData,
|
||||
characters: Vec::new(),
|
||||
scenes,
|
||||
current_scene: 0,
|
||||
}
|
||||
}
|
||||
fn create_character(&mut self) -> usize {
|
||||
|
@ -175,24 +184,8 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
self.scenes.keys().map(|k| *k).collect()
|
||||
}
|
||||
|
||||
fn scene_characters(&self, scene: usize, id: usize) -> Option<Vec<CharacterShort>> {
|
||||
let party = self.characters.get(id).map(|c| c.1.party).unwrap_or_default();
|
||||
self.scenes.get(&scene).map(|s| {
|
||||
s.characters
|
||||
.iter()
|
||||
.filter(|c| party.can_see(c.1))
|
||||
.map(|c| self.characters.get(c.0).map(|e| e.0.short(c.0)))
|
||||
.flatten()
|
||||
.collect()
|
||||
})
|
||||
}
|
||||
|
||||
fn scene_map(&self, scene_id: usize) -> Option<&Map> {
|
||||
self.scenes.get(&scene_id).map(|s| s.map.as_ref()).flatten()
|
||||
}
|
||||
|
||||
fn current_scene(&self) -> usize {
|
||||
self.current_scene
|
||||
fn get_scene(&self, id: usize) -> Option<&Scene> {
|
||||
self.scenes.get(&id)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,9 @@ use std::collections::HashMap;
|
|||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Scene {
|
||||
/// Scene title/name
|
||||
pub title: String,
|
||||
pub visible_to_users: bool,
|
||||
/// Map for the scene, None in case of theater of the mind kinda gameplay
|
||||
pub map: Option<Map>,
|
||||
/// List of character ids, and can the party see them (maybe change that to a different thing to allow maybe 2 parties? maybe a bit field)
|
||||
|
|
53
src/lib.rs
53
src/lib.rs
|
@ -156,13 +156,19 @@ impl GameServer {
|
|||
}
|
||||
}
|
||||
}
|
||||
api::Request::GetCurrentScene => {
|
||||
let scene = self.game.current_scene();
|
||||
api::Request::GetScene { id: scene_id } => {
|
||||
if self
|
||||
.game
|
||||
.get_scene(scene_id)
|
||||
.map(|s| s.visible_to_users)
|
||||
.unwrap_or(false) ||
|
||||
*self.users.get(&id).unwrap_or(&false)
|
||||
{
|
||||
let scene_tokens = self
|
||||
.game
|
||||
.available_tokens(scene)
|
||||
.available_tokens(scene_id)
|
||||
.iter()
|
||||
.map(|&id| self.game.token_info(scene, id).map(|info| (id, info)))
|
||||
.map(|&id| self.game.token_info(scene_id, id).map(|info| (id, info)))
|
||||
.flatten()
|
||||
.map(|(id, info)| SpawnToken {
|
||||
token_id: id,
|
||||
|
@ -171,11 +177,11 @@ impl GameServer {
|
|||
img: info.img_source.clone(),
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
let map = self.game.scene_map(scene);
|
||||
let map = self.game.get_scene(scene_id).map(|s| s.map.as_ref()).flatten();
|
||||
_ = broadcast.send((
|
||||
Some(id.clone()),
|
||||
api::Response::ShowScene {
|
||||
scene: scene,
|
||||
scene: scene_id,
|
||||
tokens: scene_tokens,
|
||||
background: map.map(|m| m.background.clone()),
|
||||
grid_cell_size: map.map(|m| m.grid_cell_size),
|
||||
|
@ -183,6 +189,20 @@ impl GameServer {
|
|||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
api::Request::GetSceneList => {
|
||||
let admin = *self.users.get(&id).unwrap_or(&false);
|
||||
let scenes = self
|
||||
.game
|
||||
.scenes()
|
||||
.iter()
|
||||
.map(|id| self.game.get_scene(*id).map(|s| (id, s)))
|
||||
.flatten()
|
||||
.filter(|(_, scene)| admin || scene.visible_to_users)
|
||||
.map(|(id, s)| (*id, s.title.to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
_ = broadcast.send((Some(id.clone()), api::Response::SceneList { scenes: scenes }))
|
||||
}
|
||||
api::Request::SpawnToken {
|
||||
map_id,
|
||||
character,
|
||||
|
@ -190,13 +210,10 @@ impl GameServer {
|
|||
y,
|
||||
img_path,
|
||||
} => {
|
||||
// TODO: Make sure the user is an admin
|
||||
let token_id = self.game.create_token(map_id, character, img_path.clone(), x, y);
|
||||
_ = broadcast.send((
|
||||
if map_id == self.game.current_scene() {
|
||||
None
|
||||
} else {
|
||||
Some(id.clone())
|
||||
},
|
||||
None, // TODO: add the option to spawn the token hidden
|
||||
api::Response::SpawnToken(SpawnToken {
|
||||
token_id,
|
||||
x,
|
||||
|
@ -250,11 +267,15 @@ impl GameServer {
|
|||
_ = broadcast.send((Some(id), api::Response::Shutdown));
|
||||
}
|
||||
api::Request::Shutdown => break,
|
||||
api::Request::CharacterDisplay { id } => todo!(),
|
||||
api::Request::CharacterInputs { id } => todo!(),
|
||||
api::Request::CharacterGetField { id, field } => todo!(),
|
||||
api::Request::CharacterSetField { id, field, val } => todo!(),
|
||||
api::Request::CharacterAssign { id, user } => todo!(),
|
||||
api::Request::CharacterDisplay { id: _ } => {}
|
||||
api::Request::CharacterInputs { id: _ } => todo!(),
|
||||
api::Request::CharacterGetField { id: _, field: _ } => todo!(),
|
||||
api::Request::CharacterSetField {
|
||||
id: _,
|
||||
field: _,
|
||||
val: _,
|
||||
} => todo!(),
|
||||
api::Request::CharacterAssign { id: _, user: _ } => todo!(),
|
||||
}
|
||||
}
|
||||
_ = broadcast.send((None, api::Response::Shutdown));
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue