Retune Iron Coast waves, enemy attacks and weapon presentation

This commit is contained in:
raptorreplica committed 2026-10-17 18:13:20 +11:00
1 parent 00cfcc2031
commit c4a4d821d5
145 files changed
+9242 -2951

No files matched your search

+17 -5
View File
@@ -1,9 +1,21 @@
# Pulse Cannon 1.0.0
# Pulse Cannon
Enable **Pulse Cannon** in Mods and apply at the main menu. This independent API 1 example has no dependencies. Buy its equipment in the shop (185,000 credits, maximum one) or collect its pickup. It is a primary upgrade and fires alongside the starter guns.
API 1 package, version 1.1.0. Enable it in Mods at the main menu and apply the selection. This package has no mod dependencies.
Projectile: 4 damage, 8 logical pixels per step, 9 step cooldown, `straight`, air and ground targets. Every third paired burst adds two angled shots using deterministic random values. Rhai stores bounded burst/hit counters and emits local firing/impact sounds and effects.
Always-equipped primary upgrade. Fires paired pulses at air and ground targets alongside your other primary guns. Every third burst adds two angled shots. Hold Fire; no ammunition is consumed.
`art/equipment-sd.png` is a 16×16 logical icon, `art/equipment-hd.png` its detailed 4× variant. Projectile canvases are 8×12 SD and 32×48 HD. Switch HD artwork in Options. The generated master images, SD/HD sprites, WAV files, provenance and reproduction inputs are tracked in Git. Original game assets and decoded reference images remain outside version control. `python3 generate_audio.py` reproduces both sound files.
Buy **Pulse Cannon** for **185,000 credits**; carry at most 1. Select it through Inventory or the Cycle control when it is a special weapon.
Validate locally with `cargo run --no-default-features -- --mods mods --validate-mods pulse_cannon`. Read the shared [API documentation](../../docs/modding-api.md) for save pins, dependencies and sandbox limits.
`pulse_cannon:projectile`: 4 damage, speed 8, 9-tick cooldown, `straight` behavior.
## Feedback
Projectile files remain 8×12 SD and 32×48 HD, displayed on a proportional **4×6 logical canvas** (previously 8×12). The point collision, 8px/tick speed, 4 damage, paired offsets, 9-tick cadence and third-volley spread are unchanged. A hit uses a compact 4px contact arc for 2 ticks (86ms), with one flash per projectile and at least 3 ticks between flashes; impact audio is limited to one event per 6 ticks. Fire/impact volume is 72/50. The price remains 185,000 because damage, range and utility are unchanged.
Only declared display sizing, cosmetic hit commands and playback levels/priorities change. No PNG/WAV pixels or samples, equipment icon dimensions, projectile collision rules, damage, cooldown, offsets, targeting, ammunition, capacity, controls or prices are changed. No new muzzle explosion or persistent trail is emitted. Normal original enemy deaths and terrain-destruction effects remain engine-controlled.
The three rapid weapons retain their prior burst/hit counters and reset only their cosmetic tick/projectile gates when mission ticks restart, accepting older saved script state without new fields. This version changes the package save pin; existing pilot compatibility confirmation remains the normal mod mechanism.
## Validation and history
See the [current Iron Coast retune results and limits](../../docs/iron-coast-retune.md). Package-local acceptance reports, validation results and retained test sources describe pre-retune 1.0 behavior unless a result explicitly identifies the newer package and runner. They remain historical material, not current playthrough guarantees.
+6 -6
View File
@@ -21,8 +21,8 @@
"art": {
"png": "art/projectile-sd.png",
"hd_png": "art/projectile-hd.png",
"logical_width": 8,
"logical_height": 12,
"logical_width": 4,
"logical_height": 6,
"nearest": true
},
"script": "scripts/weapon.rhai"
@@ -54,19 +54,19 @@
"id": "pulse_cannon:fire",
"name": "Pulse Cannon Fire",
"file": "audio/fire.wav",
"volume": 92,
"volume": 72,
"pan": 127,
"pitch": 128,
"priority": 1
"priority": 2
},
{
"id": "pulse_cannon:impact",
"name": "Pulse Cannon Impact",
"file": "audio/impact.wav",
"volume": 78,
"volume": 50,
"pan": 127,
"pitch": 128,
"priority": 2
"priority": 3
}
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
id = "pulse_cannon"
name = "Pulse Cannon"
version = "1.0.0"
version = "1.1.0"
api_version = 1
description = "Naval science-fiction pulse cannon: independent weapon, equipment, SD/HD artwork and local firing/impact audio."
content = "content.json"
+32 -7
View File
@@ -1,6 +1,12 @@
// Built-in straight projectiles handle the normal volley and collision rules.
// Firing callbacks add audio and counters; impact callbacks add hit visuals.
// Firing callbacks add audio and counters; compact two-tick contact arcs are cosmetic.
fn on_fire(event, state, random) {
// Runtime script state can survive a mission change while tick/entity IDs restart.
// Reset only cosmetic gates; the existing burst/hit counters keep their semantics.
let last_fire = if state.contains("last_fire_tick") { state.last_fire_tick } else { -1 };
let restarted = event.tick <= last_fire
|| (state.contains("feedback_tick") && event.tick <= state.feedback_tick)
|| (state.contains("sound_tick") && event.tick <= state.sound_tick);
let bursts = if state.contains("bursts") { state.bursts % 100000 + 1 } else { 1 };
let hits = if state.contains("hits") { state.hits } else { 0 };
let commands = [
@@ -13,15 +19,34 @@ fn on_fire(event, state, random) {
commands.push(#{ op: "projectile", weapon: "pulse_cannon:projectile", x: event.x - 8, y: event.y - 3, vx: -spread, vy: -8 });
commands.push(#{ op: "projectile", weapon: "pulse_cannon:projectile", x: event.x + 8, y: event.y - 3, vx: spread, vy: -8 });
}
#{ commands: commands, state: #{ bursts: bursts, hits: hits } }
#{ commands: commands, state: #{ bursts: bursts, hits: hits,
// A new volley cannot be the preceding beam's second hit tick. Forget its
// entity ID, which may be reused in another mission even after a late first shot.
feedback_projectile: -1,
feedback_tick: if !restarted && state.contains("feedback_tick") { state.feedback_tick } else { -10 },
sound_tick: if !restarted && state.contains("sound_tick") { state.sound_tick } else { -10 },
last_fire_tick: event.tick } }
}
fn on_hit(event, state, random) {
let bursts = if state.contains("bursts") { state.bursts } else { 0 };
let hits = if state.contains("hits") { state.hits % 100000 + 1 } else { 1 };
let commands = [
#{ op: "play_sound", sound: "pulse_cannon:impact" },
#{ op: "effect", x: event.x, y: event.y }
];
#{ commands: commands, state: #{ bursts: bursts, hits: hits } }
// One compact contact flash per projectile, not one explosion per beam target.
// A separate six-tick audio gate also bounds coincident paired/swarm hits.
let previous_projectile = if state.contains("feedback_projectile") { state.feedback_projectile } else { -1 };
let previous_tick = if state.contains("feedback_tick") { state.feedback_tick } else { -10 };
let sound_tick = if state.contains("sound_tick") { state.sound_tick } else { -10 };
let commands = [];
let feedback = event.projectile != previous_projectile && event.tick - previous_tick >= 3;
if feedback {
commands.push(#{op: "arc", x: event.x - 2, y: event.y + 2,
to_x: event.x + 2, to_y: event.y - 2, ticks: 2});
}
let audible = event.tick - sound_tick >= 6;
if audible { commands.push(#{op: "play_sound", sound: "pulse_cannon:impact"}); }
#{ commands: commands, state: #{ bursts: bursts, hits: hits,
feedback_projectile: if feedback { event.projectile } else { previous_projectile },
feedback_tick: if feedback { event.tick } else { previous_tick },
sound_tick: if audible { event.tick } else { sound_tick },
last_fire_tick: if state.contains("last_fire_tick") { state.last_fire_tick } else { -1 } } }
}