From ae96db7c485953c2cd384f8a94d6c597bf16a5c4 Mon Sep 17 00:00:00 2001 From: Ethanw05 <46205865+Ethanw05@users.noreply.github.com> Date: Tue, 8 Sep 2026 20:10:20 -0600 Subject: [PATCH] Revert "Add mod gameplay APIs and zombie shooter example" This reverts commit df0affd2e3059daca75064159a2a87278999ca5d. --- crates/skate-game/src/animation.rs | 7 -- crates/skate-game/src/modding/mod.rs | 103 +------------------ crates/skate-mods/src/api.lua | 3 - crates/skate-mods/src/vm.rs | 26 ----- crates/skate-mods/tests/runtime.rs | 22 +--- docs/lua-modding.md | 5 +- docs/multiplayer-mods.md | 1 - scripts/Build-Release.ps1 | 9 -- scripts/Build.ps1 | 6 +- scripts/Stage-SteamRelay.ps1 | 20 ---- sdk/examples/zombie-shooter/README.md | 9 -- sdk/examples/zombie-shooter/main.lua | 141 -------------------------- sdk/examples/zombie-shooter/mod.json | 38 ------- sdk/skate.lua | 8 +- 14 files changed, 9 insertions(+), 389 deletions(-) delete mode 100644 scripts/Stage-SteamRelay.ps1 delete mode 100644 sdk/examples/zombie-shooter/README.md delete mode 100644 sdk/examples/zombie-shooter/main.lua delete mode 100644 sdk/examples/zombie-shooter/mod.json diff --git a/crates/skate-game/src/animation.rs b/crates/skate-game/src/animation.rs index f51c796d..a103f2ae 100644 --- a/crates/skate-game/src/animation.rs +++ b/crates/skate-game/src/animation.rs @@ -16,13 +16,6 @@ struct BoneBinding { parent_bone: Option, } impl AnimationStatus { - pub(crate) fn entity_for_bone(&self, bone: usize) -> Option { - self.bindings - .iter() - .find(|binding| binding.bone == bone) - .map(|binding| binding.entity) - } - /// Prepare a hidden imported scene without disturbing the live bindings. pub(crate) fn for_scene( root: Entity, diff --git a/crates/skate-game/src/modding/mod.rs b/crates/skate-game/src/modding/mod.rs index cfade063..fa3a7fd0 100644 --- a/crates/skate-game/src/modding/mod.rs +++ b/crates/skate-game/src/modding/mod.rs @@ -25,11 +25,6 @@ struct Owned { entity: Entity, mesh: Option>, material: Option>, - attachment: Option, -} -#[derive(Component)] -struct HeldAttachment { - bone: usize, } pub(crate) struct ModdingPlugin; impl Plugin for ModdingPlugin { @@ -96,12 +91,7 @@ impl Plugin for ModdingPlugin { .after(crate::app::SimulationSet::Physics) .run_if(crate::graphics_menu::gameplay_active), ) - .add_systems( - Update, - (attach_held, update) - .chain() - .after(crate::app::FrameSet::Animation), - ); + .add_systems(Update, update.after(crate::app::FrameSet::Animation)); vehicles::install(app); network::install(app); menu::install(app); @@ -220,22 +210,6 @@ fn update(world: &mut World) { apply(world, &mut mods); }); } -fn attach_held( - mut commands: Commands, - animation: Res, - held: Query<(Entity, &HeldAttachment), Without>, -) { - if !animation.ready { - return; - } - for (entity, attachment) in &held { - if let Some(parent) = animation.entity_for_bone(attachment.bone) { - commands - .entity(entity) - .insert((ChildOf(parent), Visibility::Inherited)); - } - } -} fn retire(world: &mut World, mods: &mut Mods, key: &(String, String)) { if let Some(owned) = mods.owned.remove(key) { world.despawn(owned.entity); @@ -297,9 +271,7 @@ fn apply(world: &mut World, mods: &mut Mods) { Command::Remove { key } => { keys.remove(key); } - Command::Overlay { key, .. } - | Command::Cube { key, .. } - | Command::HeldCube { key, .. } => { + Command::Overlay { key, .. } | Command::Cube { key, .. } => { keys.insert(key.clone()); if keys.len() > 64 { return Err("64 owned objects per mod maximum".into()); @@ -380,10 +352,7 @@ fn teleport_ready(world: &World) -> Result<(), String> { } fn apply_one(world: &mut World, mods: &mut Mods, id: &str, command: Command) -> Result<(), String> { let key = match &command { - Command::Cube { key, .. } - | Command::HeldCube { key, .. } - | Command::Overlay { key, .. } - | Command::Remove { key } => { + Command::Cube { key, .. } | Command::Overlay { key, .. } | Command::Remove { key } => { Some((id.to_owned(), key.clone())) } _ => None, @@ -452,7 +421,6 @@ fn apply_one(world: &mut World, mods: &mut Mods, id: &str, command: Command) -> entity, mesh: None, material: None, - attachment: None, }, ); } @@ -464,7 +432,7 @@ fn apply_one(world: &mut World, mods: &mut Mods, id: &str, command: Command) -> } => { let key = key.unwrap(); if let Some(owned) = mods.owned.get(&key) { - if owned.mesh.is_some() && owned.attachment.is_none() { + if owned.mesh.is_some() { *world.get_mut::(owned.entity).unwrap() = Transform::from_translation(Vec3::from_array(position)) .with_scale(Vec3::from_array(size)); @@ -500,72 +468,9 @@ fn apply_one(world: &mut World, mods: &mut Mods, id: &str, command: Command) -> .id(), mesh: Some(mesh.id()), material: Some(material.id()), - attachment: None, }; mods.owned.insert(key, owned); } - Command::HeldCube { - bone, - offset, - size, - color, - .. - } => { - let key = key.unwrap(); - let bone_index = world - .resource::() - .animation - .evaluator - .frames - .bone_names - .iter() - .position(|name| name.eq_ignore_ascii_case(&bone)) - .ok_or_else(|| format!("Unknown held-cube bone {bone}"))?; - let transform = Transform::from_translation(Vec3::from_array(offset)) - .with_scale(Vec3::from_array(size)); - if let Some(owned) = mods.owned.get(&key) { - if owned.mesh.is_some() && owned.attachment == Some(bone_index) { - *world.get_mut::(owned.entity).unwrap() = transform; - if let Some(material) = owned.material { - if let Some(m) = world - .resource_mut::>() - .get_mut(material) - { - m.base_color = Color::srgb(color[0], color[1], color[2]); - } - } - return Ok(()); - } - } - retire(world, mods, &key); - let mesh = world - .resource_mut::>() - .add(Cuboid::new(1., 1., 1.)); - let material = world - .resource_mut::>() - .add(StandardMaterial { - base_color: Color::srgb(color[0], color[1], color[2]), - ..default() - }); - let entity = world - .spawn(( - Mesh3d(mesh.clone()), - MeshMaterial3d(material.clone()), - transform, - Visibility::Hidden, - HeldAttachment { bone: bone_index }, - )) - .id(); - mods.owned.insert( - key, - Owned { - entity, - mesh: Some(mesh.id()), - material: Some(material.id()), - attachment: Some(bone_index), - }, - ); - } Command::Teleport { position, heading, diff --git a/crates/skate-mods/src/api.lua b/crates/skate-mods/src/api.lua index d4661622..314373d9 100644 --- a/crates/skate-mods/src/api.lua +++ b/crates/skate-mods/src/api.lua @@ -7,9 +7,6 @@ sdk.scene = {} function sdk.scene.cube(key,position,size,color) submit{kind="cube",key=key,position=position,size=size,color=color} end -function sdk.scene.held_cube(key,bone,offset,size,color) - submit{kind="held_cube",key=key,bone=bone,offset=offset,size=size,color=color} -end function sdk.scene.remove(key) submit{kind="remove",key=key} end sdk.player = {} function sdk.player.read() return sdk.snapshot.player end diff --git a/crates/skate-mods/src/vm.rs b/crates/skate-mods/src/vm.rs index 7e8a5c9a..36864e2a 100644 --- a/crates/skate-mods/src/vm.rs +++ b/crates/skate-mods/src/vm.rs @@ -38,13 +38,6 @@ pub enum Command { size: [f32; 3], color: [f32; 3], }, - HeldCube { - key: String, - bone: String, - offset: [f32; 3], - size: [f32; 3], - color: [f32; 3], - }, Remove { key: String, }, @@ -81,25 +74,6 @@ impl Command { .iter() .all(|v| v.is_finite() && (0. ..=1.).contains(v)) } - Self::HeldCube { - key, - bone, - offset, - size, - color, - } => { - crate::schema::valid_id(key) - && !bone.is_empty() - && bone.len() <= 64 - && bone - .bytes() - .all(|b| b.is_ascii_alphanumeric() || b == b'_') - && offset.iter().all(|v| v.is_finite() && v.abs() <= 10.) - && size.iter().all(|v| v.is_finite() && *v > 0. && *v <= 10.) - && color - .iter() - .all(|v| v.is_finite() && (0. ..=1.).contains(v)) - } Self::Remove { key } => crate::schema::valid_id(key), Self::Teleport { position, heading, .. diff --git a/crates/skate-mods/tests/runtime.rs b/crates/skate-mods/tests/runtime.rs index 7b7210c0..e97576af 100644 --- a/crates/skate-mods/tests/runtime.rs +++ b/crates/skate-mods/tests/runtime.rs @@ -104,26 +104,6 @@ fn libraries_are_restricted() { ); } #[test] -fn held_cube_command_is_typed_validated_and_queued() { - let f = Fixture::new( - "return {on_load=function() sdk.scene.held_cube('gun','RIGHTHAND',{0,-0.1,0.05},{0.1,0.3,0.1},{0.2,0.2,0.2}) end}", - ); - let mut m = f.manager(); - m.enable("example", true).unwrap(); - assert!(matches!( - &m.commands[0].1, - Command::HeldCube { key, bone, .. } if key == "gun" && bone == "RIGHTHAND" - )); - - let f = Fixture::new( - "return {on_load=function() sdk.scene.held_cube('gun','RIGHT-HAND',{0,0,0},{0.1,0.3,0.1},{1,1,1}) end}", - ); - let mut m = f.manager(); - m.enable("example", true).unwrap(); - assert!(!m.packages["example"].running()); - assert!(m.commands.is_empty()); -} -#[test] fn immediate_settings_persist_and_validate() { let f = Fixture::new( "return {on_settings=function(e) sdk.log(tostring(sdk.settings.count)..':'..e.key) end}", @@ -172,7 +152,7 @@ fn examples_load_and_run() { m.scan(true); assert!(m.diagnostics.is_empty(), "{:?}", m.diagnostics); let ids: Vec<_> = m.packages.keys().cloned().collect(); - assert_eq!(ids, vec!["community.mario-kart", "community.native-trainer", "community.zombie-shooter"]); + assert_eq!(ids, vec!["community.mario-kart", "community.native-trainer"]); for id in ids { m.enable(&id, true).unwrap(); for _ in 0..5 { diff --git a/docs/lua-modding.md b/docs/lua-modding.md index f4378f6a..fc22cd12 100644 --- a/docs/lua-modding.md +++ b/docs/lua-modding.md @@ -124,8 +124,7 @@ All vectors are 1-based Lua arrays `{x,y,z}` in engine world coordinates, metres | `sdk.input.action(id:integer)` | number from published native gameplay action 64–81; errors outside range | | `sdk.ui.text(key:string,text:string)` | nil; create/update owned plain screen text, max 1024 bytes; stable top-left rows sorted by owner/key, 19px font, 28px row spacing | | `sdk.scene.cube(key:string,position:Vec3,size:Vec3,color:Vec3)` | nil; create/update owned visual cuboid; no collision, grind attachment or rigid body | -| `sdk.scene.held_cube(key:string,bone:string,offset:Vec3,size:Vec3,color:Vec3)` | nil; create/update an owned visual cuboid parented to an exact displayed skeleton bone; local-only, non-colliding and removed with its owner | -| `sdk.scene.remove(key:string)` | nil; remove owned world cube, held cube or text; absent key is a no-op; cannot address another mod's objects | +| `sdk.scene.remove(key:string)` | nil; remove owned cube/text; absent key is a no-op; cannot address another mod's objects | | `sdk.time.elapsed` | active Update seconds since state creation, advanced after `on_update` | | `sdk.time.after(key:string,seconds:number,callback:function)` | nil; replace keyed one-shot timer; delay 0–86400 finite seconds, key 1–64 bytes, at most 64 timers | | `sdk.time.cancel(key:string)` | nil; remove timer; absent key is a no-op | @@ -134,7 +133,7 @@ All vectors are 1-based Lua arrays `{x,y,z}` in engine world coordinates, metres Timers run after `on_update`, within its same instruction budget and transaction. Due timer keys sort lexicographically. A timer is removed before invocation. Newly scheduled timers normally wait for the next Update (replacing an already due, not-yet-delivered key can affect that key's pending delivery). Pausing stops the timer clock. Errors disable the owner. `sdk.time.elapsed` and snapshot fields should be treated as read-only by authors. -Cube positions and teleport positions must be finite, each component within ±100000. World-cube size components must be finite in `(0,100]`. Held-cube offsets are limited to ±10 metres, held-cube sizes to `(0,10]`, and the bone must be an exact installed name reported by `sdk.animation.info()`. A held cube remains hidden until that bone has a displayed binding, then follows its final animated transform. RGB components must be finite in `[0,1]`, interpreted as sRGB. Heading must be finite. Text and cubes share an owner-local key namespace: switching type replaces the old object. At most 64 visual objects per mod; update existing keys to avoid churn. At most 128 commands per Lua invocation. Invalid arguments raise Lua errors; exceeding limits stops the mod. Commands return nil on successful **queuing**, not proof of engine completion. Host rejection appears in the mod's error panel and log. Teleports reject map loading, replay and busy actor-reset conditions. This is a free-position command, not a terrain-safe spawn finder: authors must select valid destinations. It does not set velocities directly or bypass the native deferred reset/camera-cut sequence. +Cube positions and teleport positions must be finite, each component within ±100000. Size components must be finite in `(0,100]`. RGB components must be finite in `[0,1]`, interpreted as sRGB. Heading must be finite. Text and cubes share an owner-local key namespace: switching type replaces the old object. At most 64 visual objects per mod; update existing keys to avoid churn. At most 128 commands per Lua invocation. Invalid arguments raise Lua errors; exceeding limits stops the mod. Commands return nil on successful **queuing**, not proof of engine completion. Host rejection appears in the mod's error panel and log. Teleports reject map loading, replay and busy actor-reset conditions. This is a free-position command, not a terrain-safe spawn finder: authors must select valid destinations. It does not set velocities directly or bypass the native deferred reset/camera-cut sequence. ### Snapshot and event payloads diff --git a/docs/multiplayer-mods.md b/docs/multiplayer-mods.md index d9d2680d..34f94cf5 100644 --- a/docs/multiplayer-mods.md +++ b/docs/multiplayer-mods.md @@ -11,7 +11,6 @@ available. `Build.ps1` builds and stages both executables without launching them | SDK feature | Multiplayer behavior | | --- | --- | | `scene.cube` / `scene.remove` | Owner-scoped shared visual objects; updates, removal and late joins supported. Cubes remain non-colliding. | -| `scene.held_cube` | Local visual attachment to the owning player's displayed skeleton. It is not replicated by SDK 1. | | `vehicle.spawn/remove/reset/tune/control` | Matching package supplies the model and definition. Owner sends chassis pose, velocities, wheel state, controls, tuning and occupancy. Other peers render and collide with replicas. | | Vehicle entry/exit/steering | The final displayed native skeleton, including stance blending and arm steering, is replicated. Seated riders follow the same car render sample. | | Vehicle engine audio | Each occupied car has a smoothed engine voice; other players' cars attenuate with distance. | diff --git a/scripts/Build-Release.ps1 b/scripts/Build-Release.ps1 index 3238760a..d7c6b295 100644 --- a/scripts/Build-Release.ps1 +++ b/scripts/Build-Release.ps1 @@ -18,12 +18,6 @@ try { # Link this invocation directly into private staging; never copy a generic cache EXE. & cargo rustc --release --locked --target x86_64-pc-windows-msvc --target-dir $TargetDirectory -p skate-game --bin skate3rust --no-default-features -- -C extra-filename= -o "$stage/skate3rust.exe" -C "link-arg=/PDB:$symbols/skate3rust.pdb" if ($LASTEXITCODE -ne 0) { throw 'Release compilation failed' } - & cargo build --release --locked --target x86_64-pc-windows-msvc --target-dir $TargetDirectory -p skate-steam-relay - if ($LASTEXITCODE -ne 0) { throw 'Steam relay compilation failed' } - & (Join-Path $PSScriptRoot 'Stage-SteamRelay.ps1') ` - -TargetDirectory $TargetDirectory ` - -BinDirectory $stage ` - -RelayExecutable (Join-Path $TargetDirectory 'x86_64-pc-windows-msvc/release/skate-steam-relay.exe') # rustc also emits a dep-info file beside -o; it contains local source paths. $depInfo = Join-Path $stage 'skate3rust.d' if (Test-Path -LiteralPath $depInfo) { Remove-Item -LiteralPath $depInfo } @@ -76,9 +70,6 @@ try { foreach ($name in @('skate3rust.exe', 'support/skate3setup.exe', 'support/skate3update.exe')) { $files[$name] = (Get-FileHash -LiteralPath (Join-Path $stage $name) -Algorithm SHA256).Hash.ToLower() } - foreach ($name in @('steam-relay/skate-steam-relay.exe', 'steam-relay/steam_api64.dll')) { - $files[$name] = (Get-FileHash -LiteralPath (Join-Path $stage $name) -Algorithm SHA256).Hash.ToLower() - } @{ schema = 1; repository = 'SK8-ENGINE/skate-3-rust-engine'; target = 'windows-x64' build = $build; tag = $tag; revision = (& git rev-parse HEAD).Trim(); files = $files diff --git a/scripts/Build.ps1 b/scripts/Build.ps1 index 9bcce4aa..f8bc82c4 100644 --- a/scripts/Build.ps1 +++ b/scripts/Build.ps1 @@ -4,7 +4,7 @@ $ErrorActionPreference = 'Stop' Push-Location $ProjectRoot try { if (-not $StageOnly) { - & cargo build -p skate-game -p skate-steam-relay --locked --target-dir $TargetDirectory + & cargo build -p skate-game --locked --target-dir $TargetDirectory if ($LASTEXITCODE -ne 0) { throw 'Build failed; see the compiler output above.' } } $debugDirectory = Join-Path $TargetDirectory 'debug' @@ -56,10 +56,6 @@ try { } } } - & (Join-Path $PSScriptRoot 'Stage-SteamRelay.ps1') -TargetDirectory $TargetDirectory -BinDirectory $binDirectory - foreach ($name in @('steam-relay/skate-steam-relay.exe', 'steam-relay/steam_api64.dll')) { - $staged += @{name = $name; sha256 = (Get-FileHash -LiteralPath (Join-Path $binDirectory $name) -Algorithm SHA256).Hash} - } $staged | ConvertTo-Json | Set-Content -LiteralPath (Join-Path $binDirectory 'manifest.json') -Encoding UTF8 Write-Host "Ready: $binDirectory/skate3rust.exe" } finally { Pop-Location } diff --git a/scripts/Stage-SteamRelay.ps1 b/scripts/Stage-SteamRelay.ps1 deleted file mode 100644 index 46e399e1..00000000 --- a/scripts/Stage-SteamRelay.ps1 +++ /dev/null @@ -1,20 +0,0 @@ -param( - [string]$TargetDirectory = (Join-Path (Split-Path $PSScriptRoot -Parent) 'target'), - [string]$BinDirectory = (Join-Path (Split-Path $PSScriptRoot -Parent) 'bin'), - [string]$RelayExecutable = '' -) -$ErrorActionPreference = 'Stop' -$relayExecutable = if ($RelayExecutable) { $RelayExecutable } else { Join-Path $TargetDirectory 'debug/skate-steam-relay.exe' } -if (-not (Test-Path -LiteralPath $relayExecutable)) { throw 'Build skate-steam-relay before staging.' } -$relayMetadata = (& cargo metadata --format-version 1 --locked --filter-platform x86_64-pc-windows-msvc) -join "`n" -if ($LASTEXITCODE -ne 0) { throw 'Could not locate the locked Steam SDK redistributable.' } -$sdk = [regex]::Match($relayMetadata, '"name":"steamworks-sys","version":"0\.13\.0".*?"manifest_path":("(?:[^"\\]|\\.)*")') -if (-not $sdk.Success) { throw 'Expected steamworks-sys 0.13.0 in Cargo metadata.' } -$sdkManifest = ConvertFrom-Json -InputObject $sdk.Groups[1].Value -$steamDll = Join-Path (Split-Path -Parent $sdkManifest) 'lib/steam/redistributable_bin/win64/steam_api64.dll' -if (-not (Test-Path -LiteralPath $steamDll)) { throw "Missing Steam SDK DLL: $steamDll" } -$relayDirectory = Join-Path $BinDirectory 'steam-relay' -New-Item -ItemType Directory -Path $relayDirectory -Force | Out-Null -Copy-Item -LiteralPath $relayExecutable -Destination (Join-Path $relayDirectory 'skate-steam-relay.exe') -Force -Copy-Item -LiteralPath $steamDll -Destination (Join-Path $relayDirectory 'steam_api64.dll') -Force -Write-Host "Steam relay ready: $relayDirectory" diff --git a/sdk/examples/zombie-shooter/README.md b/sdk/examples/zombie-shooter/README.md deleted file mode 100644 index a0dd1398..00000000 --- a/sdk/examples/zombie-shooter/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Block Zombie Shooter - -Enable the mod from Escape > Mods. The skater carries a small block pistol. -Press controller RB or keyboard F to fire in the direction the skater faces. -Zombies are non-physical block targets that approach the player. Press R to restart. - -The gun is a local visual attached to the displayed RIGHTHAND bone. Zombie movement, -hits, health, score and waves are Lua-owned arcade rules; they do not alter native -Skate collision, ragdoll, scoring or animation state. diff --git a/sdk/examples/zombie-shooter/main.lua b/sdk/examples/zombie-shooter/main.lua deleted file mode 100644 index beec847c..00000000 --- a/sdk/examples/zombie-shooter/main.lua +++ /dev/null @@ -1,141 +0,0 @@ -local zombies = {} -local wave, score, health = 0, 0, 100 -local fire_was_down, restart_was_down = false, false - -local function remove_zombie(i) - sdk.scene.remove('zombie-body-' .. i) - sdk.scene.remove('zombie-head-' .. i) -end - -local function clear_zombies() - for i = 1, 12 do remove_zombie(i) end - zombies = {} -end - -local function equip() - -- These are bone-local primitives: body, barrel and grip form a tiny pistol. - sdk.scene.held_cube('gun-body', 'RIGHTHAND', {0.00, -0.10, 0.06}, {0.10, 0.28, 0.12}, {0.12, 0.12, 0.14}) - sdk.scene.held_cube('gun-barrel', 'RIGHTHAND', {0.00, -0.24, 0.06}, {0.07, 0.30, 0.08}, {0.20, 0.20, 0.23}) - sdk.scene.held_cube('gun-grip', 'RIGHTHAND', {0.00, -0.08, -0.02}, {0.08, 0.11, 0.18}, {0.08, 0.08, 0.09}) -end - -local function draw_zombie(i, zombie) - local hurt = zombie.hp < math.floor(sdk.settings.hits) - local body_color = hurt and {0.65, 0.18, 0.12} or {0.18, 0.55, 0.16} - sdk.scene.cube('zombie-body-' .. i, {zombie.x, zombie.y + 0.85, zombie.z}, {0.7, 1.3, 0.5}, body_color) - sdk.scene.cube('zombie-head-' .. i, {zombie.x, zombie.y + 1.75, zombie.z}, {0.55, 0.55, 0.55}, {0.35, 0.78, 0.28}) -end - -local function spawn_wave() - clear_zombies() - wave = wave + 1 - local p = sdk.player.read().position - local count = math.floor(sdk.settings.zombies) - for i = 1, count do - local angle = (i / count) * math.pi * 2 + wave * 0.37 - local radius = 12 + (i % 3) * 3 - zombies[i] = { - x = p[1] + math.sin(angle) * radius, - y = p[2], - z = p[3] + math.cos(angle) * radius, - hp = math.floor(sdk.settings.hits), - attack = 0, - } - draw_zombie(i, zombies[i]) - end -end - -local function hud(message) - sdk.ui.text('zombie-hud', string.format('ZOMBIES | health %d | score %d | wave %d', health, score, wave)) - sdk.ui.text('zombie-help', message or 'RB / F: shoot | R: restart | Shots travel where the skater faces') -end - -local function restart() - score, health, wave = 0, 100, 0 - spawn_wave() - hud() -end - -local function shoot() - if health <= 0 then return end - local player = sdk.player.read() - if player.bailing then return end - local dx, dz = math.sin(player.heading), math.cos(player.heading) - local best, best_distance - for i, zombie in pairs(zombies) do - local x, z = zombie.x - player.position[1], zombie.z - player.position[3] - local forward = x * dx + z * dz - if forward > 0 and forward < 35 then - local side_squared = math.max(0, x * x + z * z - forward * forward) - if side_squared < 0.75 * 0.75 and (not best_distance or forward < best_distance) then - best, best_distance = i, forward - end - end - end - sdk.scene.cube('muzzle-flash', { - player.position[1] + dx * 1.1, - player.position[2] + 1.25, - player.position[3] + dz * 1.1, - }, {0.13, 0.13, 0.13}, {1.0, 0.65, 0.1}) - sdk.time.after('muzzle-flash', 0.06, function() sdk.scene.remove('muzzle-flash') end) - if best then - local zombie = zombies[best] - zombie.hp = zombie.hp - 1 - if zombie.hp <= 0 then - remove_zombie(best) - zombies[best] = nil - score = score + 100 - else - draw_zombie(best, zombie) - end - end -end - -local function update() - local fire_down = sdk.input.down('KeyF') or sdk.input.action(73) > 0.5 - if fire_down and not fire_was_down then shoot() end - fire_was_down = fire_down - local restart_down = sdk.input.down('KeyR') - if restart_down and not restart_was_down then restart() end - restart_was_down = restart_down - if health <= 0 then hud('GAME OVER | Press R to restart') else hud() end -end - -local function fixed(event) - if health <= 0 then return end - local p = sdk.player.read().position - local alive = 0 - for i, zombie in pairs(zombies) do - alive = alive + 1 - local x, z = p[1] - zombie.x, p[3] - zombie.z - local distance = math.sqrt(x * x + z * z) - if distance > 0.001 then - local step = math.min(distance, sdk.settings.speed * event.dt) - zombie.x = zombie.x + x / distance * step - zombie.z = zombie.z + z / distance * step - end - zombie.y = p[2] - zombie.attack = math.max(0, zombie.attack - event.dt) - if distance < 1.25 and zombie.attack <= 0 then - health = math.max(0, health - 10) - zombie.attack = 0.75 - end - draw_zombie(i, zombie) - end - if alive == 0 then spawn_wave() end - if health <= 0 then clear_zombies() end -end - -return { - on_load = function() equip(); restart(); sdk.log('Block Zombie Shooter loaded') end, - on_update = update, - on_fixed_update = fixed, - on_settings = function() restart() end, - on_event = function(event) - if event.name == 'world_changed' then - fire_was_down, restart_was_down = false, false - equip() - restart() - end - end, -} diff --git a/sdk/examples/zombie-shooter/mod.json b/sdk/examples/zombie-shooter/mod.json deleted file mode 100644 index b6a5ca2d..00000000 --- a/sdk/examples/zombie-shooter/mod.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "id": "community.zombie-shooter", - "api": 1, - "name": "Block Zombie Shooter", - "version": "1.0.0", - "author": "Skate Rust Engine", - "description": "A tiny arcade shooter. The skater holds a block pistol; press RB or F to shoot approaching block zombies.", - "entry": "main.lua", - "settings": { - "zombies": { - "type": "number", - "label": "Zombies per wave", - "description": "Number of block zombies spawned in each wave.", - "default": 6, - "min": 1, - "max": 12, - "step": 1 - }, - "speed": { - "type": "number", - "label": "Zombie speed", - "description": "Movement speed in metres per second.", - "default": 1.5, - "min": 0.25, - "max": 5, - "step": 0.25 - }, - "hits": { - "type": "number", - "label": "Hits per zombie", - "description": "Shots required to remove one zombie.", - "default": 2, - "min": 1, - "max": 5, - "step": 1 - } - } -} diff --git a/sdk/skate.lua b/sdk/skate.lua index e8ad03bd..b8c238bc 100644 --- a/sdk/skate.lua +++ b/sdk/skate.lua @@ -81,13 +81,7 @@ function sdk.ui.text(key, text) end ---@param size Vec3 Each component >0 and <=100 metres. ---@param color Vec3 Each sRGB component in 0..1. function sdk.scene.cube(key, position, size, color) end ----@param key string Owner-local stable visual key. ----@param bone string Exact installed skeleton bone name, e.g. RIGHTHAND. ----@param offset Vec3 Local metres in the selected bone frame, each component within ±10. ----@param size Vec3 Each component >0 and <=10 metres. ----@param color Vec3 Each sRGB component in 0..1. -function sdk.scene.held_cube(key, bone, offset, size, color) end ----@param key string Remove owner's text, world cube, or held cube; absent is a no-op. +---@param key string Remove owner's text or cube; absent is a no-op. function sdk.scene.remove(key) end ---@param key string Timer key, 1..64 bytes. Existing timer is replaced. ---@param seconds number Finite delay 0..86400 active Update seconds.