222 lines
6.3 KiB
Rust
222 lines
6.3 KiB
Rust
|
|
use serde::Deserialize;
|
||
|
|
|
||
|
|
pub const T: f64 = 10.0;
|
||
|
|
pub const TERRAIN: [&str; 7] = [
|
||
|
|
"Deep water",
|
||
|
|
"Shallow water",
|
||
|
|
"Sand",
|
||
|
|
"Grass",
|
||
|
|
"Forest",
|
||
|
|
"Hills",
|
||
|
|
"Mountain",
|
||
|
|
];
|
||
|
|
pub const RES_NAMES: [&str; 4] = ["", "Tree", "Berry bush", "Stone deposit"];
|
||
|
|
|
||
|
|
#[derive(Deserialize, Clone, Default)]
|
||
|
|
pub struct Stock {
|
||
|
|
pub food: u32,
|
||
|
|
pub wood: u32,
|
||
|
|
pub stone: u32,
|
||
|
|
pub tools: u32,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// id, x, y, action, hunger, food, wood, stone
|
||
|
|
#[derive(Deserialize, Clone)]
|
||
|
|
pub struct Person(
|
||
|
|
pub u32,
|
||
|
|
pub usize,
|
||
|
|
pub usize,
|
||
|
|
pub String,
|
||
|
|
pub f32,
|
||
|
|
pub u32,
|
||
|
|
pub u32,
|
||
|
|
pub u32,
|
||
|
|
);
|
||
|
|
|
||
|
|
/// x, y, energy
|
||
|
|
#[derive(Deserialize, Clone)]
|
||
|
|
pub struct Animal(pub usize, pub usize, pub f32);
|
||
|
|
|
||
|
|
/// x, y, kind, amount
|
||
|
|
#[derive(Deserialize, Clone)]
|
||
|
|
pub struct Res(pub usize, pub usize, pub u8, pub u16);
|
||
|
|
|
||
|
|
#[derive(Deserialize, Clone)]
|
||
|
|
pub struct State {
|
||
|
|
pub paused: bool,
|
||
|
|
pub speed: u32,
|
||
|
|
pub rev: u32,
|
||
|
|
pub day: u64,
|
||
|
|
pub hour: f32,
|
||
|
|
pub daylight: f32,
|
||
|
|
pub stock: Stock,
|
||
|
|
pub home: (usize, usize),
|
||
|
|
pub houses: Vec<(usize, usize)>,
|
||
|
|
pub people: Vec<Person>,
|
||
|
|
pub rabbits: Vec<Animal>,
|
||
|
|
pub wolves: Vec<Animal>,
|
||
|
|
pub res: Vec<Res>,
|
||
|
|
pub log: Vec<String>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Deserialize, Clone)]
|
||
|
|
pub struct Terrain {
|
||
|
|
pub w: usize,
|
||
|
|
pub h: usize,
|
||
|
|
pub tiles: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Terrain {
|
||
|
|
pub fn kind(&self, x: usize, y: usize) -> usize {
|
||
|
|
self.tiles
|
||
|
|
.as_bytes()
|
||
|
|
.get(y * self.w + x)
|
||
|
|
.map_or(0, |b| (b - b'0') as usize)
|
||
|
|
.min(6)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Clone, Copy)]
|
||
|
|
pub struct Sel {
|
||
|
|
pub ax: usize,
|
||
|
|
pub ay: usize,
|
||
|
|
pub bx: usize,
|
||
|
|
pub by: usize,
|
||
|
|
}
|
||
|
|
|
||
|
|
impl Sel {
|
||
|
|
/// Normalised (x0, y0, x1, y1), inclusive.
|
||
|
|
pub fn rect(&self) -> (usize, usize, usize, usize) {
|
||
|
|
(
|
||
|
|
self.ax.min(self.bx),
|
||
|
|
self.ay.min(self.by),
|
||
|
|
self.ax.max(self.bx),
|
||
|
|
self.ay.max(self.by),
|
||
|
|
)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn tool_options(tool: &str) -> Vec<(String, &'static str)> {
|
||
|
|
match tool {
|
||
|
|
"terrain" => TERRAIN
|
||
|
|
.iter()
|
||
|
|
.enumerate()
|
||
|
|
.map(|(i, n)| (i.to_string(), *n))
|
||
|
|
.collect(),
|
||
|
|
"res" => vec![
|
||
|
|
("1".into(), "Tree"),
|
||
|
|
("2".into(), "Berry bush"),
|
||
|
|
("3".into(), "Stone"),
|
||
|
|
("0".into(), "Clear"),
|
||
|
|
],
|
||
|
|
"spawn" => vec![
|
||
|
|
("person".into(), "Person"),
|
||
|
|
("rabbit".into(), "Rabbit"),
|
||
|
|
("wolf".into(), "Wolf"),
|
||
|
|
],
|
||
|
|
"remove" => vec![
|
||
|
|
("all".into(), "Everything"),
|
||
|
|
("people".into(), "People"),
|
||
|
|
("rabbits".into(), "Rabbits"),
|
||
|
|
("wolves".into(), "Wolves"),
|
||
|
|
("houses".into(), "Houses"),
|
||
|
|
("res".into(), "Resources"),
|
||
|
|
],
|
||
|
|
_ => vec![],
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn tool_help(tool: &str) -> &'static str {
|
||
|
|
match tool {
|
||
|
|
"terrain" => "Drag a region to paint it.",
|
||
|
|
"res" => "Drag a region; amount sets units per tile.",
|
||
|
|
"spawn" => "Drag a region; one per walkable tile (max 25).",
|
||
|
|
"remove" => "Drag a region to remove things in it.",
|
||
|
|
"house" => "Drag a region to add houses.",
|
||
|
|
"energy" => "Drag a region; amount sets animal energy (1-100).",
|
||
|
|
_ => "Click or drag to inspect and edit.",
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn tool_command(tool: &str, param: &str, amount: &str, sel: Sel) -> Option<String> {
|
||
|
|
let (x0, y0, x1, y1) = sel.rect();
|
||
|
|
let r = format!("{x0} {y0} {x1} {y1}");
|
||
|
|
let a = amount.parse::<i64>().unwrap_or(1);
|
||
|
|
match tool {
|
||
|
|
"terrain" => Some(format!("terrain {r} {param}")),
|
||
|
|
"res" => Some(format!("res {r} {param} {a}")),
|
||
|
|
"spawn" => Some(format!("spawn {param} {r}")),
|
||
|
|
"remove" => Some(format!("remove {r} {param}")),
|
||
|
|
"house" => Some(format!("house {r}")),
|
||
|
|
"energy" => Some(format!("energy {r} {a}")),
|
||
|
|
_ => None,
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
pub fn describe(sel: Sel, s: &State, t: &Terrain) -> Vec<String> {
|
||
|
|
let (x0, y0, x1, y1) = sel.rect();
|
||
|
|
let inside = |x: usize, y: usize| x >= x0 && x <= x1 && y >= y0 && y <= y1;
|
||
|
|
let people: Vec<&Person> = s.people.iter().filter(|p| inside(p.1, p.2)).collect();
|
||
|
|
let rabbits: Vec<&Animal> = s.rabbits.iter().filter(|a| inside(a.0, a.1)).collect();
|
||
|
|
let wolves: Vec<&Animal> = s.wolves.iter().filter(|a| inside(a.0, a.1)).collect();
|
||
|
|
let res: Vec<&Res> = s.res.iter().filter(|r| inside(r.0, r.1)).collect();
|
||
|
|
let houses = s.houses.iter().filter(|h| inside(h.0, h.1)).count();
|
||
|
|
let mut counts = [0usize; 7];
|
||
|
|
for y in y0..=y1 {
|
||
|
|
for x in x0..=x1 {
|
||
|
|
counts[t.kind(x, y)] += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
let mut out = vec![];
|
||
|
|
if (x0, y0) == (x1, y1) {
|
||
|
|
out.push(format!("Tile ({x0}, {y0})"));
|
||
|
|
out.push(format!("Terrain: {}", TERRAIN[t.kind(x0, y0)]));
|
||
|
|
if s.home == (x0, y0) {
|
||
|
|
out.push("Village center (stockpile)".into());
|
||
|
|
}
|
||
|
|
if houses > 0 {
|
||
|
|
out.push("House".into());
|
||
|
|
}
|
||
|
|
for r in &res {
|
||
|
|
out.push(format!("{}: {} left", RES_NAMES[r.2 as usize], r.3));
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
out.push(format!("Region ({x0},{y0}) to ({x1},{y1})"));
|
||
|
|
let terrain: Vec<String> = counts
|
||
|
|
.iter()
|
||
|
|
.enumerate()
|
||
|
|
.filter(|(_, n)| **n > 0)
|
||
|
|
.map(|(i, n)| format!("{} {n}", TERRAIN[i]))
|
||
|
|
.collect();
|
||
|
|
out.push(format!("Terrain: {}", terrain.join(", ")));
|
||
|
|
let kinds: Vec<String> = (1..=3u8)
|
||
|
|
.map(|k| {
|
||
|
|
format!(
|
||
|
|
"{} {}",
|
||
|
|
RES_NAMES[k as usize],
|
||
|
|
res.iter().filter(|r| r.2 == k).count()
|
||
|
|
)
|
||
|
|
})
|
||
|
|
.collect();
|
||
|
|
out.push(format!("Resources: {}", kinds.join(", ")));
|
||
|
|
out.push(format!("Houses: {houses}"));
|
||
|
|
}
|
||
|
|
out.push(format!(
|
||
|
|
"People: {}, rabbits: {}, wolves: {}",
|
||
|
|
people.len(),
|
||
|
|
rabbits.len(),
|
||
|
|
wolves.len()
|
||
|
|
));
|
||
|
|
for p in &people {
|
||
|
|
out.push(format!(
|
||
|
|
"- Person #{}: {}, hunger {}, carrying food {} wood {} stone {}",
|
||
|
|
p.0, p.3, p.4, p.5, p.6, p.7
|
||
|
|
));
|
||
|
|
}
|
||
|
|
if rabbits.len() + wolves.len() <= 6 {
|
||
|
|
out.extend(rabbits.iter().map(|a| format!("- Rabbit: energy {}", a.2)));
|
||
|
|
out.extend(wolves.iter().map(|a| format!("- Wolf: energy {}", a.2)));
|
||
|
|
}
|
||
|
|
out
|
||
|
|
}
|