Skip to content

Commit

Permalink
add basic chat
Browse files Browse the repository at this point in the history
  • Loading branch information
Uriopass committed Aug 17, 2023
1 parent c50de96 commit 9576ccd
Show file tree
Hide file tree
Showing 12 changed files with 229 additions and 25 deletions.
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,3 @@ opt-level = 1

[profile.release]
#debug = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
25 changes: 19 additions & 6 deletions egregoria/src/engine_interaction.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
use std::collections::BTreeMap;
use std::time::Instant;

use serde::{Deserialize, Serialize};

use geom::{vec3, Vec2, OBB};
use WorldCommand::*;

use crate::economy::Government;
use crate::map::procgen::{load_parismap, load_testfield};
use crate::map::{
BuildingGen, BuildingID, BuildingKind, IntersectionID, LaneID, LanePattern, LanePatternBuilder,
LightPolicy, LotID, Map, MapProject, ProjectKind, RoadID, Terrain, TurnPolicy, Zone,
};
use crate::map_dynamic::{BuildingInfos, ParkingManagement};
use crate::multiplayer::chat::Message;
use crate::multiplayer::MultiplayerState;
use crate::transportation::testing_vehicles::RandomVehicles;
use crate::transportation::train::{spawn_train, RailWagonKind};
use crate::transportation::{spawn_parked_vehicle_with_spot, unpark, VehicleKind};
use crate::utils::rand_provider::RandProvider;
use crate::utils::time::{GameTime, Tick};
use crate::{Egregoria, EgregoriaOptions, Replay};
use geom::{vec3, Vec2, OBB};
use serde::{Deserialize, Serialize};
use std::collections::BTreeMap;
use std::time::Instant;

use WorldCommand::*;

