rstnode: implement central event handling and handler layering

wip/yesman
Katharina Fey 3 years ago
parent 4aa0222a1f
commit 657fc50a07
Signed by: kookie
GPG Key ID: 90734A9E619C8A6C
  1. 238
      games/rstnode/Cargo.lock
  2. 5
      games/rstnode/Cargo.toml
  3. 7
      games/rstnode/rst-client/src/cli.rs
  4. 6
      games/rstnode/rst-client/src/editor/mod.rs
  5. 92
      games/rstnode/rst-client/src/event.rs
  6. 4
      games/rstnode/rst-client/src/graphics/entities/mod.rs
  7. 69
      games/rstnode/rst-client/src/input.rs
  8. 76
      games/rstnode/rst-client/src/input/arbiter.rs
  9. 75
      games/rstnode/rst-client/src/input/mod.rs
  10. 42
      games/rstnode/rst-client/src/input/traits.rs
  11. 33
      games/rstnode/rst-client/src/main.rs
  12. 2
      games/rstnode/rst-client/src/settings.rs
  13. 94
      games/rstnode/rst-client/src/state/if_impl.rs
  14. 17
      games/rstnode/rst-client/src/state/mod.rs
  15. 20
      games/rstnode/rst-client/src/viewport.rs
  16. 6
      games/rstnode/rst-client/src/window.rs
  17. 17
      games/rstnode/rst-core/src/data.rs

