not sure what is going on anymore, a lot of changes as im working on the chat thing. currently simple chat seems to work
This commit is contained in:
parent
dd34317d14
commit
acf96db186
11 changed files with 170 additions and 52 deletions
|
@ -14,25 +14,57 @@
|
||||||
view.onauxclick = (e) => console.log(e);
|
view.onauxclick = (e) => console.log(e);
|
||||||
view.onmousemove = onGameMouseMove;
|
view.onmousemove = onGameMouseMove;
|
||||||
view.oncontextmenu = () => false;
|
view.oncontextmenu = () => false;
|
||||||
|
// allow sending chat message using enter (and shift-enter for new line)
|
||||||
|
document.getElementById('newmsg-content').onkeypress = (e) => {
|
||||||
|
if(e.key == "Enter" && !e.shiftKey) {
|
||||||
|
sendChatMessage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function onLoginClick() {
|
|
||||||
let username = document.getElementById('login-username').value;
|
|
||||||
let pass = document.getElementById('login-pass').value;
|
|
||||||
tavern.login(username, pass);
|
|
||||||
}
|
|
||||||
tavern.onlogin = (s) => {
|
tavern.onlogin = (s) => {
|
||||||
console.log(s);
|
|
||||||
if(s) {
|
if(s) {
|
||||||
let login = document.getElementById('login-screen');
|
let login = document.getElementById('login-screen');
|
||||||
let game = document.getElementById('game');
|
let game = document.getElementById('game');
|
||||||
login.style.display = 'none';
|
login.style.display = 'none';
|
||||||
game.style.display = 'flex';
|
game.style.display = 'flex';
|
||||||
|
// get all chat history as soon as we get in
|
||||||
|
tavern.get_chat_history(0, 0);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
alert("Invalid username or password!");
|
alert("Invalid username or password!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tavern.onmessage = (m) => {
|
||||||
|
console.log(m);
|
||||||
|
let msg = document.createElement('div');
|
||||||
|
msg.className = 'chat-message';
|
||||||
|
// #abusing_style_order_as_both_id_variable_and_forcing_chronological_order
|
||||||
|
msg.style.order = m.id;
|
||||||
|
msg.innerHTML = `
|
||||||
|
<b style='align-self: center;'>${m.source}</b>
|
||||||
|
<br>
|
||||||
|
<hr style='width: 75%;' />
|
||||||
|
${m.text}
|
||||||
|
`
|
||||||
|
let history = document.getElementById('chat-history');
|
||||||
|
// this is to force update everytime we get a duplicate msg to allow msg editing (yay)
|
||||||
|
let exists = Array.from(history.children).filter(e => e.style.order == m.id)[0];
|
||||||
|
if(exists) {
|
||||||
|
history.removeChild(exists);
|
||||||
|
}
|
||||||
|
history.appendChild(msg);
|
||||||
|
}
|
||||||
|
function onLoginClick() {
|
||||||
|
let username = document.getElementById('login-username').value;
|
||||||
|
let pass = document.getElementById('login-pass').value;
|
||||||
|
if(username == 'test') {
|
||||||
|
// TODO: Remove this for when im done dev-ing with this file
|
||||||
|
tavern.onlogin(true);
|
||||||
|
}
|
||||||
|
tavern.login(username, pass);
|
||||||
|
}
|
||||||
function onGameViewScroll(event) {
|
function onGameViewScroll(event) {
|
||||||
let map = document.getElementById('map');
|
let map = document.getElementById('map');
|
||||||
mapScale += (event.wheelDelta / 1800.0);
|
mapScale += (event.wheelDelta / 1800.0);
|
||||||
|
@ -50,6 +82,13 @@
|
||||||
map.style.top = `${mapOffsetY}px`;
|
map.style.top = `${mapOffsetY}px`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
function sendChatMessage() {
|
||||||
|
let tb = document.getElementById('newmsg-content');
|
||||||
|
// get the msg and reset the textarea
|
||||||
|
let text = tb.value;
|
||||||
|
tb.value = '';
|
||||||
|
tavern.simple_msg(text);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
|
@ -70,18 +109,30 @@
|
||||||
width: 20%;
|
width: 20%;
|
||||||
min-width: 10%;
|
min-width: 10%;
|
||||||
max-width: 50%;
|
max-width: 50%;
|
||||||
background-color: rgb(17, 0, 36);
|
background-image: linear-gradient(135deg, #0f0f2f 0px, #0f0f2f 99%, rgb(188, 255, 185) 100%);
|
||||||
background-image: linear-gradient(135deg, rgb(17, 0, 36) 0px, rgb(17, 0, 36) 98%, rgb(188, 255, 185) 99%);
|
|
||||||
}
|
}
|
||||||
#chat-history {
|
#chat-history {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 90%;
|
height: 90%;
|
||||||
resize: vertical;
|
resize: none;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
background-color:brown;
|
background-color:#0f0f2f;
|
||||||
|
}
|
||||||
|
.chat-message {
|
||||||
|
|
||||||
|
background-color: #ffffd6;
|
||||||
|
color: #000000;
|
||||||
|
border-width: 2px;
|
||||||
|
border-color: rgb(73, 49, 49);
|
||||||
|
border-style: solid;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 4px 16px;
|
||||||
|
margin: 2px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
@ -100,10 +151,11 @@
|
||||||
<div id="side-panel">
|
<div id="side-panel">
|
||||||
<div style="display: flex; flex-direction: column; width: calc(100% - 15px); height: 100%;" >
|
<div style="display: flex; flex-direction: column; width: calc(100% - 15px); height: 100%;" >
|
||||||
<div id="chat-history">
|
<div id="chat-history">
|
||||||
Chat history
|
|
||||||
</div>
|
</div>
|
||||||
<div id="chat-input" style="width: 100%; flex-grow: 1; background-color: aqua; margin: 10px 0;">
|
<div id="chat-input" style="width: 100%; flex-grow: 1; margin: 10px 0; display:flex;">
|
||||||
new message input
|
<textarea id="newmsg-content" placeholder="Message..." spellcheck="true" style="resize:none; height: auto; flex-grow: 1; min-width:0px;"></textarea>
|
||||||
|
<!-- it looks better without the button :( -->
|
||||||
|
<!-- <button style="height: fit-content; align-self: center; margin: 0 4px;" onclick="sendChatMessage()">Send</button> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,8 +17,24 @@ tavern.socket.onmessage = (m) => {
|
||||||
tavern.socket.loggedIn = m.login.success;
|
tavern.socket.loggedIn = m.login.success;
|
||||||
tavern.call(tavern.onlogin, tavern.socket.loggedIn);
|
tavern.call(tavern.onlogin, tavern.socket.loggedIn);
|
||||||
}
|
}
|
||||||
|
if(m.message) {
|
||||||
|
tavern.call(tavern.onmessage, m.message)
|
||||||
|
}
|
||||||
|
if(m.get_chat_history) {
|
||||||
|
m.get_chat_history.forEach(msg => {
|
||||||
|
tavern.call(tavern.onmessage, msg);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
tavern.login = (username, password) => {
|
tavern.login = (username, password) => {
|
||||||
if(!tavern.connected || tavern.loggedIn) { return false; }
|
if(!tavern.connected || tavern.loggedIn) { return false; }
|
||||||
tavern.socket.send(JSON.stringify({ login: { username, password }}));
|
tavern.socket.send(JSON.stringify({ login: { username, password }}));
|
||||||
|
}
|
||||||
|
tavern.simple_msg = (msg, token) => {
|
||||||
|
if(!tavern.connected || tavern.loggedIn) { return false; }
|
||||||
|
tavern.socket.send(JSON.stringify({ message: { text: msg, source: token ?? "" } }));
|
||||||
|
}
|
||||||
|
tavern.get_chat_history = (from, amount) => {
|
||||||
|
if(!tavern.connected || tavern.loggedIn) { return false; }
|
||||||
|
tavern.socket.send(JSON.stringify({ get_chat_history: { from: from, amount: amount } }))
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ pub enum Request {
|
||||||
Error,
|
Error,
|
||||||
Login(login::LoginRequest),
|
Login(login::LoginRequest),
|
||||||
Message(ChatMessage),
|
Message(ChatMessage),
|
||||||
|
GetChatHistory { amount: usize, from: usize },
|
||||||
Quit,
|
Quit,
|
||||||
Shutdown
|
Shutdown
|
||||||
}
|
}
|
||||||
|
@ -22,6 +23,7 @@ pub enum Response {
|
||||||
Error(RequestError),
|
Error(RequestError),
|
||||||
Login(login::LoginResult),
|
Login(login::LoginResult),
|
||||||
Message(ChatMessage),
|
Message(ChatMessage),
|
||||||
|
GetChatHistory(Vec<ChatMessage>),
|
||||||
Quit { id: String },
|
Quit { id: String },
|
||||||
Shutdown,
|
Shutdown,
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
use super::{action::{ActionDefinition, ActionResult, GameEntry}, chat_message::ChatMessage};
|
use super::{entry::{ActionDefinition, ActionResult, GameEntry}, chat_message::ChatMessage};
|
||||||
|
|
||||||
pub trait Character<E: GameEntry> : CharacterSheet {
|
pub trait Character<E: GameEntry> : CharacterSheet {
|
||||||
/// types of actions that can be done on the specified entry (e.g. cast for spells and wear for armor)
|
/// types of actions that can be done on the specified entry (e.g. cast for spells and wear for armor)
|
||||||
|
|
|
@ -14,9 +14,14 @@ pub struct ChatMessage {
|
||||||
/// Source/Caster/Whoever initiated the action/sent the message
|
/// Source/Caster/Whoever initiated the action/sent the message
|
||||||
pub source: String,
|
pub source: String,
|
||||||
/// Targets of the action, for a chat message this will be empty
|
/// Targets of the action, for a chat message this will be empty
|
||||||
pub targets: Option<Vec<String>>
|
pub targets: Option<Vec<String>>,
|
||||||
|
/// message id, should be left emitted or 0 for new messages
|
||||||
|
#[serde(default = "default_id")]
|
||||||
|
pub id: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_id() -> usize { 0 }
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct RollDialogOption {
|
pub struct RollDialogOption {
|
||||||
/// Field name
|
/// Field name
|
||||||
|
|
|
@ -16,7 +16,9 @@ pub trait GameEntry : Serialize + Sized {
|
||||||
/// Display name (e.g. `weapon/dagger` -> `Dagger`)
|
/// Display name (e.g. `weapon/dagger` -> `Dagger`)
|
||||||
fn display_name(&self) -> String;
|
fn display_name(&self) -> String;
|
||||||
/// Get all entries, with an optional filter (could be `weapon` for example to show only weapons)
|
/// Get all entries, with an optional filter (could be `weapon` for example to show only weapons)
|
||||||
fn all(filter: Option<&str>) -> Vec<String>;
|
fn all(filter: Option<&str>) -> Vec<String>;
|
||||||
|
/// Get all categories (such as weapons/consumables/spells)
|
||||||
|
fn categories() -> Vec<String>;
|
||||||
/// returns a chat message to show the entry (with description and all)
|
/// returns a chat message to show the entry (with description and all)
|
||||||
fn to_chat(&self, source: &str) -> ChatMessage;
|
fn to_chat(&self, source: &str) -> ChatMessage;
|
||||||
}
|
}
|
|
@ -6,19 +6,19 @@ use serde::Serialize;
|
||||||
|
|
||||||
pub mod character_sheet;
|
pub mod character_sheet;
|
||||||
pub mod chat_message;
|
pub mod chat_message;
|
||||||
pub mod action;
|
pub mod entry;
|
||||||
|
|
||||||
pub trait GameImpl<C: Character<A> + Serialize, A: action::GameEntry + Serialize> {
|
pub trait GameImpl<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
||||||
fn new() -> Self;
|
fn new() -> Self;
|
||||||
fn create_character(&mut self);
|
fn create_character(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Game<C: Character<A> + Serialize, A: action::GameEntry + Serialize> {
|
pub struct Game<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> {
|
||||||
_c: PhantomData<C>,
|
_c: PhantomData<C>,
|
||||||
_a: PhantomData<A>,
|
_a: PhantomData<A>,
|
||||||
characters: Vec<C>,
|
characters: Vec<C>,
|
||||||
}
|
}
|
||||||
impl<C: Character<A> + Serialize, A: action::GameEntry + Serialize> GameImpl<C, A> for Game<C, A> {
|
impl<C: Character<A> + Serialize, A: entry::GameEntry + Serialize> GameImpl<C, A> for Game<C, A> {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
_c: PhantomData,
|
_c: PhantomData,
|
||||||
|
|
40
src/lib.rs
40
src/lib.rs
|
@ -1,4 +1,4 @@
|
||||||
use game::{Game, GameImpl};
|
use game::{chat_message::ChatMessage, Game, GameImpl};
|
||||||
use tokio::sync::{broadcast, mpsc};
|
use tokio::sync::{broadcast, mpsc};
|
||||||
|
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
@ -8,25 +8,55 @@ pub mod game;
|
||||||
pub mod pathfinder2r_impl;
|
pub mod pathfinder2r_impl;
|
||||||
|
|
||||||
pub struct GameServer {
|
pub struct GameServer {
|
||||||
game: Game<pathfinder2r_impl::Pathfinder2rCharacterSheet, pathfinder2r_impl::entry::PEntry>
|
_game: Game<pathfinder2r_impl::Pathfinder2rCharacterSheet, pathfinder2r_impl::entry::Entry>,
|
||||||
|
chat: Vec<(String, ChatMessage)>,
|
||||||
}
|
}
|
||||||
impl GameServer {
|
impl GameServer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
game: Game::new(),
|
_game: Game::new(),
|
||||||
|
chat: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn server_loop(mut self, mut msgs: mpsc::Receiver<(String, api::Request)>, mut broadcast: broadcast::Sender<api::Response>) {
|
pub async fn server_loop(mut self, mut msgs: mpsc::Receiver<(String, api::Request)>, broadcast: broadcast::Sender<api::Response>) {
|
||||||
while let Some(req) = msgs.recv().await {
|
while let Some(req) = msgs.recv().await {
|
||||||
// TODO: do stuff yo!
|
// TODO: do stuff yo!
|
||||||
let (id, req) = req;
|
let (id, req) = req;
|
||||||
|
println!("Got message from {}: {:?}", &id, &req);
|
||||||
|
|
||||||
match req {
|
match req {
|
||||||
api::Request::Error => {},
|
api::Request::Error => {},
|
||||||
api::Request::Login(_) => {},
|
api::Request::Login(_) => {},
|
||||||
api::Request::Message(msg) => { _ = broadcast.send(api::Response::Message(msg)); },
|
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
|
||||||
|
}
|
||||||
|
// TODO: check if the editor is an admin as well
|
||||||
|
else if id == self.chat[msg.id].0 {
|
||||||
|
self.chat[msg.id] = (id.clone(), msg.clone());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// if its an edit message and editor is not the owner, skip
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if msg.source.is_empty() {
|
||||||
|
msg.source = id.clone();
|
||||||
|
}
|
||||||
|
self.chat.push((id.clone(), msg.clone()));
|
||||||
|
_ = broadcast.send(api::Response::Message(msg));
|
||||||
|
},
|
||||||
api::Request::Quit => { _ = broadcast.send(api::Response::Quit { id })},
|
api::Request::Quit => { _ = broadcast.send(api::Response::Quit { id })},
|
||||||
api::Request::Shutdown => todo!(),
|
api::Request::Shutdown => todo!(),
|
||||||
|
api::Request::GetChatHistory { mut amount, from: last_msg } => {
|
||||||
|
if amount == 0 { amount = self.chat.len(); }
|
||||||
|
let history: Vec<ChatMessage> = self.chat.iter()
|
||||||
|
.skip(last_msg)
|
||||||
|
.take(amount)
|
||||||
|
.map(|m| m.1.clone())
|
||||||
|
.collect();
|
||||||
|
_ = broadcast.send(api::Response::GetChatHistory(history));
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ = broadcast.send(api::Response::Shutdown);
|
_ = broadcast.send(api::Response::Shutdown);
|
||||||
|
|
|
@ -32,7 +32,6 @@ async fn socket_receiver(mut recv: SplitStream<ws::WebSocket>, msend: mpsc::Send
|
||||||
match msg {
|
match msg {
|
||||||
Message::Text(t) => {
|
Message::Text(t) => {
|
||||||
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
||||||
println!("Got message: {:?}", t);
|
|
||||||
let erred = msend.send((id.clone(), req)).await.is_err();
|
let erred = msend.send((id.clone(), req)).await.is_err();
|
||||||
if erred {
|
if erred {
|
||||||
break;
|
break;
|
||||||
|
@ -68,7 +67,7 @@ async fn handle_socket(mut socket: ws::WebSocket, msend: mpsc::Sender<(String, R
|
||||||
match msg {
|
match msg {
|
||||||
Message::Text(t) => {
|
Message::Text(t) => {
|
||||||
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
let req = serde_json::from_str::<Request>(&t).unwrap_or_default();
|
||||||
println!("Got message: {:?}", t);
|
println!("Got unauthorized message: {:?}", t);
|
||||||
match req {
|
match req {
|
||||||
// TODO: Actual signing in mechanism with multiple ids :)
|
// TODO: Actual signing in mechanism with multiple ids :)
|
||||||
Request::Login(r) => if r.username == "rusty" {
|
Request::Login(r) => if r.username == "rusty" {
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::game::{action::GameEntry, chat_message::ChatMessage};
|
use crate::game::{entry::GameEntry, chat_message::ChatMessage};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub enum PEntry {
|
pub enum Entry {
|
||||||
Weapon(Weapon),
|
Weapon(Weapon),
|
||||||
Consumable(Consumable),
|
Consumable(Consumable),
|
||||||
Spell(Spell),
|
Spell(Spell),
|
||||||
}
|
}
|
||||||
impl GameEntry for PEntry {
|
impl GameEntry for Entry {
|
||||||
fn display_name(&self) -> String {
|
fn display_name(&self) -> String {
|
||||||
match self {
|
match self {
|
||||||
PEntry::Weapon(weapon) => weapon.name.clone(),
|
Entry::Weapon(weapon) => weapon.name.clone(),
|
||||||
PEntry::Consumable(consumable) => consumable.name.clone(),
|
Entry::Consumable(consumable) => consumable.name.clone(),
|
||||||
PEntry::Spell(spell) => spell.name.clone(),
|
Entry::Spell(spell) => spell.name.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(entry: &str) -> Option<PEntry> {
|
fn load(entry: &str) -> Option<Entry> {
|
||||||
println!("loading {}", entry);
|
println!("loading {}", entry);
|
||||||
let json = std::fs::read_to_string(format!("assets/pf2r/{}.json", entry)).ok()?;
|
let json = std::fs::read_to_string(format!("assets/pf2r/{}.json", entry)).ok()?;
|
||||||
println!("{}", &json);
|
println!("{}", &json);
|
||||||
if entry.starts_with("weapon/") {
|
if entry.starts_with("weapon/") {
|
||||||
serde_json::from_str::<Weapon>(&json).map(|w| PEntry::Weapon(w)).ok()
|
serde_json::from_str::<Weapon>(&json).map(|w| Entry::Weapon(w)).ok()
|
||||||
}
|
}
|
||||||
else if entry.starts_with("spell/") {
|
else if entry.starts_with("spell/") {
|
||||||
serde_json::from_str::<Spell>(&json).map(|s| PEntry::Spell(s)).ok()
|
serde_json::from_str::<Spell>(&json).map(|s| Entry::Spell(s)).ok()
|
||||||
}
|
}
|
||||||
else if entry.starts_with("consumable/") {
|
else if entry.starts_with("consumable/") {
|
||||||
serde_json::from_str::<Consumable>(&json).map(|s| PEntry::Consumable(s)).ok()
|
serde_json::from_str::<Consumable>(&json).map(|s| Entry::Consumable(s)).ok()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
None
|
None
|
||||||
|
@ -43,12 +43,22 @@ impl GameEntry for PEntry {
|
||||||
actions: None,
|
actions: None,
|
||||||
source: String::from(source),
|
source: String::from(source),
|
||||||
targets: None,
|
targets: None,
|
||||||
|
id: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn all(_filter: Option<&str>) -> Vec<String> {
|
fn all(_filter: Option<&str>) -> Vec<String> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn categories() -> Vec<String> {
|
||||||
|
vec![
|
||||||
|
"weapon", "consumable", "spell"
|
||||||
|
]
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default)]
|
#[derive(Serialize, Deserialize, Default)]
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
use crate::game::{action::{ActionDefinition, ActionResult, GameEntry}, character_sheet::*, chat_message::{ChatMessage, RollDialogOption}};
|
use crate::game::{entry::{ActionDefinition, ActionResult, GameEntry}, character_sheet::*, chat_message::{ChatMessage, RollDialogOption}};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tavern_macros::CharacterSheet;
|
use tavern_macros::CharacterSheet;
|
||||||
|
|
||||||
pub mod entry;
|
pub mod entry;
|
||||||
use entry::{PEntry, Weapon};
|
use entry::{Entry, Weapon};
|
||||||
|
|
||||||
#[derive(Default, CharacterSheet, Serialize)]
|
#[derive(Default, CharacterSheet, Serialize)]
|
||||||
pub struct Pathfinder2rCharacterSheet {
|
pub struct Pathfinder2rCharacterSheet {
|
||||||
|
@ -50,14 +50,14 @@ pub struct Pathfinder2rCharacterSheet {
|
||||||
impl Pathfinder2rCharacterSheet {
|
impl Pathfinder2rCharacterSheet {
|
||||||
fn set_items(&mut self, entry: EntryType) {
|
fn set_items(&mut self, entry: EntryType) {
|
||||||
if let EntryType::Array(a) = entry {
|
if let EntryType::Array(a) = entry {
|
||||||
let ws: Vec<PEntry> = a
|
let ws: Vec<Entry> = a
|
||||||
.iter()
|
.iter()
|
||||||
.map(|e| PEntry::load(&e.as_text()))
|
.map(|e| Entry::load(&e.as_text()))
|
||||||
.flatten()
|
.flatten()
|
||||||
.collect();
|
.collect();
|
||||||
self.weapon = [None, None];
|
self.weapon = [None, None];
|
||||||
for w in ws {
|
for w in ws {
|
||||||
if let PEntry::Weapon(w) = w {
|
if let Entry::Weapon(w) = w {
|
||||||
if self.weapon[0].is_none() {
|
if self.weapon[0].is_none() {
|
||||||
self.weapon[0] = Some(w);
|
self.weapon[0] = Some(w);
|
||||||
} else if self.weapon[1].is_none() {
|
} else if self.weapon[1].is_none() {
|
||||||
|
@ -69,7 +69,7 @@ impl Pathfinder2rCharacterSheet {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
let s = entry.as_text();
|
let s = entry.as_text();
|
||||||
if let Some(PEntry::Weapon(w)) = PEntry::load(&s) {
|
if let Some(Entry::Weapon(w)) = Entry::load(&s) {
|
||||||
self.weapon[0] = Some(w);
|
self.weapon[0] = Some(w);
|
||||||
self.weapon[1] = None;
|
self.weapon[1] = None;
|
||||||
}
|
}
|
||||||
|
@ -85,18 +85,19 @@ impl Pathfinder2rCharacterSheet {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Character<PEntry> for Pathfinder2rCharacterSheet {
|
impl Character<Entry> for Pathfinder2rCharacterSheet {
|
||||||
fn use_action(&mut self, entry: &PEntry, action: &ActionResult) -> ChatMessage {
|
fn use_action(&mut self, entry: &Entry, action: &ActionResult) -> ChatMessage {
|
||||||
match entry {
|
match entry {
|
||||||
PEntry::Weapon(_) => ChatMessage {
|
Entry::Weapon(_) => ChatMessage {
|
||||||
text: String::from("Attack"),
|
text: String::from("Attack"),
|
||||||
roll: Some(vec![RollDialogOption { name: String::from("pierce"), dice_type: 4, dice_amount: 1, constant: 0, extra: String::new(), enabled: true }]),
|
roll: Some(vec![RollDialogOption { name: String::from("pierce"), dice_type: 4, dice_amount: 1, constant: 0, extra: String::new(), enabled: true }]),
|
||||||
roll_target: Some(10),
|
roll_target: Some(10),
|
||||||
actions: Some(vec!["damage".to_string(), "double".to_string()]),
|
actions: Some(vec!["damage".to_string(), "double".to_string()]),
|
||||||
source: self.name.clone(),
|
source: self.name.clone(),
|
||||||
targets: None,
|
targets: None,
|
||||||
|
id: 0,
|
||||||
},
|
},
|
||||||
PEntry::Consumable(_consumable) => if action.name == "consume" {
|
Entry::Consumable(_consumable) => if action.name == "consume" {
|
||||||
ChatMessage {
|
ChatMessage {
|
||||||
text: "Heal".to_string(),
|
text: "Heal".to_string(),
|
||||||
roll: Some(vec![RollDialogOption { name: "heal".to_string(), dice_type: 6, dice_amount: 1, constant: 0, extra: String::new(), enabled: true }]),
|
roll: Some(vec![RollDialogOption { name: "heal".to_string(), dice_type: 6, dice_amount: 1, constant: 0, extra: String::new(), enabled: true }]),
|
||||||
|
@ -104,21 +105,22 @@ impl Character<PEntry> for Pathfinder2rCharacterSheet {
|
||||||
actions: Some(vec!["heal".to_string()]),
|
actions: Some(vec!["heal".to_string()]),
|
||||||
source: self.name.clone(),
|
source: self.name.clone(),
|
||||||
targets: None,
|
targets: None,
|
||||||
|
id: 0,
|
||||||
}
|
}
|
||||||
} else { todo!() },
|
} else { todo!() },
|
||||||
PEntry::Spell(_spell) => todo!(),
|
Entry::Spell(_spell) => todo!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions(&self, entry: &PEntry) -> Vec<ActionDefinition> {
|
fn actions(&self, entry: &Entry) -> Vec<ActionDefinition> {
|
||||||
let v;
|
let v;
|
||||||
match entry {
|
match entry {
|
||||||
PEntry::Weapon(_) =>
|
Entry::Weapon(_) =>
|
||||||
// should technically check if the item is in the user's packpack or something
|
// should technically check if the item is in the user's packpack or something
|
||||||
// but for now just return a constant list
|
// but for now just return a constant list
|
||||||
v = vec![ ("wield", 0), ("attack", 1), ("drop", 0), ("stow", 0) ],
|
v = vec![ ("wield", 0), ("attack", 1), ("drop", 0), ("stow", 0) ],
|
||||||
PEntry::Consumable(_) => v = vec![("consume", 0), ("stow", 0), ("drop", 0)],
|
Entry::Consumable(_) => v = vec![("consume", 0), ("stow", 0), ("drop", 0)],
|
||||||
PEntry::Spell(_) => v = vec![("cast", 1)],
|
Entry::Spell(_) => v = vec![("cast", 1)],
|
||||||
};
|
};
|
||||||
v.iter()
|
v.iter()
|
||||||
.map(|s| ActionDefinition { name: s.0.to_string(), targets: s.1 })
|
.map(|s| ActionDefinition { name: s.0.to_string(), targets: s.1 })
|
||||||
|
|
Loading…
Reference in a new issue