Add independent Rust engine, deterministic mod API and asset pipeline

This commit is contained in:
raptorreplica committed 2026-10-10 12:17:43 +11:00
1 parent f2cce6f51b
commit dbb1d4596b
53 files changed
+18314 -56

No files matched your search

+15
View File
@@ -0,0 +1,15 @@
# Aurora Field Trials
This API v1 example adds a scripted pulse cannon, a repair pickup, weaving drones, a guardian, a mission and a campaign. It references the player's installed original sprite catalog; no game artwork is included.
From the repository root:
```sh
cargo run --no-default-features -- --mods examples/mods --validate-mods aurora
cargo run --no-default-features -- --mods examples/mods --enabled aurora \
--headless aurora:field_trials --ticks 700
```
To play, start the Rust engine with `--mods examples/mods`, enable Aurora in **Mods**, apply the selection at the main menu, create a separate pilot and choose **Aurora Field Trials**. The mission equips its cannon on the first simulation step. Keep existing pilots' required mod sets available.
Content IDs intentionally separate the inventory item `aurora:pulse_cannon` from its projectile `aurora:pulse_projectile`. The scripts demonstrate `on_fire`, `on_hit`, `on_spawn`, `on_tick`, `on_death`, `on_pickup`, `on_use`, `on_mission_start` and `on_mission_end`. See `docs/modding-api.md` for the complete API and compatibility rules.
+247
View File
@@ -0,0 +1,247 @@
{
"weapons": [
{
"id": "aurora:pulse_projectile",
"name": "Aurora Pulse",
"damage": 3,
"speed": 8,
"cooldown": 9,
"behavior": "straight",
"target": "all",
"offsets": [
[
0,
-4
]
],
"art": {
"asset": 65999,
"logical_width": 8,
"logical_height": 8,
"frames": 2
},
"script": "scripts/weapon.rhai"
},
{
"id": "aurora:drone_projectile",
"name": "Drone Pulse",
"damage": 3,
"speed": 3,
"cooldown": 32,
"behavior": "straight",
"target": "all",
"art": {
"asset": 65999,
"logical_width": 8,
"logical_height": 8,
"frames": 2
}
}
],
"items": [
{
"id": "aurora:pulse_cannon",
"name": "Aurora Pulse Cannon",
"price": 4000,
"max_count": 1,
"weapon": "aurora:pulse_projectile",
"art": {
"asset": 66225,
"logical_width": 16,
"logical_height": 16
}
},
{
"id": "aurora:repair_cell",
"name": "Aurora Repair Cell",
"price": 100,
"max_count": 10,
"consumable": true,
"shield": 12,
"art": {
"asset": 66225,
"logical_width": 16,
"logical_height": 16
},
"script": "scripts/cell.rhai",
"special": true
}
],
"enemies": [
{
"id": "aurora:weaver",
"name": "Aurora Weaver",
"health": 16,
"speed": 2,
"reward": 100,
"cooldown": 32,
"weapon": "aurora:drone_projectile",
"drop": "aurora:repair_cell",
"art": {
"asset": 66296,
"logical_width": 32,
"logical_height": 24
},
"script": "scripts/drone.rhai"
},
{
"id": "aurora:guardian",
"name": "Aurora Guardian",
"health": 160,
"speed": 1,
"reward": 1500,
"boss": true,
"cooldown": 20,
"weapon": "aurora:drone_projectile",
"drop": "aurora:repair_cell",
"path": [
[
0,
60
],
[
40,
60
],
[
-40,
60
]
],
"repeat": true,
"art": {
"asset": 66310,
"logical_width": 80,
"logical_height": 40
},
"script": "scripts/drone.rhai"
}
],
"missions": [
{
"id": "aurora:field_trials",
"name": "Aurora Field Trials",
"duration": 650,
"spawns": [
{
"tick": 30,
"enemy": "aurora:weaver",
"x": 70,
"y": -24
},
{
"tick": 60,
"enemy": "aurora:weaver",
"x": 200,
"y": -24
},
{
"tick": 110,
"enemy": "aurora:weaver",
"x": 140,
"y": -24
},
{
"tick": 150,
"enemy": "aurora:weaver",
"x": 90,
"y": -24
},
{
"tick": 180,
"enemy": "aurora:weaver",
"x": 190,
"y": -24
},
{
"tick": 230,
"enemy": "aurora:weaver",
"x": 150,
"y": -24
},
{
"tick": 300,
"enemy": "aurora:guardian",
"x": 120,
"y": -40
}
],
"tiles": [
{
"art": {
"asset": 66400,
"logical_width": 32,
"logical_height": 32
}
}
],
"columns": 9,
"layout": [
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0
],
"script": "scripts/mission.rhai"
}
],
"campaigns": [
{
"id": "aurora:trials",
"name": "Aurora Trials",
"missions": [
"aurora:field_trials"
]
}
]
}
+7
View File
@@ -0,0 +1,7 @@
id = "aurora"
name = "Aurora Field Trials"
version = "1.0.0"
api_version = 1
description = "A short mission with a scripted pulse cannon, weaving drones and repair cells. Uses your installed original artwork."
content = "content.json"
scripts = ["scripts/weapon.rhai", "scripts/drone.rhai", "scripts/cell.rhai", "scripts/mission.rhai"]
+11
View File
@@ -0,0 +1,11 @@
fn on_pickup(event, state, random) {
let recovered = if state.contains("recovered") { state.recovered + 1 } else { 1 };
#{ commands: [#{ op: "credits", amount: 25 }], state: #{ recovered: recovered } }
}
// A selected repair cell can also be consumed with the special-action button.
fn on_use(event, state, random) {
let used = if state.contains("used") { state.used + 1 } else { 1 };
state.used = used;
#{ commands: [#{ op: "objective", key: "aurora:repairs_used", value: used }], state: state }
}
+16
View File
@@ -0,0 +1,16 @@
fn on_spawn(event, state, random) {
#{ commands: [], state: state }
}
fn on_tick(event, state, random) {
// on_tick may be queued on the same step as on_death; do not command a retired entity.
if event.health <= 0 { return #{ commands: [], state: state }; }
let sideways = if (event.tick / 24) % 2 == 0 { 1 } else { -1 };
#{ commands: [#{ op: "velocity", entity: event.entity, vx: sideways, vy: 1 }], state: state }
}
fn on_death(event, state, random) {
let destroyed = if state.contains("destroyed") { state.destroyed + 1 } else { 1 };
#{ commands: [#{ op: "objective", key: "aurora:destroyed", value: destroyed }],
state: #{ destroyed: destroyed } }
}
+10
View File
@@ -0,0 +1,10 @@
fn on_mission_start(event, state, random) {
#{ commands: [
#{ op: "give_item", item: "aurora:pulse_cannon", count: 1 },
#{ op: "objective", key: "aurora:destroyed", value: 0 }
], state: state }
}
fn on_mission_end(event, state, random) {
#{ commands: [#{ op: "objective", key: "aurora:trial_finished", value: 1 }], state: state }
}
+13
View File
@@ -0,0 +1,13 @@
// The normal centre projectile is emitted by the engine. Add two angled pulses.
fn on_fire(event, state, random) {
let shots = if state.contains("shots") { state.shots + 1 } else { 1 };
let spread = 1 + random[0] % 2;
#{ commands: [
#{ op: "projectile", weapon: "aurora:pulse_projectile", x: event.x - 6, y: event.y, vx: -spread, vy: -8 },
#{ op: "projectile", weapon: "aurora:pulse_projectile", x: event.x + 6, y: event.y, vx: spread, vy: -8 }
], state: #{ shots: shots } }
}
fn on_hit(event, state, random) {
#{ commands: [#{ op: "effect", x: event.x, y: event.y }], state: state }
}