@ -1373,6 +1373,24 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "flate2"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cd3aec53de10fe96d7d8c565eb17f2c687bb5518a2ec453b5b1252964526abe0"
dependencies = [
"cfg-if 1.0.0",
"crc32fast",
"libc",
"miniz_oxide 0.4.3",
]
[[package]]
name = "float-cmp"
version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75224bec9bfe1a65e2d34132933f2de7fe79900c96a0174307554244ece8150e"
[[package]] [[package]]
name = "float-cmp" name = "float-cmp"
version = "0.8.0" version = "0.8.0"
@ -1388,6 +1406,17 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
[[package]]
name = "fontdb"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "428948a0f39fb83fe55991d4423e35a793cdbb0322ebe23853f6024124a330d7"
dependencies = [
"log",
"memmap2",
"ttf-parser 0.9.0",
]
[[package]] [[package]]
name = "foreign-types" name = "foreign-types"
version = "0.3.2" version = "0.3.2"
@ -2148,6 +2177,15 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "16cb54cd28cb3d2e964d9444ca185676a94fd9b7cce5f02b22c717947ed8e9a2"
dependencies = [
"arrayvec",
]
[[package]] [[package]]
name = "kv-log-macro" name = "kv-log-macro"
version = "1.0.7" version = "1.0.7"
@ -2253,7 +2291,7 @@ dependencies = [
"cssparser", "cssparser",
"data-url", "data-url",
"encoding", "encoding",
"float-cmp", "float-cmp 0.8.0",
"gdk-pixbuf", "gdk-pixbuf",
"gdk-pixbuf-sys", "gdk-pixbuf-sys",
"gio", "gio",
@ -2300,6 +2338,13 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3" checksum = "7fb9b38af92608140b86b693604b9ffcc5824240a484d1ecd4795bacb2fe88f3"
[[package]]
name = "little-assets"
version = "0.1.0"
dependencies = [
"resvg",
]
[[package]] [[package]]
name = "locale_config" name = "locale_config"
version = "0.3.0" version = "0.3.0"
@ -3197,9 +3242,15 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7" checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
dependencies = [ dependencies = [
"siphasher", "siphasher 0.3.3",
] ]
[[package]]
name = "pico-args"
version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28b9b4df73455c861d7cbf8be42f01d3b373ed7f02e378d55fa84eafc6f638b1"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "0.4.27" version = "0.4.27"
@ -3710,6 +3761,22 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "resvg"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cf6c4710bcfa7c15a73be647ec7af799500f30c3eecde2060568f3e44c09a52"
dependencies = [
"jpeg-decoder",
"log",
"pico-args",
"png",
"rgb",
"svgfilters",
"tiny-skia",
"usvg",
]
[[package]] [[package]]
name = "rgb" name = "rgb"
version = "0.8.25" version = "0.8.25"
@ -3753,6 +3820,15 @@ dependencies = [
"minimp3", "minimp3",
] ]
[[package]]
name = "roxmltree"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf7d7b1ea646d380d0e8153158063a6da7efe30ddbf3184042848e3f8a6f671"
dependencies = [
"xmlparser",
]
[[package]] [[package]]
name = "rst-core" name = "rst-core"
version = "0.0.0" version = "0.0.0"
@ -3895,12 +3971,37 @@ dependencies = [
"winapi 0.3.9", "winapi 0.3.9",
] ]
[[package]]
name = "rustybuzz"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ab463a295d00f3692e0974a0bfd83c7a9bcd119e27e07c2beecdb1b44a09d10"
dependencies = [
"bitflags",
"bytemuck",
"smallvec",
"ttf-parser 0.9.0",
"unicode-bidi-mirroring",
"unicode-ccc",
"unicode-general-category",
"unicode-script",
]
[[package]] [[package]]
name = "ryu" name = "ryu"
version = "1.0.5" version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e"
[[package]]
name = "safe_arch"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1ff3d6d9696af502cc3110dacce942840fb06ff4514cad92236ecc455f2ce05"
dependencies = [
"bytemuck",
]
[[package]] [[package]]
name = "same-file" name = "same-file"
version = "1.0.6" version = "1.0.6"
@ -4073,6 +4174,21 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "simplecss"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "596554e63596d556a0dbd681416342ca61c75f1a45203201e7e77d3fa2fa9014"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8de496cf83d4ed58b6be86c3a275b8602f6ffe98d3024a869e124147a9a3ac"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "0.3.3" version = "0.3.3"
@ -4286,6 +4402,26 @@ version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcbef9cf3cf75dd7772fb1f40dd6d90278a5263454db94ee399500ee9918aaa7" checksum = "dcbef9cf3cf75dd7772fb1f40dd6d90278a5263454db94ee399500ee9918aaa7"
[[package]]
name = "svgfilters"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3016b3217b82ea3bb7cd3b773030222c6acca46c52ef4e64b4e716bd4b25090e"
dependencies = [
"float-cmp 0.5.3",
"rgb",
]
[[package]]
name = "svgtypes"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c536faaff1a10837cfe373142583f6e27d81e96beba339147e77b67c9f260ff"
dependencies = [
"float-cmp 0.5.3",
"siphasher 0.2.3",
]
[[package]] [[package]]
name = "syn" name = "syn"
version = "0.15.44" version = "0.15.44"
@ -4486,6 +4622,19 @@ dependencies = [
"syn 1.0.60", "syn 1.0.60",
] ]
[[package]]
name = "tiny-skia"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60736037f43b891718dd2120a096157a188177b8b222adb72bd19e415223ec72"
dependencies = [
"arrayref",
"arrayvec",
"bytemuck",
"png",
"wide",
]
[[package]] [[package]]
name = "tinyvec" name = "tinyvec"
version = "0.3.4" version = "0.3.4"
@ -4607,6 +4756,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc" checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "ttf-parser"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62ddb402ac6c2af6f7a2844243887631c4e94b51585b229fcfddb43958cd55ca"
[[package]] [[package]]
name = "ttf-parser" name = "ttf-parser"
version = "0.11.0" version = "0.11.0"
@ -4639,6 +4794,24 @@ dependencies = [
"matches", "matches",
] ]
[[package]]
name = "unicode-bidi-mirroring"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56d12260fb92d52f9008be7e4bca09f584780eb2266dc8fecc6a192bec561694"
[[package]]
name = "unicode-ccc"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28ae07c514c335bbd0251147bb1de333e28ebc8f57d792014f919ed212d119f6"
[[package]]
name = "unicode-general-category"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f9af028e052a610d99e066b33304625dea9613170a2563314490a4e6ec5cf7f"
[[package]] [[package]]
name = "unicode-normalization" name = "unicode-normalization"
version = "0.1.17" version = "0.1.17"
@ -4648,6 +4821,18 @@ dependencies = [
"tinyvec 1.1.1", "tinyvec 1.1.1",
] ]
[[package]]
name = "unicode-script"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79bf4d5fc96546fdb73f9827097810bbda93b11a6770ff3a54e1f445d4135787"
[[package]]
name = "unicode-vo"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
[[package]] [[package]]
name = "unicode-width" name = "unicode-width"
version = "0.1.8" version = "0.1.8"
@ -4712,6 +4897,33 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "usvg"
version = "0.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49cf4a80e688e21577387cf750f0d6c18cd0dfc524ba9cf3ef7e01eae5301288"
dependencies = [
"base64 0.13.0",
"data-url",
"flate2",
"fontdb",
"kurbo",
"log",
"memmap2",
"pico-args",
"rctree",
"roxmltree",
"rustybuzz",
"simplecss",
"siphasher 0.2.3",
"svgtypes",
"ttf-parser 0.9.0",
"unicode-bidi",
"unicode-script",
"unicode-vo",
"xmlwriter",
]
[[package]] [[package]]
name = "utf-8" name = "utf-8"
version = "0.7.5" version = "0.7.5"
@ -4946,6 +5158,16 @@ version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c" checksum = "4a32b378380f4e9869b22f0b5177c68a5519f03b3454fde0b291455ddbae266c"
[[package]]
name = "wide"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c51f160ef6333906fad7d31a2010d449148dd1a26bc377e0a1ace2cc9e1e1ee"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]] [[package]]
name = "winapi" name = "winapi"
version = "0.2.8" version = "0.2.8"
@ -5096,6 +5318,18 @@ dependencies = [
"time 0.1.43", "time 0.1.43",
] ]
[[package]]
name = "xmlparser"
version = "0.13.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "114ba2b24d2167ef6d67d7d04c8cc86522b87f490025f39f0303b7db5bf5e3d8"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "yaml-rust" name = "yaml-rust"
version = "0.4.5" version = "0.4.5"

