makepad/apps/arcade/examples/bigworld_probe.rs
andodeki 435135ed50 feat(arcade): port makepad game libs + arcade from makepad-internal onto the fork
Reintroduce apps/arcade and the ten game crates that were ripped out of
the fork (assets, audio, blocks, coedit, gen, net, pkg, render, script,
session), from makepad-internal/dev. Reconcile against the fork's layout:

- makepad-game-sim/math come from the fork's existing libs/sim + sim/math;
  the copied internal libs/game/{sim,math} are dropped and the kept game
  crates plus arcade point at ../../sim and ../../sim/math.
- arcade AI is rewritten off the removed makepad-ai agent API onto the
  makepad-ai-hub headless ChatProvider worker (example/cad pattern):
  AiWorker{send,cancel,poll}, ai_worker_loop driving
  ClaudeApiChatProvider(ClaudeCli), AiWorkerEvent availability/delta/
  done/error mapped into the chat feed and authoring land_edit, with
  main.rs send_message/cancel_request/event-drain rebuilt around it.
- the game-script sandbox is adapted to the fork's jailed splash storage:
  every isolate gets mod.fs = splash_storage jail; splaes splash_storage
  set_root_for_heap is now public so the ScriptHost (or Splash) can grant
  a per-game jail root. ENT font: build.rs/dispatch.rs entities fill new
  fork sim Entity/Part fields via ..Default::default(); world_raycast now
  borrows mutably and returns the material id.

cargo check -p makepad-arcade and the game + arcade test suites pass.
2026-09-05 05:49:12 +03:00

85 lines
2.9 KiB
Rust

//! Print what the Zelda-scale plan actually builds.
//!
//! Layout is pure, so the whole world can be inspected without a window —
//! which is the point of splitting `plan` from `realise`. Run with:
//! cargo run -p makepad-arcade --example bigworld_probe --release
use makepad_arcade::bigworld::{self, Region};
fn main() {
let root = std::path::Path::new("apps/arcade/resources");
if !root.join("models/kenney").is_dir() {
eprintln!("run apps/arcade/download_assets.sh first");
return;
}
let t = std::time::Instant::now();
let index = makepad_game_assets::AssetIndex::build(root);
let index_ms = t.elapsed().as_millis();
let plan = bigworld::plan(&index, 7);
let s = &plan.stats;
println!(
"index {} entries in {index_ms} ms\nplan {} props ({} distinct models), {} tiles, {} npcs, {} pois, {} interactables in {} us",
index.len(),
s.props,
s.distinct_models,
s.tiles,
s.npcs,
plan.pois.len(),
plan.interactables.len(),
s.gen_us,
);
println!("\nper region:");
for (r, n) in &s.per_region {
println!(" {:<9} {n:>5}", r.name());
}
println!("\ncast:");
let (cj, civ) = bigworld::civilian_cast(&index);
let (hj, hero) = bigworld::hero_cast(&index);
println!(" civilians {cj} joints, {} members", civ.len());
println!(" heroes {hj} joints, {} members", hero.len());
println!("\nnpcs by region:");
for r in [Region::Village, Region::Castle, Region::Dungeon] {
let n: Vec<&str> = plan
.npcs
.iter()
.filter(|n| n.region == r)
.filter_map(|n| n.character.as_deref())
.collect();
println!(" {:<8} {}", r.name(), n.join(", "));
}
println!("\nsample models per region:");
for (r, _) in &s.per_region {
let mut ids: Vec<&str> = plan
.placements
.iter()
.filter(|p| p.region == *r)
.map(|p| p.model.as_str())
.collect();
ids.sort_unstable();
ids.dedup();
println!(" {:<9} {}", r.name(), ids.iter().take(5).cloned().collect::<Vec<_>>().join(", "));
}
// Reachability: every region centre must be within a short walk of a road
// tile, or a player cannot get there on foot.
println!("\nreachability (nearest road tile to each region centre):");
for (name, c) in [
("village", bigworld::VILLAGE),
("castle", bigworld::CASTLE),
("woods", bigworld::WOODS),
("dungeon", bigworld::DUNGEON),
("quarry", bigworld::QUARRY),
] {
let best = plan
.placements
.iter()
.filter(|p| p.region == Region::Roads)
.map(|p| ((p.pos.x - c.x).powi(2) + (p.pos.z - c.y).powi(2)).sqrt())
.fold(f32::INFINITY, f32::min);
println!(" {name:<8} {best:.1} units");
}
}