#[derive(Clone, Default)]
pub struct WorldCommands {
Expand All @@ -32,6 +36,9 @@ pub enum WorldCommand {
MapRemoveRoad(RoadID),
MapRemoveBuilding(BuildingID),
MapBuildHouse(LotID),
SendMessage {
message: Message,
},
SpawnRandomCars {
n_cars: usize,
},
Expand Down Expand Up @@ -318,6 +325,12 @@ impl WorldCommand {
goria.write::<RandomVehicles>().vehicles.insert(v_id);
}
}
SendMessage { ref message } => {
goria
.write::<MultiplayerState>()
.chat
.add_message(message.clone());
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions egregoria/src/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::map_dynamic::{
dispatch_system, itinerary_update, routing_changed_system, routing_update_system,
BuildingInfos, Dispatcher, ParkingManagement,
};
use crate::multiplayer::MultiplayerState;
use crate::physics::coworld_synchronize;
use crate::souls::freight_station::freight_station_system;
use crate::souls::goods_company::{company_system, GoodsCompanyRegistry};
Expand Down Expand Up @@ -60,6 +61,7 @@ pub fn init() {

register_init(init_market);

register_resource_default::<MultiplayerState, Bincode>("multiplayer_state");
register_resource_default::<RandomVehicles, Bincode>("random_vehicles");
register_resource_default::<Tick, Bincode>("tick");
register_resource_default::<Map, Bincode>("map");
Expand Down
1 change: 1 addition & 0 deletions egregoria/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ pub mod engine_interaction;
pub mod init;
pub mod map;
pub mod map_dynamic;
pub mod multiplayer;
pub mod physics;
pub mod souls;
#[cfg(test)]
Expand Down
44 changes: 44 additions & 0 deletions egregoria/src/multiplayer/chat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::utils::time::{GameInstant, GameTime};
use geom::Color;
use serde::{Deserialize, Serialize};

#[derive(Default, Serialize, Deserialize)]
pub struct Chat {
pub messages: Vec<Message>,
}

#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct Message {
pub name: String,
pub text: String,
pub sent_at: GameInstant,
pub color: Color,
pub kind: MessageKind,
}

#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
pub enum MessageKind {
Info,
Warning,
PlayerChat,
}

impl Chat {
pub fn add_message(&mut self, message: Message) {
self.messages.push(message);
}

pub fn messages_since(&self, time: GameInstant) -> impl Iterator<Item = &'_ Message> + '_ {
self.messages
.iter()
.filter(move |m| m.sent_at >= time)
.rev()
}
}

impl Message {
/// Returns the number of (game) seconds elapsed since the message was sent
pub fn age_secs(&self, now: &GameTime) -> f64 {
self.sent_at.elapsed(now)
}
}
9 changes: 9 additions & 0 deletions egregoria/src/multiplayer/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
use crate::multiplayer::chat::Chat;
use serde::{Deserialize, Serialize};

pub mod chat;

#[derive(Default, Serialize, Deserialize)]
pub struct MultiplayerState {
pub chat: Chat,
}
2 changes: 1 addition & 1 deletion egregoria/src/utils/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pub const TICKS_PER_SECOND: u32 = 50;
pub struct Tick(pub u32);

/// An in-game instant used to measure time differences
#[derive(Inspect, Debug, Copy, Clone, Serialize, Deserialize)]
#[derive(Inspect, PartialEq, PartialOrd, Debug, Copy, Clone, Serialize, Deserialize)]
pub struct GameInstant {
/// Time in seconds elapsed since the start of the game
pub timestamp: f64,
Expand Down
127 changes: 127 additions & 0 deletions native_app/src/gui/chat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
use egui::panel::TopBottomSide;
use egui::{Align2, Color32, Frame, RichText, ScrollArea, TextBuffer, TopBottomPanel};

use egregoria::engine_interaction::WorldCommand;
use egregoria::multiplayer::chat::{Message, MessageKind};
use egregoria::multiplayer::MultiplayerState;
use egregoria::utils::time::{GameInstant, GameTime, SECONDS_PER_REALTIME_SECOND};
use egregoria::Egregoria;
use geom::Color;

use crate::inputmap::{InputAction, InputMap};
use crate::uiworld::UiWorld;

#[derive(Default)]
pub struct GUIChatState {
cur_msg: String,
chat_bar_showed: bool,
}

pub fn chat(ui: &egui::Context, uiw: &mut UiWorld, goria: &Egregoria) {
const MAX_MESSAGES: usize = 30;
let mut state = uiw.write::<GUIChatState>();
let one_minute_ago = GameInstant {
timestamp: goria.read::<GameTime>().instant().timestamp
- 120.0 * SECONDS_PER_REALTIME_SECOND as f64,
};

let mstate = goria.read::<MultiplayerState>();

let just_opened = uiw
.read::<InputMap>()
.just_act
.contains(&InputAction::OpenChat);

if just_opened {
state.chat_bar_showed = true;
}

let msgs: Vec<_> = mstate
.chat
.messages_since(one_minute_ago)
.take(MAX_MESSAGES)
.collect();

if !state.chat_bar_showed && msgs.len() == 0 {
return;
}

egui::Window::new("Chat")
.title_bar(false)
.fixed_size(egui::Vec2::new(250.0, 300.0))
.frame(Frame::default().fill(if state.chat_bar_showed {
Color32::from_black_alpha(192)
} else {
Color32::from_black_alpha(64)
}))
.anchor(Align2::LEFT_BOTTOM, (0.0, -55.0))
.show(ui, |ui| {
ScrollArea::vertical().stick_to_bottom(true).show(ui, |ui| {
ui.allocate_space(egui::Vec2::new(250.0, 0.0));

if msgs.len() < 12 {
ui.add_space((12 - msgs.len()) as f32 * tweak!(24.0));
}

for message in msgs.iter().rev() {
let color = message.color;

let text = RichText::new(message.text.clone());

ui.horizontal_wrapped(|ui| {
ui.add_space(5.0);
ui.colored_label(
Color32::from_rgb(
(color.r * 255.0) as u8,
(color.g * 255.0) as u8,
(color.b * 255.0) as u8,
),
text,
);
});
ui.add_space(2.0);
}
});

TopBottomPanel::new(TopBottomSide::Bottom, "chat_bar")
.frame(Frame::default())
.show_separator_line(false)
.show_inside(ui, |ui| {
if state.chat_bar_showed {
let response = ui.add(
egui::TextEdit::singleline(&mut state.cur_msg)
.desired_width(250.0)
.margin(egui::Vec2::new(8.0, 6.0)),
);

if just_opened {
response.request_focus();
}

if response.lost_focus() {
let msg = state.cur_msg.take();

if msg.len() > 0 && ui.input(|i| i.key_pressed(egui::Key::Enter)) {
let rng = common::rand::randu64(common::hash_u64(msg.as_bytes()));

let color = Color::hsv(rng * 360.0, 0.8, 1.0, 1.0);

uiw.commands().push(WorldCommand::SendMessage {
message: Message {
name: "player".to_string(),
text: msg,
sent_at: goria.read::<GameTime>().instant(),
color,
kind: MessageKind::PlayerChat,
},
})
}

state.chat_bar_showed = false;
}
} else {
ui.allocate_space(egui::Vec2::new(240.0, 26.0));
}
});
});
}
1 change: 1 addition & 0 deletions native_app/src/gui/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use roadbuild::RoadBuildResource;

pub mod addtrain;
pub mod bulldozer;
pub mod chat;
pub mod follow;
pub mod inspect;
pub mod inspected_aura;
Expand Down
3 changes: 3 additions & 0 deletions native_app/src/gui/topgui.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::gui::bulldozer::BulldozerState;
use crate::gui::chat::chat;
use crate::gui::inspect::inspector;
use crate::gui::lotbrush::LotBrushResource;
use crate::gui::roadeditor::RoadEditorResource;
Expand Down Expand Up @@ -75,6 +76,8 @@ impl Gui {

inspector(ui, uiworld, goria);

chat(ui, uiworld, goria);

self.windows.render(ui, uiworld, goria);

Self::toolbox(ui, uiworld, goria);
Expand Down
2 changes: 2 additions & 0 deletions native_app/src/init.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::game_loop::Timings;
use crate::gui::bulldozer::BulldozerState;
use crate::gui::chat::GUIChatState;
use crate::gui::lotbrush::LotBrushResource;
use crate::gui::roadbuild::RoadBuildResource;
use crate::gui::roadeditor::RoadEditorResource;
Expand Down Expand Up @@ -38,6 +39,7 @@ pub fn init() {
register_resource_noserialize::<ErrorTooltip>();
register_resource_noserialize::<ExitState>();
register_resource_noserialize::<FollowEntity>();
register_resource_noserialize::<GUIChatState>();
register_resource_noserialize::<ImmediateDraw>();
register_resource_noserialize::<ImmediateSound>();
register_resource_noserialize::<InputMap>();
Expand Down
37 changes: 20 additions & 17 deletions native_app/src/inputmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ pub enum InputAction {
DownElevation,
OpenEconomyMenu,
PausePlay,
OpenChat,
}

// All unit inputs need to match
Expand Down Expand Up @@ -88,6 +89,7 @@ const DEFAULT_BINDINGS: &[(InputAction, &[&[UnitInput]])] = &[
(DownElevation, &[&[Key(K::LControl), WheelDown]]),
(OpenEconomyMenu, &[&[Key(K::E)]]),
(PausePlay, &[&[Key(K::Space)]]),
(OpenChat, &[&[Key(K::T)]]),
];

impl Default for Bindings {
Expand Down Expand Up @@ -303,23 +305,24 @@ impl Display for InputAction {
f,
"{}",
match self {
InputAction::GoLeft => "Go Left",
InputAction::GoRight => "Go Right",
InputAction::GoForward => "Go Forward",
InputAction::GoBackward => "Go Backward",
InputAction::CameraMove => "Camera Move",
InputAction::CameraRotate => "Camera Rotate",
InputAction::Zoom => "Zoom",
InputAction::Dezoom => "Dezoom",
InputAction::Rotate => "Rotate",
InputAction::Close => "Close",
InputAction::Select => "Select",
InputAction::HideInterface => "Hide interface",
InputAction::NoSnapping => "No Snapping",
InputAction::UpElevation => "Up Elevation",
InputAction::DownElevation => "Down Elevation",
InputAction::OpenEconomyMenu => "Economy Menu",
InputAction::PausePlay => "Pause/Play",
GoLeft => "Go Left",
GoRight => "Go Right",
GoForward => "Go Forward",
GoBackward => "Go Backward",
CameraMove => "Camera Move",
CameraRotate => "Camera Rotate",
Zoom => "Zoom",
Dezoom => "Dezoom",
Rotate => "Rotate",
Close => "Close",
Select => "Select",
HideInterface => "Hide interface",
NoSnapping => "No Snapping",
UpElevation => "Up Elevation",
DownElevation => "Down Elevation",
OpenEconomyMenu => "Economy Menu",
PausePlay => "Pause/Play",
OpenChat => "Interact with Chat",
}
)
}
Expand Down

0 comments on commit 9576ccd

Please sign in to comment.