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
|
[ ] Characters in current map/scene
|
||||||
[ ] initiative tracker
|
[ ] initiative tracker
|
||||||
[ ] cards system (for a playstyle that relies less on the map)
|
[ ] 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 {
|
impl std::fmt::Debug for SpawnToken {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
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")
|
f.debug_struct("SpawnToken")
|
||||||
.field("token_id", &self.token_id)
|
.field("token_id", &self.token_id)
|
||||||
.field("x", &self.x)
|
.field("x", &self.x)
|
||||||
|
|
|
@ -29,7 +29,7 @@ pub enum Request {
|
||||||
GetChatHistory { amount: usize, from: usize },
|
GetChatHistory { amount: usize, from: usize },
|
||||||
GetLastMessages { amount: usize, },
|
GetLastMessages { amount: usize, },
|
||||||
// Map requests
|
// Map requests
|
||||||
GetTokens,
|
GetTokens { scene: usize },
|
||||||
SpawnToken { map_id: usize, character: String, x: f32, y: f32, img_path: String },
|
SpawnToken { map_id: usize, character: String, x: f32, y: f32, img_path: String },
|
||||||
MoveToken { token_id: usize, x: f32, y: f32 },
|
MoveToken { token_id: usize, x: f32, y: f32 },
|
||||||
// Actions requests
|
// Actions requests
|
||||||
|
|
|
@ -56,6 +56,8 @@ impl AccessLevel {
|
||||||
/// |_______________________|
|
/// |_______________________|
|
||||||
/// ```
|
/// ```
|
||||||
pub struct CharacterShort {
|
pub struct CharacterShort {
|
||||||
|
/// Character id to make sure we all know who is who
|
||||||
|
pub id: usize,
|
||||||
/// Main title, usually the name
|
/// Main title, usually the name
|
||||||
pub title: String,
|
pub title: String,
|
||||||
/// little title under the main title, probably a title or class
|
/// little title under the main title, probably a title or class
|
||||||
|
@ -64,6 +66,12 @@ pub struct CharacterShort {
|
||||||
pub health: Option<String>,
|
pub health: Option<String>,
|
||||||
pub level: Option<i32>,
|
pub level: Option<i32>,
|
||||||
}
|
}
|
||||||
|
impl CharacterShort {
|
||||||
|
pub fn with_id(mut self, id: usize) -> Self {
|
||||||
|
self.id = id;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub enum EntryType {
|
pub enum EntryType {
|
||||||
|
|
126
src/game/mod.rs
126
src/game/mod.rs
|
@ -2,48 +2,66 @@
|
||||||
use std::{collections::HashMap, marker::PhantomData};
|
use std::{collections::HashMap, marker::PhantomData};
|
||||||
|
|
||||||
use character_sheet::{AccessLevel, Character, CharacterShort, EntryType};
|
use character_sheet::{AccessLevel, Character, CharacterShort, EntryType};
|
||||||
|
use scene::{Party, Scene, TokenInfo};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
pub mod character_sheet;
|
pub mod character_sheet;
|
||||||
pub mod chat_message;
|
pub mod chat_message;
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
|
pub mod scene;
|
||||||
|
|
||||||
pub trait GameImpl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Serialize + Deserialize<'a>> {
|
pub trait GameImpl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Serialize + Deserialize<'a>> {
|
||||||
/// Creates a new game
|
/// Creates a new game
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
|
|
||||||
|
// Character management
|
||||||
/// Creates a new character, returning the character id
|
/// Creates a new character, returning the character id
|
||||||
fn create_character(&mut self) -> usize;
|
fn create_character(&mut self) -> usize;
|
||||||
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>>;
|
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>>;
|
||||||
fn characters(&self) -> Vec<usize>;
|
fn characters(&self) -> Vec<usize>;
|
||||||
fn character_short(&self, character_id: usize) -> Option<CharacterShort>;
|
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;
|
// Scenes
|
||||||
fn move_token(&mut self, map_id: usize, token_id: usize, x: f32, y: f32) -> bool;
|
/// 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 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)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct Game<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
pub struct Game<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
||||||
_a: PhantomData<A>,
|
_a: PhantomData<A>,
|
||||||
characters: Vec<C>,
|
characters: Vec<(C, CharacterInfo)>,
|
||||||
tokens: HashMap<usize, TokenInfo>,
|
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> {
|
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 {
|
fn new() -> Self {
|
||||||
let mut tokens = HashMap::new();
|
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 {
|
Self {
|
||||||
_a: PhantomData,
|
_a: PhantomData,
|
||||||
characters: Vec::new(),
|
characters: Vec::new(),
|
||||||
tokens,
|
scenes,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn create_character(&mut self) -> usize {
|
fn create_character(&mut self) -> usize {
|
||||||
self.characters.push(C::default());
|
self.characters.push((C::default(), CharacterInfo::default()));
|
||||||
self.characters.len() - 1
|
self.characters.len() - 1
|
||||||
}
|
}
|
||||||
fn move_token(&mut self, _map_id: usize, token_id: usize, x: f32, y: f32) -> bool {
|
fn move_token(&mut self, scene: usize, token_id: usize, x: f32, y: f32) -> bool {
|
||||||
if let Some(ti) = self.tokens.get_mut(&token_id) {
|
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.x = x;
|
||||||
ti.y = y;
|
ti.y = y;
|
||||||
true
|
true
|
||||||
|
@ -52,8 +70,14 @@ impl<'a, C: Character<A> + Serialize + Deserialize<'a>, A: entry::GameEntry + Se
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn token_info(&self, _map_id: usize, token_id: usize) -> Option<&TokenInfo> {
|
fn token_info(&self, scene: usize, token_id: usize) -> Option<&TokenInfo> {
|
||||||
if let Some(ti) = self.tokens.get(&token_id) {
|
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)
|
Some(ti)
|
||||||
}
|
}
|
||||||
else {
|
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> {
|
fn available_tokens(&self, scene: usize) -> Vec<usize> {
|
||||||
self.tokens
|
self.scenes
|
||||||
|
.get(&scene)
|
||||||
|
.map(|s| s.map.as_ref())
|
||||||
|
.flatten()
|
||||||
|
.map(|m| m.tokens
|
||||||
.keys()
|
.keys()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|k| *k) // this map feels stupid but keys() turns into a &usize iterator so :shrug:
|
.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;
|
let mut id = 0;
|
||||||
while self.tokens.contains_key(&id) {
|
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;
|
id += 1;
|
||||||
}
|
}
|
||||||
self.tokens.insert(id, TokenInfo { character, map_id, img_source, x, y });
|
tokens.insert(id, TokenInfo { character, img_source, x, y });
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>> {
|
fn display_character(&self, character_id: usize, access: AccessLevel) -> Vec<Option<(String, EntryType)>> {
|
||||||
self.characters
|
self.characters
|
||||||
.get(character_id)
|
.get(character_id)
|
||||||
.map(|c| c.display(access))
|
.map(|c| c.0.display(access))
|
||||||
.unwrap_or(Vec::new())
|
.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> {
|
fn character_short(&self, character_id: usize) -> Option<CharacterShort> {
|
||||||
self.characters
|
self.characters
|
||||||
.get(character_id)
|
.get(character_id)
|
||||||
.map(|c| c.short())
|
.map(|c| c.0.short().with_id(character_id))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
fn scenes(&self) -> Vec<usize> {
|
||||||
pub struct TokenInfo {
|
self.scenes.keys().map(|k| *k).collect()
|
||||||
/// Which character the token refers to
|
}
|
||||||
pub character: String,
|
|
||||||
/// Which map does the token exists in (allowing multiple tokens in multiple maps)
|
fn scene_characters(&self, scene: usize, id: usize) -> Option<Vec<CharacterShort>> {
|
||||||
pub map_id: usize,
|
let party = self.characters.get(id).map(|c| c.1.party).unwrap_or_default();
|
||||||
/// Token image source, as path relative to the data directory
|
self.scenes
|
||||||
pub img_source: String,
|
.get(&scene)
|
||||||
// x, y are floats to allow 'free movement'
|
.map(|s| s.characters
|
||||||
/// X position, in grid slots units (integers are grid aligned)
|
.iter()
|
||||||
pub x: f32,
|
.filter(|c| party.can_see(c.1))
|
||||||
/// Y position, in grid slots units (integers are grid aligned)
|
.map(|c| self.characters.get(c.0).map(|e| e.0.short().with_id(c.0)))
|
||||||
pub y: f32,
|
.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, 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();
|
self.chat.iter().skip(start).map(|m| m.1.clone()).collect();
|
||||||
_ = broadcast.send((Some(id), api::Response::GetChatHistory(history)));
|
_ = broadcast.send((Some(id), api::Response::GetChatHistory(history)));
|
||||||
},
|
},
|
||||||
api::Request::GetTokens => {
|
api::Request::GetTokens { scene } => {
|
||||||
for token_id in self.game.available_tokens() {
|
for token_id in self.game.available_tokens(scene) {
|
||||||
if let Some(ti) = self.game.token_info(0, token_id) {
|
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 bits = std::fs::read(&ti.img_source).expect("FAILED READING TOKEN IMAGE");
|
||||||
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
let img = base64::Engine::encode(&base64::prelude::BASE64_STANDARD, &bits);
|
||||||
|
|
|
@ -133,6 +133,7 @@ impl Character<Entry> for Pathfinder2rCharacterSheet {
|
||||||
|
|
||||||
fn short(&self) -> CharacterShort {
|
fn short(&self) -> CharacterShort {
|
||||||
CharacterShort {
|
CharacterShort {
|
||||||
|
id: 0,
|
||||||
title: self.name.clone(),
|
title: self.name.clone(),
|
||||||
sub_title: Some("A Character!".to_string()),
|
sub_title: Some("A Character!".to_string()),
|
||||||
health: Some([ "Dying", "Hurt", "Harmed", "Unharmed" ][((self.health * 4) / self.max_health) as usize].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