oriverse-engine 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,60 +1,28 @@
1
1
 
2
- export function observeDevicePixels(canvas, onSize) {
3
- const hasDPCB = (() => {
4
- try { return 'devicePixelContentBoxSize' in ResizeObserverEntry.prototype; } catch { return false; }
5
- })();
6
-
7
- // Compute current CSS and device-pixel sizes and call onSize(dpW, dpH, effectiveDpr)
8
- function fireNow() {
9
- let cssW, cssH, dpW, dpH;
10
- if (hasDPCB && canvas.__lastROEntry) {
11
- const e = canvas.__lastROEntry;
12
- dpW = e.devicePixelContentBoxSize[0].inlineSize;
13
- dpH = e.devicePixelContentBoxSize[0].blockSize;
14
- cssW = e.contentBoxSize[0].inlineSize;
15
- cssH = e.contentBoxSize[0].blockSize;
16
- } else {
17
- const rect = canvas.getBoundingClientRect();
18
- const dpr = window.devicePixelRatio || 1;
19
- cssW = rect.width; cssH = rect.height;
20
- dpW = Math.max(1, Math.round(cssW * dpr));
21
- dpH = Math.max(1, Math.round(cssH * dpr));
22
- }
23
- const dprEff = cssW > 0 ? (dpW / cssW) : (window.devicePixelRatio || 1);
24
- onSize(dpW >>> 0, dpH >>> 0, dprEff);
25
- }
26
-
27
- const ro = new ResizeObserver(entries => {
28
- // Save the last entry for precise device-pixel info.
29
- canvas.__lastROEntry = entries[0];
30
- fireNow();
31
- });
32
-
33
- try { ro.observe(canvas, { box: 'device-pixel-content-box' }); }
34
- catch { ro.observe(canvas); }
35
-
36
- // DPR changes (zoom / moving across monitors) don't always reflow. Listen explicitly.
37
- let mq = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
38
- const onMQ = () => {
39
- // Force a non-layout-janking "poke" to trigger RO in some browsers.
40
- const t = canvas.style.transform;
41
- canvas.style.transform = 'translateZ(0)'; canvas.style.transform = t;
42
- fireNow();
43
- // Re-arm listener for the new DPR.
44
- mq.removeEventListener?.('change', onMQ);
45
- mq = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
46
- mq.addEventListener?.('change', onMQ);
47
- };
48
- mq.addEventListener?.('change', onMQ);
49
- window.addEventListener('pageshow', onMQ);
50
-
51
- // Fire once now so we start sized correctly.
52
- Promise.resolve().then(fireNow);
53
-
54
- // Return disposer
55
- return () => {
56
- try { ro.disconnect(); } catch {}
57
- try { mq.removeEventListener?.('change', onMQ); } catch {}
58
- try { window.removeEventListener('pageshow', onMQ); } catch {}
59
- };
2
+ export function __ori_install_webgpu_error_tap(sink) {
3
+ try {
4
+ if (typeof GPUAdapter === 'undefined' || GPUAdapter.prototype.__ori_err_tap) return;
5
+ GPUAdapter.prototype.__ori_err_tap = true;
6
+ const orig = GPUAdapter.prototype.requestDevice;
7
+ GPUAdapter.prototype.requestDevice = async function(desc) {
8
+ const device = await orig.call(this, desc);
9
+ // Only the engine's own device (wgpu_base::APP_DEVICE_LABEL) may escalate to the
10
+ // fatal path. Foreign devices — the index.html [pre_shader] throwaway runs at
11
+ // DEFAULT limits and its replay errors are fail-open by contract — still log
12
+ // through the sink for ring/field visibility (07Aug26 first-contact crash: a
13
+ // throwaway-device validation error killed a healthy boot).
14
+ const isApp = !!(desc && desc.label === 'oriverse-app-device');
15
+ if (isApp) { try { window.__ori_gpu_device = device; } catch (_) {} }
16
+ try {
17
+ device.addEventListener('uncapturederror', function(event) {
18
+ const e = event.error;
19
+ const name = (e && e.constructor && e.constructor.name) || 'GPUError';
20
+ const msg = (e && e.message) || '';
21
+ try { sink(name, msg, isApp); } catch (_) {}
22
+ try { console.error(isApp ? '[raw_webgpu_uncaptured]' : '[raw_webgpu_uncaptured_foreign]', name, msg); } catch (_) {}
23
+ });
24
+ } catch (_) {}
25
+ return device;
26
+ };
27
+ } catch (_) {}
60
28
  }
@@ -1,28 +1,60 @@
1
1
 
2
- export function __ori_install_webgpu_error_tap(sink) {
3
- try {
4
- if (typeof GPUAdapter === 'undefined' || GPUAdapter.prototype.__ori_err_tap) return;
5
- GPUAdapter.prototype.__ori_err_tap = true;
6
- const orig = GPUAdapter.prototype.requestDevice;
7
- GPUAdapter.prototype.requestDevice = async function(desc) {
8
- const device = await orig.call(this, desc);
9
- // Only the engine's own device (wgpu_base::APP_DEVICE_LABEL) may escalate to the
10
- // fatal path. Foreign devices — the index.html [pre_shader] throwaway runs at
11
- // DEFAULT limits and its replay errors are fail-open by contract — still log
12
- // through the sink for ring/field visibility (07Aug26 first-contact crash: a
13
- // throwaway-device validation error killed a healthy boot).
14
- const isApp = !!(desc && desc.label === 'oriverse-app-device');
15
- if (isApp) { try { window.__ori_gpu_device = device; } catch (_) {} }
16
- try {
17
- device.addEventListener('uncapturederror', function(event) {
18
- const e = event.error;
19
- const name = (e && e.constructor && e.constructor.name) || 'GPUError';
20
- const msg = (e && e.message) || '';
21
- try { sink(name, msg, isApp); } catch (_) {}
22
- try { console.error(isApp ? '[raw_webgpu_uncaptured]' : '[raw_webgpu_uncaptured_foreign]', name, msg); } catch (_) {}
23
- });
24
- } catch (_) {}
25
- return device;
26
- };
27
- } catch (_) {}
2
+ export function observeDevicePixels(canvas, onSize) {
3
+ const hasDPCB = (() => {
4
+ try { return 'devicePixelContentBoxSize' in ResizeObserverEntry.prototype; } catch { return false; }
5
+ })();
6
+
7
+ // Compute current CSS and device-pixel sizes and call onSize(dpW, dpH, effectiveDpr)
8
+ function fireNow() {
9
+ let cssW, cssH, dpW, dpH;
10
+ if (hasDPCB && canvas.__lastROEntry) {
11
+ const e = canvas.__lastROEntry;
12
+ dpW = e.devicePixelContentBoxSize[0].inlineSize;
13
+ dpH = e.devicePixelContentBoxSize[0].blockSize;
14
+ cssW = e.contentBoxSize[0].inlineSize;
15
+ cssH = e.contentBoxSize[0].blockSize;
16
+ } else {
17
+ const rect = canvas.getBoundingClientRect();
18
+ const dpr = window.devicePixelRatio || 1;
19
+ cssW = rect.width; cssH = rect.height;
20
+ dpW = Math.max(1, Math.round(cssW * dpr));
21
+ dpH = Math.max(1, Math.round(cssH * dpr));
22
+ }
23
+ const dprEff = cssW > 0 ? (dpW / cssW) : (window.devicePixelRatio || 1);
24
+ onSize(dpW >>> 0, dpH >>> 0, dprEff);
25
+ }
26
+
27
+ const ro = new ResizeObserver(entries => {
28
+ // Save the last entry for precise device-pixel info.
29
+ canvas.__lastROEntry = entries[0];
30
+ fireNow();
31
+ });
32
+
33
+ try { ro.observe(canvas, { box: 'device-pixel-content-box' }); }
34
+ catch { ro.observe(canvas); }
35
+
36
+ // DPR changes (zoom / moving across monitors) don't always reflow. Listen explicitly.
37
+ let mq = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
38
+ const onMQ = () => {
39
+ // Force a non-layout-janking "poke" to trigger RO in some browsers.
40
+ const t = canvas.style.transform;
41
+ canvas.style.transform = 'translateZ(0)'; canvas.style.transform = t;
42
+ fireNow();
43
+ // Re-arm listener for the new DPR.
44
+ mq.removeEventListener?.('change', onMQ);
45
+ mq = matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);
46
+ mq.addEventListener?.('change', onMQ);
47
+ };
48
+ mq.addEventListener?.('change', onMQ);
49
+ window.addEventListener('pageshow', onMQ);
50
+
51
+ // Fire once now so we start sized correctly.
52
+ Promise.resolve().then(fireNow);
53
+
54
+ // Return disposer
55
+ return () => {
56
+ try { ro.disconnect(); } catch {}
57
+ try { mq.removeEventListener?.('change', onMQ); } catch {}
58
+ try { window.removeEventListener('pageshow', onMQ); } catch {}
59
+ };
28
60
  }
@@ -11,9 +11,9 @@ loader scripts or engine URLs.
11
11
 
12
12
  ## Before writing Weave, read the guide
13
13
 
14
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/GAME_AUTHORING.md — authoring patterns + 3 complete tested
14
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/GAME_AUTHORING.md — authoring patterns + 3 complete tested
15
15
  example games (coin collector, dodge blocks, target range) to pattern-match from.
16
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/weave_documentation.txt — full language + API reference.
16
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/weave_documentation.txt — full language + API reference.
17
17
 
18
18
  Key constraints: content must be fully procedural (primitive models `"cube"`,
19
19
  `"sphere"`, ..., `#mesh`, `#texture` — no market/network assets); units are cm with Z
@@ -11,9 +11,9 @@ loader scripts or engine URLs.
11
11
 
12
12
  ## Before writing Weave, read the guide
13
13
 
14
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/GAME_AUTHORING.md — authoring patterns + 3 complete tested
14
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/GAME_AUTHORING.md — authoring patterns + 3 complete tested
15
15
  example games (coin collector, dodge blocks, target range) to pattern-match from.
16
- - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/weave_documentation.txt — full language + API reference.
16
+ - https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/weave_documentation.txt — full language + API reference.
17
17
 
18
18
  Key constraints: content must be fully procedural (primitive models `"cube"`,
19
19
  `"sphere"`, ..., `#mesh`, `#texture` — no market/network assets); units are cm with Z
@@ -2,7 +2,7 @@
2
2
  This single small file IS the whole game: engine js/wasm come from the npm CDN, your game is
3
3
  the inline Weave source below. Serve from any static host (no COOP/COEP headers) or a
4
4
  plain `python -m http.server`; open in a WebGPU browser (Chrome/Edge).
5
- The engine version is pinned in the https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist urls below (injected from
5
+ The engine version is pinned in the https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist urls below (injected from
6
6
  npm_package/package.json at packaging time) — bump the @version with engine releases.
7
7
  Content must be fully procedural: primitives ("cube", "sphere", ...), #mesh, #texture,
8
8
  #particles. Market/network assets are unavailable and will hard-error. -->
@@ -44,18 +44,18 @@ object BlueCube {
44
44
 
45
45
  <!-- Shader prewarm: replays the build's pipeline manifest on a throwaway device during
46
46
  engine download dead-time (kills the first-view compile freeze). Fail-open. -->
47
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/prewarm.js"></script>
47
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/prewarm.js"></script>
48
48
  <!-- KTX2 texture transcoder sidecar (classic scripts; ktx_lib.wasm resolves relative to the script url). -->
49
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/ktx_lib.js"></script>
50
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/ktx_bridge.js"></script>
49
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/ktx_lib.js"></script>
50
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/ktx_bridge.js"></script>
51
51
  <script type="module">
52
- import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/oriverse_wgpu.js";
52
+ import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/oriverse_wgpu.js";
53
53
  const setBoot = (t) => { const b = document.getElementById('oriverse-status-banner'); if (b) b.textContent = t; };
54
54
  (async () => {
55
55
  try {
56
56
  setBoot('starting engine…');
57
57
  // Artifact build: wasm-bindgen owns a private (non-shared) memory; no COOP/COEP needed.
58
- await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/oriverse_wgpu_bg.wasm" });
58
+ await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/oriverse_wgpu_bg.wasm" });
59
59
  } catch (e) {
60
60
  const msg = (e && e.stack) ? e.stack : String(e);
61
61
  setBoot('WASM init failed:\n' + msg);
@@ -2,7 +2,7 @@
2
2
  This single small file IS the whole game: engine js/wasm come from the npm CDN, your game is
3
3
  the inline Weave source below. Serve from any static host (no COOP/COEP headers) or a
4
4
  plain `python -m http.server`; open in a WebGPU browser (Chrome/Edge).
5
- The engine version is pinned in the https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist urls below (injected from
5
+ The engine version is pinned in the https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist urls below (injected from
6
6
  npm_package/package.json at packaging time) — bump the @version with engine releases.
7
7
  Content must be fully procedural: primitives ("cube", "sphere", ...), #mesh, #texture,
8
8
  #particles. Market/network assets are unavailable and will hard-error. -->
@@ -44,18 +44,18 @@ object BlueCube {
44
44
 
45
45
  <!-- Shader prewarm: replays the build's pipeline manifest on a throwaway device during
46
46
  engine download dead-time (kills the first-view compile freeze). Fail-open. -->
47
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/prewarm.js"></script>
47
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/prewarm.js"></script>
48
48
  <!-- KTX2 texture transcoder sidecar (classic scripts; ktx_lib.wasm resolves relative to the script url). -->
49
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/ktx_lib.js"></script>
50
- <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/ktx_bridge.js"></script>
49
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/ktx_lib.js"></script>
50
+ <script src="https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/ktx_bridge.js"></script>
51
51
  <script type="module">
52
- import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/oriverse_wgpu.js";
52
+ import initOriverse from "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/oriverse_wgpu.js";
53
53
  const setBoot = (t) => { const b = document.getElementById('oriverse-status-banner'); if (b) b.textContent = t; };
54
54
  (async () => {
55
55
  try {
56
56
  setBoot('starting engine…');
57
57
  // Artifact build: wasm-bindgen owns a private (non-shared) memory; no COOP/COEP needed.
58
- await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.5/dist/oriverse_wgpu_bg.wasm" });
58
+ await initOriverse({ module_or_path: "https://cdn.jsdelivr.net/npm/oriverse-engine@0.1.6/dist/oriverse_wgpu_bg.wasm" });
59
59
  } catch (e) {
60
60
  const msg = (e && e.stack) ? e.stack : String(e);
61
61
  setBoot('WASM init failed:\n' + msg);
@@ -1,7 +1,7 @@
1
1
 
2
2
  >>>>>>>>>>>>
3
3
  Weave World Description Language Documentation
4
- Version:17Aug26-23:47-10536
4
+ Version:19Aug26-09:37-59549
5
5
  >>>>>>>>>>>>
6
6
 
7
7
  <weave_language>
@@ -49,11 +49,11 @@ Weave World Description Language Documentation
49
49
 
50
50
  <asset_word_library>
51
51
  - Established library words per alias namespace, plus `flipbook.<word>` particle textures. PREFER an existing word when one matches the need — it binds instantly with no generation wait or spend. Other words still work: misses show a placeholder and auto-generate in the background (flipbook words are minted deliberately via the flipbook_gen tool instead).
52
- model: abandoned_car alchemist alien_bug alien_hive alley_monster amphora anchor aqua_penguin assembler athlete auntie axe ball banana banana_peel bandage bandit barn barracks barrel barrel_2 base_core baseball_bat battle_car bed bee beehive belt bench bench_2 berry_bush billboard blacksmith boar boat boat_parts bomb bone book boomerang boulder boulder_2 box bread_slice bread_slice_2 brick bridge buddhist_temple bush bush_2 bush_a butter cabbage camp_tent camp_tent_2 campfire canvas_tent cargo_truck carrot cart carved_owl_statue cash_bundle cat cat_blacksmith cat_cart_keeper cat_chef cat_engineer cat_factory_worker cat_merchant cat_rickshaw chair chair_2 checkpoint cheese cheese_2 chest chicken chicken_coop chime_lantern chunk_floor circular_saw clay_buddy cleric cloud clue_item clue_item_2 coin command_center cottage cow crate crate_2 crop crop_plot crossbow crystal crystal_cluster crystal_shard cup customer customer_2 cyclops dog dragon drill druid dual_blades durian elegant_woman ember_fox enemy_spawn energy_drink evidence_item exp_gem fan farmhouse farmhouse_floor fence finish_arch fish fish_2 fishing_rod flag flag_2 flamethrower flashlight flat_rock_slab flower food_crate forager_hut foundation fountain fox fridge frog fuel_pump furniture_bookshelf furniture_table furniture_table_2 furniture_wardrobe gargoyle gate ghoul ghoul_2 gilded_hourglass glimmer_crate goblin golden_beak_shaman golden_coin grandma grandpa grape grape_2 grass_rock_cliff great_sword green_maple_tree greenhouse grocery_box ground_item guard guard_tower hammer hamster harpy hay_bale hay_bale_2 heal_pickup held_item horned_beast horse hospital_assistant house hungry_mouse hunter_bow hunting_hammer hut ice_shard idle_character invoker_mage iron_node item_crate jam_jar kid killer knife knight ladder lamp lamp_2 lane lantern lantern_2 light_pole log_pile log_pile_2 lumber_camp machine_gun mage magnet market_stall mecha_chameleon medic medicine_crate miner minotaur moss_boar mushroom night_watchman ninja_runner nurse oak_tree officer owl paddle palm_tree patient pebble person pickaxe pickup_orb pig pine_tree pistol plate plate_2 player_char police_car pond poop projectile pumpkin rabbit rabbit_runner ragdoll_fighter rain_mill rain_totem ranger requiem_gun resident revolver rifle road_sign robot_runner rock rocket rocket_launcher rocksample rope rope_bundle runic_obelisk sea_mine sedan sentinel_drone shark shield shipping_container shop_counter shop_floor shopkeeper shotgun shuriken signpost signpost_2 signpost_3 skull slime small_rock smg smoke_bomb snail sniper_rifle soil_tile soldier spider sports_car stair star star_core statue stilt_house stone_pile stonecutter_lodge storehouse storm_hearth storm_shrine surgeon survivor sword table tank_hull tent timber_bundle toaster toaster_2 tool_chest torch torch_2 tower training_dummy training_dummy_2 travel_pack tree tribal_cat_elder tribal_cat_villager trophy trunk turret villager volleyball wagon wall warehouse watchtower water_barrel water_well watercan watering_can weapon_rack weapon_rack_2 weapon_shop_sign well wheat wheelbarrow white_tribal_cat_player wizard_staff wood_fence wood_fence_2 wooden_stool worker_robot zebrite_totem zombie
53
- image: alien_bug angled_player_banner axe_icon bean_icon bean_seeds_icon berries_icon book_icon boss_skull bullet_icon cabbage_icon campfire_icon carrot_icon clock_icon coin_icon crop_icon crossed_swords crossed_swords_2 cute_paw_cursor dash_icon deck_icon deck_icon_2 energy_icon fish_icon fishing_rod_icon flint_icon fluffy_sheep_party_portrait forest_ancient_tree_spirit_wide_splash forest_ent gold_coin_icon gold_icon gold_icon_2 hand_icon health_icon health_icon_2 hoe_icon horse_party_portrait hunger_icon inverse_trapezoid_top_bar iron_ore_icon knight_helmet loading_splash log_icon monster_head moon_icon orange_cat_party_portrait panel_frame parsnip_icon parsnip_seeds_icon pickaxe_icon poison_icon potato_icon potato_seeds_icon purple_hive_mind_insect_warrior_wide_splash qa_probe_gem radish_icon revolver_icon rifle_icon robot_face robot_vending_mech_champion_wide_splash round_table_knight_wide_splash ruby_gem_icon sanity_icon scythe_icon seed_bag_icon seed_icon skill_dagger_icon skill_fireaura_icon skill_hammer_icon skill_shield_icon skill_stealth_icon snowflake_spin stone_icon sun_icon tft_alien_portrait_black tft_boss_skull_black tft_clock_black tft_crossed_swords_black tft_forest_portrait_black tft_monster_head_black tft_robot_portrait_black tft_yokai_portrait_black torch_icon twigs_icon undead_death_knight_wide_splash undead_skull water_drop_icon watering_can_icon white_chicken_party_portrait wood_icon yokai_mask yokai_samurai_oni_wide_splash
54
- sound: ambient_horror apartment_ambience arena_warning baby_raptor_step banana_boomerang baton_hit baton_swing bear_shotgun bite blast body_thump body_thump_2 boost boost_burst bow_hit branch_snap brass_gong cannon_blast cannon_fire car_horn card_draw card_play cash_pickup cash_pickup_2 character_select checkpoint chomp coin_pickup coin_pickup_2 corn_burst countdown countdown_beep countdown_tick crab_blaster crab_hit crab_skill crash_burst critical_impact critical_impact_2 crow_call crowd_cheer dash_burst dodge dodge_dust dodge_dust_2 dog_bark dog_pistol door_close door_open door_open_2 door_unlock dual_hit earthquake_rumble earthquake_rumble_2 electric_burst eliminated energy_empty engine_acceleration engine_idle exit_unlock explosion finish finish_chime fish_caught fishing_cast fishing_reel flamethrower_burst food_complete food_complete_2 front_weapon_activation game_check game_end game_start gameplay_music gameplay_music_2 glimmer_bell gold_pickup golf_cup golf_swing grenade_launcher hammer_hit hammer_hit_2 heartbeat hit hook_impact hook_throw item_pickup item_pickup_2 laser_shot laser_shot_2 lasgun_shot level_up level_up_2 level_up_3 level_up_4 level_up_5 line_clear machine_gun machine_gun_reload machine_gun_shot magic_blast medkit_pickup medkit_use menu_music metal_hit metal_hit_2 mine missile_launch mole_sniper monster_attack monster_attack_2 monster_roar monster_roar_2 owl_hoot pickup piece_capture piece_lock piece_move pistol_reload pistol_shot place player_hit player_hurt player_hurt_2 police_siren punch rain_ambience raptor_step ready_up rear_ability_activation repair resource_pickup respawn revive rifle_shot ring_out ringout robot_beam robot_blast robot_charge robot_laser rock_riff rocket_shot rot_loop royal_capture score_boost shield shield_block ship_destroyed shotgun_shot slap slap_hit survivor_step sword_clang sword_clang_2 sword_hit sword_hit_2 tire_skid toast_pop toy_pop trex_step turn_alert ui_click ui_click_2 ui_click_3 ui_click_4 ui_click_5 vehicle_damaged vehicle_destroyed victory victory_fanfare victory_fanfare_2 victory_sting victory_sting_2 vortex_hum wall_deploy wall_pickup warning_siren water_splash wave_crash weapon_switch whistle wind_gust wind_gust_2 wolf_howl wood_impact zebrite_chime zombie_attack zombie_growl zombie_roar
55
- effect: acid_splash arrow_hit bite_splash blood_hit blood_splash boost_burst cannon_blast capture_burst cartoon_impact cash_sparkle cash_sparkle_2 coin_sparkle collapse_dust crash_burst critical_smoke damage_smoke damage_sparks damage_sparks_2 damage_sparks_3 dash_trail dash_trail_2 dismember dodge_dust dust_puff dust_puff_2 dust_puff_3 electric_burst eliminated_burst explosion explosion_burst finish_confetti fire_burst fireball fireball_burst gold_rush golf_cup golf_hit grenade_blast hammer_impact heal_glow healing_glow hit_burst hook_hit impact_flash_critical impact_flash_small impact_shockwave impact_shockwave_2 impact_shockwave_3 impact_sparks ink_cloud item_glow magic_blast magic_sparkle magic_vortex metal_hit meteor_fire mine_burst muzzle_flash objective_pickup player_hit rage_burst rainbow_burst rainbow_burst_2 rapid_slash respawn_burst revive_burst revive_glow rickshaw_burn ringout_explosion rocket_explosion rot_pulse rune_pickup shield_burst shield_pop slap_hit slash_burst slash_burst_2 slash_burst_3 smoke_puff smoke_puff_2 sonar_pulse sparkle_burst sparkle_burst_2 sparkle_burst_3 sparkler speed_burst stun_stars stun_stars_2 stun_stars_3 sword_slash tank_burning tank_explosion terrain_blast top_collision_impact water_burst water_splash wind_leaves
56
- flipbook: blue_flame bubble_float dust_puff electric_spark ember_glow explosion_burst gray_smoke heart_pulse leaf_flutter magic_sparkle orange_flame poison_cloud smoke_puff snowflake_spin star_twinkle water_splash white_smoke
52
+ model: abandoned_car alchemist alien_bug alien_hive alley_monster amphora anchor aqua_penguin assembler athlete aunt auntie axe ball banana banana_peel bandage bandit barn barracks barrel barrel_2 base_core baseball_bat battle_car bed bee beehive belt bench bench_2 berry_bush billboard blacksmith boar boat boat_parts bomb bone book boomerang boulder boulder_2 box bread_slice bread_slice_2 brick bridge buddhist_temple bush bush_2 bush_a butter cabbage camp_tent camp_tent_2 campfire canvas_tent cargo_truck carrot cart carved_owl_statue cash_bundle cat cat_blacksmith cat_cart_keeper cat_chef cat_engineer cat_factory_worker cat_merchant cat_rickshaw chair chair_2 checkpoint cheese cheese_2 chest chicken chicken_coop chime_lantern chunk_floor circular_saw clay_buddy cleric cloud clue_item clue_item_2 coin command_center cottage cousin cow crate crate_2 crop crop_plot crossbow crystal crystal_cluster crystal_shard cup customer customer_2 cutting_board cutting_board_2 cyclops dog dragon drill druid dual_blades durian elegant_woman ember_fox enemy_spawn energy_drink evidence_item exp_gem fan farmhouse farmhouse_floor fence finish_arch fish fish_2 fishing_rod flag flag_2 flamethrower flashlight flat_rock_slab flower food_crate forager_hut foundation fountain fox fridge fridge_2 frog frying_pan frying_pan_2 fuel_pump furniture_bookshelf furniture_table furniture_table_2 furniture_wardrobe gargoyle gate ghoul ghoul_2 gilded_hourglass glimmer_crate goblin godfather golden_beak_shaman golden_coin grandma grandpa grape grape_2 grass_rock grass_rock_cliff great_sword green_maple_tree greenhouse grocery_box ground_item guard guard_tower hammer hamster harpy hay_bale hay_bale_2 heal_pickup held_item horned_beast horse hospital_assistant house hungry_mouse hunter_bow hunting_hammer hut ice_shard idle_character invoker_mage iron_node item_crate jam_jar kid killer knife knight ladder lamp lamp_2 lane lantern lantern_2 light_pole log_pile log_pile_2 lumber_camp machine_gun mage magnet market_stall mecha_chameleon medic medicine_crate miner minotaur moss_boar mushroom nephew niece night_watchman ninja_runner npc nurse oak_tree officer owl paddle palm_tree patient pearl_bush pebble person pickaxe pickup_orb pig pine_tree pistol plate plate_2 player_char police_car pond poop projectile pumpkin rabbit rabbit_runner ragdoll_fighter rain_mill rain_totem ranger requiem_gun resident revolver rifle road_sign robot_grunt robot_runner rock rocket rocket_launcher rocksample rope rope_bundle runic_obelisk sandbag sea_mine sedan sentinel_drone shark shield shipping_container shop_counter shop_floor shopkeeper shotgun shuriken signpost signpost_2 signpost_3 skull slime small_rock smg smoke_bomb snail sniper_rifle soil_tile soldier spider sports_car squid stair star star_core statue stepmother stilt_house stone_pile stonecutter_lodge storehouse storm_hearth storm_shrine surgeon survivor sword table tank_hull tent timber_bundle toaster toaster_2 tool_chest torch torch_2 tower training_dummy training_dummy_2 travel_pack tree tribal_cat_elder tribal_cat_villager trophy trunk turret uncle villager volleyball wagon wall warehouse watchtower water_barrel water_well watercan watering_can weapon_rack weapon_rack_2 weapon_shop_sign well wheat wheelbarrow white_tribal_cat_player wizard_staff wood_fence wood_fence_2 wooden_stool worker_robot zebrite_totem zombie
53
+ image: airtank_icon alien_bug angled_player_banner axe_icon bandage_icon bean_icon bean_seeds_icon berries_icon book_icon boss_skull bread_icon bullet_icon cabbage_icon campfire_icon carrot_icon cheese_icon clock_icon coin_icon crop_icon crossed_swords crossed_swords_2 crosshair cute_paw_cursor dash_icon deck_icon deck_icon_2 energy_icon fish_icon fishing_rod_icon flamethrower_icon flint_icon fluffy_sheep_party_portrait forest_ancient_tree_spirit_wide_splash forest_ent gold_coin_icon gold_icon gold_icon_2 hand_icon health_icon health_icon_2 hoe_icon horse_party_portrait hunger_icon inverse_trapezoid_top_bar iron_ore_icon knight_helmet loading_splash log_icon machine_gun_icon mackerel_icon monster_head moon_icon orange_cat_party_portrait panel_frame parsnip_icon parsnip_seeds_icon pickaxe_icon poison_icon potato_icon potato_seeds_icon purple_hive_mind_insect_warrior_wide_splash qa_probe_gem radish_icon revolver_icon rifle_icon robot_face robot_vending_mech_champion_wide_splash rocket_launcher_icon round_table_knight_wide_splash ruby_gem_icon salvage_icon sanity_icon sardine_icon scythe_icon seed_bag_icon seed_icon shotgun_icon skill_dagger_icon skill_fireaura_icon skill_hammer_icon skill_shield_icon skill_stealth_icon snapper_icon snowflake_spin squid_icon steak_icon stone_icon sun_icon tft_alien_portrait_black tft_boss_skull_black tft_clock_black tft_crossed_swords_black tft_forest_portrait_black tft_monster_head_black tft_robot_portrait_black tft_yokai_portrait_black torch_icon tuna_icon twigs_icon undead_death_knight_wide_splash undead_skull water_drop_icon watering_can_icon white_chicken_party_portrait wood_icon yokai_mask yokai_samurai_oni_wide_splash
54
+ sound: ambient_horror apartment_ambience arena_warning baby_raptor_step banana_boomerang baton_hit baton_swing bear_shotgun bite blast body_thump body_thump_2 boost boost_burst bow_hit branch_snap brass_gong cannon_blast cannon_fire car_horn card_draw card_play cash_pickup cash_pickup_2 character_select checkpoint chomp coin_pickup coin_pickup_2 corn_burst countdown countdown_beep countdown_tick crab_blaster crab_hit crab_skill crash crash_burst critical_impact critical_impact_2 croc_machinegun crow_call crowd_cheer crystal_pickup dash_burst dodge dodge_dust dodge_dust_2 dog_bark dog_pistol door_close door_open door_open_2 door_unlock dual_hit earthquake_rumble earthquake_rumble_2 electric_burst electric_shock eliminated energy_empty engine_acceleration engine_idle exit_unlock explosion finish finish_chime fish_caught fishing_cast fishing_reel flamethrower_burst food_complete food_complete_2 front_weapon_activation game_check game_end game_start gameplay_music gameplay_music_2 glimmer_bell gold_pickup golf_cup golf_swing grenade_launcher gunshot hammer_hit hammer_hit_2 heartbeat hit hook_impact hook_impact_2 hook_throw hook_throw_2 item_pickup item_pickup_2 laser_shot laser_shot_2 lasgun_shot level_up level_up_2 level_up_3 level_up_4 level_up_5 line_clear machine_gun machine_gun_reload machine_gun_shot magic_blast medkit_pickup medkit_use menu_music metal_hit metal_hit_2 mine missile_launch mole_sniper monster_attack monster_attack_2 monster_roar monster_roar_2 owl_hoot pickup piece_capture piece_lock piece_move pistol_reload pistol_shot place player_hit player_hurt player_hurt_2 police_siren punch rain_ambience raptor_step ready_up rear_ability_activation repair resource_pickup respawn revive rifle_reload rifle_shot rifle_shot_2 ring_out ringout robot_beam robot_blast robot_charge robot_laser rock_riff rocket_shot rot_loop royal_capture score_boost shield shield_block ship_destroyed shotgun_shot signal_ping siren slab_turn slap slap_hit stone_rotate survivor_step sword_clang sword_clang_2 sword_hit sword_hit_2 tire_skid toast_pop toy_pop trex_step turn_alert ui_click ui_click_2 ui_click_3 ui_click_4 ui_click_5 vehicle_damaged vehicle_destroyed victory victory_fanfare victory_fanfare_2 victory_sting victory_sting_2 vortex_hum wall_deploy wall_pickup warning_siren water_splash wave_crash weapon_switch whistle wind_gust wind_gust_2 wolf_howl wood_impact zebrite_chime zombie_attack zombie_growl zombie_roar
55
+ effect: acid_splash arrow_hit bite_splash blood_hit blood_splash boost_burst cannon_blast capture_burst cartoon_impact cash_sparkle cash_sparkle_2 coin_sparkle collapse_dust crash_burst critical_smoke damage_smoke damage_sparks damage_sparks_2 damage_sparks_3 dash_trail dash_trail_2 dismember dodge_dust dust_puff dust_puff_2 dust_puff_3 electric_burst eliminated_burst explosion explosion_burst finish_confetti fire_burst fireball fireball_burst gold_rush golf_cup golf_hit grenade_blast hammer_impact heal_glow healing_glow hit_burst hook_hit hook_hit_2 impact_flash_critical impact_flash_small impact_shockwave impact_shockwave_2 impact_shockwave_3 impact_sparks ink_cloud item_glow magic_blast magic_sparkle magic_vortex metal_hit meteor_fire mine_burst muzzle_flash objective_pickup player_hit poison_cloud rage_burst rainbow_burst rainbow_burst_2 rapid_slash respawn_burst revive_burst revive_glow rickshaw_burn ringout_explosion rocket_explosion rot_pulse rune_pickup shield_burst shield_pop slap_hit slash_burst slash_burst_2 slash_burst_3 smoke_puff smoke_puff_2 sonar_pulse sparkle_burst sparkle_burst_2 sparkle_burst_3 sparkler speed_burst stun_stars stun_stars_2 stun_stars_3 sword_slash tank_burning tank_explosion terrain_blast top_collision_impact water_burst water_splash wind_leaves
56
+ flipbook: blood_mist blue_flame bubble_float dust_puff electric_spark ember_glow explosion_burst gray_smoke heart_pulse hot_spark_burst leaf_flutter magic_sparkle muzzle_flash_star orange_flame poison_cloud smoke_puff snowflake_spin star_twinkle tracer_streak water_splash white_smoke
57
57
  </asset_word_library>
58
58
 
59
59
  <texture_dsl_usage>
@@ -95,9 +95,9 @@ knob SetGrassHeightMaxCm = 45 range 0..120
95
95
  - `#particles Name ... #end` defines local effect asset `effect.Name`; `#particles effect.Name` also works, but prefer short `Name`. Body is a JSON array; each object is one emitter component.
96
96
  - Every local component needs a top-level `"particleTextureName"`: `"flipbook.<word>"` for an animated sprite-sheet atlas from the shared flipbook library (best for fire/smoke/magic; lowercase subject words like `flipbook.orange_flame`; misses render a glow placeholder until the word is minted), `"texture.TextureName.color"` with a matching `#texture TextureName`, or `"image.<User>.<Name>"` referencing a real ori image asset you actually know (never invent one). Use `texture.*.color` (`baked.*` addresses material-slot maps, not particle textures; `.normal`/`.orm` are not particle flipbooks).
97
97
  - Spawn with scene `effect Spark { effect = "effect.Name" }` - or, for prop-attached fire/smoke, prefer a `particles("<word>", at: ...)` socket inside the `#mesh` (see <mesh_dsl_usage>). From code pass the ASSET as a static import: `SpawnEffectWithAutoDestroy(Ori.effect.Name, pos)` (returns the spawned object — `.SetSizeUniform(n)` to size it), or `Ori.alias.effect.<word>` for aliases. A `Scene.<object>` reference is not an effect asset (W2C0405).
98
- - Sizing: declare `"boundingBoxSize": [x, y, z]` (meters, Z-up; per-component, max wins) as the effect's authored real-world footprint, and author emitters (`radius`, `sizeSequence`, speeds) at that real scale. The object's `size` (cm, like any object) then scales the effect by `size / boundingBoxSize` — e.g. a campfire authored with `boundingBoxSize [1, 1, 1.5]` placed with `size = 100` renders at its authored 1m footprint. Without `boundingBoxSize` the base is a legacy 10cm box, so `size = 100` blows the effect up 10x — always declare it. Size effects with `size`, not `scale`. The effect's authored local origin renders at the object's `position` (no anchoring): author ground effects with their base at z=0 and bursts centered on the origin.
98
+ - Sizing: declare `"boundingBoxSize": [x, y, z]` (meters, Z-up; per-component, max wins) as the effect's authored real-world footprint, and author emitters (`radius`, `sizeSequence`, speeds) at that real scale. The object's `size` (cm, like any object) then scales the effect by `size / boundingBoxSize` — e.g. a campfire authored with `boundingBoxSize [1, 1, 1.5]` placed with `size = 100` renders at its authored 1m footprint. Without `boundingBoxSize` the base is a legacy 10cm box, so `size = 100` blows the effect up 10x — always declare it. Size effects with `size`, not `scale`. Author emitters freely around your origin; when the content is not origin-centered, add `"boundingBoxCenter": [x, y, z]` (meters; where the box CENTER sits relative to that origin; default [0,0,0]) so the box covers the content — e.g. a ground flame rising 0..1.5m declares [0, 0, 0.75]. Placement matches meshes: the object's `position` rests the declared box's bottom on the point (`transform_pivot = center` centers it); code spawns (`SpawnEffect*`) put the box CENTER at the given position.
99
99
  - JSON values: scalars are numbers, vecs are `[x,y]`/`[x,y,z]`, curves are up to 8 `[[life01, value], ...]`; `colorSequence` uses `[[life01, [r,g,b]], ...]`. Prefer numeric `0/1` for booleans.
100
- - CPU/import keys: `_particleName_` ignored label; `particleCount` live-particle capacity override, not spawn count; `emitterPosition`/`emitterRotation` component-local offset/XYZ degrees; `boundingBoxSize` authored footprint in meters (see sizing bullet above).
100
+ - CPU/import keys: `_particleName_` ignored label; `particleCount` live-particle capacity override, not spawn count; `emitterPosition`/`emitterRotation` component-local offset/XYZ degrees; `boundingBoxSize`/`boundingBoxCenter` authored footprint + box placement in meters (see sizing bullet above).
101
101
  - Flipbook keys: `framesX`, `framesY`, `totalFrames`, `flipbookAnimationSpeed`, `flipbookStartRandom`. For `flipbook.<word>` refs these auto-fill from the library at load; set them only to override.
102
102
  - Spawn keys: `spawnStartDelay`, `spawnRate` per sec, `isLooping`, `duration` seconds (`0` means no shader stop; use finite duration for one-shots), `burstParticleCount` (fires at t=0, then every `burstInterval` seconds while emitting; `burstInterval` unset = the t=0 burst only).
103
103
  - Shape keys: `emitterShapeType` = `"sphere"|"box"|"cylinder"|"disc"` or `0..3`; `shapeStyle` = `"volume"|"surface"`; `shapeInOut` = `"in"|"out"|"both"` (omitted defaults inward); `radius`, `cylinderHeight`, `boxSize`, `shapePartial`, `lifeTimeRange`, `speedRange`, `spreadAngle` `[yaw,pitch]` degrees, `lockedToEmitter` (`0` default = particles keep world position once spawned - trails stay behind a moving emitter; `1` = particles ride it, e.g. torch flame). Box surface emits from +Z face; cylinder height is local X; disc uses `cylinderHeight` as thickness. Emission direction: each shape emits along its own radial direction; `shapeInOut` omitted = INWARD, `"out"` flips outward, `"both"` = 50/50. `spreadAngle [yaw, pitch]` then jitters that direction by uniform +/-yaw degrees about the emitter's local Y axis and +/-pitch degrees about its X axis (fixed local axes, not a cone around the direction).
@@ -233,9 +233,15 @@ event OnSpawned {
233
233
  - `TileGridCreate(TilesX, TilesY, DefaultWalkable)` returns an opaque compact List. `TileGridCreate(TilesX, TilesY, DefaultWalkable, EdgeBlockingEnabled)` enables optional edge-wall storage when the Bool is true. Do not call generic mutating List functions (`Add`, `Set`, `Resize`, `Clear`, `Pop`, etc.) on it; use TileGrid helpers. Padded grid capacity is capped at 1,048,576 tiles.
234
234
  - Edge-blocking grids can use `TileGridSetEdgeBlocked(Grid, X1, Y1, X2, Y2, Blocked)` for cardinal-adjacent tile pairs. Compact grids reject SetEdgeBlocked; `TileGridIsEdgeBlocked(...)` returns false for valid compact-grid edges. Edge-enabled diagonal pathfinding does not cross blocked cardinal edges, and a diagonal step requires all four cardinal edges around the crossed 2x2 tile corner to be clear.
235
235
  - `BuildTileBlockedGridFromTag(OriginTile, TilesX, TilesY, BlockerTag)` creates a walkable grid and blocks tiles whose world-center point overlaps an object with the tag. Build/rebuild grids when blockers change, not every frame.
236
- - `TileGridSetWalkableFromStaticColliders(Grid, OriginTile, ZMin, ZMax)` re-rasterizes an existing non-layered grid from the static world colliders: every tile resets, and a tile blocks when any static collider's world AABB overlaps its rect within the [ZMin, ZMax] cm band (rotated colliders use their conservative AABB; colliders covering the whole grid footprint - floors, world bounds - are ignored). Keep ZMin above the walk surface (e.g. 20) so partial floor slabs don't block their tiles. Call once after building/editing static geometry - walls and crates then block FindPath without hand-authored SetWalkable loops; re-call after edits.
236
+ - `TileGridSetWalkableFromStaticColliders(Grid, OriginTile, ZMin, ZMax)` re-rasterizes an existing grid from the static colliders: every tile resets, and a tile blocks when any static collider's world AABB overlaps its rect within the [ZMin, ZMax] cm band (rotated colliders use their conservative AABB; colliders covering the whole grid footprint - floors, world bounds - are ignored). Static world objects, static PhysicsObjects, and tilemap solid cells all block; keep ZMin above the walk surface AND above any solid ground tilemap's top (e.g. 20) so floor slabs don't block their tiles. Layered grids rasterize per layer ([ZMin, ZMax] relative to each layer plane). It RESETS every tile - call it BEFORE the FromTerrain pair; for runtime placements stamp instead of re-rastering.
237
+ - `TileGridSetWalkableFromTerrain(Grid, OriginTile, MaxSlope01)` marks tiles UNWALKABLE where the topmost terrain at the tile center is steeper than MaxSlope01 (slope01 = g/(1+g); 0.5 = 45deg; walkers wedge above ~0.5, so 0.4-0.45 is a safe unit ceiling) or absent. Subtractive only - never re-opens tiles.
238
+ - `TileGridSetEdgesFromTerrain(Grid, OriginTile, MaxStepCm)` (edge-blocking grids) blocks the edge between cardinal neighbors whose terrain height difference exceeds MaxStepCm: cliffs become impassable EDGES while rim tiles stay standable; graded ramps (small per-tile rise) stay open. Tile centers sample mid-cell, so a hard TierCm cliff reads as two TierCm/2 steps - MaxStepCm = TierCm/3 separates cliff jumps from ramp rise; size ramps >= 2 tiles of run per tier step so their slope stays below the ~45deg walk limit.
239
+ - Cliff-map load order (manager OnSpawned): collider raster -> FromTerrain -> EdgesFromTerrain -> BakeCrowdObstacles -> IsReachable asserts. Then placements only stamp.
240
+ - `TileGridStampRect(Grid, X1, Y1, X2, Y2, Blocked)` / `TileGridStampObject(Grid, OriginTile, Obj, Blocked)` stamp footprints incrementally (rect corners clamp to the grid; spawn: true, despawn: false re-opens the stamped rect - overlapping footprints must re-stamp their survivors) - cheaper than re-rastering after each placement.
241
+ - `TileGridIsReachable(Grid, X1, Y1, X2, Y2)` (layered: 7-arg form) = "does an 8-dir path exist" without building one - assert connectivity (spawn points, ramps) in a manager OnSpawned with `RaiseWorldError` so broken maps fail at load, not mid-game; also check before wall placements that could seal a region.
242
+ - `TileGridBakeCrowdObstacles(Grid, OriginTile)` bakes avoidance line obstacles from the grid's blocked edges and unwalkable borders so crowd units are never pushed off cliffs or into tilemap walls avoidance cannot see. It is a snapshot: re-bake after runtime walkability edits (opened gates, razed bridges); placed buildings already repel via their own colliders, no re-bake needed.
237
243
  - One grid, many movers (RTS): build the grid ONCE on a manager scene object (`var Grid: List<Number>` field; `TileGridCreate` + `TileGridSetWalkableFromStaticColliders` in its OnSpawned) and let every unit reach it through a scene cast - `TileGridFindPath8((Scene.Manager as HQ).Grid, ...)`. Cross-class field access works for List fields; never rebuild the grid per unit.
238
- - `TileGridFindPath4/8(Grid, StartX, StartY, GoalX, GoalY[, GoalToleranceTiles])` returns a reversed path List: Goal ... Start. Empty means blocked start/goal, out of bounds, or no path. GoalToleranceTiles (0-16) accepts the cheapest walkable tile within that ring of the goal - set it when ordering onto colliders (buildings, resource nodes); empty then means the ring itself is unreachable. Compact TileGridFindPath8 preserves corner cutting through unwalkable side tiles; edge-enabled grids still require crossed cardinal edges to be clear.
244
+ - `TileGridFindPath4/8(Grid, StartX, StartY, GoalX, GoalY[, GoalToleranceTiles])` returns a reversed path List: Goal ... Start. Empty means blocked start/goal, out of bounds, or no path (a TRUE verdict - the search budget covers the whole grid, so a no-path query floods the goal's component; on big maps gate mass orders with one TileGridIsReachable, share flow fields, and cache no-path results). GoalToleranceTiles (0-16) accepts the cheapest walkable tile within that ring of the goal - set it when ordering onto colliders (buildings, resource nodes); empty then means the ring itself is unreachable. Compact TileGridFindPath8 preserves corner cutting through unwalkable side tiles; edge-enabled grids still require crossed cardinal edges to be clear.
239
245
  - `TileGridHasLineOfSight(Grid, StartX, StartY, GoalX, GoalY)` (layered: 7-arg form, same Z only) returns true when the straight tile-center corridor is fully walkable (crossed edges clear on edge grids; exact corner crossings also need both side tiles). Chasing a moving target: LOS true -> `MoveTowardContinuous` at the live position, false -> FindPath + follow.
240
246
  - `TileGridSmoothPath(Grid, Path)` returns a new reversed path with waypoints dropped wherever the straight corridor between the survivors is clear (string pulling; never smooths across a layer change). Walkers zigzagging tile centers -> smooth once after FindPath, before following.
241
247
  - To follow a path, call `MoveAlongTilePathContinuous(Path, Grid, OriginTile, Speed[, ArriveWithinCm[, OrientTurnRate]])` each frame: one call steps the mover, pops reached waypoints, and returns true once the path is consumed (false for an already-empty path). ArriveWithinCm (default 1) completes the FINAL waypoint within that distance - set it when the goal can be occupied (resource node, building) so contact does not stall arrival. OrientTurnRate faces the mover along its travel (deg/s; 0 = instant) - RTS vehicles read wrong without it. Prefer it over manual MoveToward + Pop loops; it is one sim call per mover per frame.
@@ -467,6 +473,7 @@ event OnSpawned {
467
473
  - PhysicsObject: like WorldObject, but starts as dynamic/CanCollide physics
468
474
  - CrowdObject: Useful as creatures/walkable-NPCs. like WorldObject, but starts with crowd collision avoidance
469
475
  - it steers around other crowd agents and obstacles it approaches at an angle; a blocker dead-ahead STOPS it (avoidance is anti-collision, not pathfinding) - route around structures with tile pathfinding
476
+ - RTS units: route with `MoveAlongTilePathContinuous` on a `crowd` receiver - the path sets the preferred velocity and avoidance shapes it every frame (global path + local separation). The zero-call `StartTilePathFollow` is waypoint-granular, so avoidance cannot shape it mid-segment - use it for overlap-tolerant swarms instead. Terrain cliffs and tilemap walls are invisible to avoidance until `TileGridBakeCrowdObstacles` bakes them from the nav grid.
470
477
  - Avoidance scope: XY-plane only; Z/height is ignored.
471
478
  - Gravity is ON at spawn; crowd bodies stand on static geometry, terrain, and tilemaps (not on dynamic objects). ClimbingCrowdObject remains the climb/stack-capable crowd.
472
479
  - For grid-based or fixed-lane games (e.g., Tower Defense), avoid using CrowdObject, as its built-in avoidance can cause objects to move off their intended path.
@@ -479,6 +486,7 @@ event OnSpawned {
479
486
  - Terrain + static primitive obstacles; supports elevator-like climbing
480
487
  - Can climb/stack on other climbers; large swarms may pile into a single moving blob
481
488
  - DisplayObject: like WorldObject but visual-only — no collision, no interaction events (Touched, ClickThisObject, etc. will never fire), doesn’t respond to forces
489
+ - When a display shell forms a player-reachable structure (building, wall), add a static collision core or invisible collider proxy behind it — display-only geometry is walked through.
482
490
  - EffectObject: visual effects
483
491
  - pointLight: local point light. `pointLight L { position = (0, 0, 150) radius = 500 intensity = 4 color = (1, 0.85, 0.6) }` - radius cm (default 100 = dim 1m pool); optional cast_shadow = true, light_diffuse_scale/light_specular_scale. For a bulb on a mesh prop, prefer an emitter(...) socket in the #mesh (see <mesh_dsl_usage>)
484
492
  - spotLight: cone along the object's +X facing (aim with rotation); pointLight keys plus cos_inner/cos_outer = cosine of the half-angle, not degrees (defaults 24deg/30deg)
@@ -1478,7 +1486,7 @@ Object.ShowOutlineHighlight(Player: Object, ThicknessPixels: Number, Color: Vect
1478
1486
  Object.HideOutlineHighlight(Player: Object) // Removes this player's outline on caller (no-op if absent)
1479
1487
  Object.EnableLocalSelectionBox(BorderColor: Vector, FillColor: Vector, FillOpacity: Number, BorderWidthPixels: Number, MinDragPixels: Number) // PlayerObject only. Enables a visual-only local drag box for this player; gameplay selection should use Event.MouseDownPosition/Event.MousePosition
1480
1488
  Object.DisableLocalSelectionBox() // PlayerObject only. Disables the visual-only local drag box
1481
- Object.EnableLocalWeaponView(Offset: Position, RotationDegrees: Vector, Scale: Number) // PlayerObject only. Renders this player's equipped weapon as a local first-person camera-attached visual for the owning client (active only in first-person camera). Offset is camera-local cm (+X right, +Y up, +Z forward); RotationDegrees (0,0,0) = barrel forward; e.g. (Position(15, -25, 32), Vector(2, 4, 0), 1.0) for an FPS-style close lower-right framing (a close-in gun keeps the auto-arms shoulder area off-screen). Visual-only; gameplay still uses EquipWeapon/GetHeldWeapon.
1489
+ Object.EnableLocalWeaponView(Offset: Position, RotationDegrees: Vector, Scale: Number) // PlayerObject only. Renders this player's equipped weapon as a local first-person camera-attached visual for the owning client (active only in first-person camera). Offset is camera-local cm (+X right, +Y up, +Z forward); RotationDegrees is a first-person camera tilt, (0,0,0) = barrel forward (third person seats the weapon on the hand frame + grip anchor, not on this value); e.g. (Position(15, -25, 32), Vector(2, 4, 0), 1.0) for an FPS-style close lower-right framing (a close-in gun keeps the auto-arms shoulder area off-screen). Visual-only; gameplay still uses EquipWeapon/GetHeldWeapon.
1482
1490
  Object.DisableLocalWeaponView() // PlayerObject only. Disables the local first-person weapon visual
1483
1491
  Object.GetWeaponAnchorWorldDir(AnchorName: Text) -> Vector // Anchor local +X (forward) transformed to world space
1484
1492
  Object.GetWeaponAnchorWorldUp(AnchorName: Text) -> Vector // Anchor local +Z transformed to world space
@@ -1508,7 +1516,7 @@ Object.PressAIAction(Action: Number) // Bot action tap (Action.Jump etc). Down f
1508
1516
  Object.DespawnAIPlayer() // Removes a bot (errors on real players). Plain Destroy() also works on bots
1509
1517
  Object.SetTerrainGrass(Model: Object, Material: Object) // Terrain only; Model: Ori.model.* or Ori.model.mesh.* (#mesh blade), None = built-in grass (tuft cards; see SetTerrainGrassStyle); Material: Material.X (#texture auto-material) or None = built-in atlas (usually enough; colors come from the grass knobs). On tuft cards the material's texture replaces the built-in card atlas (2x2 variant cells, uv+clamp+cutout; greyscale luminance = root->tip gradient, color still comes from the grass color knobs). Per-terrain (each terrain can have its own grass style)
1510
1518
  Object.SetTerrainGrassWeightmap(Material: Object) // Terrain only; Material: #texture auto-material; R channel of its color = grass density 0..1 across the terrain; None clears (full density). Overrides #terrain_splat out.grass; prefer out.grass when density should follow the terrain (height/slope), since a #texture cannot see it
1511
- Object.SetWeaponConfig(WeaponModel: Object, IdleAnim: Object, ReloadAnim: Object, FireAnim: Object, ForegripAnchor: Text, GripAnchor: Text, MuzzleAnchor: Text, FireInterval: Number, ReloadDuration: Number) // Register per-weapon-model TPS config. IdleAnim auto-plays as upper-body loop on EquipWeapon (if no upper animation is active); anims accept InvalidId = off. Anchor names may be "" = built-in defaults (foregrip/grip). FireInterval/ReloadDuration are seconds, readable via GetWeaponFireInterval/GetWeaponReloadDuration for script fire loops.
1519
+ Object.SetWeaponConfig(WeaponModel: Object, IdleAnim: Object, ReloadAnim: Object, FireAnim: Object, ForegripAnchor: Text, GripAnchor: Text, MuzzleAnchor: Text, FireInterval: Number, ReloadDuration: Number) // Register per-weapon-model held-weapon config. IdleAnim auto-plays as upper-body loop on EquipWeapon (if no upper animation is active); anims accept InvalidId = off. Anchor names may be "" = built-in defaults (foregrip/grip). GripAnchor seats third-person AND the viewmodel; no foregrip anchor on the model = support hand dropped = one-handed hold. FireInterval/ReloadDuration are seconds, readable via GetWeaponFireInterval/GetWeaponReloadDuration for script fire loops.
1512
1520
  Object.GetWeaponFireInterval(WeaponModel: Object) -> Number // Seconds between shots from the weapon model's SetWeaponConfig (0 when unset)
1513
1521
  Object.GetWeaponReloadDuration(WeaponModel: Object) -> Number // Reload seconds from the weapon model's SetWeaponConfig (0 when unset)
1514
1522
  Object.SetEmitting(IsOn: Bool) // EffectObject only. false: stop spawning new particles (existing ones finish while still following the emitter); true: resume. Emitter stays alive.
@@ -1661,7 +1669,7 @@ Vector.RotateByEuler(Yaw: Number, Pitch: Number, Roll: Number) -> Vector // Rota
1661
1669
  Vector.ToYawPitch() -> Vector // Treat this vector as a direction: returns Vector(yaw, pitch, 0) in degrees. Yaw 0 = +X (counter-clockwise toward +Y), pitch positive = up. Zero-length input gives (0, 0, 0)
1662
1670
  YawPitchToDirection(Yaw: Number, Pitch: Number) -> Vector // Unit direction from aim angles in degrees (inverse of ToYawPitch): yaw 0 = +X, pitch positive = up
1663
1671
  SetDefaultFont(Font: Object) // World default font for all UI text/buttons that don't set one (e.g. Ori.font.google.Fredoka); None reverts to the engine default (Nunito)
1664
- SetModelAnchor(Model: Object, AnchorName: Text, LocalPosition: Position, LocalRotationEulerDegrees: Vector) // Weapon sits wrong in the hand / anchor misplaced -> define or replace any named anchor ("grip", "muzzle", custom): model-root local, cm Z-up, rotation maps anchor axes (+X = forward) into model axes; wins over the asset's own anchors, call before or after EquipWeapon
1672
+ SetModelAnchor(Model: Object, AnchorName: Text, LocalPosition: Position, LocalRotationEulerDegrees: Vector) // Weapon sits wrong in the hand / anchor misplaced -> define or replace any named anchor ("grip", "muzzle", custom): model-root local, cm Z-up, rotation maps anchor axes (+X = forward) into model axes; wins over the asset's own anchors, call before or after EquipWeapon; a grip anchor seats BOTH the third-person hold and the EnableLocalWeaponView viewmodel
1665
1673
  SwitchStage(Name: Text) // `#stage` projects only: switch to another stage of this project (multiplayer-safe; all stages ship in the project source, so Name may be dynamic). Use LoadWorldTransferPush before switching to carry values across.
1666
1674
  Object.SetRenderOffset(OffsetCm: Vector, RotationEulerDegrees: Vector, Scale: Number) // World/display/player objects only (crowds/instance batches reject). Visual-only offset on top of the sim transform (bob/lean/sway); colliders/AI/queries unaffected. OffsetCm shifts in WORLD space (not rotated into the object's local frame); rotation composes after the object's own and pivots at the object CENTER (SetRotation pivots at origin = ref-box bottom-center). Call per-frame for procedural animation; ClearRenderOffset resets
1667
1675
  Object.ClearRenderOffset() // Remove this object's visual render offset
@@ -1750,6 +1758,16 @@ SessionErrorText() -> Text // Reason for state 3 (empty otherwise), e.g. relay u
1750
1758
  SessionPlayerCount(Index: Number) -> Number // Connected player count for a listed session (0 = unknown).
1751
1759
  OpenSessionBrowser() // Open the engine's built-in session browser UI (no-op in headless/validation).
1752
1760
  Object.ShatterAndDestroy() // Destroy() plus a debris burst from the model's baked chunk set (needs `#mesh Name destructible: <word>`; without it, plain Destroy()). Debris is display-only - no sim collision.
1761
+ TerrainSlopeAtXY(X: Number, Y: Number) -> Number // slope01 (g/(1+g); 0.5 = 45deg) of topmost terrain at world XY; 1 if no terrain (void = unwalkable). Walkers wedge above ~0.5
1762
+ TileGridSetWalkableFromTerrain(Grid: List, OriginTile: Vector, MaxSlope01: Number)
1763
+ TileGridSetEdgesFromTerrain(Grid: List, OriginTile: Vector, MaxStepCm: Number)
1764
+ TileGridStampRect(Grid: List, X1: Number, Y1: Number, X2: Number, Y2: Number, Blocked: Bool)
1765
+ TileGridStampObject(Grid: List, OriginTile: Vector, Obj: Object, Blocked: Bool)
1766
+ TileGridIsReachable(Grid: List, X1: Number, Y1: Number, X2: Number, Y2: Number) -> Bool
1767
+ TileGridIsReachable(Grid: List, X1: Number, Y1: Number, Z1: Number, X2: Number, Y2: Number, Z2: Number) -> Bool
1768
+ TileGridBakeCrowdObstacles(Grid: List, OriginTile: Vector)
1769
+ TerrainHeightsFromTiers(Heights: List, SamplesX: Number, SamplesY: Number, Tiers: Text, TierCm: Number) // REPLACE Heights from an ASCII tier map (digits = tier * TierCm, '.' = 0, 'r' = ramp; first row = max Y). See <terrain_generation>
1770
+ RaiseWorldError(Message: Text) // Fail-loud assert: raises a Weave runtime error carrying Message (validation surfaces it; in play the program pauses like any script error)
1753
1771
 
1754
1772
  </built_in_function_list>
1755
1773
 
@@ -1792,6 +1810,8 @@ event OnSpawned {
1792
1810
  - Flat gameplay areas: TerrainHeightsFlattenDisc/Rect/Path AFTER noise/erosion so nothing roughens them again. ThermalErode + RemapCurve make raw noise look natural (erode first, then remap for plateaus/cliffs).
1793
1811
  - Prefer 129..257 samples per axis (513 max; big lists inflate sim state — generate in OnSpawned local vars so they are GC'd after spawn).
1794
1812
  - GroundHeightAtXY(x, y) -> world Z cm of the topmost ground at world XY (downward raycast over terrain + static colliders; 0 if nothing). Use for placing props/villages on generated ground; works immediately after SpawnTerrainFromHeights in the same event.
1813
+ - TerrainSlopeAtXY(x, y) -> slope01 of the topmost terrain at world XY (0.5 = 45deg; no terrain = 1, treat void as unwalkable). Slopes steeper than ~0.5 silently immobilize walkers - keep unit routes below it or block those tiles/edges in the nav grid (see <tile_pathfinding>).
1814
+ - TerrainHeightsFromTiers(Heights, SamplesX, SamplesY, Tiers, TierCm) REPLACES the field with plateau heights from an ASCII tier map in a triple-quoted Text (digits 0-9 = tier, h = tier * TierCm; '.' = 0; 'r' = ramp graded toward its lower neighbor; rows = lines, FIRST row = MAX Y / north). Hand-drawn RTS cliff maps: one char per nav tile, then TileGridSetEdgesFromTerrain(MaxStepCm = TierCm/3) derives the cliff edges; compose Smooth/Erode/splat after.
1795
1815
  - Realism combo: Warp after base noise (kills grid-straight lines), then ErosionFilter (fast stateless gullies/ridges; run AFTER RemapCurve so the detail lands at final scale, and its look continues sub-cell on the GPU display surface). Avoid HydraulicErode/ThermalErode by default — slow iterative CPU sims; reach for HydraulicErode only when routed drainage or its flow/deposit masks are truly needed. MaskFromFlow gives a wetness mask WITHOUT eroding.
1796
1816
  - Terrain-following rivers: FlowPath traces downhill from a spring cell into a CarvePath-ready point list (stops at sea level = MinHeightCm). Check PathXYOut has >= 4 numbers (2 points) before carving.
1797
1817
  - Roads that climb: GradePath lerps the flatten height start->end along the path (use terrain heights at the endpoints); FlattenPath only does constant height.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oriverse-engine",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Prebuilt Oriverse engine bundle (WebGPU wasm + js glue) for serverless single-player games authored as one game.html with inline Weave source. Compiled engine only - contains no source code.",
5
5
  "license": "SEE LICENSE IN LICENSE.md",
6
6
  "files": [