@ -7,7 +7,10 @@ members = [
"rst-server", "rst-server",
# Utility libraries # Utility libraries
"async-quic" "async-quic",
# Initial set of little engine crates
"little-assets"
] ]
# [profile.release.overrides."*"] # [profile.release.overrides."*"]

@ -14,6 +14,11 @@ pub fn parse(settings: &mut GameSettings) {
.version(VERSION) .version(VERSION)
.author("Bread Machine (Katharina Fey <kookie@spacekookie.de)") .author("Bread Machine (Katharina Fey <kookie@spacekookie.de)")
.about("Main game client - consider running the game via the launcher instead") .about("Main game client - consider running the game via the launcher instead")
.arg(
Arg::with_name("editor")
.help("Instead of running the main game screen, run the map editor instead")
.long("editor"),
)
.arg( .arg(
Arg::with_name("assets") Arg::with_name("assets")
.required(true) .required(true)
@ -54,4 +59,6 @@ pub fn parse(settings: &mut GameSettings) {
if matches.is_present("fullscreen") { if matches.is_present("fullscreen") {
settings.window.window_mode = WindowMode::Fullscreen; settings.window.window_mode = WindowMode::Fullscreen;
} }
settings.editor = matches.is_present("editor");
} }

@ -1,4 +1,4 @@
use crate::{assets::Assets, input::InputHandle, ui::Button, viewport::Viewport, GameSettings}; use crate::{assets::Assets, input::InputArbiter, ui::Button, viewport::Viewport, GameSettings};
use ggez::{ use ggez::{
event::{EventHandler, MouseButton}, event::{EventHandler, MouseButton},
graphics::{self, Color}, graphics::{self, Color},
@ -8,7 +8,7 @@ use ggez::{
pub struct EditorState { pub struct EditorState {
assets: Assets, assets: Assets,
settings: GameSettings, settings: GameSettings,
input: InputHandle, input: InputArbiter,
vp: Viewport, vp: Viewport,
btn: Button, btn: Button,
} }
@ -20,7 +20,7 @@ impl EditorState {
assets, assets,
settings, settings,
vp: Viewport::new(), vp: Viewport::new(),
input: InputHandle::new(), input: InputArbiter::new(),
btn: Button::new( btn: Button::new(
(25.0, 25.0).into(), (25.0, 25.0).into(),
(250.0, 125.0).into(), (250.0, 125.0).into(),

@ -0,0 +1,92 @@
use ggez::{
event::{EventHandler, KeyCode, KeyMods, MouseButton},
Context, GameResult,
};
pub struct EventLayer<L: EventHandler> {
layers: Vec<Box<dyn EventHandler>>,
render: L,
}
impl<L: EventHandler> EventLayer<L> {
pub fn new(render: L) -> Self {
Self {
layers: vec![],
render,
}
}
}
impl<L: EventHandler> EventHandler for EventLayer<L> {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
self.layers
.iter_mut()
.map(|l| l.update(ctx))
.collect::<GameResult<Vec<()>>>()?;
self.render.update(ctx)?;
Ok(())
}
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
self.render.draw(ctx)?;
Ok(())
}
fn mouse_button_down_event(&mut self, ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
self.layers
.iter_mut()
.map(|l| l.mouse_button_down_event(ctx, button, x, y))
.collect::<Vec<_>>();
}
fn mouse_button_up_event(&mut self, ctx: &mut Context, button: MouseButton, x: f32, y: f32) {
self.layers
.iter_mut()
.map(|l| l.mouse_button_up_event(ctx, button, x, y))
.collect::<Vec<_>>();
}
fn mouse_motion_event(&mut self, ctx: &mut Context, x: f32, y: f32, dx: f32, dy: f32) {
self.layers
.iter_mut()
.map(|l| l.mouse_motion_event(ctx, x, y, dx, dy))
.collect::<Vec<_>>();
}
/// A keyboard button was pressed.
///
/// The default implementation of this will call `ggez::event::quit()`
/// when the escape key is pressed. If you override this with
/// your own event handler you have to re-implment that
/// functionality yourself.
fn key_down_event(
&mut self,
ctx: &mut Context,
keycode: KeyCode,
keymods: KeyMods,
repeat: bool,
) {
self.layers
.iter_mut()
.map(|l| l.key_down_event(ctx, keycode, keymods, repeat))
.collect::<Vec<_>>();
}
/// A keyboard button was released.
fn key_up_event(&mut self, ctx: &mut Context, keycode: KeyCode, keymods: KeyMods) {
self.layers
.iter_mut()
.map(|l| l.key_up_event(ctx, keycode, keymods))
.collect::<Vec<_>>();
}
/// A unicode character was received, usually from keyboard input.
/// This is the intended way of facilitating text input.
fn text_input_event(&mut self, ctx: &mut Context, character: char) {
self.layers
.iter_mut()
.map(|l| l.text_input_event(ctx, character))
.collect::<Vec<_>>();
}
}

@ -27,8 +27,8 @@ impl Renderer for NodeRndr {
let frame = s.assets().find("frame/frame_s").unwrap(); let frame = s.assets().find("frame/frame_s").unwrap();
let icon = s.assets().find("relay/relay1").unwrap(); let icon = s.assets().find("relay/relay1").unwrap();
let x = self.loc.0 - frame.width() as f32; let x = self.inner.pos.x - frame.width() as f32;
let y = self.loc.1 - frame.height() as f32; let y = self.inner.pos.y - frame.height() as f32;
let color = match self.inner.owner { let color = match self.inner.owner {
Owner::Player(ref p) => color::to(p.color), Owner::Player(ref p) => color::to(p.color),

@ -1,69 +0,0 @@
//! Advanced input handler
use crate::{graphics::Vector2, viewport::Viewport};
use ggez::{
event::EventHandler,
input::mouse::{self, MouseButton},
Context, GameResult,
};
pub struct InputHandle {
/// The mouse position on the viewport
pub mouse_pos: Vector2,
/// Whether the left mouse button is pressed this frame
pub left_pressed: bool,
/// Whether the middle mouse button is pressed this frame
pub middle_pressed: bool,
/// Whether the right mouse button is pressed this frame
pub right_pressed: bool,
/// Set when pressing left mouse and unset when releasing it
pub drag_point: Option<Vector2>,
/// Get the scroll-wheel position this frame
pub scroll_offset: f32,
}
impl InputHandle {
pub fn new() -> Self {
Self {
mouse_pos: Vector2::new(0.0, 0.0),
left_pressed: false,
middle_pressed: false,
right_pressed: false,
drag_point: None,
scroll_offset: 1.0,
}
}
/// Get the unprojected mouse coordinates
pub fn unproject(&self, vp: &Viewport) -> Vector2 {
// self.mouse_pos.clone() - vp.start().clone()
todo!()
}
}
impl EventHandler for InputHandle {
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
self.mouse_pos = mouse::position(&ctx).into();
self.left_pressed = mouse::button_pressed(ctx, MouseButton::Left);
self.middle_pressed = mouse::button_pressed(ctx, MouseButton::Middle);
self.right_pressed = mouse::button_pressed(ctx, MouseButton::Right);
// Only set the drag_point once and unset when we release Left button
if self.middle_pressed && self.drag_point.is_none() {
self.drag_point = Some(self.mouse_pos.clone());
} else if !self.middle_pressed {
self.drag_point = None;
}
Ok(())
}
fn mouse_wheel_event(&mut self, _ctx: &mut Context, _: f32, y: f32) {
self.scroll_offset -= y * 0.25;
}
fn draw(&mut self, _: &mut Context) -> GameResult<()> {
panic!("Don't draw the input handle!");
}
}

@ -0,0 +1,76 @@
use super::{InputDriver, MouseButton};
use ggez::{
event::{EventHandler, KeyCode, KeyMods, MouseButton as EzBtn},
Context, GameResult,
};
/// Incoming event handler core component
///
/// An input event handler component that receives events from APIs
/// and maps them onto an input subscriber collection. Events can be
/// consumed, or passed through priority trees.
pub struct InputArbiter {
map: Vec<Box<dyn InputDriver>>,
}
impl InputArbiter {
pub fn new() -> Self {
Self { map: vec![] }
}
/// Add a new input handler subscription
pub(crate) fn add_handler(&mut self, dri: impl InputDriver + 'static) {
self.map.push(Box::new(dri));
}
}
/// Implement the ggez event trait for the input arbiter. These
/// function calls map onto a little-input system which maps them
/// across event handlers. An event handler is attached to a UI
/// element, which was subscribed to a partical type and scope.
impl EventHandler for InputArbiter {
fn draw(&mut self, _: &mut Context) -> GameResult<()> {
Ok(())
}
fn update(&mut self, _: &mut Context) -> GameResult<()> {
Ok(())
}
fn mouse_button_down_event(&mut self, _: &mut Context, button: EzBtn, x: f32, y: f32) {
self.map.iter_mut().for_each(|dri| {
// TODO: add drag event handling
dri.mouse_down(x, y, button.into());
});
}
fn mouse_button_up_event(&mut self, _: &mut Context, button: EzBtn, x: f32, y: f32) {
self.map.iter_mut().for_each(|dri| {
dri.mouse_up(x, y, button.into());
});
}
fn mouse_motion_event(&mut self, _ctx: &mut Context, _x: f32, _y: f32, _dx: f32, _dy: f32) {}
/// A keyboard button was pressed.
///
/// The default implementation of this will call `ggez::event::quit()`
/// when the escape key is pressed. If you override this with
/// your own event handler you have to re-implment that
/// functionality yourself.
fn key_down_event(
&mut self,
ctx: &mut Context,
keycode: KeyCode,
_keymods: KeyMods,
_repeat: bool,
) {
}
/// A keyboard button was released.
fn key_up_event(&mut self, _ctx: &mut Context, _keycode: KeyCode, _keymods: KeyMods) {}
/// A unicode character was received, usually from keyboard input.
/// This is the intended way of facilitating text input.
fn text_input_event(&mut self, _ctx: &mut Context, _character: char) {}
}

@ -0,0 +1,75 @@
//! Advanced input handler
mod arbiter;
pub use arbiter::*;
mod traits;
pub use traits::*;
// use crate::{graphics::Vector2, viewport::Viewport};
// use ggez::{
// event::EventHandler,
// input::mouse::{self, MouseButton},
// Context, GameResult,
// };
// pub struct InputHandle {
// /// The mouse position on the viewport
// pub mouse_pos: Vector2,
// /// Whether the left mouse button is pressed this frame
// pub left_pressed: bool,
// /// Whether the middle mouse button is pressed this frame
// pub middle_pressed: bool,
// /// Whether the right mouse button is pressed this frame
// pub right_pressed: bool,
// /// Set when pressing left mouse and unset when releasing it
// pub drag_point: Option<Vector2>,
// /// Get the scroll-wheel position this frame
// pub scroll_offset: f32,
// }
// impl InputHandle {
// pub fn new() -> Self {
// Self {
// mouse_pos: Vector2::new(0.0, 0.0),
// left_pressed: false,
// middle_pressed: false,
// right_pressed: false,
// drag_point: None,
// scroll_offset: 1.0,
// }
// }
// /// Get the unprojected mouse coordinates
// pub fn unproject(&self, vp: &Viewport) -> Vector2 {
// // self.mouse_pos.clone() - vp.start().clone()
// todo!()
// }
// }
// impl EventHandler for InputHandle {
// fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
// self.mouse_pos = mouse::position(&ctx).into();
// self.left_pressed = mouse::button_pressed(ctx, MouseButton::Left);
// self.middle_pressed = mouse::button_pressed(ctx, MouseButton::Middle);
// self.right_pressed = mouse::button_pressed(ctx, MouseButton::Right);
// // Only set the drag_point once and unset when we release Left button
// if self.middle_pressed && self.drag_point.is_none() {
// self.drag_point = Some(self.mouse_pos.clone());
// } else if !self.middle_pressed {
// self.drag_point = None;
// }
// Ok(())
// }
// fn mouse_wheel_event(&mut self, _ctx: &mut Context, _: f32, y: f32) {
// self.scroll_offset -= y * 0.25;
// }
// fn draw(&mut self, _: &mut Context) -> GameResult<()> {
// panic!("Don't draw the input handle!");
// }
// }

@ -0,0 +1,42 @@
use ggez::event::MouseButton as EzBtn;
/// Encodes a mouse button
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum MouseButton {
/// Left mouse
Left,
/// Right mouse
Right,
/// Middle mouse (or wheel)
Middle,
/// Additional mouse key-codes
Other(u16),
}
impl From<EzBtn> for MouseButton {
fn from(btn: EzBtn) -> Self {
match btn {
EzBtn::Left => Self::Left,
EzBtn::Right => Self::Right,
EzBtn::Middle => Self::Middle,
EzBtn::Other(c) => Self::Other(c),
}
}
}
/// A trait that represents an input event loop connection
///
/// Because `little-input` can be built built against different game
/// framework backends (ggez, ...), this trait abstracts over these
/// different APIs with this trait. This trait is implemented between
/// ggez and the `little-input` event arbiter.
pub trait InputDriver {
/// Mouse button pressed events
fn mouse_down(&mut self, x: f32, y: f32, btn: MouseButton) {}
/// Mouse button released events
fn mouse_up(&mut self, x: f32, y: f32, btn: MouseButton) {}
/// Mouse wheel response
fn scroll(&mut self, f: f32) {}
/// Mouse drag events (toggled by mouse_down events)
fn mouse_drag(&mut self, x: f32, y: f32, btn: MouseButton) {}
}

@ -11,30 +11,35 @@ mod cli;
mod color; mod color;
mod constants; mod constants;
mod ctx; mod ctx;
mod editor;
mod error; mod error;
mod event;
mod graphics; mod graphics;
mod input; mod input;
mod log; mod log;
mod settings; mod settings;
mod state; mod state;
mod ui;
mod viewport; mod viewport;
mod window; mod window;
pub(crate) use editor::*;
pub(crate) use event::*;
#[allow(unused)] #[allow(unused)]
pub(crate) use settings::{GameSettings, GraphicsSettings, WindowSettings}; pub(crate) use settings::{GameSettings, GraphicsSettings, WindowSettings};
pub(crate) use state::*; pub(crate) use state::*;
#[async_std::main] #[async_std::main]
async fn main() { async fn main() {
// Initialise logging mechanism
log::initialise();
// Initialise default game settings // Initialise default game settings
let mut settings = settings::default(); let mut settings = settings::default();
// Parse commandline arguments // Parse commandline arguments
cli::parse(&mut settings); cli::parse(&mut settings);
// Initialise logging mechanism
log::initialise();
// Initialise window context // Initialise window context
let mut window = window::create(&settings); let mut window = window::create(&settings);
@ -42,12 +47,22 @@ async fn main() {
let assets = let assets =
assets::load_tree(window.ctx(), &settings).unwrap_or_else(|e| fatal!("LoadError: {}!", e)); assets::load_tree(window.ctx(), &settings).unwrap_or_else(|e| fatal!("LoadError: {}!", e));
// Create the client state // Either create client state or editor state
let mut state = ClientState::new(settings, assets); if settings.editor {
let state = EditorState::new(settings, assets);
// Initialise the viewport first!
// state.viewport().init(window.ctx());
// Window goes brrrr
window.run(state)
} else {
let mut state = ClientState::new(settings, assets);
// Initialise the viewport first! // Initialise the viewport first!
state.viewport().init(window.ctx()); state.viewport().init(window.ctx());
// Window goes brrrr // Window goes brrrr
window.run(state) window.run(state)
}
} }

@ -5,6 +5,7 @@ use std::path::PathBuf;
pub fn default() -> GameSettings { pub fn default() -> GameSettings {
GameSettings { GameSettings {
editor: false,
assets: None, assets: None,
window: WindowSettings { window: WindowSettings {
width: 1280, width: 1280,
@ -20,6 +21,7 @@ pub fn default() -> GameSettings {
/// Complete tree of basic game client settings /// Complete tree of basic game client settings
pub struct GameSettings { pub struct GameSettings {
pub editor: bool,
pub assets: Option<PathBuf>, pub assets: Option<PathBuf>,
pub window: WindowSettings, pub window: WindowSettings,
pub graphics: GraphicsSettings, pub graphics: GraphicsSettings,

@ -12,59 +12,59 @@ use rst_core::{
}; };
use std::sync::Arc; use std::sync::Arc;
#[async_trait] // #[async_trait]
impl GameIf for ClientState { // impl GameIf for ClientState {
async fn register(self: Arc<Self>, name: String, pw: String) -> Result<UserId, RegErr> { // async fn register(self: Arc<Self>, name: String, pw: String) -> Result<UserId, RegErr> {
todo!() // todo!()
} // }
async fn login(self: Arc<Self>, name: String, pw: String) -> Result<User, AuthErr> { // async fn login(self: Arc<Self>, name: String, pw: String) -> Result<User, AuthErr> {
todo!() // todo!()
} // }
async fn logout(self: Arc<Self>, user: User) -> Result<(), AuthErr> { // async fn logout(self: Arc<Self>, user: User) -> Result<(), AuthErr> {
todo!() // todo!()
} // }
async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> { // async fn anonymous(self: Arc<Self>, name: String) -> Result<User, AuthErr> {
todo!() // todo!()
} // }
async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> { // async fn join(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<Lobby, LobbyErr> {
todo!() // todo!()
} // }
async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> { // async fn leave(self: Arc<Self>, user: User, lobby: LobbyId) -> Result<(), LobbyErr> {
todo!() // todo!()
} // }
async fn ready( // async fn ready(
self: Arc<Self>, // self: Arc<Self>,
user: User, // user: User,
lobby: LobbyId, // lobby: LobbyId,
ready: bool, // ready: bool,
) -> Result<LobbyUpdate, LobbyErr> { // ) -> Result<LobbyUpdate, LobbyErr> {
todo!() // todo!()
} // }
async fn start_req( // async fn start_req(
self: Arc<Self>, // self: Arc<Self>,
user: UserId, // user: UserId,
lobby: LobbyId, // lobby: LobbyId,
) -> Result<DateTime<Utc>, LobbyErr> { // ) -> Result<DateTime<Utc>, LobbyErr> {
todo!() // todo!()
} // }
async fn perform_action( // async fn perform_action(
self: Arc<Self>, // self: Arc<Self>,
user: User, // user: User,
mtch: MatchId, // mtch: MatchId,
act: Action, // act: Action,
) -> UpdateState { // ) -> UpdateState {
todo!() // todo!()
} // }
async fn leave_match(self: Arc<Self>, user: User, mtch: MatchId) -> Result<(), MatchErr> { // async fn leave_match(self: Arc<Self>, user: User, mtch: MatchId) -> Result<(), MatchErr> {
todo!() // todo!()
} // }
} // }

@ -11,13 +11,13 @@ use crate::{
entities::{Coordinates, NodeRndr}, entities::{Coordinates, NodeRndr},
Renderer, Renderer,
}, },
input::InputHandle, input::InputArbiter,
viewport::Viewport, viewport::Viewport,
GameSettings, GameSettings,
}; };
use ggez::{event::EventHandler, graphics, Context, GameResult}; use ggez::{event::EventHandler, graphics, Context, GameResult};
use rst_core::{ use rst_core::{
data::{Color, Level, Link, Node, Owner, Player, Position, Upgrade}, data::{Color, Level, Link, Node, Owner, Player, Pos, Upgrade},
io::Io, io::Io,
}; };
use std::sync::Arc; use std::sync::Arc;
@ -25,7 +25,7 @@ use std::sync::Arc;
pub struct ClientState { pub struct ClientState {
assets: Assets, assets: Assets,
settings: GameSettings, settings: GameSettings,
input: InputHandle, input: InputArbiter,
vp: Viewport, vp: Viewport,
// Game state // Game state
@ -36,6 +36,7 @@ pub struct ClientState {
impl ClientState { impl ClientState {
pub fn new(settings: GameSettings, assets: Assets) -> Self { pub fn new(settings: GameSettings, assets: Assets) -> Self {
info!("Initialising game client state");
let link = Arc::new(Link { let link = Arc::new(Link {
id: 0, id: 0,
a: 0, a: 0,
@ -49,11 +50,11 @@ impl ClientState {
assets, assets,
settings, settings,
vp: Viewport::new(), vp: Viewport::new(),
input: InputHandle::new(), input: InputArbiter::new(),
node1: NodeRndr { node1: NodeRndr {
inner: Arc::new(Node { inner: Arc::new(Node {
id: 0, id: 0,
pos: Position { x: 512.0, y: 512.0 }, pos: Pos { x: 512.0, y: 512.0 },
health: 100.into(), health: 100.into(),
max_health: 100.into(), max_health: 100.into(),
owner: Owner::Player(Player { owner: Owner::Player(Player {
@ -72,7 +73,7 @@ impl ClientState {
node2: NodeRndr { node2: NodeRndr {
inner: Arc::new(Node { inner: Arc::new(Node {
id: 0, id: 0,
pos: Position { x: 128.0, y: 128.0 }, pos: Pos { x: 128.0, y: 128.0 },
health: 100.into(), health: 100.into(),
max_health: 100.into(), max_health: 100.into(),
owner: Owner::Neutral, owner: Owner::Neutral,
@ -113,8 +114,8 @@ impl EventHandler for ClientState {
fn mouse_wheel_event(&mut self, ctx: &mut Context, x: f32, y: f32) { fn mouse_wheel_event(&mut self, ctx: &mut Context, x: f32, y: f32) {
self.input.mouse_wheel_event(ctx, x, y); self.input.mouse_wheel_event(ctx, x, y);
let zoom = self.input.scroll_offset; // let zoom = self.input.scroll_offset;
self.viewport().zoom(zoom); // self.viewport().zoom(zoom);
} }
fn draw(&mut self, ctx: &mut Context) -> GameResult<()> { fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {

@ -1,6 +1,6 @@
//! Viewport utilities //! Viewport utilities
use crate::{graphics::Vector2, input::InputHandle}; use crate::{graphics::Vector2, input::InputArbiter};
use ggez::{ use ggez::{
error::GameResult, error::GameResult,
graphics::{self, Rect}, graphics::{self, Rect},
@ -46,16 +46,16 @@ impl Viewport {
} }
/// Apply changes from the input handle to the viewport /// Apply changes from the input handle to the viewport
pub fn apply(&mut self, _: &mut Context, input: &InputHandle) -> GameResult<()> { pub fn apply(&mut self, _: &mut Context, input: &InputArbiter) -> GameResult<()> {
// Move the viewport around // // Move the viewport around
if input.middle_pressed { // if input.middle_pressed {
let drag = input.drag_point.as_ref().unwrap().clone(); // let drag = input.drag_point.as_ref().unwrap().clone();
let pos = input.mouse_pos.clone(); // let pos = input.mouse_pos.clone();
self.start = self.prev_start + (drag - pos); // self.start = self.prev_start + (drag - pos);
} else { // } else {
self.prev_start = self.start; // self.prev_start = self.start;
} // }
// Compute the scroll level // Compute the scroll level
Ok(()) Ok(())

@ -2,7 +2,7 @@
use crate::{ctx, state::ClientState, GameSettings}; use crate::{ctx, state::ClientState, GameSettings};
use ggez::{ use ggez::{
event::{self, EventLoop}, event::{self, EventHandler, EventLoop},
Context, Context,
}; };
@ -15,8 +15,8 @@ impl Window {
pub fn ctx(&mut self) -> &mut Context { pub fn ctx(&mut self) -> &mut Context {
&mut self.ctx &mut self.ctx
} }
pub fn run(self, state: ClientState) -> ! { pub fn run(self, state: impl EventHandler + 'static) -> ! {
event::run(self.ctx, self.eloop, state) event::run(self.ctx, self.eloop, state)
} }
} }

@ -14,12 +14,25 @@ use std::{
pub type NodeId = usize; pub type NodeId = usize;
/// An x/y position in the game
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
pub struct Position { pub struct Pos {
pub x: f32, pub x: f32,
pub y: f32, pub y: f32,
} }
impl Pos {
pub fn xy(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl From<(f32, f32)> for Pos {
fn from((x, y): (f32, f32)) -> Self {
Self { x, y }
}
}
/// A node is a computer on the network graph /// A node is a computer on the network graph
/// ///
/// It's owned by a player, and has some upgrade state, as well as /// It's owned by a player, and has some upgrade state, as well as
@ -29,7 +42,7 @@ pub struct Node {
/// Each node has a unique ID by which it's addressed /// Each node has a unique ID by which it's addressed
pub id: NodeId, pub id: NodeId,
/// The position of this node on the map /// The position of this node on the map
pub pos: Position, pub pos: Pos,
/// The current health /// The current health
pub health: AtomicU32, pub health: AtomicU32,
/// The max health /// The max health

Loading…
Cancel
Save