it seems i didnt commit before taking a break for the semester
This commit is contained in:
parent
4a0673bde5
commit
dbeda509fc
8 changed files with 170 additions and 41 deletions
21
readme.md
21
readme.md
|
@ -47,3 +47,24 @@ shit that needs to be done with characters handling:
|
|||
[ ] Characters in current map/scene
|
||||
[ ] initiative tracker
|
||||
[ ] cards system (for a playstyle that relies less on the map)
|
||||
|
||||
|
||||
|
||||
Scene:
|
||||
Map:
|
||||
Background image (the map itself)
|
||||
Tokens:
|
||||
Location
|
||||
Image
|
||||
Character
|
||||
...
|
||||
...
|
||||
Images (popup images) (ability to list)
|
||||
Characters (ability to list)
|
||||
|
||||
Get a list of scenes
|
||||
user picks a scene to view
|
||||
get scene characters
|
||||
get short for each character
|
||||
get the map
|
||||
IF map THEN get tokens
|
|
@ -26,6 +26,7 @@ pub struct SpawnToken {
|
|||
}
|
||||
impl std::fmt::Debug for SpawnToken {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
// I dont want it to print the `img` field because it VERY long :)
|
||||
f.debug_struct("SpawnToken")
|
||||
.field("token_id", &self.token_id)
|
||||
.field("x", &self.x)
|
||||
|
|
|
@ -29,7 +29,7 @@ pub enum Request {
|
|||
GetChatHistory { amount: usize, from: usize },
|
||||
GetLastMessages { amount: usize, },
|
||||
// Map requests
|
||||
GetTokens,
|
||||
GetTokens { scene: usize },
|
||||
SpawnToken { map_id: usize, character: String, x: f32, y: f32, img_path: String },
|
||||
MoveToken { token_id: usize, x: f32, y: f32 },
|
||||
// Actions requests
|
||||
|
|
|
@ -56,6 +56,8 @@ impl AccessLevel {
|
|||
/// |_______________________|
|
||||
/// ```
|
||||
pub struct CharacterShort {
|
||||
/// Character id to make sure we all know who is who
|
||||
pub id: usize,
|
||||
/// Main title, usually the name
|
||||
pub title: String,
|
||||
/// little title under the main title, probably a title or class
|
||||
|
@ -64,6 +66,12 @@ pub struct CharacterShort {
|
|||
pub health: Option<String>,
|
||||
pub level: Option<i32>,
|
||||
}
|
||||
impl CharacterShort {
|
||||
pub fn with_id(mut self, id: usize) -> Self {
|
||||
self.id = id;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub enum EntryType {
|
||||
|
|
132
src/game/mod.rs
132
src/game/mod.rs
|
@ -2,48 +2,66 @@
|
|||
use std::{collections::HashMap, marker::PhantomData};
|
||||
|
||||
use character_sheet::{AccessLevel, Character, CharacterShort, EntryType};
|
||||
use scene::{Party, Scene, TokenInfo};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
pub mod character_sheet;
|
||||
pub mod chat_message;
|
||||
pub mod entry;
|
||||
pub mod scene;
|
||||
|
||||
pub trait GameImpl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Serialize + Deserialize<'a>> {
|
||||
/// Creates a new game
|
||||
fn new() -> Self;
|
||||
|
||||
// Character management
|
||||
/// Creates a new character, returning the character id
|
||||
fn create_character(&mut self) -> usize;
|
||||
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>>;
|
||||
fn characters(&self) -> Vec<usize>;
|
||||
fn character_short(&self, character_id: usize) -> Option<CharacterShort>;
|
||||
fn create_token(&mut self, map_id: usize, character: String, img_source: String, x: f32, y: f32) -> usize;
|
||||
fn move_token(&mut self, map_id: usize, token_id: usize, x: f32, y: f32) -> bool;
|
||||
// Scenes
|
||||
/// the list of available scenes
|
||||
fn scenes(&self) -> Vec<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<String>;
|
||||
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, map_id: usize, token_id: usize) -> Option<&TokenInfo>;
|
||||
fn available_tokens(&self) -> impl Iterator<Item = usize>;
|
||||
fn available_tokens(&self, scene: usize) -> Vec<usize>;
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct Game<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
||||
_a: PhantomData<A>,
|
||||
characters: Vec<C>,
|
||||
tokens: HashMap<usize, TokenInfo>,
|
||||
characters: Vec<(C, CharacterInfo)>,
|
||||
scenes: HashMap<usize, Scene>,
|
||||
}
|
||||
impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Serialize + Deserialize<'a>> GameImpl<'a, C, A> for Game<C, A> {
|
||||
fn new() -> Self {
|
||||
let mut tokens = HashMap::new();
|
||||
tokens.insert(0, TokenInfo { character: "bart".to_string(), map_id: 0, img_source: "assets/pf2r/tokens/louise.jpg".to_string(), x: 2.0, y: 2.0 });
|
||||
tokens.insert(0, TokenInfo { character: "bart".to_string(), img_source: "assets/pf2r/tokens/louise.jpg".to_string(), x: 2.0, y: 2.0 });
|
||||
let mut scenes = HashMap::new();
|
||||
scenes.insert(0, Scene { map: None, characters: vec![(0, Party(true))] });
|
||||
Self {
|
||||
_a: PhantomData,
|
||||
characters: Vec::new(),
|
||||
tokens,
|
||||
scenes,
|
||||
}
|
||||
}
|
||||
fn create_character(&mut self) -> usize {
|
||||
self.characters.push(C::default());
|
||||
self.characters.push((C::default(), CharacterInfo::default()));
|
||||
self.characters.len() - 1
|
||||
}
|
||||
fn move_token(&mut self, _map_id: usize, token_id: usize, x: f32, y: f32) -> bool {
|
||||
if let Some(ti) = self.tokens.get_mut(&token_id) {
|
||||
fn move_token(&mut self, scene: usize, token_id: usize, x: f32, y: f32) -> bool {
|
||||
let token = self.scenes
|
||||
.get_mut(&scene)
|
||||
.map(|s| s.map.as_mut())
|
||||
.flatten()
|
||||
.map(|m| m.tokens.get_mut(&token_id))
|
||||
.flatten();
|
||||
if let Some(ti) = token {
|
||||
ti.x = x;
|
||||
ti.y = y;
|
||||
true
|
||||
|
@ -52,8 +70,14 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
false
|
||||
}
|
||||
}
|
||||
fn token_info(&self, _map_id: usize, token_id: usize) -> Option<&TokenInfo> {
|
||||
if let Some(ti) = self.tokens.get(&token_id) {
|
||||
fn token_info(&self, scene: usize, token_id: usize) -> Option<&TokenInfo> {
|
||||
let token = self.scenes
|
||||
.get(&scene)
|
||||
.map(|s| s.map.as_ref())
|
||||
.flatten()
|
||||
.map(|m| m.tokens.get(&token_id))
|
||||
.flatten();
|
||||
if let Some(ti) = token {
|
||||
Some(ti)
|
||||
}
|
||||
else {
|
||||
|
@ -61,26 +85,43 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
}
|
||||
}
|
||||
|
||||
fn available_tokens(&self) -> impl Iterator<Item = usize> {
|
||||
self.tokens
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|k| *k) // this map feels stupid but keys() turns into a &usize iterator so :shrug:
|
||||
fn available_tokens(&self, scene: usize) -> Vec<usize> {
|
||||
self.scenes
|
||||
.get(&scene)
|
||||
.map(|s| s.map.as_ref())
|
||||
.flatten()
|
||||
.map(|m| m.tokens
|
||||
.keys()
|
||||
.into_iter()
|
||||
.map(|k| *k)
|
||||
.collect()
|
||||
) // this map feels stupid but keys() turns into a &usize iterator so :shrug:
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
fn create_token(&mut self, map_id: usize, character: String, img_source: String, x: f32, y: f32) -> usize {
|
||||
fn create_token(&mut self, scene: usize, character: String, img_source: String, x: f32, y: f32) -> usize {
|
||||
let mut id = 0;
|
||||
while self.tokens.contains_key(&id) {
|
||||
id += 1;
|
||||
let tokens = self.scenes
|
||||
.get_mut(&scene)
|
||||
.map(|s| s.map.as_mut())
|
||||
.flatten()
|
||||
.map(|m| &mut m.tokens);
|
||||
if let Some(tokens) = tokens {
|
||||
while tokens.contains_key(&id) {
|
||||
id += 1;
|
||||
}
|
||||
tokens.insert(id, TokenInfo { character, img_source, x, y });
|
||||
id
|
||||
}
|
||||
else {
|
||||
0
|
||||
}
|
||||
self.tokens.insert(id, TokenInfo { character, map_id, img_source, x, y });
|
||||
id
|
||||
}
|
||||
|
||||
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>> {
|
||||
self.characters
|
||||
.get(character_id)
|
||||
.map(|c| c.display(access))
|
||||
.map(|c| c.0.display(access))
|
||||
.unwrap_or(Vec::new())
|
||||
}
|
||||
|
||||
|
@ -93,21 +134,38 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
|||
fn character_short(&self, character_id: usize) -> Option<CharacterShort> {
|
||||
self.characters
|
||||
.get(character_id)
|
||||
.map(|c| c.short())
|
||||
.map(|c| c.0.short().with_id(character_id))
|
||||
}
|
||||
|
||||
fn scenes(&self) -> Vec<usize> {
|
||||
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().with_id(c.0)))
|
||||
.flatten()
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
fn scene_map(&self, scene_id: usize) -> Option<String> {
|
||||
self.scenes
|
||||
.get(&scene_id)
|
||||
.map(|s| s.map.as_ref().map(|m| m.background.clone()))
|
||||
.flatten()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TokenInfo {
|
||||
/// Which character the token refers to
|
||||
pub character: String,
|
||||
/// Which map does the token exists in (allowing multiple tokens in multiple maps)
|
||||
pub map_id: usize,
|
||||
/// Token image source, as path relative to the data directory
|
||||
pub img_source: String,
|
||||
// x, y are floats to allow 'free movement'
|
||||
/// X position, in grid slots units (integers are grid aligned)
|
||||
pub x: f32,
|
||||
/// Y position, in grid slots units (integers are grid aligned)
|
||||
pub y: f32,
|
||||
|
||||
#[derive(Serialize, Deserialize, Default, Debug, Clone)]
|
||||
struct CharacterInfo {
|
||||
pub owner: String,
|
||||
pub party: Party,
|
||||
}
|
40
src/game/scene.rs
Normal file
40
src/game/scene.rs
Normal file
|
@ -0,0 +1,40 @@
|
|||
use std::collections::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Scene {
|
||||
/// 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)
|
||||
pub characters: Vec<(usize, Party)>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct Map {
|
||||
/// Image source for the background of the map
|
||||
pub background: String,
|
||||
/// Tokens in the current map (should be of characters), maps from token_id to its info
|
||||
pub tokens: HashMap<usize, TokenInfo>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct TokenInfo {
|
||||
/// Which character the token refers to
|
||||
pub character: String,
|
||||
/// Token image source, as path relative to the data directory
|
||||
pub img_source: String,
|
||||
// x, y are floats to allow 'free movement'
|
||||
/// X position, in grid slots units (integers are grid aligned)
|
||||
pub x: f32,
|
||||
/// Y position, in grid slots units (integers are grid aligned)
|
||||
pub y: f32,
|
||||
}
|
||||
|
||||
// for now this is defined so i could easily modify this later if needed (and only change initializations)
|
||||
#[derive(Serialize, Deserialize, Debug, Clone, Copy, Default)]
|
||||
pub struct Party(pub bool);
|
||||
impl Party {
|
||||
pub fn can_see(&self, other: Party) -> bool {
|
||||
!self.0 || other.0
|
||||
}
|
||||
}
|
|
@ -120,8 +120,8 @@ impl GameServer {
|
|||
self.chat.iter().skip(start).map(|m| m.1.clone()).collect();
|
||||
_ = broadcast.send((Some(id), api::Response::GetChatHistory(history)));
|
||||
},
|
||||
api::Request::GetTokens => {
|
||||
for token_id in self.game.available_tokens() {
|
||||
api::Request::GetTokens { scene } => {
|
||||
for token_id in self.game.available_tokens(scene) {
|
||||
if let Some(ti) = self.game.token_info(0, token_id) {
|
||||
let bits = std::fs::read(&ti.img_source).expect("FAILED READING TOKEN IMAGE");
|
||||
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
||||
|
|
|
@ -133,6 +133,7 @@ impl Character<Entry> for Pathfinder2rCharacterSheet {
|
|||
|
||||
fn short(&self) -> CharacterShort {
|
||||
CharacterShort {
|
||||
id: 0,
|
||||
title: self.name.clone(),
|
||||
sub_title: Some("A Character!".to_string()),
|
||||
health: Some([ "Dying", "Hurt", "Harmed", "Unharmed" ][((self.health * 4) / self.max_health) as usize].to_string()),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue