dead chat: send fixes and more message types
This commit is contained in:
parent
f126cc8d09
commit
241420757e
|
|
@ -39,7 +39,7 @@ use crate::{
|
|||
},
|
||||
message::{
|
||||
CharacterState, ClientDeadChat, Identification, ServerToClientMessage,
|
||||
dead::DeadChatMessage,
|
||||
dead::{DeadChatContent, DeadChatMessage},
|
||||
host::{HostDayMessage, HostGameMessage, HostNightMessage, ServerToHostMessage},
|
||||
night::ActionResponse,
|
||||
},
|
||||
|
|
@ -72,6 +72,10 @@ impl Game {
|
|||
})
|
||||
}
|
||||
|
||||
pub const fn started(&self) -> DateTime<Utc> {
|
||||
self.started
|
||||
}
|
||||
|
||||
pub const fn village(&self) -> &Village {
|
||||
match &self.state {
|
||||
GameState::Day { village, marked: _ } => village,
|
||||
|
|
@ -91,10 +95,12 @@ impl Game {
|
|||
match message {
|
||||
ClientDeadChat::Send(message) => {
|
||||
let msg = DeadChatMessage {
|
||||
message,
|
||||
id: Uuid::new_v4(),
|
||||
from: char.identity(),
|
||||
timestamp: Utc::now(),
|
||||
message: DeadChatContent::PlayerMessage {
|
||||
message,
|
||||
from: char.identity(),
|
||||
},
|
||||
};
|
||||
match &mut self.state {
|
||||
GameState::Day { village, .. } => {
|
||||
|
|
|
|||
|
|
@ -184,16 +184,17 @@ impl Village {
|
|||
return Ok(Some(game_over));
|
||||
}
|
||||
self.time = self.time.next();
|
||||
self.set_dead_chat_dead();
|
||||
self.dead_chat_time_transition();
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn set_dead_chat_dead(&mut self) {
|
||||
self.dead_chat
|
||||
.set_dead(self.characters.iter().filter_map(|c| {
|
||||
c.died_to()
|
||||
.map(|died_to| (died_to.date_time(), c.character_id()))
|
||||
}));
|
||||
fn dead_chat_time_transition(&mut self) {
|
||||
self.dead_chat.set_dead(
|
||||
self.characters
|
||||
.iter()
|
||||
.filter_map(|c| c.died_to().map(|died_to| (died_to.date_time(), c.clone()))),
|
||||
self.time,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn to_day(&mut self) -> Result<GameTime> {
|
||||
|
|
@ -201,7 +202,7 @@ impl Village {
|
|||
return Err(GameError::AlreadyDaytime);
|
||||
}
|
||||
self.time = self.time.next();
|
||||
self.set_dead_chat_dead();
|
||||
self.dead_chat_time_transition();
|
||||
Ok(self.time)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,13 @@ use chrono::{DateTime, Utc};
|
|||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use crate::{character::CharacterId, error::GameError, game::GameTime, message::CharacterIdentity};
|
||||
use crate::{
|
||||
character::{Character, CharacterId},
|
||||
diedto::DiedTo,
|
||||
error::GameError,
|
||||
game::GameTime,
|
||||
message::CharacterIdentity,
|
||||
};
|
||||
|
||||
type Result<T> = core::result::Result<T, GameError>;
|
||||
|
||||
|
|
@ -31,9 +37,18 @@ pub struct DeadChat {
|
|||
|
||||
impl DeadChat {
|
||||
pub fn new() -> Self {
|
||||
let mut messages = HashMap::new();
|
||||
messages.insert(
|
||||
GameTime::Night { number: 0 },
|
||||
vec![DeadChatMessage {
|
||||
id: Uuid::new_v4(),
|
||||
timestamp: Utc::now(),
|
||||
message: DeadChatContent::TimeChange(GameTime::Night { number: 0 }),
|
||||
}],
|
||||
);
|
||||
Self {
|
||||
messages,
|
||||
deaths: Vec::new(),
|
||||
messages: HashMap::new(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,7 +66,7 @@ impl DeadChat {
|
|||
if !self
|
||||
.deaths
|
||||
.iter()
|
||||
.any(|(t, ch)| time >= *t && *ch == msg.from.character_id)
|
||||
.any(|(t, ch)| time >= *t && msg.message.is_from_character(*ch))
|
||||
{
|
||||
return Err(GameError::NotDead);
|
||||
}
|
||||
|
|
@ -63,8 +78,57 @@ impl DeadChat {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn set_dead(&mut self, dead: impl Iterator<Item = (GameTime, CharacterId)>) {
|
||||
self.deaths = dead.collect();
|
||||
pub fn set_dead(
|
||||
&mut self,
|
||||
dead: impl Iterator<Item = (GameTime, Character)> + Clone,
|
||||
time: GameTime,
|
||||
) {
|
||||
let newly_dead = dead
|
||||
.clone()
|
||||
.filter_map(|(t, c)| (t.next() == time).then_some(c))
|
||||
.collect::<Box<_>>();
|
||||
self.deaths = dead.clone().map(|(t, c)| (t, c.character_id())).collect();
|
||||
let Some(prev) = time.previous() else {
|
||||
return;
|
||||
};
|
||||
let messages = if let Some(messages) = self.messages.get_mut(&prev) {
|
||||
messages
|
||||
} else {
|
||||
self.messages.insert(
|
||||
prev,
|
||||
vec![DeadChatMessage {
|
||||
id: Uuid::new_v4(),
|
||||
timestamp: Utc::now(),
|
||||
message: DeadChatContent::TimeChange(prev),
|
||||
}],
|
||||
);
|
||||
self.messages.get_mut(&prev).unwrap()
|
||||
};
|
||||
for dead in newly_dead {
|
||||
let Some(died_to) = dead.died_to() else {
|
||||
continue;
|
||||
};
|
||||
messages.push(DeadChatMessage {
|
||||
id: Uuid::new_v4(),
|
||||
timestamp: Utc::now(),
|
||||
message: DeadChatContent::Death {
|
||||
character: dead.identity(),
|
||||
cause: died_to.clone(),
|
||||
},
|
||||
});
|
||||
}
|
||||
messages.sort_by_key(|c| c.timestamp);
|
||||
let id = Uuid::new_v4();
|
||||
if let Some(existing) = self.messages.insert(
|
||||
time,
|
||||
vec![DeadChatMessage {
|
||||
id,
|
||||
timestamp: Utc::now(),
|
||||
message: DeadChatContent::TimeChange(time),
|
||||
}],
|
||||
) {
|
||||
log::warn!("replaced: {existing:?}");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_since(&self, t: DateTime<Utc>, character: CharacterId) -> Box<[DeadChatMessage]> {
|
||||
|
|
@ -85,11 +149,7 @@ impl DeadChat {
|
|||
.flatten()
|
||||
.cloned()
|
||||
.collect::<Box<_>>();
|
||||
#[cfg(debug_assertions)]
|
||||
let orig_msg = messages.clone();
|
||||
messages.sort_by_key(|m| m.timestamp);
|
||||
#[cfg(debug_assertions)]
|
||||
assert_eq!(orig_msg, messages);
|
||||
|
||||
messages
|
||||
}
|
||||
|
|
@ -98,7 +158,36 @@ impl DeadChat {
|
|||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct DeadChatMessage {
|
||||
pub id: Uuid,
|
||||
pub from: CharacterIdentity,
|
||||
pub timestamp: DateTime<Utc>,
|
||||
pub message: String,
|
||||
pub message: DeadChatContent,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum DeadChatContent {
|
||||
PlayerMessage {
|
||||
from: CharacterIdentity,
|
||||
message: String,
|
||||
},
|
||||
Death {
|
||||
character: CharacterIdentity,
|
||||
cause: DiedTo,
|
||||
},
|
||||
TimeChange(GameTime),
|
||||
}
|
||||
|
||||
impl DeadChatContent {
|
||||
pub fn is_from_character(&self, character_id: CharacterId) -> bool {
|
||||
match self {
|
||||
DeadChatContent::PlayerMessage { from, .. } => from.character_id == character_id,
|
||||
DeadChatContent::Death { character, .. } => character.character_id == character_id,
|
||||
DeadChatContent::TimeChange(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn message(&self) -> Option<&str> {
|
||||
match self {
|
||||
DeadChatContent::PlayerMessage { message, .. } => Some(message.as_str()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -294,7 +294,7 @@ impl GameRunner {
|
|||
return None;
|
||||
}
|
||||
Ok(Message::Client(IdentifiedClientMessage {
|
||||
identity: Identification { player_id, public },
|
||||
identity: Identification { player_id, .. },
|
||||
update: ClientUpdate::Message(ClientMessage::GetState),
|
||||
})) => {
|
||||
let Some(char) = self.game.village().character_by_player_id(player_id) else {
|
||||
|
|
@ -364,15 +364,19 @@ impl GameRunner {
|
|||
.village()
|
||||
.dead_characters()
|
||||
.into_iter()
|
||||
.filter_map(|c| {
|
||||
c.died_to()
|
||||
.and_then(|d| (d.date_time() == pre_time).then_some(c.player_id()))
|
||||
})
|
||||
.collect::<Box<_>>();
|
||||
.filter_map(|c| c.died_to().map(|_| (c.character_id(), c.player_id())));
|
||||
|
||||
for (char, player) in newly_dead {
|
||||
let msgs = self
|
||||
.game
|
||||
.village()
|
||||
.dead_chat()
|
||||
.get_since(self.game.started(), char);
|
||||
self.joined_players
|
||||
.send_to(&newly_dead, ServerToClientMessage::DeadChat(Box::new([])))
|
||||
.send_to(&[player], ServerToClientMessage::DeadChat(msgs))
|
||||
.await;
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2995,6 +2995,16 @@ dialog {
|
|||
.message-content {
|
||||
flex-grow: 1;
|
||||
font-size: 1.2em;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: baseline;
|
||||
gap: 1ch;
|
||||
}
|
||||
|
||||
.time-change {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -178,7 +178,6 @@ async fn worker(mut recv: Receiver<HostMessage>, scope: Scope<Host>) {
|
|||
Ok(ServerToHostMessage::Error(GameError::AwaitingResponse)) => {}
|
||||
Ok(msg) => {
|
||||
log::debug!("got message: {:?}", msg.title());
|
||||
log::trace!("message content: {msg:?}");
|
||||
scope.send_message::<HostEvent>(msg.into())
|
||||
}
|
||||
Err(err) => {
|
||||
|
|
@ -324,7 +323,6 @@ impl Component for Host {
|
|||
}
|
||||
|
||||
fn view(&self, _ctx: &Context<Self>) -> Html {
|
||||
log::trace!("state: {:?}", self.state);
|
||||
let content = match self.state.clone() {
|
||||
HostState::ScreenOverrides { .. } => {
|
||||
let send = {
|
||||
|
|
|
|||
|
|
@ -14,12 +14,16 @@
|
|||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
use chrono::{DateTime, Utc};
|
||||
use chrono_humanize::Humanize;
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use yew::prelude::*;
|
||||
|
||||
use wasm_bindgen::{JsCast, UnwrapThrowExt};
|
||||
use web_sys::{HtmlElement, HtmlInputElement};
|
||||
use werewolves_proto::message::{PublicIdentity, dead::DeadChatMessage};
|
||||
use werewolves_proto::message::{
|
||||
PublicIdentity,
|
||||
dead::{DeadChatContent, DeadChatMessage},
|
||||
};
|
||||
|
||||
use crate::components::{Icon, IconSource, IconType, attributes::DiedToSpan};
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct DeadChatProperties {
|
||||
|
|
@ -32,19 +36,8 @@ pub fn DeadChat(DeadChatProperties { on_send, messages }: &DeadChatProperties) -
|
|||
let messages = messages
|
||||
.iter()
|
||||
.map(|m| {
|
||||
let DeadChatMessage {
|
||||
from,
|
||||
timestamp,
|
||||
message,
|
||||
..
|
||||
} = m;
|
||||
|
||||
html! {
|
||||
<li class="message">
|
||||
<Timestamp timestamp={*timestamp} />
|
||||
<DeadChatIdent ident={from.clone().into_public()}/>
|
||||
<span class="message-content">{message.clone()}</span>
|
||||
</li>
|
||||
<ChatMessage message={m.clone()} />
|
||||
}
|
||||
})
|
||||
.collect::<Html>();
|
||||
|
|
@ -174,3 +167,53 @@ pub fn Timestamp(TimestampProps { timestamp }: &TimestampProps) -> Html {
|
|||
</span>
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Properties)]
|
||||
pub struct ChatMessageProps {
|
||||
pub message: DeadChatMessage,
|
||||
}
|
||||
|
||||
#[function_component]
|
||||
pub fn ChatMessage(
|
||||
ChatMessageProps {
|
||||
message:
|
||||
DeadChatMessage {
|
||||
id: _,
|
||||
timestamp,
|
||||
message,
|
||||
},
|
||||
}: &ChatMessageProps,
|
||||
) -> Html {
|
||||
match message {
|
||||
DeadChatContent::PlayerMessage { from, message } => {
|
||||
html! {
|
||||
<li class="message">
|
||||
<Timestamp timestamp={*timestamp} />
|
||||
<DeadChatIdent ident={from.clone().into_public()}/>
|
||||
<span class="message-content">{message.clone()}</span>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
DeadChatContent::Death { character, cause } => {
|
||||
html! {
|
||||
<li class="message">
|
||||
<Timestamp timestamp={*timestamp} />
|
||||
<Icon source={IconSource::Skull} icon_type={IconType::Fit}/>
|
||||
<DeadChatIdent ident={character.clone().into_public()}/>
|
||||
<span class="message-content">
|
||||
{"died to "}
|
||||
// <DiedToSpan died_to={cause.title()}/>
|
||||
{cause.title().to_string()}
|
||||
</span>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
DeadChatContent::TimeChange(time) => {
|
||||
html! {
|
||||
<li class="message">
|
||||
<span class="time-change">{time.to_string()}</span>
|
||||
</li>
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue