Wood: giant mushrooms underground, felled by imps; wooden bridges and doors cost wood
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
c5adc3e902
commit
398bb95681
28 files changed
+447
-31
No files matched your search
+156
@@ -0,0 +1,156 @@
|
||||
// Wood, the second resource. Underground it comes from giant mushrooms (few, growing back
|
||||
// slowly), above ground from trees (many). Pit imps fell them like they dig (mark them with the
|
||||
// pick); the wood goes to the master at once. Wooden bridges, wooden drawbridges and wooden
|
||||
// doors cost wood; stone bridges, iron and magic doors do not.
|
||||
|
||||
use crate::game::*;
|
||||
use crate::map::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
// every master starts with a little wood
|
||||
pub const WOOD_START: f32 = 12.0;
|
||||
// wood from one giant mushroom / one tree
|
||||
pub const WOOD_MUSHROOM: f32 = 4.0;
|
||||
pub const WOOD_TREE: f32 = 3.0;
|
||||
// strikes a plant takes
|
||||
pub const MUSHROOM_HP: f32 = 4.0;
|
||||
#[allow(dead_code)] // trees come with the surface world
|
||||
pub const TREE_HP: f32 = 6.0;
|
||||
// seconds until a felled giant mushroom grows back
|
||||
pub const REGROW: f32 = 240.0;
|
||||
// wood a wooden bridge tile, a wooden drawbridge tile and a wooden door cost
|
||||
pub const BRIDGE_WOOD: f32 = 2.0;
|
||||
pub const DRAWBRIDGE_WOOD: f32 = 4.0;
|
||||
pub const DOOR_WOOD: f32 = 3.0;
|
||||
// giant mushrooms in a cavern of a random map: one per this many open tiles
|
||||
const MUSHROOM_SHARE: usize = 70;
|
||||
|
||||
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
|
||||
pub struct WoodWork {
|
||||
pub regrow: Vec<(usize, f32)>, // felled mushroom tile, seconds until it grows back
|
||||
}
|
||||
|
||||
// a tile where a plant stands alone: all eight neighbours open ground, no other plant next to
|
||||
// it (so a plant never closes a corridor)
|
||||
fn lonely_spot(map: &Map, x: i32, y: i32) -> bool {
|
||||
if !inb(x, y) || map.t[idx(x, y)].kind != Kind::Floor || map.t[idx(x, y)].owner != Owner::None {
|
||||
return false;
|
||||
}
|
||||
DIR8.iter().all(|&(dx, dy)| map.get(x + dx, y + dy).map_or(false, |n| n.walkable() && !n.bridge() && n.kind != Kind::Door))
|
||||
&& (-2..=2).all(|dy| (-2..=2).all(|dx| !map.get(x + dx, y + dy).map_or(false, |n| n.plant())))
|
||||
}
|
||||
|
||||
impl Game {
|
||||
// giant mushrooms in the open caverns of a random map (deterministic, from the seed)
|
||||
pub fn grow_mushrooms(&mut self, seed: u32) {
|
||||
let mut rng = Rng(seed.wrapping_mul(0x9e37_79b9) ^ 0x6d2b_79f5 | 1);
|
||||
let (cx, cy) = (self.map.core.0 + 1, self.map.core.1 + 1);
|
||||
let open: Vec<usize> = (0..self.map.t.len())
|
||||
.filter(|&i| {
|
||||
let (x, y) = xy(i);
|
||||
(x - cx).abs().max((y - cy).abs()) > 10 && lonely_spot(&self.map, x, y)
|
||||
})
|
||||
.collect();
|
||||
let want = (open.len() / MUSHROOM_SHARE).max(6);
|
||||
let mut placed = 0;
|
||||
let mut tries = 0;
|
||||
while placed < want && tries < want * 20 && !open.is_empty() {
|
||||
tries += 1;
|
||||
let i = open[(rng.next() as usize) % open.len()];
|
||||
let (x, y) = xy(i);
|
||||
if self.ents.iter().any(|e| e.alive && e.tile() == i) || !lonely_spot(&self.map, x, y) {
|
||||
continue;
|
||||
}
|
||||
self.plant_mushroom(i);
|
||||
placed += 1;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn plant_mushroom(&mut self, i: usize) {
|
||||
let t = &mut self.map.t[i];
|
||||
t.kind = Kind::Mushroom;
|
||||
t.owner = Owner::None;
|
||||
t.hp = MUSHROOM_HP;
|
||||
t.marked = false;
|
||||
}
|
||||
|
||||
// an imp felled the plant at tile i: wood for its master, a mushroom grows back later
|
||||
pub(crate) fn felled(&mut self, i: usize) {
|
||||
let kind = self.map.t[i].kind;
|
||||
let wood = if kind == Kind::Tree { WOOD_TREE } else { WOOD_MUSHROOM };
|
||||
self.wood += wood;
|
||||
if kind == Kind::Mushroom {
|
||||
self.woodwork.regrow.push((i, REGROW));
|
||||
}
|
||||
let (x, y) = xy(i);
|
||||
self.fx(5, x as f32 + 0.5, y as f32 + 0.5, 0.0, 0.0, 0.5, 0xff3070a0);
|
||||
}
|
||||
|
||||
// pays wood; false (and nothing paid) if there is not enough
|
||||
pub fn spend_wood(&mut self, n: f32) -> bool {
|
||||
if self.wood + 1e-3 < n {
|
||||
return false;
|
||||
}
|
||||
self.wood = (self.wood - n).max(0.0);
|
||||
true
|
||||
}
|
||||
|
||||
// the shared step: felled mushrooms grow back where there is room
|
||||
pub(crate) fn update_wood(&mut self, dt: f32) {
|
||||
let mut k = 0;
|
||||
while k < self.woodwork.regrow.len() {
|
||||
self.woodwork.regrow[k].1 -= dt;
|
||||
if self.woodwork.regrow[k].1 > 0.0 {
|
||||
k += 1;
|
||||
continue;
|
||||
}
|
||||
let i = self.woodwork.regrow[k].0;
|
||||
let (x, y) = xy(i);
|
||||
if lonely_spot(&self.map, x, y) && !self.ents.iter().any(|e| e.alive && e.tile() == i) {
|
||||
self.plant_mushroom(i);
|
||||
self.woodwork.regrow.swap_remove(k);
|
||||
} else if self.map.t[i].kind != Kind::Floor || self.map.t[i].owner != Owner::None {
|
||||
// built over or claimed: it does not come back
|
||||
self.woodwork.regrow.swap_remove(k);
|
||||
} else {
|
||||
self.woodwork.regrow[k].1 = 30.0;
|
||||
k += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn random_maps_have_lonely_giant_mushrooms() {
|
||||
let g = Game::new(9, 96, 96);
|
||||
let n = g.map.t.iter().filter(|t| t.kind == Kind::Mushroom).count();
|
||||
assert!(n >= 6, "{} mushrooms", n);
|
||||
assert!((g.wood - WOOD_START).abs() < 1e-3);
|
||||
// none closes a corridor: every neighbour is open ground
|
||||
for i in 0..g.map.t.len() {
|
||||
if g.map.t[i].kind == Kind::Mushroom {
|
||||
let (x, y) = xy(i);
|
||||
assert!(DIR8.iter().all(|&(dx, dy)| g.map.get(x + dx, y + dy).map_or(false, |n| n.walkable() || n.plant())));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn felling_gives_wood_and_mushrooms_grow_back() {
|
||||
let mut g = Game::new(9, 96, 96);
|
||||
let i = g.map.t.iter().position(|t| t.kind == Kind::Mushroom).unwrap();
|
||||
let w0 = g.wood;
|
||||
g.felled(i);
|
||||
g.dig_out(i);
|
||||
assert_eq!(g.wood, w0 + WOOD_MUSHROOM);
|
||||
assert_eq!(g.map.t[i].kind, Kind::Floor);
|
||||
g.woodwork.regrow[0].1 = 0.01;
|
||||
g.update_wood(0.05);
|
||||
assert_eq!(g.map.t[i].kind, Kind::Mushroom, "grown back");
|
||||
assert!(g.spend_wood(3.0) && !g.spend_wood(1000.0));
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user