Commit 376c1e2f authored by Benjamin Lee's avatar Benjamin Lee 💬
Browse files

Added animations to identify each round winner.

parent a9ec875a
......@@ -113,8 +113,8 @@ dependencies = [
"colored 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
"crossbeam 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)",
"ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
"easer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
"either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
"enum-kinds 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)",
"failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx-backend-vulkan 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"gfx-hal 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
......@@ -364,18 +364,17 @@ version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "either"
version = "1.5.0"
name = "easer"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "enum-kinds"
version = "0.4.1"
name = "either"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "failure"
......@@ -966,14 +965,6 @@ name = "pkg-config"
version = "0.3.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "proc-macro2"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "proc-macro2"
version = "0.4.26"
......@@ -982,14 +973,6 @@ dependencies = [
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "0.6.11"
......@@ -1326,16 +1309,6 @@ dependencies = [
"syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.12.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "0.14.9"
......@@ -1723,8 +1696,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum ctrlc 3.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "630391922b1b893692c6334369ff528dcc3a9d8061ccf4c803aa8f83cb13db5e"
"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
"checksum downcast-rs 1.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "18df8ce4470c189d18aa926022da57544f31e154631eb4cfe796aea97051fe6c"
"checksum easer 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8dde460cecb776d6524d53ca7acf960561686a620d3791c0dc56c3ada406191a"
"checksum either 1.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3be565ca5c557d7f59e7cfcf1844f9e3033650c929c6566f511e8005f205c1d0"
"checksum enum-kinds 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d0f21c374dea848c19071b1504ca5ad03c9ad0d03d2e509e68f6623b8fcac4b5"
"checksum failure 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "795bd83d3abeb9220f257e597aa0080a508b27533824adf336529648f6abf7e2"
"checksum failure_derive 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "ea1063915fd7ef4309e222a5a07cf9c319fb9c7836b1f89b85458672dbb127e1"
"checksum foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1"
......@@ -1792,9 +1765,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum phf_generator 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "09364cc93c159b8b06b1f4dd8a4398984503483891b0c26b867cf431fb132662"
"checksum phf_shared 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)" = "234f71a15de2288bcb7e3b6515828d22af7ec8598ee6d24c3b526fa0a80b67a0"
"checksum pkg-config 0.3.14 (registry+https://github.com/rust-lang/crates.io-index)" = "676e8eb2b1b4c9043511a9b7bea0915320d7e502b0a079fb03f9635a5252b18c"
"checksum proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cd07deb3c6d1d9ff827999c7f9b04cdfd66b1b17ae508e14fe47b620f2282ae0"
"checksum proc-macro2 0.4.26 (registry+https://github.com/rust-lang/crates.io-index)" = "38fddd23d98b2144d197c0eca5705632d4fe2667d14a6be5df8934f8d74f1978"
"checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408"
"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1"
"checksum rand 0.5.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c618c47cd3ebd209790115ab837de41425723956ad3ce2e6a7f09890947cacb9"
"checksum rand 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3906503e80ac6cbcacb2c2973fa8e473f24d7e2747c8c92bb230c2441cad96b5"
......@@ -1834,7 +1805,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4f380125926a99e52bc279241539c018323fab05ad6368b56f93d9369ff550"
"checksum structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "670ad348dc73012fcf78c71f06f9d942232cdd4c859d4b6975e27836c3efc0c3"
"checksum structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)" = "ef98172b1a00b0bec738508d3726540edcbd186d50dfd326f2b1febbb3559f04"
"checksum syn 0.12.15 (registry+https://github.com/rust-lang/crates.io-index)" = "c97c05b8ebc34ddd6b967994d5c6e9852fa92f8b82b3858c39451f97346dcce5"
"checksum syn 0.14.9 (registry+https://github.com/rust-lang/crates.io-index)" = "261ae9ecaa397c42b960649561949d69311f08eeaea86a65696e6e46517cf741"
"checksum syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)" = "f92e629aa1d9c827b2bb8297046c1ccffc57c99b947a680d3ccff1f136a3bee9"
"checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015"
......
......@@ -36,9 +36,9 @@ ord_subset = "3.1.1"
nalgebra = { version = "0.16.13", features = ["serde-serialize"] }
either = "1.5.0"
crossbeam = "0.6.0"
enum-kinds = "0.4.1"
take_mut = "0.2.2"
itertools = "0.8.0"
easer = "0.2.1"
[build-dependencies]
shaderc = "0.3.13"
......
......@@ -37,9 +37,11 @@ pub struct Players<'a, S> {
}
pub struct Game {
players: HashMap<PlayerId, StaticPlayerState>,
pub players: HashMap<PlayerId, StaticPlayerState>,
pub last_round: Option<RoundState>,
pub round: RoundState,
pub round_duration: f32,
snapshots: VecDeque<(Snapshot, Instant)>,
round: RoundState,
settings: GameSettings,
settings_handle: Arc<SettingsHandle>,
cursor: Arc<Mutex<Point2<f32>>>,
......@@ -159,6 +161,8 @@ impl Game {
pub fn new(
players: HashMap<PlayerId, StaticPlayerState>,
snapshot: Snapshot,
round: RoundState,
round_duration: f32,
settings: GameSettings,
player_id: PlayerId,
cursor: Point2<f32>,
......@@ -176,7 +180,9 @@ impl Game {
snapshots,
cursor: cursor.clone(),
events: events_rx,
round: RoundState::default(),
round,
last_round: None,
round_duration,
player_id,
settings,
settings_handle: Arc::clone(&settings_handle),
......@@ -209,6 +215,8 @@ impl Game {
match event {
Event::RoundState(round) => {
info!("transitioning to round state {:?}", round);
self.last_round = Some(self.round);
self.round_duration = 0.0;
self.round = round;
},
Event::Settings(settings) => {
......@@ -240,8 +248,8 @@ impl Game {
/// Steps client prediction forward in time.
pub fn tick(&mut self, dt: f32) {
self.round_duration += dt;
self.handle_events();
self.round.tick(dt);
}
/// Updates the cursor position for this client player.
......
use crate::graphics::Circle;
use enum_kinds::EnumKind;
use nalgebra::{self, Point2, Vector2};
use palette::LinSrgb;
use serde::{Deserialize, Serialize};
......@@ -17,17 +16,18 @@ pub use self::snapshot::*;
pub type PlayerId = u16;
/// Finite state machine for the round state.
#[derive(Debug, Copy, Clone, Serialize, Deserialize, EnumKind)]
#[enum_kind(RoundStateKind)]
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub enum RoundState {
/// Less than two players, so nothing happens.
Lobby,
/// More than two players, waiting for a round to start.
Waiting(f32),
Waiting,
/// More than one player left alive.
Round,
/// One or less players alive, waiting for the round to end.
PostRound(f32),
/// One or less players left alive, waiting to end the round.
RoundEnd,
/// Winner declared, starting the next round.
Winner(Option<PlayerId>),
}
#[derive(Copy, Clone, Debug, PartialEq, Serialize, Deserialize)]
......@@ -212,47 +212,14 @@ impl Default for RoundState {
}
impl RoundState {
/// Waiting state, for the default initial time.
pub fn waiting() -> RoundState {
RoundState::Waiting(3.0) // 3 seconds
}
/// Post-round state, for the default initial time.
pub fn post_round() -> RoundState {
RoundState::PostRound(3.0) // 3 seconds
}
/// Whether a round is actually running in this state.
pub fn running(self) -> bool {
match self {
RoundState::Round | RoundState::PostRound(_) => true,
RoundState::Round => true,
RoundState::RoundEnd => true,
_ => false,
}
}
/// Tick any timers on this state, returning a possible transition.
///
/// The reason that this doesn't just transition itself is that
/// sometimes you want to ignore the transition. For example, the
/// client should just wait for a message from the server.
pub fn tick(&mut self, dt: f32) -> Option<RoundState> {
match self {
RoundState::Waiting(ref mut time) => {
*time -= dt;
if *time < 0.0 {
return Some(RoundState::Round);
}
},
RoundState::PostRound(ref mut time) => {
*time -= dt;
if *time < 0.0 {
return Some(RoundState::waiting());
}
},
_ => (),
}
None
}
}
/// Steps over a given time interval in chunks of at most a specified duration.
......
......@@ -57,6 +57,7 @@ pub struct Player {
pub struct Game {
pub players: HashMap<PlayerId, Player>,
pub round: RoundState,
pub round_duration: f32,
pub settings: GameSettings,
next_id: PlayerId,
}
......@@ -125,11 +126,48 @@ impl Game {
}
}
fn switch_round(&mut self, round: RoundState) {
self.round = round;
self.round_duration = 0.0;
}
/// Steps the whole game world forward in time.
pub fn tick(&mut self, dt: f32) -> impl Iterator<Item = Event> {
let transition = self.round.tick(dt);
self.round_duration += dt;
let transition = match self.round {
RoundState::Lobby => None,
RoundState::Waiting => {
if self.round_duration > 3.0 {
Some(RoundState::Round)
} else {
None
}
},
RoundState::Round => None,
RoundState::RoundEnd => {
if self.round_duration > 2.0 {
// If a player is still alive, they win.
let winner = self
.players
.iter()
.filter(|(_, player)| player.state.alive())
.next()
.map(|(&id, _)| id);
Some(RoundState::Winner(winner))
} else {
None
}
},
RoundState::Winner(_) => {
if self.round_duration > 1.0 {
Some(RoundState::Waiting)
} else {
None
}
},
};
if let Some(round) = transition {
self.round = round;
self.switch_round(round);
}
if !self.round.running() {
......@@ -216,15 +254,15 @@ impl Game {
}
if let RoundState::Round = self.round {
// End the round if there are one or less players still
// alive.
// Start the round ending if there are one or less players
// still alive.
let num_alive = self
.players
.values()
.filter(|player| player.state.alive())
.count();
if num_alive <= 1 {
self.round = RoundState::post_round();
self.switch_round(RoundState::RoundEnd);
return Some(Event::RoundState(self.round)).into_iter();
}
}
......@@ -287,7 +325,7 @@ impl Game {
// Queue a waiting round if there are two or more players.
if let RoundState::Lobby = self.round {
if self.players.len() >= 2 {
self.round = RoundState::waiting();
self.switch_round(RoundState::Waiting);
events.push(Event::RoundState(self.round));
}
}
......@@ -306,7 +344,7 @@ impl Game {
events.push(Event::RemovePlayer(id));
// If there are less than two players left, stop the round.
if self.players.len() < 2 {
self.round = RoundState::Lobby;
self.switch_round(RoundState::Lobby);
events.push(Event::RoundState(self.round))
}
......
......@@ -556,12 +556,21 @@ impl Client {
match packet {
ServerPacket::Handshake {
players,
round,
round_duration,
settings,
snapshot,
id,
} => {
let (game, game_handle) =
Game::new(players, snapshot, settings, id, *cursor);
let (game, game_handle) = Game::new(
players,
snapshot,
round,
round_duration,
settings,
id,
*cursor,
);
let tick = Interval::new(TICK_RATE);
let ping = Interval::new(PING_RATE);
// Start the timer for sending input ticks and pings.
......
......@@ -5,7 +5,7 @@ use crate::game::{
GameSettings,
GetPlayer,
PlayerId,
RoundStateKind,
RoundState,
Snapshot,
StaticPlayerState,
};
......@@ -60,6 +60,8 @@ pub enum ServerPacket {
id: PlayerId,
settings: GameSettings,
players: HashMap<PlayerId, StaticPlayerState>,
round: RoundState,
round_duration: f32,
snapshot: Snapshot,
},
}
......@@ -154,7 +156,7 @@ impl ServerPacket {
ServerPacket::Event(Event::RoundState(round)) => {
// Only resend if the round state hasn't
// changed again since it was sent.
RoundStateKind::from(round) == RoundStateKind::from(game.round)
*round == game.round
},
ServerPacket::Event(Event::Settings(settings)) => {
// Only resend if the round state hasn't
......@@ -444,6 +446,8 @@ impl Server {
.players()
.map(|(id, player)| (id, player.static_state().clone()))
.collect(),
round: self.game.round,
round_duration: self.game.round_duration,
snapshot: self.game.snapshot(),
};
let (packet, _) = client.encode(&packet);
......
use crate::debug::DebugState;
use crate::game::{clamp_cursor, client::Game, GameSettings, GetPlayer};
use crate::game::{
clamp_cursor,
client::Game,
GameSettings,
GetPlayer,
RoundState,
};
use crate::graphics::{Circle, CircleRenderer, DrawContext};
use crate::networking::{
self,
client::{self, ClientHandle, ConnectedHandle, ConnectingHandle},
server::{self, ServerHandle},
};
use easer::functions::*;
use gfx_hal::Backend;
use imgui::{im_str, ImString, Ui};
use log::{error, warn};
......@@ -24,14 +31,14 @@ use winit::{
const SCALE: f32 = 0.9;
fn bounds_circle(settings: Option<&GameSettings>) -> Circle {
fn bounds_circle(scale: f32, settings: Option<&GameSettings>) -> Circle {
let bounds_radius = match settings {
Some(settings) => settings.bounds_radius,
None => 1.0,
};
Circle {
center: Point2::new(0.0, 0.0),
radius: SCALE * bounds_radius,
radius: scale * bounds_radius,
color: LinSrgb::new(1.0, 1.0, 1.0),
}
}
......@@ -257,7 +264,7 @@ impl GameState {
Screen::MainMenu {
..
} => {
circle_rend.draw(ctx, iter::once(bounds_circle(None)));
circle_rend.draw(ctx, iter::once(bounds_circle(SCALE, None)));
},
Screen::InGame {
ref mut game,
......@@ -267,23 +274,71 @@ impl GameState {
game.clean_old_snapshots(now, debug.interpolation_delay);
let (round_circles, scale) = match (game.last_round, game.round)
{
(Some(RoundState::Winner(_)), RoundState::Waiting) => {
let scale = Expo::ease_out(
game.round_duration,
0.0,
SCALE,
0.3,
);
(None, scale)
},
(_, RoundState::Winner(winner)) => {
// No winner is black
let color = match winner {
Some(id) => game.players[&id].color,
None => LinSrgb::new(0.5, 0.5, 0.5),
};
let radius = Expo::ease_out(
game.round_duration,
0.0,
game.settings().bounds_radius,
0.3,
);
let scale = if game.round_duration > 0.5 {
Expo::ease_in(
game.round_duration - 0.5,
SCALE,
-SCALE,
0.3,
)
.max(0.0)
} else {
SCALE
};
(
Some(Circle {
center: Point2::new(0.0, 0.0),
radius: scale * radius,
color,
}),
scale,
)
},
_ => (None, SCALE),
};
let players = game.interpolated_players(
now,
clamp_cursor(self.cursor, game.settings()),
debug.interpolation_delay,
);
let circles = players.into_iter().flat_map(|(_, player)| {
player.draw(SCALE, game.settings())
player.draw(scale, game.settings())
});
let bounds_circle = bounds_circle(Some(game.settings()));
let bounds_circle = bounds_circle(scale, Some(game.settings()));
if debug.draw_latest_snapshot {
let players = game.latest_players();
let debug_circles = players
.into_iter()
.flat_map(|(_, player)| {
player.draw(SCALE, game.settings())
player.draw(scale, game.settings())
})
.map(|circle| {
Circle {
......@@ -295,11 +350,16 @@ impl GameState {
ctx,
iter::once(bounds_circle)
.chain(debug_circles)
.chain(circles),
.chain(circles)
.chain(round_circles),
);
} else {
circle_rend
.draw(ctx, iter::once(bounds_circle).chain(circles));
circle_rend.draw(
ctx,
iter::once(bounds_circle)
.chain(circles)
.chain(round_circles),
);
}
},
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment