Creature overview: a toolbar button opens the list of all creatures grouped by kind (level, mood, wage, job, health) with wages and mood in total; a click moves the camera there
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
bbab2b5a58
commit
51814b8ac8
3 files changed
+124
-2
No files matched your search
@@ -916,3 +916,10 @@ hint.order_nobody = Keine Kreatur ist für diesen Befehl frei.
|
|||||||
msg.order_attack = {0} Kreaturen greifen an!
|
msg.order_attack = {0} Kreaturen greifen an!
|
||||||
msg.order_guard = {0} Kreaturen beziehen Stellung.
|
msg.order_guard = {0} Kreaturen beziehen Stellung.
|
||||||
msg.orders_cancelled = Wachbefehle aufgehoben.
|
msg.orders_cancelled = Wachbefehle aufgehoben.
|
||||||
|
|
||||||
|
# creature overview
|
||||||
|
cat.overview = Kreaturen
|
||||||
|
ov.title = Deine Kreaturen
|
||||||
|
ov.summary = {0} Kreaturen - Lohn {1} Gold pro Zahltag - Laune {2}%
|
||||||
|
ov.none = Noch keine Kreaturen. Baue ein Quartier und einen Pilzgarten und nimm ein Portal ein.
|
||||||
|
ov.hint = St., Laune, Lohn, Tätigkeit, Leben - Klick: hingehen
|
||||||
@@ -917,3 +917,10 @@ hint.order_nobody = No creature is free for that order.
|
|||||||
msg.order_attack = {0} creatures attack!
|
msg.order_attack = {0} creatures attack!
|
||||||
msg.order_guard = {0} creatures take their positions.
|
msg.order_guard = {0} creatures take their positions.
|
||||||
msg.orders_cancelled = Guard orders cancelled.
|
msg.orders_cancelled = Guard orders cancelled.
|
||||||
|
|
||||||
|
# creature overview
|
||||||
|
cat.overview = Creatures
|
||||||
|
ov.title = Your creatures
|
||||||
|
ov.summary = {0} creatures - wages {1} gold per payday - mood {2}%
|
||||||
|
ov.none = No creatures yet. Build a lair and a mushroom garden and claim a portal.
|
||||||
|
ov.hint = Lv, mood, wage, job, health - click: go there
|
||||||
@@ -326,6 +326,9 @@ enum TBtn {
|
|||||||
Cat(usize),
|
Cat(usize),
|
||||||
Tool(usize),
|
Tool(usize),
|
||||||
Menu,
|
Menu,
|
||||||
|
Overview,
|
||||||
|
OvRow(u32),
|
||||||
|
OvScroll(i32),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum UiAction {
|
pub enum UiAction {
|
||||||
@@ -346,6 +349,8 @@ pub enum UiAction {
|
|||||||
pub struct Ui {
|
pub struct Ui {
|
||||||
pub tool: Tool,
|
pub tool: Tool,
|
||||||
pub show_help: bool,
|
pub show_help: bool,
|
||||||
|
pub show_overview: bool, // list of all creatures
|
||||||
|
ov_scroll: usize,
|
||||||
pub show_langsel: bool,
|
pub show_langsel: bool,
|
||||||
pub log: Vec<(Msg, f64)>,
|
pub log: Vec<(Msg, f64)>,
|
||||||
pub rects: Vec<Rect>,
|
pub rects: Vec<Rect>,
|
||||||
@@ -398,6 +403,8 @@ impl Ui {
|
|||||||
Ui {
|
Ui {
|
||||||
tool: Tool::Dig,
|
tool: Tool::Dig,
|
||||||
show_help: true,
|
show_help: true,
|
||||||
|
show_overview: false,
|
||||||
|
ov_scroll: 0,
|
||||||
show_langsel: false,
|
show_langsel: false,
|
||||||
log: Vec::new(),
|
log: Vec::new(),
|
||||||
rects: Vec::new(),
|
rects: Vec::new(),
|
||||||
@@ -461,6 +468,20 @@ impl Ui {
|
|||||||
self.open_cat = None;
|
self.open_cat = None;
|
||||||
UiAction::OpenMenu
|
UiAction::OpenMenu
|
||||||
}
|
}
|
||||||
|
TBtn::Overview => {
|
||||||
|
self.open_cat = None;
|
||||||
|
self.show_overview = !self.show_overview;
|
||||||
|
self.ov_scroll = 0;
|
||||||
|
UiAction::Consumed
|
||||||
|
}
|
||||||
|
TBtn::OvScroll(d) => {
|
||||||
|
self.ov_scroll = (self.ov_scroll as i32 + d).max(0) as usize;
|
||||||
|
UiAction::Consumed
|
||||||
|
}
|
||||||
|
TBtn::OvRow(id) => match g.ents.iter().find(|e| e.id == id) {
|
||||||
|
Some(e) => UiAction::MinimapJump(e.x, e.y),
|
||||||
|
None => UiAction::Consumed,
|
||||||
|
},
|
||||||
TBtn::Tool(i) if !g.tool_allowed(i) => {
|
TBtn::Tool(i) if !g.tool_allowed(i) => {
|
||||||
self.last_locked = Some(i);
|
self.last_locked = Some(i);
|
||||||
UiAction::Locked
|
UiAction::Locked
|
||||||
@@ -643,7 +664,7 @@ impl Ui {
|
|||||||
let mut popup_top = tb_y;
|
let mut popup_top = tb_y;
|
||||||
// categories + menu button; a category with several tools opens a row above
|
// categories + menu button; a category with several tools opens a row above
|
||||||
// (the same layout for mouse and touch; on the desktop the keys still select tools directly)
|
// (the same layout for mouse and touch; on the desktop the keys still select tools directly)
|
||||||
let n = CATS.len() + 1;
|
let n = CATS.len() + 2;
|
||||||
let bcols = (((cols - 2) as usize / n) as f32).clamp(4.0, 8.0);
|
let bcols = (((cols - 2) as usize / n) as f32).clamp(4.0, 8.0);
|
||||||
let bw = bcols * CW();
|
let bw = bcols * CW();
|
||||||
let x0 = grid_center(sw, bw * n as f32);
|
let x0 = grid_center(sw, bw * n as f32);
|
||||||
@@ -672,10 +693,22 @@ impl Ui {
|
|||||||
}
|
}
|
||||||
self.touch_btns.push((r, TBtn::Cat(c)));
|
self.touch_btns.push((r, TBtn::Cat(c)));
|
||||||
}
|
}
|
||||||
// menu button
|
// creature overview button
|
||||||
let bx = x0 + CATS.len() as f32 * bw;
|
let bx = x0 + CATS.len() as f32 * bw;
|
||||||
let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH());
|
let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH());
|
||||||
let hovered = !touch_ui() && r.contains(vec2(mx, my));
|
let hovered = !touch_ui() && r.contains(vec2(mx, my));
|
||||||
|
draw_rectangle(r.x, r.y, r.w, r.h, if hovered || self.show_overview { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) });
|
||||||
|
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, if self.show_overview { FRAME_HI } else { Color::new(0.35, 0.28, 0.2, 1.0) });
|
||||||
|
gfx.glyph(0x02, bx + (bw - GW * TITLE()) * 0.5, by + 2.0 * px, TITLE(), FRAME_HI);
|
||||||
|
let ol = tr("cat.overview");
|
||||||
|
let maxc = ((bw - 2.0 * px) / (GW * SMALL())).floor() as usize;
|
||||||
|
let ol: String = ol.chars().take(maxc).collect();
|
||||||
|
gfx.text(&ol, bx + (bw - gfx.measure(&ol, SMALL())) * 0.5, by + 2.0 * CH() + 4.0 * px, SMALL(), TXT);
|
||||||
|
self.touch_btns.push((r, TBtn::Overview));
|
||||||
|
// menu button
|
||||||
|
let bx = x0 + (CATS.len() + 1) as f32 * bw;
|
||||||
|
let r = Rect::new(bx + px, by, bw - 2.0 * px, 3.0 * CH());
|
||||||
|
let hovered = !touch_ui() && r.contains(vec2(mx, my));
|
||||||
draw_rectangle(r.x, r.y, r.w, r.h, if hovered { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) });
|
draw_rectangle(r.x, r.y, r.w, r.h, if hovered { Color::new(0.18, 0.13, 0.1, 1.0) } else { Color::new(0.1, 0.07, 0.07, 1.0) });
|
||||||
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, Color::new(0.35, 0.28, 0.2, 1.0));
|
draw_rectangle_lines(r.x, r.y, r.w, r.h, 2.0 * px, Color::new(0.35, 0.28, 0.2, 1.0));
|
||||||
gfx.glyph(0xF0, bx + (bw - GW * TITLE()) * 0.5, by + 2.0 * px, TITLE(), FRAME_HI);
|
gfx.glyph(0xF0, bx + (bw - GW * TITLE()) * 0.5, by + 2.0 * px, TITLE(), FRAME_HI);
|
||||||
@@ -872,6 +905,11 @@ impl Ui {
|
|||||||
popup_top = py0;
|
popup_top = py0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---------------- creature overview
|
||||||
|
if self.show_overview {
|
||||||
|
self.draw_overview(g, gfx, tb_y);
|
||||||
|
}
|
||||||
|
|
||||||
// ---------------- Tooltip
|
// ---------------- Tooltip
|
||||||
if let Some(i) = hover_tool {
|
if let Some(i) = hover_tool {
|
||||||
let t = TOOLS[i];
|
let t = TOOLS[i];
|
||||||
@@ -897,6 +935,76 @@ impl Ui {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all creatures, grouped by kind: level, mood, wage, job, health; a click jumps there
|
||||||
|
fn draw_overview(&mut self, g: &Game, gfx: &Gfx, bottom: f32) {
|
||||||
|
let (sw, _) = (sw(), sh());
|
||||||
|
let (mx, my) = mouse();
|
||||||
|
let cols = ((sw / CW()) as i32 - 2).min(56);
|
||||||
|
let top = CH() * 4.0;
|
||||||
|
let rows = (((bottom - top) / CH()) as i32 - 1).clamp(8, 40);
|
||||||
|
let x = (sw - cols as f32 * CW() - CW()).max(0.0);
|
||||||
|
let y = (bottom - rows as f32 * CH()).max(top);
|
||||||
|
gfx.window_titled(x, y, cols, rows, S(), false, FRAME_HI, BG, &tr("ov.title"), FRAME_HI);
|
||||||
|
self.rects.push(Rect::new(x, y, cols as f32 * CW(), rows as f32 * CH()));
|
||||||
|
let mine: Vec<&Ent> = g.ents.iter().filter(|e| e.alive && e.creature()).collect();
|
||||||
|
let wage: f32 = mine.iter().map(|e| st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)).sum();
|
||||||
|
let mood = if mine.is_empty() { 0.0 } else { mine.iter().map(|e| e.happy.clamp(0.0, 1.0)).sum::<f32>() / mine.len() as f32 };
|
||||||
|
let head = trf("ov.summary", &[mine.len().to_string(), (wage as i32).to_string(), ((mood * 100.0) as i32).to_string()]);
|
||||||
|
gfx.text(&head, x + CW() * 2.0, y + CH(), S(), GOLD);
|
||||||
|
// lines: kind headers and creatures
|
||||||
|
let mut kinds: Vec<EK> = Vec::new();
|
||||||
|
for e in &mine {
|
||||||
|
if !kinds.contains(&e.kind) {
|
||||||
|
kinds.push(e.kind);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
kinds.sort_by_key(|k| std::cmp::Reverse(mine.iter().filter(|e| e.kind == *k).count()));
|
||||||
|
let mut lines: Vec<(String, Color, Option<u32>, f32)> = Vec::new();
|
||||||
|
for k in kinds {
|
||||||
|
let group: Vec<&&Ent> = mine.iter().filter(|e| e.kind == k).collect();
|
||||||
|
lines.push((format!("{} x{}", tr(st(k).name), group.len()), FRAME_HI, None, -1.0));
|
||||||
|
let mut group = group;
|
||||||
|
group.sort_by_key(|e| std::cmp::Reverse(e.level));
|
||||||
|
for e in group {
|
||||||
|
let w = (st(e.kind).wage as f32 * (1.0 + 0.1 * (e.level - 1) as f32)) as i32;
|
||||||
|
let happy = (e.happy.clamp(0.0, 1.0) * 100.0) as i32;
|
||||||
|
let job: String = tr(job_name(e)).chars().take(16).collect();
|
||||||
|
let line = format!(" {:>2} {:>3}% {:>4}g {:<16}", e.level, happy, w, job);
|
||||||
|
let col = if e.happy > 0.6 { TXT } else if e.happy > 0.25 { GOLD } else { RED };
|
||||||
|
lines.push((line, col, Some(e.id), e.hp / e.max_hp));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let first = y + CH() * 3.0;
|
||||||
|
let visible = (rows - 5).max(1) as usize;
|
||||||
|
self.ov_scroll = self.ov_scroll.min(lines.len().saturating_sub(visible));
|
||||||
|
let lw = (cols - 4) as f32 * CW();
|
||||||
|
for (n, (text, col, id, hp)) in lines.iter().enumerate().skip(self.ov_scroll).take(visible) {
|
||||||
|
let ly = first + (n - self.ov_scroll) as f32 * CH();
|
||||||
|
let r = Rect::new(x + CW() * 2.0, ly, lw, CH());
|
||||||
|
if id.is_some() && r.contains(vec2(mx, my)) {
|
||||||
|
draw_rectangle(r.x, r.y, r.w, r.h, Color::new(0.25, 0.16, 0.08, 1.0));
|
||||||
|
}
|
||||||
|
gfx.text(text, r.x, ly, S(), *col);
|
||||||
|
if let Some(id) = id {
|
||||||
|
ascii_bar(gfx, r.x + r.w - CW() * 4.0, ly + (CH() - GH * SMALL()) * 0.5, 6, *hp, if *hp > 0.5 { GREEN } else { RED }, SMALL());
|
||||||
|
self.touch_btns.push((r, TBtn::OvRow(*id)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if lines.len() > visible {
|
||||||
|
let up = Rect::new(x + (cols - 4) as f32 * CW(), y + CH(), CW() * 2.0, CH() * 1.5);
|
||||||
|
let down = Rect::new(x + (cols - 4) as f32 * CW(), y + (rows - 2) as f32 * CH(), CW() * 2.0, CH() * 1.5);
|
||||||
|
gfx.text("\u{25b2}", up.x + CW() * 0.5, up.y, S(), FRAME_HI);
|
||||||
|
gfx.text("\u{25bc}", down.x + CW() * 0.5, down.y, S(), FRAME_HI);
|
||||||
|
self.touch_btns.push((up, TBtn::OvScroll(-(visible as i32) / 2)));
|
||||||
|
self.touch_btns.push((down, TBtn::OvScroll(visible as i32 / 2)));
|
||||||
|
}
|
||||||
|
if mine.is_empty() {
|
||||||
|
gfx.text(&tr("ov.none"), x + CW() * 2.0, first, S(), DIM);
|
||||||
|
}
|
||||||
|
let hint = tr("ov.hint");
|
||||||
|
gfx.text(&hint, x + CW() * 2.0, y + (rows - 2) as f32 * CH(), SMALL(), DIM);
|
||||||
|
}
|
||||||
|
|
||||||
// Dialogs on top of everything (help, language selection, end screen, pause)
|
// Dialogs on top of everything (help, language selection, end screen, pause)
|
||||||
pub fn draw_dialogs(&mut self, g: &Game, gfx: &Gfx) {
|
pub fn draw_dialogs(&mut self, g: &Game, gfx: &Gfx) {
|
||||||
let sw = sw();
|
let sw = sw();
|
||||||
|
|||||||
Reference in new issue
Block a user