Co-op campaign matches and command markers: mission relay, allied masters, temporary flags
This commit is contained in:
1 parent
dc22e27184
commit
5928acf7f6
12 files changed
+433
-27
No files matched your search
+61
-1
@@ -3,7 +3,7 @@
|
||||
// network match run exactly the same path. Commands are applied at step boundaries on every client,
|
||||
// in the order the server confirmed them.
|
||||
|
||||
use crate::game::{Game, TowerKind};
|
||||
use crate::game::{Game, TowerKind, MARK_ATTACK, MARK_BRIDGE, MARK_CANCEL, MARK_DIG, MARK_DROP, MARK_FLAG, MARK_GUARD, MARK_HEAL, MARK_LIGHTNING, MARK_PICKUP, MARK_ROOM, MARK_SELL, MARK_SLAP, MARK_SPELL, MARK_STEEL, MARK_SUMMON, MARK_TOWER, MARK_TRAP};
|
||||
use crate::spells::Spell;
|
||||
use crate::traps::TrapKind;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@@ -103,6 +103,7 @@ impl Game {
|
||||
let seq = self.cmd_seq;
|
||||
self.cmd_seq = self.cmd_seq.wrapping_add(1);
|
||||
self.pending.push(Command { side: self.local_side, seq, act });
|
||||
self.mark_command(act, self.local_side);
|
||||
}
|
||||
|
||||
// one command of one master: that master's state is switched in, the action runs, the local
|
||||
@@ -113,6 +114,7 @@ impl Game {
|
||||
}
|
||||
self.switch_to(c.side);
|
||||
self.run_act(c.act);
|
||||
self.mark_command(c.act, c.side);
|
||||
let local = self.local_side;
|
||||
self.switch_to(local);
|
||||
}
|
||||
@@ -127,6 +129,39 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn mark_command(&mut self, act: Act, side: u8) {
|
||||
let ((x, y), kind, dur) = match act {
|
||||
Act::Dig(r) => (rect_center(r), MARK_DIG, 4.0),
|
||||
Act::Room { r, .. } => (rect_center(r), MARK_ROOM, 5.0),
|
||||
Act::Sell(r) => (rect_center(r), MARK_SELL, 4.0),
|
||||
Act::Bridge(r) => (rect_center(r), MARK_BRIDGE, 4.0),
|
||||
Act::Steel(r) => (rect_center(r), MARK_STEEL, 4.0),
|
||||
Act::Tower { x, y, .. } => (point(x as f32, y as f32), MARK_TOWER, 6.0),
|
||||
Act::Trap { x, y, .. } => (point(x as f32, y as f32), MARK_TRAP, 6.0),
|
||||
Act::Cast { x, y, .. } => (point(x, y), MARK_SPELL, 5.0),
|
||||
Act::Lightning { x, y } => (point(x, y), MARK_LIGHTNING, 5.0),
|
||||
Act::Heal { x, y } => (point(x, y), MARK_HEAL, 5.0),
|
||||
Act::Summon { x, y } => (point(x, y), MARK_SUMMON, 6.0),
|
||||
Act::Flag { x, y } => (point(x as f32, y as f32), MARK_FLAG, 4.0),
|
||||
Act::Pickup { ent } => match self.find(ent) {
|
||||
Some(j) => (point(self.ents[j].x as f32, self.ents[j].y as f32), MARK_PICKUP, 3.5),
|
||||
None => (point(self.map.core.0 as f32, self.map.core.1 as f32), MARK_PICKUP, 3.5),
|
||||
},
|
||||
Act::Drop { x, y } => (point(x, y), MARK_DROP, 3.5),
|
||||
Act::Slap { ent } => match self.find(ent) {
|
||||
Some(j) => (point(self.ents[j].x as f32, self.ents[j].y as f32), MARK_SLAP, 4.0),
|
||||
None => (point(self.map.core.0 as f32, self.map.core.1 as f32), MARK_SLAP, 4.0),
|
||||
},
|
||||
Act::Attack { ent, x, y } => match ent.and_then(|id| self.find(id)) {
|
||||
Some(j) => (point(self.ents[j].x as f32, self.ents[j].y as f32), MARK_ATTACK, 10.0),
|
||||
None => (point(x, y), MARK_ATTACK, 10.0),
|
||||
},
|
||||
Act::Guard { x, y } => (point(x as f32, y as f32), MARK_GUARD, 10.0),
|
||||
Act::CancelOrders => (point(self.map.core.0 as f32, self.map.core.1 as f32), MARK_CANCEL, 4.0),
|
||||
};
|
||||
self.add_mark(x, y, kind, side, dur);
|
||||
}
|
||||
|
||||
fn run_act(&mut self, act: Act) {
|
||||
match act {
|
||||
Act::Dig(r) => {
|
||||
@@ -165,6 +200,17 @@ impl Game {
|
||||
}
|
||||
}
|
||||
|
||||
fn rect_center(r: Rect) -> (f32, f32) {
|
||||
(
|
||||
(r.x0 as f32 + r.x1 as f32 + 1.0) * 0.5,
|
||||
(r.y0 as f32 + r.y1 as f32 + 1.0) * 0.5,
|
||||
)
|
||||
}
|
||||
|
||||
fn point(x: f32, y: f32) -> (f32, f32) {
|
||||
(x.floor() + 0.5, y.floor() + 0.5)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -184,11 +230,25 @@ mod tests {
|
||||
let (x, y) = (cx + 6, cy); // outside the treasury ring: rock that can be marked
|
||||
g.queue(Act::Dig(Rect::new(x, y, x + 1, y + 1)));
|
||||
assert!(!g.map.t[idx(x, y)].marked, "queued, not applied yet");
|
||||
assert!(g.marks.iter().any(|m| m.kind == MARK_DIG), "queued command has a marker");
|
||||
g.update(STEP);
|
||||
assert!(g.map.t[idx(x, y)].marked, "applied at the step boundary");
|
||||
assert!(g.pending.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn commands_leave_visible_markers() {
|
||||
let mut g = game(12);
|
||||
let (cx, cy) = g.map.core;
|
||||
g.queue(Act::Dig(Rect::new(cx + 6, cy, cx + 7, cy + 1)));
|
||||
g.queue(Act::Guard { x: cx, y: cy });
|
||||
g.queue(Act::Attack { ent: None, x: (cx + 20) as f32, y: (cy + 20) as f32 });
|
||||
g.update(STEP);
|
||||
assert!(g.marks.iter().any(|m| m.kind == MARK_DIG && m.side == 0), "dig marker");
|
||||
assert!(g.marks.iter().any(|m| m.kind == MARK_GUARD && m.side == 0), "guard marker");
|
||||
assert!(g.marks.iter().any(|m| m.kind == MARK_ATTACK && m.side == 0), "attack marker");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_command_of_another_master_leaves_the_local_one_alone() {
|
||||
let mut g = game(8);
|
||||
|
||||
Reference in new issue
Block a user