dice rolling (with a dialog and basic chat output)
This commit is contained in:
parent
45106498b4
commit
8b9b5db299
7 changed files with 244 additions and 63 deletions
|
@ -13,8 +13,6 @@
|
||||||
function init() {
|
function init() {
|
||||||
let view = document.getElementById('game-view');
|
let view = document.getElementById('game-view');
|
||||||
view.onwheel = onGameViewScroll;
|
view.onwheel = onGameViewScroll;
|
||||||
view.onclick = (e) => console.log('click', e);
|
|
||||||
view.onauxclick = (e) => console.log(e);
|
|
||||||
view.onmousemove = onGameMouseMove;
|
view.onmousemove = onGameMouseMove;
|
||||||
view.onmouseup = onGameMouseUp;
|
view.onmouseup = onGameMouseUp;
|
||||||
view.oncontextmenu = () => false;
|
view.oncontextmenu = () => false;
|
||||||
|
@ -73,7 +71,7 @@
|
||||||
</p>
|
</p>
|
||||||
<hr style='width: 75%;' />
|
<hr style='width: 75%;' />
|
||||||
<p style='margin: 4px 2px;'>
|
<p style='margin: 4px 2px;'>
|
||||||
${m.text}
|
${m.text.replace('\n', '\n<br>\n')}
|
||||||
</p>
|
</p>
|
||||||
`
|
`
|
||||||
msg.oncontextmenu = (e) => {
|
msg.oncontextmenu = (e) => {
|
||||||
|
@ -84,6 +82,21 @@
|
||||||
cm.style.left = `${e.pageX}px`;
|
cm.style.left = `${e.pageX}px`;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if(m.actions) {
|
||||||
|
let holder = document.createElement('div');
|
||||||
|
holder.style.display = 'flex';
|
||||||
|
holder.style.flexWrap = 'wrap';
|
||||||
|
msg.appendChild(holder);
|
||||||
|
for (const act of m.actions) {
|
||||||
|
let button = document.createElement('button');
|
||||||
|
button.innerText = act.display_name ?? act.name;
|
||||||
|
button.action = act;
|
||||||
|
button.onclick = () => console.log(button.action);
|
||||||
|
button.style.margin = '2px';
|
||||||
|
button.onclick = () => openRollsPopup(act);
|
||||||
|
holder.appendChild(button);
|
||||||
|
}
|
||||||
|
}
|
||||||
let history = document.getElementById('chat-history');
|
let history = document.getElementById('chat-history');
|
||||||
// this is to force update everytime we get a duplicate msg to allow msg editing (yay)
|
// 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];
|
let exists = Array.from(history.children).filter(e => e.style.order == m.id)[0];
|
||||||
|
@ -111,7 +124,7 @@
|
||||||
draggedToken.offY = ((e.clientY - mapOffsetY) / mapScale) - parseInt(token.style.top);
|
draggedToken.offY = ((e.clientY - mapOffsetY) / mapScale) - parseInt(token.style.top);
|
||||||
token.children[0].style.cursor = 'grabbing';
|
token.children[0].style.cursor = 'grabbing';
|
||||||
}
|
}
|
||||||
map.append(token);
|
map.appendChild(token);
|
||||||
}
|
}
|
||||||
tavern.onmovetoken = (m) => {
|
tavern.onmovetoken = (m) => {
|
||||||
let token = Array.from(document.getElementsByClassName('token')).filter(t => t.token_id == m.token_id)[0]
|
let token = Array.from(document.getElementsByClassName('token')).filter(t => t.token_id == m.token_id)[0]
|
||||||
|
@ -148,7 +161,6 @@
|
||||||
else if(draggedToken.token != null && event.buttons == 1) {
|
else if(draggedToken.token != null && event.buttons == 1) {
|
||||||
let top = (event.clientY - mapOffsetY) / mapScale - draggedToken.offY;
|
let top = (event.clientY - mapOffsetY) / mapScale - draggedToken.offY;
|
||||||
let left = (event.clientX - mapOffsetX) / mapScale - draggedToken.offX;
|
let left = (event.clientX - mapOffsetX) / mapScale - draggedToken.offX;
|
||||||
console.log(event.clientX, event.clientY, draggedToken.offX, draggedToken.offY);
|
|
||||||
draggedToken.token.style.top = `${top}px`;
|
draggedToken.token.style.top = `${top}px`;
|
||||||
draggedToken.token.style.left = `${left}px`;
|
draggedToken.token.style.left = `${left}px`;
|
||||||
}
|
}
|
||||||
|
@ -172,6 +184,61 @@
|
||||||
tb.value = '';
|
tb.value = '';
|
||||||
tavern.simple_msg(text);
|
tavern.simple_msg(text);
|
||||||
}
|
}
|
||||||
|
function openRollsPopup(action) {
|
||||||
|
let holder = document.getElementById('dice-roll-holder');
|
||||||
|
holder.innerHTML = ''; // remove all holder children
|
||||||
|
holder.action = action;
|
||||||
|
if(action.rolls != undefined && Array.isArray(action.rolls)) {
|
||||||
|
for (const r of action.rolls) {
|
||||||
|
// name (extra) (dice_amount)d(dice_type) + constant | enabled
|
||||||
|
console.log(r);
|
||||||
|
let row = document.createElement('div');
|
||||||
|
row.style.display = 'flex';
|
||||||
|
row.roll = r;
|
||||||
|
row.innerHTML = `
|
||||||
|
<p style='flex-grow: 1;'>${r.name} ${r.extra != null ? '(' + r.extra + ')' : ''}</p>
|
||||||
|
<p style='margin-right: 8px;'>${r.dice_amount}d${r.dice_type} + ${r.constant}</p>
|
||||||
|
<input type='checkbox' ${r.enabled ? 'checked' : ''} />
|
||||||
|
`;
|
||||||
|
holder.appendChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.getElementById('dice-roll-title').innerText = action.display_name ?? action.display;
|
||||||
|
document.getElementById('dice-roll-popup').style.display = 'flex';
|
||||||
|
}
|
||||||
|
function rollPopup() {
|
||||||
|
// TODO: Maybe let the server roll the dice?
|
||||||
|
// first - hide the popup
|
||||||
|
document.getElementById('dice-roll-popup').style.display = 'none';
|
||||||
|
// get the holder and start rolling dice
|
||||||
|
let rolls = [];
|
||||||
|
let holder = document.getElementById('dice-roll-holder');
|
||||||
|
for(const h of holder.children) {
|
||||||
|
if(h.roll && h.children[2].checked) {
|
||||||
|
let roll = { name: h.roll.name, extra: h.roll.extra };
|
||||||
|
let msg = '';
|
||||||
|
let sum = 0;
|
||||||
|
for (let i = 0; i < h.roll.dice_amount; i++) {
|
||||||
|
// Math.random gives a value in [0, 1), so we need to add 1 at the end
|
||||||
|
let roll = Math.floor(Math.random() * h.roll.dice_type) + 1;
|
||||||
|
sum += roll;
|
||||||
|
msg += `${roll}`;
|
||||||
|
if(i != h.roll.dice_amount - 1 || h.roll.constant != 0) {
|
||||||
|
msg += ' + ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(h.roll.constant != 0) {
|
||||||
|
sum += h.roll.constant;
|
||||||
|
msg += `${h.roll.constant}`;
|
||||||
|
}
|
||||||
|
roll.result = sum;
|
||||||
|
roll.result_text = msg;
|
||||||
|
rolls.push(roll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
console.log(rolls);
|
||||||
|
tavern.action_result(holder.action.name, 'Louise', [], rolls);
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style>
|
<style>
|
||||||
html, body {
|
html, body {
|
||||||
|
@ -278,6 +345,22 @@
|
||||||
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 1;" >
|
<div style="position:absolute; top: 10px; left: 5px; background-color: rgb(255, 166, 0); z-index: 1;" >
|
||||||
floating<br>stuff
|
floating<br>stuff
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dice-roll-popup" style="display:none; justify-content: center; width: 100%; height: 100%; background-color: transparent; position: absolute; z-index: 2; top: 0px; left: 0px">
|
||||||
|
<div style="display:flex; width: 50%; justify-content: center; flex-direction: column;">
|
||||||
|
<div style="display: flex; height: fit-content; flex-direction: column; background-color: #ffffd6; color: black; border: black solid 1px; padding: 8px; border-radius: 8px;">
|
||||||
|
<h3 id="dice-roll-title" style="align-self: center;">
|
||||||
|
THIS IS THE ROLL DISPLAY
|
||||||
|
</h3>
|
||||||
|
<div id="dice-roll-holder" style="display: flex; flex-direction: column;">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div style="display: flex;">
|
||||||
|
<button style="flex-grow: 1; margin: 4px;" onclick="document.getElementById('dice-roll-popup').style.display = 'none';">Cancel</button>
|
||||||
|
<button style="flex-grow: 1; margin: 4px;" onclick="rollPopup()">Roll</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div id="map" style="position:absolute; transform-origin: top left;">
|
<div id="map" style="position:absolute; transform-origin: top left;">
|
||||||
<img src="https://rustystriker.dev/molly.jpg" height="200%" >
|
<img src="https://rustystriker.dev/molly.jpg" height="200%" >
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -86,4 +86,8 @@ tavern.get_tokens = () => {
|
||||||
tavern.move_token = (id, x, y) => {
|
tavern.move_token = (id, x, y) => {
|
||||||
if(!tavern.connected || tavern.loggedIn) { return false; }
|
if(!tavern.connected || tavern.loggedIn) { return false; }
|
||||||
tavern.socket.send(JSON.stringify({ move_token: { token_id: id, x: x, y: y } }));
|
tavern.socket.send(JSON.stringify({ move_token: { token_id: id, x: x, y: y } }));
|
||||||
|
}
|
||||||
|
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 } }));
|
||||||
}
|
}
|
|
@ -5,23 +5,28 @@ pub mod map_actions;
|
||||||
use game_actions::SpawnToken;
|
use game_actions::SpawnToken;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::game::chat_message::ChatMessage;
|
use crate::game::{chat_message::ChatMessage, entry::ActionResult};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Default, Debug)]
|
#[derive(Serialize, Deserialize, Default, Debug)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
pub enum Request {
|
pub enum Request {
|
||||||
#[default]
|
#[default]
|
||||||
Error,
|
Error,
|
||||||
|
// Connection requests
|
||||||
Login(login::LoginRequest),
|
Login(login::LoginRequest),
|
||||||
|
Quit,
|
||||||
|
Kick(String),
|
||||||
|
Shutdown,
|
||||||
|
// Chat requests
|
||||||
Message(ChatMessage),
|
Message(ChatMessage),
|
||||||
GetChatHistory { amount: usize, from: usize },
|
GetChatHistory { amount: usize, from: usize },
|
||||||
GetLastMessages { amount: usize, },
|
GetLastMessages { amount: usize, },
|
||||||
|
// Map requests
|
||||||
GetTokens,
|
GetTokens,
|
||||||
SpawnToken { x: i32, y: i32, img_path: String },
|
SpawnToken { x: i32, y: i32, img_path: String },
|
||||||
MoveToken { token_id: usize, x: i32, y: i32 },
|
MoveToken { token_id: usize, x: i32, y: i32 },
|
||||||
Quit,
|
// Actions requests
|
||||||
Kick(String),
|
ActionResult(ActionResult)
|
||||||
Shutdown
|
|
||||||
}
|
}
|
||||||
#[derive(Serialize, Clone, Debug)]
|
#[derive(Serialize, Clone, Debug)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::entry::ActionDefinition;
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
pub struct ChatMessage {
|
pub struct ChatMessage {
|
||||||
|
@ -12,12 +14,10 @@ pub struct ChatMessage {
|
||||||
pub character: Option<String>,
|
pub character: Option<String>,
|
||||||
/// whisper item
|
/// whisper item
|
||||||
pub whisper: Option<String>,
|
pub whisper: Option<String>,
|
||||||
/// Rolls of the action, if not empty a roll should happen before
|
/// If the originating action had a dice roll with a DC (or similar), an option to show if the action roll succeeded
|
||||||
pub roll: Option<Vec<RollDialogOption>>,
|
pub success: Option<bool>,
|
||||||
/// Optional roll target
|
|
||||||
pub roll_target: Option<i32>,
|
|
||||||
/// Optional action buttons, for a chat message this will be empty
|
/// Optional action buttons, for a chat message this will be empty
|
||||||
pub actions: Option<Vec<String>>,
|
pub actions: Option<Vec<ActionDefinition>>,
|
||||||
/// 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
|
/// message id, should be left emitted or 0 for new messages
|
||||||
|
@ -35,6 +35,10 @@ impl ChatMessage {
|
||||||
text, ..Default::default()
|
text, ..Default::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn character(mut self, character: Option<String>) -> Self {
|
||||||
|
self.character = character;
|
||||||
|
self
|
||||||
|
}
|
||||||
pub fn source(mut self, source: String) -> Self {
|
pub fn source(mut self, source: String) -> Self {
|
||||||
self.source = source;
|
self.source = source;
|
||||||
self
|
self
|
||||||
|
@ -45,31 +49,17 @@ impl ChatMessage {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// sets the roll value for the message (chaining multiple will override each other)
|
/// sets the roll value for the message (chaining multiple will override each other)
|
||||||
pub fn roll(mut self, roll: Option<Vec<RollDialogOption>>) -> Self {
|
pub fn success(mut self, success: Option<bool>) -> Self {
|
||||||
self.roll = roll;
|
self.success = success;
|
||||||
self
|
|
||||||
}
|
|
||||||
/// adds a single roll to the message (chaining multiple will add multiple rolls)
|
|
||||||
pub fn with_roll(mut self, roll: RollDialogOption) -> Self {
|
|
||||||
if let Some(rs) = &mut self.roll {
|
|
||||||
rs.push(roll);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
self.roll = Some(vec![roll]);
|
|
||||||
}
|
|
||||||
self
|
|
||||||
}
|
|
||||||
pub fn roll_target(mut self, target: Option<i32>) -> Self {
|
|
||||||
self.roll_target = target;
|
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// sets the actions value (chaining multiple will override)
|
/// sets the actions value (chaining multiple will override)
|
||||||
pub fn actions(mut self, actions: Option<Vec<String>>) -> Self {
|
pub fn actions(mut self, actions: Option<Vec<ActionDefinition>>) -> Self {
|
||||||
self.actions = actions;
|
self.actions = actions;
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// adds a single action to the message (chaining multiple will add multiple actions)
|
/// adds a single action to the message (chaining multiple will add multiple actions)
|
||||||
pub fn with_action(mut self, action: String) -> Self {
|
pub fn with_action(mut self, action: ActionDefinition) -> Self {
|
||||||
if let Some(acts) = &mut self.actions {
|
if let Some(acts) = &mut self.actions {
|
||||||
acts.push(action);
|
acts.push(action);
|
||||||
}
|
}
|
||||||
|
@ -103,20 +93,4 @@ impl ChatMessage {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_id() -> usize { 0 }
|
fn default_id() -> usize { 0 }
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
|
||||||
pub struct RollDialogOption {
|
|
||||||
/// Field name
|
|
||||||
pub name: String,
|
|
||||||
/// dice size (aka d6, d12, d20)
|
|
||||||
pub dice_type: u16,
|
|
||||||
/// amount of dice (aka 1d6, 2d12, 10d20)
|
|
||||||
pub dice_amount: u16,
|
|
||||||
/// Constant amout to add (+7, +3, -1)
|
|
||||||
pub constant: i16,
|
|
||||||
/// Extra data, like damage type
|
|
||||||
pub extra: String,
|
|
||||||
/// should be enabled by default
|
|
||||||
pub enabled: bool
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
use serde::Serialize;
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::chat_message::ChatMessage;
|
use super::chat_message::ChatMessage;
|
||||||
|
|
||||||
|
@ -23,13 +23,93 @@ pub trait GameEntry : Serialize + Sized {
|
||||||
fn to_chat(&self) -> ChatMessage;
|
fn to_chat(&self) -> ChatMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
pub struct ActionDefinition {
|
pub struct ActionDefinition {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
pub display_name: Option<String>,
|
||||||
|
pub source: Option<String>,
|
||||||
pub targets: i32,
|
pub targets: i32,
|
||||||
|
pub rolls: Option<Vec<DiceRoll>>,
|
||||||
|
}
|
||||||
|
impl ActionDefinition {
|
||||||
|
pub fn new(name: String) -> Self {
|
||||||
|
Self {
|
||||||
|
name, ..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn display_name(mut self, name: Option<String>) -> Self {
|
||||||
|
self.display_name = name;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn source(mut self, source: Option<String>) -> Self {
|
||||||
|
self.source = source;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn targets(mut self, targets: i32) -> Self {
|
||||||
|
self.targets = targets;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn rolls(mut self, rolls: Option<Vec<DiceRoll>>) -> Self {
|
||||||
|
self.rolls = rolls;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn with_roll(mut self, roll: DiceRoll) -> Self {
|
||||||
|
if let Some(rolls) = &mut self.rolls {
|
||||||
|
rolls.push(roll);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self.rolls = Some(vec![roll]);
|
||||||
|
}
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
pub struct ActionResult {
|
pub struct ActionResult {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub roll_result: i32,
|
pub source: String,
|
||||||
pub roll_target: i32,
|
pub targets: Vec<String>,
|
||||||
|
pub results: Vec<DiceRollResult>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone, Default)]
|
||||||
|
pub struct DiceRoll {
|
||||||
|
/// Field name
|
||||||
|
pub name: String,
|
||||||
|
/// dice size (aka d6, d12, d20)
|
||||||
|
pub dice_type: u16,
|
||||||
|
/// amount of dice (aka 1d6, 2d12, 10d20)
|
||||||
|
pub dice_amount: u16,
|
||||||
|
/// Constant amout to add (+7, +3, -1)
|
||||||
|
pub constant: i16,
|
||||||
|
/// should be enabled by default
|
||||||
|
pub enabled: bool,
|
||||||
|
/// Extra data, like damage type
|
||||||
|
pub extra: Option<String>,
|
||||||
|
}
|
||||||
|
impl DiceRoll {
|
||||||
|
pub fn new(name: String, dice_type: u16, dice_amount: u16) -> Self {
|
||||||
|
Self {
|
||||||
|
name, dice_type, dice_amount, enabled: true, ..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn constant(mut self, constant: i16) -> Self {
|
||||||
|
self.constant = constant;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn enabled(mut self, enabled: bool) -> Self {
|
||||||
|
self.enabled = enabled;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
pub fn extra(mut self, extra: Option<String>) -> Self {
|
||||||
|
self.extra = extra;
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct DiceRollResult {
|
||||||
|
pub name: String,
|
||||||
|
pub result: i32,
|
||||||
|
pub result_text: String,
|
||||||
|
pub extra: Option<String>,
|
||||||
}
|
}
|
35
src/lib.rs
35
src/lib.rs
|
@ -1,5 +1,5 @@
|
||||||
use api::game_actions::SpawnToken;
|
use api::game_actions::SpawnToken;
|
||||||
use game::{chat_message::ChatMessage, Game, GameImpl};
|
use game::{chat_message::ChatMessage, entry::{ActionDefinition, DiceRoll}, Game, GameImpl};
|
||||||
use tokio::sync::{broadcast, mpsc};
|
use tokio::sync::{broadcast, mpsc};
|
||||||
|
|
||||||
pub mod api;
|
pub mod api;
|
||||||
|
@ -18,7 +18,22 @@ impl GameServer {
|
||||||
Self {
|
Self {
|
||||||
_game: Game::new(),
|
_game: Game::new(),
|
||||||
tokens: vec![("assets/pf2r/tokens/louise.jpg".to_string(), 2, 2)],
|
tokens: vec![("assets/pf2r/tokens/louise.jpg".to_string(), 2, 2)],
|
||||||
chat: Vec::new(),
|
chat: vec![
|
||||||
|
(
|
||||||
|
"Server".to_string(),
|
||||||
|
ChatMessage::new("a weapon description".to_string())
|
||||||
|
.id(1)
|
||||||
|
.character(Some("Sword or something".to_string()))
|
||||||
|
.with_action(
|
||||||
|
ActionDefinition::new("weapon/attack".to_string())
|
||||||
|
.display_name(Some("Attack +7".to_string()))
|
||||||
|
.with_roll(DiceRoll::new("Pierce".to_string(), 12, 1).constant(1))
|
||||||
|
.with_roll(DiceRoll::new("Fire".to_string(), 4, 2))
|
||||||
|
)
|
||||||
|
.with_action(ActionDefinition::new("Attack +3".to_string()))
|
||||||
|
.with_action(ActionDefinition::new("Attack -1".to_string()))
|
||||||
|
)
|
||||||
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +91,7 @@ impl GameServer {
|
||||||
amount = self.chat.len();
|
amount = self.chat.len();
|
||||||
}
|
}
|
||||||
let start = if amount >= self.chat.len() {
|
let start = if amount >= self.chat.len() {
|
||||||
self.chat.len()
|
0
|
||||||
} else {
|
} else {
|
||||||
self.chat.len() - amount
|
self.chat.len() - amount
|
||||||
};
|
};
|
||||||
|
@ -106,6 +121,20 @@ impl GameServer {
|
||||||
_ = broadcast.send((None, api::Response::MoveToken { token_id, x, y }));
|
_ = broadcast.send((None, api::Response::MoveToken { token_id, x, y }));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
api::Request::ActionResult(result) => {
|
||||||
|
let msg = ChatMessage::new(
|
||||||
|
result.results.iter()
|
||||||
|
// .map(|d| &d.result_text)
|
||||||
|
.fold(String::new(), |a,b| a + &format!("{}: {} = {}\n", &b.name, &b.result_text, b.result))
|
||||||
|
)
|
||||||
|
.character(Some(result.name))
|
||||||
|
.source(id.clone())
|
||||||
|
.targets(Some(result.targets))
|
||||||
|
.id(self.chat.len() + 1);
|
||||||
|
self.chat.push((id, msg.clone()));
|
||||||
|
_ = broadcast.send((None, api::Response::Message(msg)));
|
||||||
|
|
||||||
|
}
|
||||||
api::Request::Quit => _ = broadcast.send((None, api::Response::Quit { id })),
|
api::Request::Quit => _ = broadcast.send((None, api::Response::Quit { id })),
|
||||||
api::Request::Kick(id) => _ = broadcast.send((Some(id), api::Response::Shutdown)),
|
api::Request::Kick(id) => _ = broadcast.send((Some(id), api::Response::Shutdown)),
|
||||||
api::Request::Shutdown => break,
|
api::Request::Shutdown => break,
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use crate::game::{entry::{ActionDefinition, ActionResult, GameEntry}, character_sheet::*, chat_message::{ChatMessage, RollDialogOption}};
|
use crate::game::{character_sheet::*, chat_message::ChatMessage, entry::{ActionDefinition, ActionResult, DiceRoll, GameEntry}};
|
||||||
use serde::Serialize;
|
use serde::Serialize;
|
||||||
use tavern_macros::CharacterSheet;
|
use tavern_macros::CharacterSheet;
|
||||||
|
|
||||||
|
@ -89,14 +89,20 @@ impl Character<Entry> for Pathfinder2rCharacterSheet {
|
||||||
fn use_action(&mut self, entry: &Entry, action: &ActionResult) -> ChatMessage {
|
fn use_action(&mut self, entry: &Entry, action: &ActionResult) -> ChatMessage {
|
||||||
match entry {
|
match entry {
|
||||||
Entry::Weapon(_) => ChatMessage::new("Attack".to_string())
|
Entry::Weapon(_) => ChatMessage::new("Attack".to_string())
|
||||||
.with_roll(RollDialogOption { name: String::from("pierce"), dice_type: 4, dice_amount: 1, constant: 0, extra: String::new(), enabled: true })
|
.with_action(
|
||||||
.roll_target(Some(10))
|
ActionDefinition::new("Attack".to_string())
|
||||||
.with_action("damage".to_string())
|
.with_roll(DiceRoll::new("Piercing".to_string(), 12, 1))
|
||||||
.with_action("double".to_string()),
|
)
|
||||||
|
.with_action(
|
||||||
|
ActionDefinition::new("Double".to_string())
|
||||||
|
.with_roll(DiceRoll::new("Piercing".to_string(), 12, 2))
|
||||||
|
),
|
||||||
Entry::Consumable(_consumable) => if action.name == "consume" {
|
Entry::Consumable(_consumable) => if action.name == "consume" {
|
||||||
ChatMessage::new("Heal".to_string())
|
ChatMessage::new("Heal".to_string())
|
||||||
.with_roll(RollDialogOption { name: "heal".to_string(), dice_type: 6, dice_amount: 1, constant: 0, extra: String::new(), enabled: true })
|
.with_action(
|
||||||
.with_action("heal".to_string())
|
ActionDefinition::new("Heal".to_string())
|
||||||
|
.with_roll(DiceRoll::new("Heal".to_string(), 6, 0).constant(6))
|
||||||
|
)
|
||||||
} else { todo!() },
|
} else { todo!() },
|
||||||
Entry::Spell(_spell) => todo!(),
|
Entry::Spell(_spell) => todo!(),
|
||||||
}
|
}
|
||||||
|
@ -113,7 +119,7 @@ impl Character<Entry> for Pathfinder2rCharacterSheet {
|
||||||
Entry::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, display_name: None, source: None, rolls: None })
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue