Minimap view outline is cut at the map border instead of squeezing its corners; beds grow out of the floor while their creature sets them up
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
468fa03db9
commit
1696297ac3
2 files changed
+94
-15
No files matched your search
+49
-10
@@ -6,7 +6,7 @@
|
||||
|
||||
use crate::game::*;
|
||||
use crate::map::*;
|
||||
use crate::render3d::{disc, line3, Cam};
|
||||
use crate::render3d::{disc, Cam};
|
||||
use macroquad::prelude::*;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
@@ -20,15 +20,49 @@ fn rgb(r: u8, g: u8, b: u8, light: f32) -> Color {
|
||||
Color::new(r as f32 / 255.0 * light, g as f32 / 255.0 * light, b as f32 / 255.0 * light, 1.0)
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
// a bed being set up grows out of the floor: centre and size factor
|
||||
static GROW: std::cell::Cell<Option<(Vec3, f32)>> = std::cell::Cell::new(None);
|
||||
}
|
||||
|
||||
fn grown(p: Vec3) -> Vec3 {
|
||||
match GROW.with(|g| g.get()) {
|
||||
Some((c, k)) => c + (p - c) * k,
|
||||
None => p,
|
||||
}
|
||||
}
|
||||
|
||||
fn cube(p: Vec3, s: Vec3, c: Color) {
|
||||
draw_cube(p, s, None, c);
|
||||
let k = GROW.with(|g| g.get()).map_or(1.0, |g| g.1);
|
||||
draw_cube(grown(p), s * k, None, c);
|
||||
}
|
||||
|
||||
fn line3(a: Vec3, b: Vec3, w: f32, c: Color) {
|
||||
let k = GROW.with(|g| g.get()).map_or(1.0, |g| g.1);
|
||||
crate::render3d::line3(grown(a), grown(b), w * k, c);
|
||||
}
|
||||
|
||||
// a bed: complete, or growing while its creature sets it up (progress 0..1)
|
||||
fn bed_built(kind: EK, c: Vec3, h: u32, l: f32, t: f32, progress: f32) {
|
||||
if progress <= 0.0 {
|
||||
return;
|
||||
}
|
||||
if progress < 1.0 {
|
||||
// eased growth with a little bounce at the end
|
||||
let k = (progress * (2.0 - progress)).min(1.0) * (1.0 + 0.08 * (progress * 9.0).sin() * (1.0 - progress));
|
||||
GROW.with(|g| g.set(Some((c, k))));
|
||||
bed(kind, c, h, l, t);
|
||||
GROW.with(|g| g.set(None));
|
||||
} else {
|
||||
bed(kind, c, h, l, t);
|
||||
}
|
||||
}
|
||||
|
||||
// what is going on in the rooms this frame
|
||||
struct Use {
|
||||
busy: HashSet<usize>, // tiles where a creature trains, studies or prays
|
||||
tortured: HashSet<usize>, // torture tiles with a prisoner on them
|
||||
beds: HashMap<u32, EK>, // bed owner (entity id + 1) -> kind
|
||||
beds: HashMap<u32, (EK, f32)>, // bed owner (entity id + 1) -> kind, how far it is set up
|
||||
}
|
||||
|
||||
fn usage(g: &Game) -> Use {
|
||||
@@ -48,7 +82,12 @@ fn usage(g: &Game) -> Use {
|
||||
}
|
||||
// the bed only stands once its owner has set it up (it is reserved earlier)
|
||||
if (!e.hero || e.captive == 0) && e.settled {
|
||||
u.beds.insert(e.id + 1, e.kind);
|
||||
u.beds.insert(e.id + 1, (e.kind, 1.0));
|
||||
} else if let Job::MakeBed(_) = e.job {
|
||||
// being set up: grows while the creature works on it (four seconds)
|
||||
if e.path.is_empty() {
|
||||
u.beds.insert(e.id + 1, (e.kind, (1.0 - e.timer / 4.0).clamp(0.0, 1.0)));
|
||||
}
|
||||
}
|
||||
}
|
||||
u
|
||||
@@ -81,13 +120,13 @@ pub fn draw_props_solid(g: &Game, cam: &Cam, t: f32) {
|
||||
let h = hash(x, z, 77);
|
||||
match tile.kind {
|
||||
Kind::Lair if tile.bed != 0 => {
|
||||
if let Some(&kind) = u.beds.get(&tile.bed) {
|
||||
bed(kind, c, h, l, t);
|
||||
if let Some(&(kind, progress)) = u.beds.get(&tile.bed) {
|
||||
bed_built(kind, c, h, l, t, progress);
|
||||
}
|
||||
}
|
||||
Kind::Aerie if tile.bed != 0 => {
|
||||
if let Some(&kind) = u.beds.get(&tile.bed) {
|
||||
bed(kind, c, h, l, t);
|
||||
if let Some(&(kind, progress)) = u.beds.get(&tile.bed) {
|
||||
bed_built(kind, c, h, l, t, progress);
|
||||
}
|
||||
}
|
||||
Kind::Aerie if h % 3 == 0 => perch(c, h, l),
|
||||
@@ -187,7 +226,7 @@ pub fn draw_props_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
}
|
||||
Kind::Aerie if tile.bed != 0 => {
|
||||
// the wyrm's nest glows
|
||||
if let Some(EK::Wyrm) = u.beds.get(&tile.bed) {
|
||||
if let Some((EK::Wyrm, 1.0)) = u.beds.get(&tile.bed).copied() {
|
||||
for n in 0..5 {
|
||||
let a = n as f32 * 1.3 + k;
|
||||
let f = 0.6 + 0.4 * (t * 3.0 + a).sin();
|
||||
@@ -213,7 +252,7 @@ pub fn draw_props_glow(g: &Game, cam: &Cam, t: f32) {
|
||||
}
|
||||
Kind::Lair if tile.bed != 0 => {
|
||||
// the flame priestess sleeps on embers
|
||||
if let Some(EK::Priestess) = u.beds.get(&tile.bed) {
|
||||
if let Some((EK::Priestess, 1.0)) = u.beds.get(&tile.bed).copied() {
|
||||
for n in 0..3 {
|
||||
let a = n as f32 * 2.1 + k;
|
||||
let f = 0.6 + 0.4 * (t * 4.0 + a).sin();
|
||||
|
||||
@@ -707,12 +707,11 @@ impl Ui {
|
||||
// camera footprint on the ground (the 3D camera works in CSS pixels)
|
||||
let (lw, lh) = (screen_width(), screen_height());
|
||||
let corners = [cam.ground_point(0.0, 0.0), cam.ground_point(lw, 0.0), cam.ground_point(lw, lh), cam.ground_point(0.0, lh)];
|
||||
// the edges are cut off at the map border (moving the corners would distort the shape)
|
||||
for k in 0..4 {
|
||||
let a = corners[k];
|
||||
let b = corners[(k + 1) % 4];
|
||||
let clampp = |p: Vec2| vec2(mrx + p.x.clamp(0.0, mw() as f32) * ms, mry + p.y.clamp(0.0, mh() as f32) * ms);
|
||||
let (pa, pb) = (clampp(a), clampp(b));
|
||||
draw_line(pa.x, pa.y, pb.x, pb.y, px, WHITE);
|
||||
if let Some((a, b)) = clip_segment(corners[k], corners[(k + 1) % 4], mw() as f32, mh() as f32) {
|
||||
draw_line(mrx + a.x * ms, mry + a.y * ms, mrx + b.x * ms, mry + b.y * ms, px, WHITE);
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- mission goals (campaign)
|
||||
@@ -1209,3 +1208,44 @@ pub fn hover_info(g: &Game, ent: Option<usize>, tile: Option<(i32, i32)>) -> Vec
|
||||
}
|
||||
out
|
||||
}
|
||||
|
||||
// the part of the segment a-b inside the rectangle (0, 0)-(w, h) (Liang-Barsky)
|
||||
fn clip_segment(a: Vec2, b: Vec2, w: f32, h: f32) -> Option<(Vec2, Vec2)> {
|
||||
let d = b - a;
|
||||
let (mut t0, mut t1) = (0.0f32, 1.0f32);
|
||||
for (p, q) in [(-d.x, a.x), (d.x, w - a.x), (-d.y, a.y), (d.y, h - a.y)] {
|
||||
if p == 0.0 {
|
||||
if q < 0.0 {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
let r = q / p;
|
||||
if p < 0.0 {
|
||||
t0 = t0.max(r);
|
||||
} else {
|
||||
t1 = t1.min(r);
|
||||
}
|
||||
if t0 > t1 {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
}
|
||||
Some((a + d * t0, a + d * t1))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod clip_tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn view_edges_are_cut_at_the_border() {
|
||||
// inside: unchanged
|
||||
let (a, b) = clip_segment(vec2(1.0, 1.0), vec2(5.0, 5.0), 10.0, 10.0).unwrap();
|
||||
assert_eq!((a, b), (vec2(1.0, 1.0), vec2(5.0, 5.0)));
|
||||
// crossing the left border: cut at x = 0, same direction
|
||||
let (a, b) = clip_segment(vec2(-4.0, 2.0), vec2(4.0, 6.0), 10.0, 10.0).unwrap();
|
||||
assert!((a.x - 0.0).abs() < 1e-5 && (a.y - 4.0).abs() < 1e-5 && b == vec2(4.0, 6.0));
|
||||
// completely outside: nothing
|
||||
assert!(clip_segment(vec2(-5.0, -1.0), vec2(-1.0, 20.0), 10.0, 10.0).is_none());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user