SHOOTER - QBASIC to QuadooScript Application
==========================================

Open ShooterExample.qsproj with the current Win32 Quadoo Studio, then choose
Project > Start Without Debugging (Ctrl+F5). Use the matching November 4, 2026
or later AQVMT.bin / QuadooForm runtime with Canvas, Sprite and Host sound
support. The ZIP also includes a ready-to-run build in bin\Win32.

Controls
--------
Arrow keys: move (held keys and quick taps both work)
Space: fire, at most one player shot per 150 ms
P: pause/resume
Enter / New game: start a new game
Esc / Exit: close the application
Switching to another window pauses and clears held keys. Press P to resume.

Canvas and events
-----------------
Video is a Canvas with BackColor=0 (black), AutoRedraw=true and
DoubleBuffered=true. Its 960x525 logical bounds display the BASIC 640x350
world at 1.5x at 96 DPI. Sprite pixels are scaled with nearest-neighbor
sampling at startup and on Canvas resize. Drawing coordinates are physical
pixels. The floating-point scale is the LEFT operand of drawing arithmetic,
because the current VM converts the right operand to the left operand type.

FrameTimer is a nonvisual Windows Timer with Interval=50 ms. It advances
one simulation step and redraws the complete Canvas. This replaces SCREEN 9,
GET/PUT page operations and PCOPY with a retained native drawing surface.
No busy loop or sleep runs inside the UI callback.

ShooterForm.KeyPreview=true. Form OnKeyDown/OnKeyUp/OnKeyPress handle movement,
firing and P. Handled keys are marked on the event arguments. Enter/Escape
are native dialog commands which bypass KeyPreview in the current runtime;
the Form's Default NewGameButton and Cancel ExitButton route those keys to
Form methods. There are no keyboard handlers on the Canvas.

Sound conversion and preload
----------------------------
generate_assets.py recreates the BASIC shape artwork and writes PCM WAVs
from every SOUND sequence: fire, enemy spawn, hit, extra life, boss intro,
and eight seeded variants of the boss's five random 50-149 Hz tones.
It uses 22050 Hz, mono, 16-bit square waves, short click-reducing ramps,
and duration / 18.2 seconds per BASIC duration value.

Assets/sounds.json lists every frequency, duration, file and actual PCM
length. Assets/*.wav are the editable source assets. The generator also
embeds the WAV bytes and sprite pixels in ShooterAssets.quadoo.
LoadShooterSounds() decodes ALL 13 WAV files to binary objects before
FrameTimer is enabled. Playback uses only:
    Host.PlaySoundAsync(Sounds[name]);
No file reads, tone synthesis, or Base64 decoding occur while firing.

WinMM provides a single replacement channel. Each frame selects the most
important effect: boss arrival, extra life, hit/rumble, spawn, then fire.
Lower-priority effects do not interrupt a higher-priority cue before its
nominal duration. Equal-priority effects may replace one another. The Host
owns a safe playback snapshot; its Boolean result reports that the native
playback call was accepted, not that a listener heard the entire sound.
Closing the application stops its asynchronous sound through Host shutdown.

Port details
------------
The original shooter.bas is included unchanged. The port retains the two
ship colors, missile directions, ten enemy slots, 100 missile slots,
movement bounds, +20 kills, -50 escapes, extra-life milestone condition,
three-minute boss trigger, two reserved boss slots, 50 boss hit points,
+500 boss reward, explosion, and return to ordinary waves.

The original starts its Lives counter at 3 and ends when it drops below 0;
that means four ship losses, and this port retains that rule.

Intentional adaptations:
- The original unrestricted loop, alternating background refresh, and SOUND
  delays had machine-dependent pacing. This version uses nominal 50 ms
  simulation steps; the boss arrives after 3601 unpaused steps (~180 s).
  Windows Timer delivery is not a real-time guarantee and there is no
  catch-up burst after a delayed callback.
- Held movement/fire, pause, automatic pause on focus loss, and New game
  make the Windows UI usable. Original arrow movement was INKEY$-driven.
- An enemy already exploding cannot award another kill. A missile which
  hits the player is consumed once. Escaped enemies do not skip updating
  the remaining enemies in that frame.
- Player explosion is a short animation rather than a blocking PCOPY loop.
- Sprites have transparent black surroundings, a fixed terrain texture,
  and nearest-neighbor approximations of the original EGA drawing commands.
  The random boss sound uses eight pre-generated bursts rather than
  synthesizing an unbounded set of sequences during gameplay.

Project contents and distribution
---------------------------------
ShooterGame.quadoo       UI-independent game state and rules
ShooterForm.quadoo       Form events, Canvas rendering, sound selection
ShooterForm.qform.json   Black Canvas, labels, Timer and dialog buttons
ShooterAssets.quadoo     Generated embedded graphics and WAV preload code
ShooterTests.quadoo      Standalone QVM checks, excluded from GUI project
generate_assets.py       Rebuild asset files / embedded source (Python+Pillow)
Assets/                 Five PNGs, 13 WAVs and sound manifest
shooter.bas              Unmodified QBASIC source
bin/Win32/               Ready-to-run EXE and matching runtime DLLs

Distribute bin\Win32 as a unit. Required files are ShooterExample.exe,
QuadooForm.dll, QuadooVM.dll, QuadooParser.dll, JSON.dll and SimbeyCore.dll.
WINMM.dll and the normal system DLLs are supplied by Windows. The release
binaries require the matching x86 Visual C++ 2008 CRT. No optional plug-in
DLL, external WAV/PNG, project file, or BASIC interpreter is needed at
runtime. All assets are embedded into the compiled application.

To run the console checks, use a matching QVM.exe with ShooterTests.quadoo
from this project folder. They verify movement, timing, collisions, scoring,
boss lifecycle, WAV binary preload and sprite scaling. They do not create
a Form or play audio. The native application is tested separately.

Website: http://localhost/qs/shooter.html
