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
+1
-1
@@ -1302,7 +1302,7 @@ blk.no = nein
|
|||||||
blk.on = an
|
blk.on = an
|
||||||
blk.off = aus
|
blk.off = aus
|
||||||
blk.place_core = dem Kern
|
blk.place_core = dem Kern
|
||||||
blk.place_event = der auslösenden Einheit
|
blk.place_event = dem Auslöser
|
||||||
blk.place_zone = Zone {0}
|
blk.place_zone = Zone {0}
|
||||||
demo.name = Demo: alle Trigger
|
demo.name = Demo: alle Trigger
|
||||||
demo.branch = Reich genug für einen Umweg
|
demo.branch = Reich genug für einen Umweg
|
||||||
|
|||||||
+1
-1
@@ -1068,7 +1068,7 @@ ed.add_branch = + Branch
|
|||||||
ed.goal.killall = Defeat all heroes
|
ed.goal.killall = Defeat all heroes
|
||||||
ed.goal.shards = Take the shards
|
ed.goal.shards = Take the shards
|
||||||
ed.goal.heart = Destroy the gate
|
ed.goal.heart = Destroy the gate
|
||||||
ed.goal.towers = Raze the knight towers
|
ed.goal.towers = Raze knight towers
|
||||||
ed.goal.rivals = Defeat the rivals
|
ed.goal.rivals = Defeat the rivals
|
||||||
ed.goal.loot = Loot gold
|
ed.goal.loot = Loot gold
|
||||||
ed.goal.survive = Survive waves
|
ed.goal.survive = Survive waves
|
||||||
|
|||||||
+2
-2
@@ -1064,7 +1064,7 @@ ed.add_branch = + Diramazione
|
|||||||
ed.goal.killall = Sconfiggi gli eroi
|
ed.goal.killall = Sconfiggi gli eroi
|
||||||
ed.goal.shards = Prendi i frammenti
|
ed.goal.shards = Prendi i frammenti
|
||||||
ed.goal.heart = Distruggi la porta
|
ed.goal.heart = Distruggi la porta
|
||||||
ed.goal.towers = Radi al suolo le torri
|
ed.goal.towers = Abbatti le torri
|
||||||
ed.goal.rivals = Sconfiggi i rivali
|
ed.goal.rivals = Sconfiggi i rivali
|
||||||
ed.goal.loot = Saccheggia oro
|
ed.goal.loot = Saccheggia oro
|
||||||
ed.goal.survive = Supera ondate
|
ed.goal.survive = Supera ondate
|
||||||
@@ -1100,7 +1100,7 @@ ed.edit_blocks = Modifica
|
|||||||
ed.triggers = Trigger
|
ed.triggers = Trigger
|
||||||
ed.triggers_hint = Un trigger esegue i suoi blocchi quando la sua condizione è soddisfatta. Le zone si disegnano nell'editor mappe.
|
ed.triggers_hint = Un trigger esegue i suoi blocchi quando la sua condizione è soddisfatta. Le zone si disegnano nell'editor mappe.
|
||||||
ed.add_trigger = + Trigger
|
ed.add_trigger = + Trigger
|
||||||
ed.once = una volta
|
ed.once = 1 volta
|
||||||
ed.enabled = attivo
|
ed.enabled = attivo
|
||||||
ed.edit_trigger_blocks = Blocchi ({0})
|
ed.edit_trigger_blocks = Blocchi ({0})
|
||||||
ed.pick_zone = - zona -
|
ed.pick_zone = - zona -
|
||||||
|
|||||||
+1
-1
@@ -1068,7 +1068,7 @@ ed.goal.towers = Снеси рыцарские башни
|
|||||||
ed.goal.rivals = Победи соперников
|
ed.goal.rivals = Победи соперников
|
||||||
ed.goal.loot = Награбь золота
|
ed.goal.loot = Награбь золота
|
||||||
ed.goal.survive = Отбей волны
|
ed.goal.survive = Отбей волны
|
||||||
ed.goal.springs = Захвати источники маны
|
ed.goal.springs = Захвати источники
|
||||||
ed.goal.freed = Освободи существ
|
ed.goal.freed = Освободи существ
|
||||||
ed.map_random = Случайная карта
|
ed.map_random = Случайная карта
|
||||||
ed.map_fixed = Своя карта
|
ed.map_fixed = Своя карта
|
||||||
|
|||||||
+134
-108
@@ -36,6 +36,7 @@ pub struct BlockEdit {
|
|||||||
num_buf: String, // the number being typed (focused number field)
|
num_buf: String, // the number being typed (focused number field)
|
||||||
num_focus: Option<u64>,
|
num_focus: Option<u64>,
|
||||||
help: String,
|
help: String,
|
||||||
|
rows: std::collections::HashMap<u64, usize>, // rows each block's fields needed (wrapping)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum Op {
|
enum Op {
|
||||||
@@ -424,7 +425,8 @@ impl BlockEdit {
|
|||||||
let cat = list[k].category();
|
let cat = list[k].category();
|
||||||
let col = cat_color(cat);
|
let col = cat_color(cat);
|
||||||
// the spawn block has many fields: two rows
|
// 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 header = Rect::new(x, y, w, CH() * ROW * rows);
|
||||||
let problem = ctx.problems.iter().find(|(p, _)| *p == path).map(|(_, k)| *k);
|
let problem = ctx.problems.iter().find(|(p, _)| *p == path).map(|(_, k)| *k);
|
||||||
let selected = self.selected.as_ref() == Some(&path);
|
let selected = self.selected.as_ref() == Some(&path);
|
||||||
@@ -447,8 +449,10 @@ impl BlockEdit {
|
|||||||
ops.push(Op::StartDrag(path.clone()));
|
ops.push(Op::StartDrag(path.clone()));
|
||||||
}
|
}
|
||||||
let fy = y + (CH() * ROW - CH() * 1.5) * 0.5;
|
let fy = y + (CH() * ROW - CH() * 1.5) * 0.5;
|
||||||
let mut cx = x + CW() * 1.8;
|
let cx = x + CW() * 1.8;
|
||||||
self.block_fields(im, gfx, &mut list[k], &path, &mut cx, fy, x + CW() * 3.0, ctx);
|
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
|
// a click on the block's free area selects it
|
||||||
if im.clicked(header) {
|
if im.clicked(header) {
|
||||||
ops.push(Op::Select(path.clone()));
|
ops.push(Op::Select(path.clone()));
|
||||||
@@ -501,37 +505,25 @@ impl BlockEdit {
|
|||||||
y - y0
|
y - y0
|
||||||
}
|
}
|
||||||
|
|
||||||
// the fields inside a block's header row
|
// the fields inside a block's header (they wrap into more rows when the block is too narrow)
|
||||||
#[allow(clippy::too_many_arguments)]
|
fn block_fields(&mut self, im: &mut Im, gfx: &Gfx, b: &mut Block, path: &[usize], lay: &mut Lay, ctx: &Ctx) {
|
||||||
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;
|
|
||||||
};
|
|
||||||
let name = tr(&format!("blk.{}", b.kind_name()));
|
let name = tr(&format!("blk.{}", b.kind_name()));
|
||||||
word(cx, &name);
|
lay.word(gfx, &name);
|
||||||
let is_set = matches!(b, Block::SetVar { .. });
|
let is_set = matches!(b, Block::SetVar { .. });
|
||||||
let fh = CH() * 1.5;
|
|
||||||
match b {
|
match b {
|
||||||
Block::Message { text } | Block::Objective { text } | Block::Hint { text } => {
|
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);
|
let w = (lay.max - lay.x).max(CW() * 24.0).min(CW() * 60.0);
|
||||||
*cx += CW() * 45.0;
|
let r = lay.take(w);
|
||||||
|
im.text_field(gfx, fid(path, 0), r, text, 200);
|
||||||
}
|
}
|
||||||
Block::Spawn { side, kind, count, level, place, name } => {
|
Block::Spawn { side, kind, count, level, place, name } => {
|
||||||
self.value(im, gfx, count, Ty::Num, path, 1, cx, y, ctx);
|
self.value(im, gfx, count, Ty::Num, path, 1, lay, ctx);
|
||||||
word(cx, "×");
|
lay.word(gfx, "×");
|
||||||
self.kind_dd(im, gfx, kind, false, path, 2, cx, y);
|
self.kind_dd(im, gfx, kind, false, path, 2, lay);
|
||||||
word(cx, &tr("blk.w_level"));
|
lay.word(gfx, &tr("blk.w_level"));
|
||||||
self.value(im, gfx, level, Ty::Num, path, 3, cx, y, ctx);
|
self.value(im, gfx, level, Ty::Num, path, 3, lay, ctx);
|
||||||
self.side_dd(im, gfx, side, false, path, 4, cx, y);
|
self.side_dd(im, gfx, side, false, path, 4, lay);
|
||||||
// second row: where, and the name
|
lay.word(gfx, &tr("blk.w_at"));
|
||||||
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"));
|
|
||||||
let mut opts = vec![tr("blk.place_core"), tr("blk.place_event")];
|
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()])));
|
opts.extend(ctx.zones.iter().map(|z| trf("blk.place_zone", &[z.clone()])));
|
||||||
let cur = match place {
|
let cur = match place {
|
||||||
@@ -539,124 +531,117 @@ impl BlockEdit {
|
|||||||
Place::Event => opts[1].clone(),
|
Place::Event => opts[1].clone(),
|
||||||
Place::Zone(z) => trf("blk.place_zone", &[z.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 {
|
*place = match c {
|
||||||
0 => Place::Core,
|
0 => Place::Core,
|
||||||
1 => Place::Event,
|
1 => Place::Event,
|
||||||
c => Place::Zone(ctx.zones[c - 2].clone()),
|
c => Place::Zone(ctx.zones[c - 2].clone()),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
*cx += CW() * 17.0;
|
lay.word(gfx, &tr("blk.w_name"));
|
||||||
word(cx, &tr("blk.w_name"));
|
let r = lay.take(CW() * 10.0);
|
||||||
im.text_field(gfx, fid(path, 6), Rect::new(*cx, y, CW() * 10.0, fh), name, 16);
|
im.text_field(gfx, fid(path, 6), r, name, 16);
|
||||||
*cx += CW() * 11.0;
|
|
||||||
}
|
}
|
||||||
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 } => {
|
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);
|
let r = lay.take(CW() * 12.0);
|
||||||
*cx += CW() * 13.0;
|
im.text_field(gfx, fid(path, 0), r, name, 24);
|
||||||
word(cx, &tr(if is_set { "blk.w_to" } else { "blk.w_by" }));
|
lay.word(gfx, &tr(if is_set { "blk.w_to" } else { "blk.w_by" }));
|
||||||
self.value(im, gfx, value, Ty::Num, path, 1, cx, y, ctx);
|
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, .. } => {
|
Block::Repeat { times, .. } => {
|
||||||
self.value(im, gfx, times, Ty::Num, path, 1, cx, y, ctx);
|
self.value(im, gfx, times, Ty::Num, path, 1, lay, ctx);
|
||||||
word(cx, &tr("blk.w_times"));
|
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 } => {
|
Block::Wait { secs } => {
|
||||||
self.value(im, gfx, secs, Ty::Num, path, 1, cx, y, ctx);
|
self.value(im, gfx, secs, Ty::Num, path, 1, lay, ctx);
|
||||||
word(cx, &tr("blk.w_seconds"));
|
lay.word(gfx, &tr("blk.w_seconds"));
|
||||||
}
|
}
|
||||||
Block::Win | Block::Lose => {}
|
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 } => {
|
Block::Terrain { zone, kind } => {
|
||||||
self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx);
|
self.zone_dd(im, gfx, zone, path, 1, lay, ctx);
|
||||||
word(cx, &tr("blk.w_to"));
|
lay.word(gfx, &tr("blk.w_to"));
|
||||||
let opts: Vec<String> = TERRAIN.iter().map(|t| tr(&format!("ed.terrain.{}", t))).collect();
|
let opts: Vec<String> = TERRAIN.iter().map(|t| tr(&format!("ed.terrain.{}", t))).collect();
|
||||||
let cur = tr(&format!("ed.terrain.{}", kind));
|
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();
|
*kind = TERRAIN[c].to_string();
|
||||||
}
|
}
|
||||||
*cx += CW() * 15.0;
|
|
||||||
}
|
}
|
||||||
Block::Kill { zone, side } => {
|
Block::Kill { zone, side } => {
|
||||||
self.side_dd(im, gfx, side, true, path, 2, cx, y);
|
self.side_dd(im, gfx, side, true, path, 2, lay);
|
||||||
word(cx, &tr("blk.w_in"));
|
lay.word(gfx, &tr("blk.w_in"));
|
||||||
self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx);
|
self.zone_dd(im, gfx, zone, path, 1, lay, ctx);
|
||||||
}
|
}
|
||||||
Block::Trigger { name, on } => {
|
Block::Trigger { name, on } => {
|
||||||
let cur = if name.is_empty() { tr("ed.pick_trigger") } else { name.clone() };
|
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();
|
*name = ctx.triggers[c].clone();
|
||||||
}
|
}
|
||||||
*cx += CW() * 17.0;
|
|
||||||
let opts = vec![tr("blk.on"), tr("blk.off")];
|
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;
|
*on = c == 0;
|
||||||
}
|
}
|
||||||
*cx += CW() * 11.0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[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() };
|
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();
|
*zone = ctx.zones[c].clone();
|
||||||
}
|
}
|
||||||
*cx += CW() * 15.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[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 sides: Vec<i32> = SIDES.iter().copied().filter(|&s| any || s >= 0).collect();
|
||||||
let opts: Vec<String> = sides.iter().map(|&s| side_label(s)).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];
|
*side = sides[c];
|
||||||
}
|
}
|
||||||
*cx += CW() * 15.0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[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();
|
let mut opts: Vec<String> = Vec::new();
|
||||||
if any {
|
if any {
|
||||||
opts.push(tr("ed.any_kind"));
|
opts.push(tr("ed.any_kind"));
|
||||||
}
|
}
|
||||||
opts.extend(KINDS.iter().map(|k| kind_label(k)));
|
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() };
|
*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
|
// a value slot: a small switch for the value's kind, then the value itself
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[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) {
|
fn value(&mut self, im: &mut Im, gfx: &Gfx, v: &mut Val, want: Ty, path: &[usize], f: usize, lay: &mut Lay, ctx: &Ctx) {
|
||||||
let fh = CH() * 1.5;
|
|
||||||
let kinds: &[&str] = if want == Ty::Num { &NUM_VALUES } else { &BOOL_VALUES };
|
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();
|
let opts: Vec<String> = kinds.iter().map(|k| tr(&format!("blk.v.{}", k))).collect();
|
||||||
// the kind switch
|
// 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));
|
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(c) = im.dropdown(gfx, fid(path, f * 31 + 1), sw_, "", &opts) {
|
||||||
if let Some(nv) = Val::template(kinds[c]) {
|
if let Some(nv) = Val::template(kinds[c]) {
|
||||||
*v = nv;
|
*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 sub = |k: usize| f * 31 + 2 + k;
|
||||||
let and = matches!(v, Val::And { .. });
|
let and = matches!(v, Val::And { .. });
|
||||||
match v {
|
match v {
|
||||||
Val::Num { n } => {
|
Val::Num { n } => {
|
||||||
let id_ = fid(path, f * 31);
|
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) };
|
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_) {
|
if im.focus == Some(id_) && self.num_focus != Some(id_) {
|
||||||
self.num_focus = Some(id_);
|
self.num_focus = Some(id_);
|
||||||
@@ -668,71 +653,112 @@ impl BlockEdit {
|
|||||||
*n = x;
|
*n = x;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*cx += CW() * 8.6;
|
|
||||||
}
|
}
|
||||||
Val::Bool { b } => {
|
Val::Bool { b } => {
|
||||||
let o = vec![tr("blk.yes"), tr("blk.no")];
|
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;
|
*b = c == 0;
|
||||||
}
|
}
|
||||||
*cx += CW() * 7.6;
|
|
||||||
}
|
}
|
||||||
Val::Var { name } => {
|
Val::Var { name } => {
|
||||||
let cur = if name.is_empty() { tr("ed.pick_var") } else { name.clone() };
|
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();
|
*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 } => {
|
Val::Creatures { side, kind } => {
|
||||||
word(cx, &tr("blk.v.creatures"));
|
lay.word(gfx, &tr("blk.v.creatures"));
|
||||||
self.kind_dd(im, gfx, kind, true, path, sub(0), cx, y);
|
self.kind_dd(im, gfx, kind, true, path, sub(0), lay);
|
||||||
self.side_dd(im, gfx, side, true, path, sub(1), cx, y);
|
self.side_dd(im, gfx, side, true, path, sub(1), lay);
|
||||||
}
|
}
|
||||||
Val::InZone { zone, side, kind } => {
|
Val::InZone { zone, side, kind } => {
|
||||||
word(cx, &tr("blk.v.inzone"));
|
lay.word(gfx, &tr("blk.v.inzone"));
|
||||||
self.kind_dd(im, gfx, kind, true, path, sub(0), cx, y);
|
self.kind_dd(im, gfx, kind, true, path, sub(0), lay);
|
||||||
self.side_dd(im, gfx, side, true, path, sub(1), cx, y);
|
self.side_dd(im, gfx, side, true, path, sub(1), lay);
|
||||||
word(cx, &tr("blk.w_in"));
|
lay.word(gfx, &tr("blk.w_in"));
|
||||||
self.zone_dd(im, gfx, zone, path, sub(2), cx, y, ctx);
|
self.zone_dd(im, gfx, zone, path, sub(2), lay, ctx);
|
||||||
}
|
}
|
||||||
Val::Random { a, b } => {
|
Val::Random { a, b } => {
|
||||||
word(cx, &tr("blk.v.random"));
|
lay.word(gfx, &tr("blk.v.random"));
|
||||||
self.value(im, gfx, a, Ty::Num, path, sub(0), cx, y, ctx);
|
self.value(im, gfx, a, Ty::Num, path, sub(0), lay, ctx);
|
||||||
word(cx, "..");
|
lay.word(gfx, "..");
|
||||||
self.value(im, gfx, b, Ty::Num, path, sub(1), cx, y, ctx);
|
self.value(im, gfx, b, Ty::Num, path, sub(1), lay, ctx);
|
||||||
}
|
}
|
||||||
Val::Math { op, a, b } | Val::Cmp { op, a, b } => {
|
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() };
|
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() };
|
||||||
word(cx, "(");
|
lay.word(gfx, "(");
|
||||||
self.value(im, gfx, a, Ty::Num, path, sub(0), cx, y, ctx);
|
self.value(im, gfx, a, Ty::Num, path, sub(0), lay, ctx);
|
||||||
if let Some(c) = im.dropdown(gfx, fid(path, sub(1)), Rect::new(*cx, y, CW() * 4.0, fh), op, &ops) {
|
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();
|
*op = ops[c].clone();
|
||||||
}
|
}
|
||||||
*cx += CW() * 4.6;
|
self.value(im, gfx, b, Ty::Num, path, sub(2), lay, ctx);
|
||||||
self.value(im, gfx, b, Ty::Num, path, sub(2), cx, y, ctx);
|
lay.word(gfx, ")");
|
||||||
word(cx, ")");
|
|
||||||
}
|
}
|
||||||
Val::And { a, b } | Val::Or { a, b } => {
|
Val::And { a, b } | Val::Or { a, b } => {
|
||||||
word(cx, "(");
|
lay.word(gfx, "(");
|
||||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), cx, y, ctx);
|
self.value(im, gfx, a, Ty::Bool, path, sub(0), lay, ctx);
|
||||||
word(cx, &tr(if and { "blk.and" } else { "blk.or" }));
|
lay.word(gfx, &tr(if and { "blk.and" } else { "blk.or" }));
|
||||||
self.value(im, gfx, b, Ty::Bool, path, sub(1), cx, y, ctx);
|
self.value(im, gfx, b, Ty::Bool, path, sub(1), lay, ctx);
|
||||||
word(cx, ")");
|
lay.word(gfx, ")");
|
||||||
}
|
}
|
||||||
Val::Not { a } => {
|
Val::Not { a } => {
|
||||||
word(cx, &tr("blk.not"));
|
lay.word(gfx, &tr("blk.not"));
|
||||||
self.value(im, gfx, a, Ty::Bool, path, sub(0), cx, y, ctx);
|
self.value(im, gfx, a, Ty::Bool, path, sub(0), lay, ctx);
|
||||||
}
|
}
|
||||||
Val::EventKind { kind } => {
|
Val::EventKind { kind } => {
|
||||||
word(cx, &tr("blk.v.eventkind"));
|
lay.word(gfx, &tr("blk.v.eventkind"));
|
||||||
self.kind_dd(im, gfx, kind, false, path, sub(0), cx, y);
|
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
|
// math operators are + - * /, comparisons the rest
|
||||||
fn v_kind_is_math(op: &str) -> bool {
|
fn v_kind_is_math(op: &str) -> bool {
|
||||||
MATH_OPS.contains(&op)
|
MATH_OPS.contains(&op)
|
||||||
|
|||||||
+2
-2
@@ -779,10 +779,10 @@ impl MapEdit {
|
|||||||
bx += gfx.measure(&tr("ed.fill"), S()) + CW() * 4.0;
|
bx += gfx.measure(&tr("ed.fill"), S()) + CW() * 4.0;
|
||||||
}
|
}
|
||||||
let mirrors: Vec<String> = (0..4).map(|k| tr(&format!("ed.mirror{}", k))).collect();
|
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;
|
self.mirror = c as u8;
|
||||||
}
|
}
|
||||||
bx += CW() * 19.0;
|
bx += CW() * 24.0;
|
||||||
let uw = gfx.measure(&tr("ed.undo"), S()) + CW() * 2.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")) {
|
if im.button(gfx, Rect::new(bx, by, uw, CH() * 1.7), &tr("ed.undo")) {
|
||||||
self.undo();
|
self.undo();
|
||||||
|
|||||||
+25
-5
@@ -107,7 +107,9 @@ pub fn demo_campaign() -> CampaignDoc {
|
|||||||
let (ax, ay) = cl(cx + 7, cy - 2);
|
let (ax, ay) = cl(cx + 7, cy - 2);
|
||||||
let (bx, by) = cl(cx + 10, cy + 2);
|
let (bx, by) = cl(cx + 10, cy + 2);
|
||||||
let vault = zone_rect("vault", ax, ay, bx, by);
|
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 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 (lx0, ly0) = cl(cx + 14, cy - 8);
|
||||||
let (lx1, ly1) = 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 (gx0, gy0) = cl(cx + 16, cy - 5);
|
||||||
let (gx1, gy1) = cl(cx + 22, cy + 5);
|
let (gx1, gy1) = cl(cx + 22, cy + 5);
|
||||||
let guard = zone_rect("field", gx0, gy0, gx1, gy1);
|
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 };
|
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
|
// the den is open ground, sealed by earth all around; the tunnel is solid earth to dig
|
||||||
for i in crate::mscript::zone_tiles(&den) {
|
let den_tiles = crate::mscript::zone_tiles(&den);
|
||||||
if g.map.t[i].solid() {
|
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.dig_out(i);
|
||||||
}
|
}
|
||||||
|
g.map.t[i].owner = crate::map::Owner::None;
|
||||||
}
|
}
|
||||||
for &(x, y) in &dig_tiles {
|
for &(x, y) in &dig_tiles {
|
||||||
let t = &mut g.map.t[crate::map::idx(x, y)];
|
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
|
// 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);
|
let mut g = crate::campaign::start_editor_mission(&doc.missions[0], 0, 99);
|
||||||
assert!(g.script.is_some());
|
assert!(g.script.is_some());
|
||||||
|
|
||||||
for _ in 0..(20 * 40) {
|
for _ in 0..(20 * 40) {
|
||||||
g.update(0.05);
|
g.update(0.05);
|
||||||
}
|
}
|
||||||
let s = g.script.as_ref().unwrap();
|
let s = g.script.as_ref().unwrap();
|
||||||
assert!(s.error.is_none(), "{:?}", s.error);
|
assert!(s.error.is_none(), "{:?}", s.error);
|
||||||
assert!(s.vars.contains_key("kills"), "the start script ran");
|
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
|
// the random-map mission starts too
|
||||||
let g2 = crate::campaign::start_editor_mission(&doc.missions[1], 1, 99);
|
let g2 = crate::campaign::start_editor_mission(&doc.missions[1], 1, 99);
|
||||||
assert!(g2.mission.is_some());
|
assert!(g2.mission.is_some());
|
||||||
|
|||||||
Reference in new issue
Block a user