Masters for a match: the four sides red, green, blue and yellow (side 4 gets its own owner), and start_masters gives every master his core and his starting area
This commit is contained in:
1 parent
35ddfa134c
commit
87890adc53
10 files changed
+132
-9
No files matched your search
+16
-1
@@ -169,7 +169,7 @@ impl Game {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::game::{EK, Job, STEP};
|
||||
use crate::map::idx;
|
||||
use crate::map::{idx, Kind, Owner, mh, mw};
|
||||
|
||||
fn game(seed: u32) -> Game {
|
||||
let mut g = Game::new(seed, 64, 64);
|
||||
@@ -272,4 +272,19 @@ mod tests {
|
||||
let json = serde_json::to_string(&c).unwrap();
|
||||
assert_eq!(serde_json::from_str::<Command>(&json).unwrap(), c);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_match_command_builds_on_the_ground_of_its_own_master() {
|
||||
let mut g = Game::new(13, 96, 128);
|
||||
g.init_fog();
|
||||
assert!(g.start_masters(&[0, 2], 0), "a match of two masters");
|
||||
let home = g.map.t.iter().position(|t| t.kind == Kind::Core && t.owner == Owner::Rival1).expect("the green master got his core");
|
||||
let (cx, cy) = ((home % mw() as usize) as i32, (home / mh() as usize) as i32);
|
||||
let gold = g.gold;
|
||||
g.apply_turn(&[Command { side: 2, seq: 0, act: Act::Room { room: 0, r: Rect::new(cx + 4, cy + 4, cx + 4, cy + 4) } }], 1);
|
||||
let t = &g.map.t[idx(cx + 4, cy + 4)];
|
||||
assert_eq!(t.kind, Kind::Treasury, "he builds on his own ground");
|
||||
assert_eq!(t.owner, Owner::Rival1);
|
||||
assert_eq!(g.gold, gold, "my gold is untouched");
|
||||
}
|
||||
}
|
||||
+88
-3
@@ -1287,9 +1287,9 @@ impl Game {
|
||||
self.put_active(target);
|
||||
}
|
||||
|
||||
// a new master on `side` with its core at (cx, cy) (top-left of the 3x3 core). Used by the
|
||||
// multiplayer lobby (and its tests); single player games have no stored masters.
|
||||
#[allow(dead_code)]
|
||||
// a new master on `side` with his core at (cx, cy) (top-left of the 3x3 core); single player
|
||||
// games have no stored masters
|
||||
#[allow(dead_code)] // used by the tests; the *Multiplayer* menu (step D) will call it
|
||||
pub fn add_master(&mut self, side: u8, core: (i32, i32)) {
|
||||
let mut m = self.take_active();
|
||||
let me = std::mem::replace(&mut m, MasterState::default());
|
||||
@@ -1311,6 +1311,62 @@ impl Game {
|
||||
self.masters.push(fresh);
|
||||
}
|
||||
|
||||
// A match: `sides` (out of 0, 2, 3, 4 - side 1 is the heroes') play against each other and
|
||||
// `local` is the master who plays at this computer. Side 0 keeps the core of the generated map
|
||||
// (that is where the hero gate path leads); the other masters get their cores at fixed places
|
||||
// worked out from the map size alone, so every client of a match puts them in the same spots.
|
||||
#[allow(dead_code)] // used by the tests; the *Multiplayer* menu (step D) will call it
|
||||
pub fn start_masters(&mut self, sides: &[u8], local: u8) -> bool {
|
||||
let sides: Vec<u8> = sides.iter().copied().filter(|s| Owner::MASTER_SIDES.contains(s)).collect();
|
||||
if sides.len() < 2 || !sides.contains(&0) || !sides.contains(&local) {
|
||||
return false;
|
||||
}
|
||||
let (w, h) = (mw() as i32, mh() as i32);
|
||||
if w < 72 || h < 72 {
|
||||
return false; // the master areas need room
|
||||
}
|
||||
let cores: Vec<(i32, i32)> = sides.iter().map(|&s| match s { 0 => self.map.core, 2 => (w - 30, 12), 3 => (w - 30, h - 16), _ => (14, h - 16) }).collect();
|
||||
for (k, side) in sides.iter().enumerate() {
|
||||
if *side == self.me_side {
|
||||
continue;
|
||||
}
|
||||
self.carve_master_area(*side, cores[k]);
|
||||
self.add_master(*side, cores[k]);
|
||||
}
|
||||
self.local_side = local;
|
||||
self.switch_to(local);
|
||||
true
|
||||
}
|
||||
|
||||
// the starting area of a master: a 9x9 room of his own floor and his 3x3 soul core
|
||||
#[allow(dead_code)] // called by start_masters only
|
||||
fn carve_master_area(&mut self, side: u8, core: (i32, i32)) {
|
||||
let owner = Owner::of_side(side);
|
||||
for y in core.1 - 3..core.1 + 6 {
|
||||
for x in core.0 - 3..core.0 + 6 {
|
||||
if x < 2 || y < 2 || x >= mw() as i32 - 2 || y >= mh() as i32 - 2 {
|
||||
continue;
|
||||
}
|
||||
let t = &mut self.map.t[idx(x, y)];
|
||||
if matches!(t.kind, Kind::Portal | Kind::Gate | Kind::Core) {
|
||||
continue;
|
||||
}
|
||||
t.kind = Kind::Floor;
|
||||
t.owner = owner;
|
||||
t.gold = 0;
|
||||
t.marked = false;
|
||||
t.reinforced = false;
|
||||
}
|
||||
}
|
||||
for y in 0..3 {
|
||||
for x in 0..3 {
|
||||
let t = &mut self.map.t[idx(core.0 + x, core.1 + y)];
|
||||
t.kind = Kind::Core;
|
||||
t.owner = owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_units(&mut self, dt: f32) {
|
||||
for i in 0..self.ents.len() {
|
||||
if !self.ents[i].alive || self.ents[i].held {
|
||||
@@ -5341,6 +5397,35 @@ mod tests {
|
||||
assert!(e.alive && g.map.walk(e.x as i32, e.y as i32));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_match_gives_every_master_his_core_and_his_ground() {
|
||||
let mut g = Game::new(7, 96, 128);
|
||||
g.init_fog();
|
||||
assert!(g.start_masters(&[0, 2, 3, 4], 2), "four masters, and I am the green one");
|
||||
assert_eq!(g.local_side, 2);
|
||||
assert_eq!(g.masters.len(), 3);
|
||||
for side in Owner::MASTER_SIDES {
|
||||
let owner = Owner::of_side(side);
|
||||
assert!(g.is_master(side));
|
||||
let cores = g.map.t.iter().filter(|t| t.kind == Kind::Core && t.owner == owner).count();
|
||||
assert_eq!(cores, 9, "master {side} has his soul core");
|
||||
let ground = g.map.t.iter().filter(|t| t.kind == Kind::Floor && t.owner == owner).count();
|
||||
assert!(ground >= 40, "master {side} has a starting area of his own");
|
||||
g.switch_to(side);
|
||||
assert_eq!(g.core_hp, CORE_HP, "every core starts intact");
|
||||
assert_eq!(g.gold, 1500.0, "and every master with the same gold");
|
||||
}
|
||||
g.switch_to(2);
|
||||
let green = g.map.core;
|
||||
g.switch_to(4);
|
||||
assert_ne!(g.map.core, green, "the masters live apart");
|
||||
for _ in 0..50 {
|
||||
g.update(STEP);
|
||||
}
|
||||
assert_eq!(g.sim_steps, 50, "every master is simulated");
|
||||
assert_eq!(g.me_side, 2, "the saddle goes back to the master who plays here");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lava_burns_and_needs_stone_bridges() {
|
||||
let (mut g, rx) = river_game(true);
|
||||
|
||||
+4
-1
@@ -79,6 +79,7 @@ pub const T_GUARDPOST: u32 = 174;
|
||||
pub const T_ARENA: u32 = 178;
|
||||
pub const T_ALTAR: u32 = 182;
|
||||
pub const I_ORDERS: u32 = 186; // attack, guard
|
||||
pub const T_RIVAL3: u32 = 189; // 4 variants: floor of the yellow master
|
||||
|
||||
pub struct Gfx {
|
||||
pub atlas: Texture2D,
|
||||
@@ -1035,7 +1036,7 @@ fn gen_atlas() -> Image {
|
||||
}
|
||||
// floors of the rival masters: like the player's claimed floor, in their colours
|
||||
for v in 0..4u32 {
|
||||
for (id, base, grout, mark) in [(T_RIVAL1, (44, 74, 50), (20, 36, 24), (90, 170, 100)), (T_RIVAL2, (44, 54, 86), (20, 24, 44), (90, 120, 200))] {
|
||||
for (id, base, grout, mark) in [(T_RIVAL1, (44, 74, 50), (20, 36, 24), (90, 170, 100)), (T_RIVAL2, (44, 54, 86), (20, 24, 44), (90, 120, 200)), (T_RIVAL3, (74, 62, 26), (36, 30, 12), (210, 185, 70))] {
|
||||
cv.cell(id + v, |x, y| {
|
||||
let col = stones(x, y, v + 250, base, grout, 10);
|
||||
if (x % 8 == 4 && y % 8 == 4) && hash(x / 8, y / 8, v + 251) % 2 == 0 {
|
||||
@@ -1815,6 +1816,8 @@ impl Gfx {
|
||||
T_RIVAL1 + v
|
||||
} else if owner == Owner::Rival2 {
|
||||
T_RIVAL2 + v
|
||||
} else if owner == Owner::Rival3 {
|
||||
T_RIVAL3 + v
|
||||
} else if hero {
|
||||
T_HEROFLOOR + v
|
||||
} else {
|
||||
|
||||
+7
-1
@@ -145,9 +145,10 @@ pub enum Owner {
|
||||
Hero,
|
||||
Rival1, // AI dungeon masters (free play, campaign)
|
||||
Rival2,
|
||||
Rival3, // the fourth master side: a third rival, or a human master in a match
|
||||
}
|
||||
|
||||
const OWNERS: [Owner; 5] = [Owner::None, Owner::Player, Owner::Hero, Owner::Rival1, Owner::Rival2];
|
||||
const OWNERS: [Owner; 6] = [Owner::None, Owner::Player, Owner::Hero, Owner::Rival1, Owner::Rival2, Owner::Rival3];
|
||||
|
||||
impl Owner {
|
||||
pub fn of_side(side: u8) -> Owner {
|
||||
@@ -156,9 +157,14 @@ impl Owner {
|
||||
1 => Owner::Hero,
|
||||
2 => Owner::Rival1,
|
||||
3 => Owner::Rival2,
|
||||
4 => Owner::Rival3,
|
||||
_ => Owner::None,
|
||||
}
|
||||
}
|
||||
|
||||
// the masters of a match: the four sides a dungeon master can play (1 is the heroes' side)
|
||||
#[allow(dead_code)] // the sides of a match; the lobby (step C/D) uses them
|
||||
pub const MASTER_SIDES: [u8; 4] = [0, 2, 3, 4];
|
||||
}
|
||||
|
||||
// Short serde names (only used by version 1 savegames, newer ones use the packed codec)
|
||||
|
||||
@@ -2555,6 +2555,7 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
||||
Owner::Hero => [170, 175, 190, 255],
|
||||
Owner::Rival1 => [70, 150, 80, 255],
|
||||
Owner::Rival2 => [70, 100, 180, 255],
|
||||
Owner::Rival3 => [150, 130, 50, 255],
|
||||
Owner::None => [70, 64, 60, 255],
|
||||
},
|
||||
Kind::Treasury => [200, 160, 60, 255],
|
||||
@@ -2565,6 +2566,7 @@ pub fn minimap_color(tile: &Tile) -> [u8; 4] {
|
||||
Kind::Core => match tile.owner {
|
||||
Owner::Rival1 => [90, 240, 110, 255],
|
||||
Owner::Rival2 => [100, 150, 255, 255],
|
||||
Owner::Rival3 => [250, 220, 90, 255],
|
||||
_ => [255, 60, 60, 255],
|
||||
},
|
||||
Kind::Portal => [190, 90, 240, 255],
|
||||
|
||||
@@ -1300,6 +1300,7 @@ pub fn hover_info(g: &Game, ent: Option<usize>, tile: Option<(i32, i32)>) -> Vec
|
||||
Owner::Hero => "owner.hero",
|
||||
Owner::Rival1 => "owner.rival1",
|
||||
Owner::Rival2 => "owner.rival2",
|
||||
Owner::Rival3 => "owner.rival3",
|
||||
Owner::None => "owner.none",
|
||||
};
|
||||
match t.kind {
|
||||
|
||||
Reference in new issue
Block a user