Editor: blocks wrap their fields, wider fields, the demo den behind a tunnel
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
caad441c72
commit
a0df40ab19
7 files changed
+166
-120
No files matched your search
+134
-108
@@ -36,6 +36,7 @@ pub struct BlockEdit {
|
||||
num_buf: String, // the number being typed (focused number field)
|
||||
num_focus: Option<u64>,
|
||||
help: String,
|
||||
rows: std::collections::HashMap<u64, usize>, // rows each block's fields needed (wrapping)
|
||||
}
|
||||
|
||||
enum Op {
|
||||
@@ -424,7 +425,8 @@ impl BlockEdit {
|
||||
let cat = list[k].category();
|
||||
let col = cat_color(cat);
|
||||
// the spawn block has many fields: two rows
|
||||
let rows = if matches!(list[k], Block::Spawn { .. }) { 2.0 } else { 1.0 };
|
||||
// rows the fields needed last frame (they wrap when the block is too narrow)
|
||||
let rows = self.rows.get(&fid(&path, 9999)).copied().unwrap_or(1) as f32;
|
||||
let header = Rect::new(x, y, w, CH() * ROW * rows);
|
||||
let problem = ctx.problems.iter().find(|(p, _)| *p == path).map(|(_, k)| *k);
|
||||
let selected = self.selected.as_ref() == Some(&path);
|
||||
@@ -447,8 +449,10 @@ impl BlockEdit {
|
||||
ops.push(Op::StartDrag(path.clone()));
|
||||
}
|
||||
let fy = y + (CH() * ROW - CH() * 1.5) * 0.5;
|
||||
let mut cx = x + CW() * 1.8;
|
||||
self.block_fields(im, gfx, &mut list[k], &path, &mut cx, fy, x + CW() * 3.0, ctx);
|
||||
let cx = x + CW() * 1.8;
|
||||
let mut lay = Lay { x: cx, y: fy, x2: x + CW() * 3.0, max: x + w - CW() * 2.5, rows: 1 };
|
||||
self.block_fields(im, gfx, &mut list[k], &path, &mut lay, ctx);
|
||||
self.rows.insert(fid(&path, 9999), lay.rows);
|
||||
// a click on the block's free area selects it
|
||||
if im.clicked(header) {
|
||||
ops.push(Op::Select(path.clone()));
|
||||
@@ -501,37 +505,25 @@ impl BlockEdit {
|
||||
y - y0
|
||||
}
|
||||
|
||||
// the fields inside a block's header row
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn block_fields(&mut self, im: &mut Im, gfx: &Gfx, b: &mut Block, path: &[usize], cx: &mut f32, y: f32, x2: f32, ctx: &Ctx) {
|
||||
let word = |cx: &mut f32, s: &str| {
|
||||
gfx.text(s, *cx, y + CH() * 0.25, S(), TXT);
|
||||
*cx += gfx.measure(s, S()) + CW() * 0.6;
|
||||
};
|
||||
// the fields inside a block's header (they wrap into more rows when the block is too narrow)
|
||||
fn block_fields(&mut self, im: &mut Im, gfx: &Gfx, b: &mut Block, path: &[usize], lay: &mut Lay, ctx: &Ctx) {
|
||||
let name = tr(&format!("blk.{}", b.kind_name()));
|
||||
word(cx, &name);
|
||||
lay.word(gfx, &name);
|
||||
let is_set = matches!(b, Block::SetVar { .. });
|
||||
let fh = CH() * 1.5;
|
||||
match b {
|
||||
Block::Message { text } | Block::Objective { text } | Block::Hint { text } => {
|
||||
im.text_field(gfx, fid(path, 0), Rect::new(*cx, y, CW() * 44.0, fh), text, 200);
|
||||
*cx += CW() * 45.0;
|
||||
let w = (lay.max - lay.x).max(CW() * 24.0).min(CW() * 60.0);
|
||||
let r = lay.take(w);
|
||||
im.text_field(gfx, fid(path, 0), r, text, 200);
|
||||
}
|
||||
Block::Spawn { side, kind, count, level, place, name } => {
|
||||
self.value(im, gfx, count, Ty::Num, path, 1, cx, y, ctx);
|
||||
word(cx, "×");
|
||||
self.kind_dd(im, gfx, kind, false, path, 2, cx, y);
|
||||
word(cx, &tr("blk.w_level"));
|
||||
self.value(im, gfx, level, Ty::Num, path, 3, cx, y, ctx);
|
||||
self.side_dd(im, gfx, side, false, path, 4, cx, y);
|
||||
// second row: where, and the name
|
||||
let y = y + CH() * ROW;
|
||||
*cx = x2;
|
||||
let word = |cx: &mut f32, s: &str| {
|
||||
gfx.text(s, *cx, y + CH() * 0.25, S(), TXT);
|
||||
*cx += gfx.measure(s, S()) + CW() * 0.6;
|
||||
};
|
||||
word(cx, &tr("blk.w_at"));
|
||||
self.value(im, gfx, count, Ty::Num, path, 1, lay, ctx);
|
||||
lay.word(gfx, "×");
|
||||
self.kind_dd(im, gfx, kind, false, path, 2, lay);
|
||||
lay.word(gfx, &tr("blk.w_level"));
|
||||
self.value(im, gfx, level, Ty::Num, path, 3, lay, ctx);
|
||||
self.side_dd(im, gfx, side, false, path, 4, lay);
|
||||
lay.word(gfx, &tr("blk.w_at"));
|
||||
let mut opts = vec![tr("blk.place_core"), tr("blk.place_event")];
|
||||
opts.extend(ctx.zones.iter().map(|z| trf("blk.place_zone", &[z.clone()])));
|
||||
let cur = match place {
|
||||
@@ -539,124 +531,117 @@ impl BlockEdit {
|
||||
Place::Event => opts[1].clone(),
|
||||
Place::Zone(z) => trf("blk.place_zone", &[z.clone()]),
|
||||
};
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 5), Rect::new(*cx, y, CW() * 16.0, fh), &cur, &opts) {
|
||||
let r = lay.take(CW() * 24.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 5), r, &cur, &opts) {
|
||||
*place = match c {
|
||||
0 => Place::Core,
|
||||
1 => Place::Event,
|
||||
c => Place::Zone(ctx.zones[c - 2].clone()),
|
||||
};
|
||||
}
|
||||
*cx += CW() * 17.0;
|
||||
word(cx, &tr("blk.w_name"));
|
||||
im.text_field(gfx, fid(path, 6), Rect::new(*cx, y, CW() * 10.0, fh), name, 16);
|
||||
*cx += CW() * 11.0;
|
||||
lay.word(gfx, &tr("blk.w_name"));
|
||||
let r = lay.take(CW() * 10.0);
|
||||
im.text_field(gfx, fid(path, 6), r, name, 16);
|
||||
}
|
||||
Block::Wave { count } | Block::Gold { amount: count } | Block::Mana { amount: count } => self.value(im, gfx, count, Ty::Num, path, 1, cx, y, ctx),
|
||||
Block::Wave { count } | Block::Gold { amount: count } | Block::Mana { amount: count } => self.value(im, gfx, count, Ty::Num, path, 1, lay, ctx),
|
||||
Block::SetVar { name, value } | Block::AddVar { name, value } => {
|
||||
im.text_field(gfx, fid(path, 0), Rect::new(*cx, y, CW() * 12.0, fh), name, 24);
|
||||
*cx += CW() * 13.0;
|
||||
word(cx, &tr(if is_set { "blk.w_to" } else { "blk.w_by" }));
|
||||
self.value(im, gfx, value, Ty::Num, path, 1, cx, y, ctx);
|
||||
let r = lay.take(CW() * 12.0);
|
||||
im.text_field(gfx, fid(path, 0), r, name, 24);
|
||||
lay.word(gfx, &tr(if is_set { "blk.w_to" } else { "blk.w_by" }));
|
||||
self.value(im, gfx, value, Ty::Num, path, 1, lay, ctx);
|
||||
}
|
||||
Block::If { cond, .. } => self.value(im, gfx, cond, Ty::Bool, path, 1, cx, y, ctx),
|
||||
Block::If { cond, .. } | Block::Until { cond, .. } | Block::DoUntil { cond, .. } => self.value(im, gfx, cond, Ty::Bool, path, 1, lay, ctx),
|
||||
Block::Repeat { times, .. } => {
|
||||
self.value(im, gfx, times, Ty::Num, path, 1, cx, y, ctx);
|
||||
word(cx, &tr("blk.w_times"));
|
||||
self.value(im, gfx, times, Ty::Num, path, 1, lay, ctx);
|
||||
lay.word(gfx, &tr("blk.w_times"));
|
||||
}
|
||||
Block::Until { cond, .. } | Block::DoUntil { cond, .. } => self.value(im, gfx, cond, Ty::Bool, path, 1, cx, y, ctx),
|
||||
Block::Wait { secs } => {
|
||||
self.value(im, gfx, secs, Ty::Num, path, 1, cx, y, ctx);
|
||||
word(cx, &tr("blk.w_seconds"));
|
||||
self.value(im, gfx, secs, Ty::Num, path, 1, lay, ctx);
|
||||
lay.word(gfx, &tr("blk.w_seconds"));
|
||||
}
|
||||
Block::Win | Block::Lose => {}
|
||||
Block::Reveal { zone } | Block::Camera { zone } => self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx),
|
||||
Block::Reveal { zone } | Block::Camera { zone } => self.zone_dd(im, gfx, zone, path, 1, lay, ctx),
|
||||
Block::Terrain { zone, kind } => {
|
||||
self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx);
|
||||
word(cx, &tr("blk.w_to"));
|
||||
self.zone_dd(im, gfx, zone, path, 1, lay, ctx);
|
||||
lay.word(gfx, &tr("blk.w_to"));
|
||||
let opts: Vec<String> = TERRAIN.iter().map(|t| tr(&format!("ed.terrain.{}", t))).collect();
|
||||
let cur = tr(&format!("ed.terrain.{}", kind));
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 2), Rect::new(*cx, y, CW() * 14.0, fh), &cur, &opts) {
|
||||
let r = lay.take(CW() * 19.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 2), r, &cur, &opts) {
|
||||
*kind = TERRAIN[c].to_string();
|
||||
}
|
||||
*cx += CW() * 15.0;
|
||||
}
|
||||
Block::Kill { zone, side } => {
|
||||
self.side_dd(im, gfx, side, true, path, 2, cx, y);
|
||||
word(cx, &tr("blk.w_in"));
|
||||
self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx);
|
||||
self.side_dd(im, gfx, side, true, path, 2, lay);
|
||||
lay.word(gfx, &tr("blk.w_in"));
|
||||
self.zone_dd(im, gfx, zone, path, 1, lay, ctx);
|
||||
}
|
||||
Block::Trigger { name, on } => {
|
||||
let cur = if name.is_empty() { tr("ed.pick_trigger") } else { name.clone() };
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 1), Rect::new(*cx, y, CW() * 16.0, fh), &cur, &ctx.triggers) {
|
||||
let r = lay.take(CW() * 16.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 1), r, &cur, &ctx.triggers) {
|
||||
*name = ctx.triggers[c].clone();
|
||||
}
|
||||
*cx += CW() * 17.0;
|
||||
let opts = vec![tr("blk.on"), tr("blk.off")];
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 2), Rect::new(*cx, y, CW() * 10.0, fh), &opts[if *on { 0 } else { 1 }], &opts) {
|
||||
let r = lay.take(CW() * 10.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, 2), r, &opts[if *on { 0 } else { 1 }], &opts) {
|
||||
*on = c == 0;
|
||||
}
|
||||
*cx += CW() * 11.0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn zone_dd(&mut self, im: &mut Im, gfx: &Gfx, zone: &mut String, path: &[usize], f: usize, cx: &mut f32, y: f32, ctx: &Ctx) {
|
||||
fn zone_dd(&mut self, im: &mut Im, gfx: &Gfx, zone: &mut String, path: &[usize], f: usize, lay: &mut Lay, ctx: &Ctx) {
|
||||
let cur = if zone.is_empty() { tr("ed.pick_zone") } else { zone.clone() };
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), Rect::new(*cx, y, CW() * 14.0, CH() * 1.5), &cur, &ctx.zones) {
|
||||
let r = lay.take(CW() * 14.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), r, &cur, &ctx.zones) {
|
||||
*zone = ctx.zones[c].clone();
|
||||
}
|
||||
*cx += CW() * 15.0;
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn side_dd(&mut self, im: &mut Im, gfx: &Gfx, side: &mut i32, any: bool, path: &[usize], f: usize, cx: &mut f32, y: f32) {
|
||||
fn side_dd(&mut self, im: &mut Im, gfx: &Gfx, side: &mut i32, any: bool, path: &[usize], f: usize, lay: &mut Lay) {
|
||||
let sides: Vec<i32> = SIDES.iter().copied().filter(|&s| any || s >= 0).collect();
|
||||
let opts: Vec<String> = sides.iter().map(|&s| side_label(s)).collect();
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), Rect::new(*cx, y, CW() * 14.0, CH() * 1.5), &side_label(*side), &opts) {
|
||||
let r = lay.take(CW() * 16.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), r, &side_label(*side), &opts) {
|
||||
*side = sides[c];
|
||||
}
|
||||
*cx += CW() * 15.0;
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn kind_dd(&mut self, im: &mut Im, gfx: &Gfx, kind: &mut String, any: bool, path: &[usize], f: usize, cx: &mut f32, y: f32) {
|
||||
fn kind_dd(&mut self, im: &mut Im, gfx: &Gfx, kind: &mut String, any: bool, path: &[usize], f: usize, lay: &mut Lay) {
|
||||
let mut opts: Vec<String> = Vec::new();
|
||||
if any {
|
||||
opts.push(tr("ed.any_kind"));
|
||||
}
|
||||
opts.extend(KINDS.iter().map(|k| kind_label(k)));
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), Rect::new(*cx, y, CW() * 14.0, CH() * 1.5), &kind_label(kind), &opts) {
|
||||
let r = lay.take(CW() * 17.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f), r, &kind_label(kind), &opts) {
|
||||
*kind = if any && c == 0 { String::new() } else { KINDS[c - any as usize].to_string() };
|
||||
}
|
||||
*cx += CW() * 15.0;
|
||||
}
|
||||
|
||||
// a value slot: a small switch for the value's kind, then the value itself
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn value(&mut self, im: &mut Im, gfx: &Gfx, v: &mut Val, want: Ty, path: &[usize], f: usize, cx: &mut f32, y: f32, ctx: &Ctx) {
|
||||
let fh = CH() * 1.5;
|
||||
fn value(&mut self, im: &mut Im, gfx: &Gfx, v: &mut Val, want: Ty, path: &[usize], f: usize, lay: &mut Lay, ctx: &Ctx) {
|
||||
let kinds: &[&str] = if want == Ty::Num { &NUM_VALUES } else { &BOOL_VALUES };
|
||||
let opts: Vec<String> = kinds.iter().map(|k| tr(&format!("blk.v.{}", k))).collect();
|
||||
// the kind switch
|
||||
let sw_ = Rect::new(*cx, y, CW() * 1.6, fh);
|
||||
let sw_ = lay.take_tight(CW() * 1.6);
|
||||
draw_rectangle(sw_.x, sw_.y, sw_.w, sw_.h, Color::new(0.0, 0.0, 0.0, 0.35));
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, f * 31 + 1), sw_, "", &opts) {
|
||||
if let Some(nv) = Val::template(kinds[c]) {
|
||||
*v = nv;
|
||||
}
|
||||
}
|
||||
*cx += CW() * 1.8;
|
||||
let word = |cx: &mut f32, s: &str| {
|
||||
gfx.text(s, *cx, y + CH() * 0.25, S(), TXT);
|
||||
*cx += gfx.measure(s, S()) + CW() * 0.5;
|
||||
};
|
||||
let sub = |k: usize| f * 31 + 2 + k;
|
||||
let and = matches!(v, Val::And { .. });
|
||||
match v {
|
||||
Val::Num { n } => {
|
||||
let id_ = fid(path, f * 31);
|
||||
let r = Rect::new(*cx, y, CW() * 8.0, fh);
|
||||
let r = lay.take(CW() * 8.0);
|
||||
let mut s = if im.focus == Some(id_) && self.num_focus == Some(id_) { self.num_buf.clone() } else { fmt_num(*n) };
|
||||
if im.focus == Some(id_) && self.num_focus != Some(id_) {
|
||||
self.num_focus = Some(id_);
|
||||
@@ -668,71 +653,112 @@ impl BlockEdit {
|
||||
*n = x;
|
||||
}
|
||||
}
|
||||
*cx += CW() * 8.6;
|
||||
}
|
||||
Val::Bool { b } => {
|
||||
let o = vec![tr("blk.yes"), tr("blk.no")];
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(0)), Rect::new(*cx, y, CW() * 7.0, fh), &o[if *b { 0 } else { 1 }], &o) {
|
||||
let r = lay.take(CW() * 7.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(0)), r, &o[if *b { 0 } else { 1 }], &o) {
|
||||
*b = c == 0;
|
||||
}
|
||||
*cx += CW() * 7.6;
|
||||
}
|
||||
Val::Var { name } => {
|
||||
let cur = if name.is_empty() { tr("ed.pick_var") } else { name.clone() };
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(0)), Rect::new(*cx, y, CW() * 12.0, fh), &cur, &ctx.vars) {
|
||||
let r = lay.take(CW() * 15.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(0)), r, &cur, &ctx.vars) {
|
||||
*name = ctx.vars[c].clone();
|
||||
}
|
||||
*cx += CW() * 12.6;
|
||||
}
|
||||
Val::Gold | Val::Mana | Val::Time => word(cx, &tr(&format!("blk.v.{}", v.kind_name()))),
|
||||
Val::Gold | Val::Mana | Val::Time => lay.word(gfx, &tr(&format!("blk.v.{}", v.kind_name()))),
|
||||
Val::Creatures { side, kind } => {
|
||||
word(cx, &tr("blk.v.creatures"));
|
||||
self.kind_dd(im, gfx, kind, true, path, sub(0), cx, y);
|
||||
self.side_dd(im, gfx, side, true, path, sub(1), cx, y);
|
||||
lay.word(gfx, &tr("blk.v.creatures"));
|
||||
self.kind_dd(im, gfx, kind, true, path, sub(0), lay);
|
||||
self.side_dd(im, gfx, side, true, path, sub(1), lay);
|
||||
}
|
||||
Val::InZone { zone, side, kind } => {
|
||||
word(cx, &tr("blk.v.inzone"));
|
||||
self.kind_dd(im, gfx, kind, true, path, sub(0), cx, y);
|
||||
self.side_dd(im, gfx, side, true, path, sub(1), cx, y);
|
||||
word(cx, &tr("blk.w_in"));
|
||||
self.zone_dd(im, gfx, zone, path, sub(2), cx, y, ctx);
|
||||
lay.word(gfx, &tr("blk.v.inzone"));
|
||||
self.kind_dd(im, gfx, kind, true, path, sub(0), lay);
|
||||
self.side_dd(im, gfx, side, true, path, sub(1), lay);
|
||||
lay.word(gfx, &tr("blk.w_in"));
|
||||
self.zone_dd(im, gfx, zone, path, sub(2), lay, ctx);
|
||||
}
|
||||
Val::Random { a, b } => {
|
||||
word(cx, &tr("blk.v.random"));
|
||||
self.value(im, gfx, a, Ty::Num, path, sub(0), cx, y, ctx);
|
||||
word(cx, "..");
|
||||
self.value(im, gfx, b, Ty::Num, path, sub(1), cx, y, ctx);
|
||||
lay.word(gfx, &tr("blk.v.random"));
|
||||
self.value(im, gfx, a, Ty::Num, path, sub(0), lay, ctx);
|
||||
lay.word(gfx, "..");
|
||||
self.value(im, gfx, b, Ty::Num, path, sub(1), lay, ctx);
|
||||
}
|
||||
Val::Math { op, a, b } | Val::Cmp { op, a, b } => {
|
||||
let ops: Vec<String> = if matches!(v_kind_is_math(op), true) { MATH_OPS.iter().map(|s| s.to_string()).collect() } else { CMP_OPS.iter().map(|s| s.to_string()).collect() };
|
||||
word(cx, "(");
|
||||
self.value(im, gfx, a, Ty::Num, path, sub(0), cx, y, ctx);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(1)), Rect::new(*cx, y, CW() * 4.0, fh), op, &ops) {
|
||||
let ops: Vec<String> = if v_kind_is_math(op) { MATH_OPS.iter().map(|s| s.to_string()).collect() } else { CMP_OPS.iter().map(|s| s.to_string()).collect() };
|
||||
lay.word(gfx, "(");
|
||||
self.value(im, gfx, a, Ty::Num, path, sub(0), lay, ctx);
|
||||
let r = lay.take(CW() * 4.0);
|
||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(1)), r, op, &ops) {
|
||||
*op = ops[c].clone();
|
||||
}
|
||||
*cx += CW() * 4.6;
|
||||
self.value(im, gfx, b, Ty::Num, path, sub(2), cx, y, ctx);
|
||||
word(cx, ")");
|
||||
self.value(im, gfx, b, Ty::Num, path, sub(2), lay, ctx);
|
||||
lay.word(gfx, ")");
|
||||
}
|
||||
Val::And { a, b } | Val::Or { a, b } => {
|
||||
word(cx, "(");
|
||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), cx, y, ctx);
|
||||
word(cx, &tr(if and { "blk.and" } else { "blk.or" }));
|
||||
self.value(im, gfx, b, Ty::Bool, path, sub(1), cx, y, ctx);
|
||||
word(cx, ")");
|
||||
lay.word(gfx, "(");
|
||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), lay, ctx);
|
||||
lay.word(gfx, &tr(if and { "blk.and" } else { "blk.or" }));
|
||||
self.value(im, gfx, b, Ty::Bool, path, sub(1), lay, ctx);
|
||||
lay.word(gfx, ")");
|
||||
}
|
||||
Val::Not { a } => {
|
||||
word(cx, &tr("blk.not"));
|
||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), cx, y, ctx);
|
||||
lay.word(gfx, &tr("blk.not"));
|
||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), lay, ctx);
|
||||
}
|
||||
Val::EventKind { kind } => {
|
||||
word(cx, &tr("blk.v.eventkind"));
|
||||
self.kind_dd(im, gfx, kind, false, path, sub(0), cx, y);
|
||||
lay.word(gfx, &tr("blk.v.eventkind"));
|
||||
self.kind_dd(im, gfx, kind, false, path, sub(0), lay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// where the next field of a block goes: left to right, into a new row at the block's edge
|
||||
struct Lay {
|
||||
x: f32,
|
||||
y: f32,
|
||||
x2: f32, // start of a new row
|
||||
max: f32, // right edge
|
||||
rows: usize,
|
||||
}
|
||||
|
||||
impl Lay {
|
||||
fn need(&mut self, w: f32) {
|
||||
if self.x + w > self.max && self.x > self.x2 + 1.0 {
|
||||
self.x = self.x2;
|
||||
self.y += CH() * ROW;
|
||||
self.rows += 1;
|
||||
}
|
||||
}
|
||||
|
||||
// room for a field of width w (wraps if needed)
|
||||
fn take(&mut self, w: f32) -> Rect {
|
||||
self.need(w);
|
||||
let r = Rect::new(self.x, self.y, w, CH() * 1.5);
|
||||
self.x += w + CW() * 0.6;
|
||||
r
|
||||
}
|
||||
|
||||
fn take_tight(&mut self, w: f32) -> Rect {
|
||||
// the kind switch stays with its value: room for both
|
||||
self.need(w + CW() * 8.0);
|
||||
let r = Rect::new(self.x, self.y, w, CH() * 1.5);
|
||||
self.x += w + CW() * 0.2;
|
||||
r
|
||||
}
|
||||
|
||||
fn word(&mut self, gfx: &Gfx, s: &str) {
|
||||
let w = gfx.measure(s, S());
|
||||
self.need(w);
|
||||
gfx.text(s, self.x, self.y + CH() * 0.25, S(), TXT);
|
||||
self.x += w + CW() * 0.6;
|
||||
}
|
||||
}
|
||||
|
||||
// math operators are + - * /, comparisons the rest
|
||||
fn v_kind_is_math(op: &str) -> bool {
|
||||
MATH_OPS.contains(&op)
|
||||
|
||||
+2
-2
@@ -779,10 +779,10 @@ impl MapEdit {
|
||||
bx += gfx.measure(&tr("ed.fill"), S()) + CW() * 4.0;
|
||||
}
|
||||
let mirrors: Vec<String> = (0..4).map(|k| tr(&format!("ed.mirror{}", k))).collect();
|
||||
if let Some(c) = im.dropdown(gfx, id("mirror", 0), Rect::new(bx, by, CW() * 18.0, CH() * 1.7), &mirrors[self.mirror as usize], &mirrors) {
|
||||
if let Some(c) = im.dropdown(gfx, id("mirror", 0), Rect::new(bx, by, CW() * 23.0, CH() * 1.7), &mirrors[self.mirror as usize], &mirrors) {
|
||||
self.mirror = c as u8;
|
||||
}
|
||||
bx += CW() * 19.0;
|
||||
bx += CW() * 24.0;
|
||||
let uw = gfx.measure(&tr("ed.undo"), S()) + CW() * 2.0;
|
||||
if im.button(gfx, Rect::new(bx, by, uw, CH() * 1.7), &tr("ed.undo")) {
|
||||
self.undo();
|
||||
|
||||
+25
-5
@@ -107,7 +107,9 @@ pub fn demo_campaign() -> CampaignDoc {
|
||||
let (ax, ay) = cl(cx + 7, cy - 2);
|
||||
let (bx, by) = cl(cx + 10, cy + 2);
|
||||
let vault = zone_rect("vault", ax, ay, bx, by);
|
||||
let (dx, dy) = cl(cx - 2, cy + 6);
|
||||
// the den lies behind a band of earth west of the dungeon (away from the knights' way to the
|
||||
// heart, who dig through earth): dig the tunnel first
|
||||
let (dx, dy) = cl((cx - 11).max(5), cy + 1);
|
||||
let den = ZoneDoc { name: "den".into(), shape: "circle".into(), at: Some((dx as f32, dy as f32)), radius: Some(2.5), tiles: None, from: None, to: None, width: None };
|
||||
let (lx0, ly0) = cl(cx + 14, cy - 8);
|
||||
let (lx1, ly1) = cl(cx + 14, cy + 8);
|
||||
@@ -115,13 +117,29 @@ pub fn demo_campaign() -> CampaignDoc {
|
||||
let (gx0, gy0) = cl(cx + 16, cy - 5);
|
||||
let (gx1, gy1) = cl(cx + 22, cy + 5);
|
||||
let guard = zone_rect("field", gx0, gy0, gx1, gy1);
|
||||
let dig_tiles: Vec<(i32, i32)> = (0..3).flat_map(|k| [(cx - 6 - k, cy), (cx - 6 - k, cy + 1)]).map(|(x, y)| cl(x, y)).collect();
|
||||
let dig_tiles: Vec<(i32, i32)> = (0..3).flat_map(|k| [(cx - 5 - k, cy + 1), (cx - 5 - k, cy + 2)]).map(|(x, y)| cl(x, y)).collect();
|
||||
let dig = ZoneDoc { name: "tunnel".into(), shape: "tiles".into(), at: None, radius: None, tiles: Some(dig_tiles.clone()), from: None, to: None, width: None };
|
||||
// the den is open ground, the tunnel solid earth to dig
|
||||
for i in crate::mscript::zone_tiles(&den) {
|
||||
if g.map.t[i].solid() {
|
||||
// the den is open ground, sealed by earth all around; the tunnel is solid earth to dig
|
||||
let den_tiles = crate::mscript::zone_tiles(&den);
|
||||
for &i in &den_tiles {
|
||||
let (x, y) = crate::map::xy(i);
|
||||
for (ox, oy) in [(-1, -1), (0, -1), (1, -1), (-1, 0), (1, 0), (-1, 1), (0, 1), (1, 1)] {
|
||||
if !crate::map::inb(x + ox, y + oy) {
|
||||
continue;
|
||||
}
|
||||
let j = crate::map::idx(x + ox, y + oy);
|
||||
if !den_tiles.contains(&j) && !g.map.t[j].solid() {
|
||||
let t = &mut g.map.t[j];
|
||||
t.kind = crate::map::Kind::Earth;
|
||||
t.owner = crate::map::Owner::None;
|
||||
}
|
||||
}
|
||||
}
|
||||
for &i in &den_tiles {
|
||||
if g.map.t[i].solid() || g.map.t[i].liquid() {
|
||||
g.dig_out(i);
|
||||
}
|
||||
g.map.t[i].owner = crate::map::Owner::None;
|
||||
}
|
||||
for &(x, y) in &dig_tiles {
|
||||
let t = &mut g.map.t[crate::map::idx(x, y)];
|
||||
@@ -233,12 +251,14 @@ mod tests {
|
||||
// it starts on its painted map and the scripts run without errors
|
||||
let mut g = crate::campaign::start_editor_mission(&doc.missions[0], 0, 99);
|
||||
assert!(g.script.is_some());
|
||||
|
||||
for _ in 0..(20 * 40) {
|
||||
g.update(0.05);
|
||||
}
|
||||
let s = g.script.as_ref().unwrap();
|
||||
assert!(s.error.is_none(), "{:?}", s.error);
|
||||
assert!(s.vars.contains_key("kills"), "the start script ran");
|
||||
assert!(!s.log.iter().any(|(_, n)| n == "den_enter" || n == "tunnel_dug"), "the den waits behind its tunnel: {:?}", s.log);
|
||||
// the random-map mission starts too
|
||||
let g2 = crate::campaign::start_editor_mission(&doc.missions[1], 1, 99);
|
||||
assert!(g2.mission.is_some());
|
||||
|
||||
Reference in new issue
Block a user