Wait and both kinds of until loops; blocks run directly

Script runs are resumable threads saved with the game; Rhai is gone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard ZinkandClaude Opus 5.5 committed 2026-11-05 18:57:36 +01:00
1 parent 95cf3ce81c
commit d17668f3b2
13 files changed
+466 -686

No files matched your search

+8 -3
View File
@@ -85,7 +85,7 @@ fn list_mut<'a>(root: &'a mut Vec<Block>, lp: &[usize]) -> Option<&'a mut Vec<Bl
els
}
}
Block::Repeat { body, .. } => body,
Block::Repeat { body, .. } | Block::Until { body, .. } | Block::DoUntil { body, .. } => body,
_ => return None,
};
k += 2;
@@ -432,7 +432,7 @@ impl BlockEdit {
// slots: upper half before this block, lower half after it
slots.push((Rect::new(x, y - CH() * 0.3, w, header.h * 0.5 + CH() * 0.3), lp.to_vec(), k));
slots.push((Rect::new(x, y + header.h * 0.5, w, header.h * 0.5), lp.to_vec(), k + 1));
let nested = matches!(list[k], Block::If { .. } | Block::Repeat { .. });
let nested = matches!(list[k], Block::If { .. } | Block::Repeat { .. } | Block::Until { .. } | Block::DoUntil { .. });
// the block: header row with its fields
draw_block_shape(header, col, selected, found);
if let Some(p) = problem {
@@ -479,7 +479,7 @@ impl BlockEdit {
els
}
}
Block::Repeat { body, .. } => body,
Block::Repeat { body, .. } | Block::Until { body, .. } | Block::DoUntil { body, .. } => body,
_ => unreachable!(),
};
let start = y;
@@ -563,6 +563,11 @@ impl BlockEdit {
self.value(im, gfx, times, Ty::Num, path, 1, cx, y, ctx);
word(cx, &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"));
}
Block::Win | Block::Lose => {}
Block::Reveal { zone } | Block::Camera { zone } => self.zone_dd(im, gfx, zone, path, 1, cx, y, ctx),
Block::Terrain { zone, kind } => {
+25 -3
View File
@@ -164,16 +164,38 @@ pub fn demo_campaign() -> CampaignDoc {
trig("vault_claimed", TriggerKind::ZoneClaimed { zone: "vault".into() }, true, vec![say("demo.vault"), Block::Gold { amount: n(500.0) }]),
trig("tunnel_dug", TriggerKind::ZoneDug { zone: "tunnel".into() }, true, vec![say("demo.tunnel")]),
trig("crowd", TriggerKind::ZoneCount { zone: "field".into(), side: 1, kind: String::new(), op: ">=".into(), n: 4.0 }, true, vec![say("demo.crowd"), Block::Camera { zone: "field".into() }]),
trig("reinforcements", TriggerKind::Timer { secs: 90.0, every: false }, true, vec![say("demo.wave"), Block::Spawn { side: 1, kind: "Archer".into(), count: n(2.0), level: n(2.0), place: Place::Zone("field".into()), name: String::new() }]),
// repeat until (checked first): one archer every 15 seconds until four heroes stand on the field
trig(
"reinforcements",
TriggerKind::Timer { secs: 90.0, every: false },
true,
vec![
say("demo.wave"),
Block::Until {
cond: cmp(">=", Val::InZone { zone: "field".into(), side: 1, kind: String::new() }, n(4.0)),
body: vec![Block::Spawn { side: 1, kind: "Archer".into(), count: n(1.0), level: n(2.0), place: Place::Zone("field".into()), name: String::new() }, Block::Wait { secs: n(15.0) }],
},
],
),
trig("clock", TriggerKind::Timer { secs: 30.0, every: true }, false, vec![Block::AddVar { name: "ticks".into(), value: n(1.0) }]),
trig("knight_falls", TriggerKind::UnitDies { side: 1, kind: String::new(), name: String::new() }, false, vec![Block::AddVar { name: "kills".into(), value: n(1.0) }, Block::Message { text: tr("demo.kills") }]),
trig("captain_falls", TriggerKind::UnitDies { side: 1, kind: String::new(), name: "captain".into() }, true, vec![say("demo.captain"), Block::Mana { amount: n(50.0) }]),
trig("captain_falls", TriggerKind::UnitDies { side: 1, kind: String::new(), name: "captain".into() }, true, vec![say("demo.captain"), Block::Repeat { times: n(5.0), body: vec![Block::Mana { amount: n(10.0) }] }]),
trig("tower_down", TriggerKind::TowerFalls { side: 1 }, false, vec![say("demo.tower"), Block::Gold { amount: Val::Random { a: Box::new(n(100.0)), b: Box::new(n(300.0)) } }]),
trig("rich", TriggerKind::Value { what: "gold".into(), var: String::new(), op: ">=".into(), n: 8000.0 }, true, vec![say("demo.rich")]),
trig("lair", TriggerKind::RoomBuilt { room: "Lair".into() }, true, vec![Block::Hint { text: tr("demo.lair") }]),
trig("spell", TriggerKind::SpellCast { spell: String::new() }, false, vec![Block::Mana { amount: n(10.0) }]),
trig("slap", TriggerKind::Slap, true, vec![say("demo.slap")]),
trig("research", TriggerKind::Research, true, vec![say("demo.research"), Block::Repeat { times: n(2.0), body: vec![Block::Gold { amount: n(100.0) }] }]),
// repeat ... until (checked after): a little gold three times, a second apart
trig(
"research",
TriggerKind::Research,
true,
vec![
say("demo.research"),
Block::SetVar { name: "bonus".into(), value: n(0.0) },
Block::DoUntil { cond: cmp(">=", var("bonus"), n(3.0)), body: vec![Block::Gold { amount: n(100.0) }, Block::AddVar { name: "bonus".into(), value: n(1.0) }, Block::Wait { secs: n(1.0) }] },
],
),
];
doc.missions.push(m);