Add Operation FALSE ALARM adventure compendium

A complete, importable 3-4 hour Delta Green scenario (packs/false-alarm,
Adventure type). Importing creates four 'Operation FALSE ALARM' folders and:
- 3 actors: Dr. Rostova (npc, frozen), Deputy Vance Changed (unnatural),
  The Tear (unnatural, firearms-immune, 1D6/1D20 SAN).
- 6 items: service pistol (3 rounds missing), pipe wrench, corrupted data
  drive, keycard, thermite, cold-weather kit.
- 7 journals: player briefing + 3 image handouts (radar log, static
  waveform, the tear) + 3 GM guides (scenes/clues, NPCs/threat, cover-up).
- 3 scenes with blueprint white-on-black maps and placed NPC tokens:
  the compound, the server room, the radar dome.

All art is hand-authored SVG in the classified-dossier house style
(assets/adventures/false-alarm). Verified importing cleanly in an isolated
Foundry: folders, actors, journals and mapped scenes all materialise.
Also drop an invalid prototypeToken.sight override from the agent build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-15 23:34:51 +01:00
co-authored by Claude Fable 5
parent 1a516d1a55
commit 1e0186a3a8
18 changed files with 727 additions and 0 deletions
+36
View File
@@ -0,0 +1,36 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" font-family="Courier New, monospace">
<defs>
<radialGradient id="cbg" cx="50%" cy="42%" r="70%">
<stop offset="0%" stop-color="#12171c"/><stop offset="100%" stop-color="#050708"/>
</radialGradient>
</defs>
<rect width="1024" height="1024" fill="url(#cbg)"/>
<!-- snow -->
<g fill="#fff" opacity="0.12">
<circle cx="120" cy="200" r="2"/><circle cx="300" cy="500" r="2"/><circle cx="800" cy="260" r="2"/>
<circle cx="650" cy="700" r="2"/><circle cx="900" cy="820" r="2"/><circle cx="200" cy="820" r="2"/>
<circle cx="480" cy="160" r="2"/><circle cx="760" cy="540" r="2"/>
</g>
<!-- dome silhouette -->
<g transform="translate(512 560)">
<circle r="300" fill="#0b0f13" stroke="#aebcc4" stroke-width="4"/>
<g stroke="#2d3a41" stroke-width="2" fill="none">
<polygon points="0,-300 174,-244 174,244 0,300 -174,244 -174,-244"/>
<path d="M0,-300 V300 M-174,-244 L174,244 M174,-244 L-174,244 M-260,0 H260"/>
</g>
<!-- the tear bleeding at center -->
<circle r="70" fill="#8fd4e8" opacity="0.25"/>
<g stroke="#8fd4e8" stroke-width="2" fill="none" opacity="0.85">
<polygon points="0,-70 60,-35 60,35 0,70 -60,35 -60,-35"/>
<polygon points="0,-44 38,-22 38,22 0,44 -38,22 -38,-22" transform="rotate(18)"/>
</g>
<circle r="12" fill="#050708"/>
</g>
<!-- classified banners -->
<rect x="0" y="120" width="1024" height="64" fill="#8aa353"/>
<text x="512" y="164" fill="#0a0d10" font-size="40" text-anchor="middle" letter-spacing="10">TOP SECRET // DELTA GREEN</text>
<text x="512" y="900" fill="#dfe8ee" font-size="72" text-anchor="middle" letter-spacing="6">FALSE ALARM</text>
<text x="512" y="952" fill="#8aa353" font-size="28" text-anchor="middle" letter-spacing="4">OPERATION FILE · EYES ONLY</text>
<rect x="0" y="984" width="1024" height="40" fill="#8b1a12"/>
<text x="512" y="1012" fill="#e8ecee" font-size="22" text-anchor="middle" letter-spacing="6">NORTHERN MAINE · FAA RADAR STATION</text>
</svg>

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -0,0 +1,42 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1200" font-family="Courier New, monospace">
<defs>
<radialGradient id="tear" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#e9f6ff"/>
<stop offset="18%" stop-color="#8fd4e8"/>
<stop offset="45%" stop-color="#3a2a5a"/>
<stop offset="100%" stop-color="#050608"/>
</radialGradient>
<filter id="warp"><feTurbulence type="fractalNoise" baseFrequency="0.012" numOctaves="3" seed="7" result="n"/>
<feDisplacementMap in="SourceGraphic" in2="n" scale="26"/></filter>
</defs>
<rect width="1200" height="1200" fill="#050608"/>
<!-- charged air rings -->
<g stroke="#8fd4e8" fill="none" opacity="0.18">
<circle cx="600" cy="600" r="520"/><circle cx="600" cy="600" r="440"/><circle cx="600" cy="600" r="360"/>
</g>
<!-- fractal silhouette: nested rotating hexagrams + tendrils -->
<g transform="translate(600 600)" filter="url(#warp)">
<circle r="300" fill="url(#tear)"/>
<g stroke="#eafff2" fill="none" stroke-width="2" opacity="0.9">
<polygon points="0,-300 260,-150 260,150 0,300 -260,150 -260,-150"/>
<polygon points="0,-210 182,-105 182,105 0,210 -182,105 -182,-105" transform="rotate(18)"/>
<polygon points="0,-140 121,-70 121,70 0,140 -121,70 -121,-70" transform="rotate(-12)"/>
<polygon points="0,-90 78,-45 78,45 0,90 -78,45 -78,-45" transform="rotate(24)"/>
</g>
<!-- impossible tendrils -->
<g stroke="#8fd4e8" fill="none" stroke-width="3" opacity="0.7">
<path d="M0 -300 C 120 -420 -140 -520 40 -600"/>
<path d="M260 150 C 420 220 520 60 640 260"/>
<path d="M-260 150 C -430 210 -520 380 -640 250"/>
<path d="M0 300 C 80 470 -120 560 60 700"/>
</g>
<!-- singular black core -->
<circle r="46" fill="#050608"/>
<circle r="46" fill="none" stroke="#eafff2" stroke-width="1.5"/>
</g>
<text x="600" y="1120" fill="#8fd4e8" font-size="26" text-anchor="middle" opacity="0.9" letter-spacing="3">IT DOES NOT FIT THE SPACE IT OCCUPIES</text>
<text x="600" y="70" fill="#dfe8ee" font-size="28" text-anchor="middle" letter-spacing="2">THE TEAR — DO NOT SHOW UNTIL CLIMAX</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,34 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1200 1200" font-family="Courier New, monospace">
<rect width="1200" height="1200" fill="#04160c"/>
<!-- radar scope -->
<g transform="translate(600 600)">
<circle r="520" fill="#061e10" stroke="#1f7a44" stroke-width="3"/>
<g stroke="#1f7a44" stroke-width="2" fill="none" opacity="0.7">
<circle r="130"/><circle r="260"/><circle r="390"/><circle r="500"/>
<path d="M-520 0 H520 M0 -520 V520 M-368 -368 L368 368 M368 -368 L-368 368"/>
</g>
<!-- range labels -->
<g fill="#3fdc7a" font-size="20" opacity="0.8">
<text x="140" y="-6">40</text><text x="270" y="-6">80</text><text x="400" y="-6">120</text>
</g>
<!-- sweep -->
<path d="M0 0 L360 -260" stroke="#6bffa6" stroke-width="4" opacity="0.9"/>
<path d="M0 0 L360 -260 A440 440 0 0 1 300 320 Z" fill="#3fdc7a" opacity="0.10"/>
<!-- normal contacts -->
<circle cx="-180" cy="120" r="7" fill="#6bffa6"/>
<circle cx="90" cy="-300" r="7" fill="#6bffa6"/>
<!-- THE GEOMETRIC SHADOW over the dish (center) -->
<g opacity="0.95">
<polygon points="0,-90 78,-45 78,45 0,90 -78,45 -78,-45" fill="none" stroke="#eafff2" stroke-width="3"/>
<polygon points="0,-60 52,-30 52,30 0,60 -52,30 -52,-30" fill="none" stroke="#eafff2" stroke-width="2"/>
<polygon points="0,-30 26,-15 26,15 0,30 -26,15 -26,-15" fill="#eafff2" opacity="0.35"/>
<path d="M0,-90 L0,90 M-78,-45 L78,45 M78,-45 L-78,45" stroke="#eafff2" stroke-width="1.5"/>
</g>
<text x="0" y="150" fill="#eafff2" font-size="22" text-anchor="middle" opacity="0.9">UNRESOLVED CONTACT</text>
<text x="0" y="178" fill="#3fdc7a" font-size="18" text-anchor="middle" opacity="0.8">no transponder · no motion vector</text>
</g>
<!-- header/footer text -->
<text x="40" y="60" fill="#3fdc7a" font-size="26">FAA ASR-11 // TERMINAL LOG — FINAL FRAMES</text>
<text x="40" y="1130" fill="#eafff2" font-size="22">CLASSIFICATION: &#8220;GEOMETRIC SHADOW&#8221; · alt: N/A · hdg: N/A</text>
<text x="40" y="1165" fill="#d4553c" font-size="22">SYSTEM FAULT 0xDEAD — CORRUPTED DATA LOOP — CRASH</text>
</svg>

After

Width:  |  Height:  |  Size: 2.1 KiB

@@ -0,0 +1,38 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1600 900" font-family="Courier New, monospace">
<rect width="1600" height="900" fill="#070a0c"/>
<!-- frame -->
<rect x="40" y="120" width="1520" height="560" fill="#0a1013" stroke="#2d3a41" stroke-width="2"/>
<line x1="40" y1="400" x2="1560" y2="400" stroke="#1d2a30" stroke-width="1"/>
<!-- oscillating static waveform -->
<path fill="none" stroke="#8fd4e8" stroke-width="2.5" opacity="0.9"
d="M40 400
Q 90 300 140 400 T 240 400 T 340 400
Q 390 180 440 400 T 540 400
Q 590 250 640 400 T 740 400 T 840 400
Q 890 120 940 400 T 1040 400
Q 1090 300 1140 400 T 1240 400
Q 1290 200 1340 400 T 1440 400 T 1540 400"/>
<!-- dense noise overlay -->
<g stroke="#4a6b78" stroke-width="1" opacity="0.5">
<path d="M40 400 L60 340 L80 460 L100 360 L120 440 L140 380 L160 420 L180 350 L200 450 L220 390 L240 410
L260 330 L280 470 L300 370 L320 430 L340 400 L360 460 L380 340 L400 420 L420 380 L440 440
L460 360 L480 450 L500 390 L520 410 L540 330 L560 470 L580 370 L600 430 L620 400 L640 460
L660 340 L680 420 L700 380 L720 440 L740 360 L760 450 L780 390 L800 410 L820 330 L840 470
L860 370 L880 430 L900 400 L920 460 L940 340 L960 420 L980 380 L1000 440 L1020 360 L1040 450
L1060 390 L1080 410 L1100 330 L1120 470 L1140 370 L1160 430 L1180 400 L1200 460 L1220 340
L1240 420 L1260 380 L1280 440 L1300 360 L1320 450 L1340 390 L1360 410 L1380 330 L1400 470
L1420 370 L1440 430 L1460 400 L1480 460 L1500 340 L1520 420 L1540 380"/>
</g>
<!-- hidden low-frequency human voice envelope -->
<path fill="none" stroke="#8aa353" stroke-width="4" opacity="0.85"
d="M40 620 Q 300 540 560 620 T 1080 620 T 1560 600"/>
<text x="60" y="720" fill="#8aa353" font-size="26">▁▂ SUB-CARRIER — HUMAN VOICE (rev. filter) ▂▁</text>
<text x="60" y="758" fill="#dfe8ee" font-size="30" letter-spacing="4">&#8220; . . . it is saying our names . . . &#8221;</text>
<!-- header -->
<text x="40" y="70" fill="#dfe8ee" font-size="30" letter-spacing="2">FINAL TRANSMISSION — AUDIO CAPTURE</text>
<text x="40" y="102" fill="#5c6b73" font-size="20">rhythmic oscillating static · 00:00:06 loop · SAN 0/1D4 to listen closely</text>
<text x="40" y="860" fill="#5c6b73" font-size="18">amplitude (dB) vs time · buried voiceprint isolated at 38 Hz</text>
</svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

@@ -0,0 +1,107 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2600 1800" font-family="Courier New, monospace">
<defs>
<radialGradient id="snow" cx="50%" cy="42%" r="75%">
<stop offset="0%" stop-color="#12171c"/>
<stop offset="100%" stop-color="#080a0d"/>
</radialGradient>
<pattern id="grid" width="100" height="100" patternUnits="userSpaceOnUse">
<path d="M100 0H0V100" fill="none" stroke="#1d2a30" stroke-width="1"/>
</pattern>
<g id="pine">
<path d="M0 60 L20 -10 L40 60 Z M6 30 L20 -30 L34 30 Z" fill="#c9d6dd" opacity="0.5"/>
</g>
<g id="print">
<ellipse cx="0" cy="0" rx="11" ry="22" fill="#8aa353" opacity="0.85"/>
</g>
</defs>
<rect width="2600" height="1800" fill="url(#snow)"/>
<rect width="2600" height="1800" fill="url(#grid)"/>
<!-- snow speckle -->
<g fill="#ffffff" opacity="0.10">
<circle cx="220" cy="330" r="2"/><circle cx="480" cy="900" r="2"/><circle cx="900" cy="220" r="2"/>
<circle cx="1500" cy="1500" r="2"/><circle cx="2100" cy="500" r="2"/><circle cx="1900" cy="1300" r="2"/>
<circle cx="700" cy="1400" r="2"/><circle cx="1300" cy="700" r="2"/><circle cx="2300" cy="1000" r="2"/>
<circle cx="360" cy="1200" r="2"/><circle cx="1750" cy="300" r="2"/><circle cx="1100" cy="1250" r="2"/>
</g>
<!-- tree line -->
<g>
<use href="#pine" x="120" y="150"/><use href="#pine" x="240" y="120"/><use href="#pine" x="360" y="160"/>
<use href="#pine" x="1900" y="130"/><use href="#pine" x="2020" y="160"/><use href="#pine" x="2140" y="120"/><use href="#pine" x="2260" y="160"/>
<use href="#pine" x="120" y="1600"/><use href="#pine" x="260" y="1630"/><use href="#pine" x="2200" y="1600"/><use href="#pine" x="2360" y="1620"/>
<use href="#pine" x="2380" y="700"/><use href="#pine" x="2420" y="850"/><use href="#pine" x="2400" y="1000"/>
</g>
<!-- perimeter fence -->
<g stroke="#aebcc4" stroke-width="4" fill="none">
<path d="M400 400 H2200 V1400 H1500"/>
<path d="M1100 1400 H400 V400"/>
<!-- chain-link hatch -->
</g>
<g stroke="#5c6b73" stroke-width="1" opacity="0.6">
<path d="M400 400 L2200 400 M400 500 L2200 500 M400 1400 L2200 1400"/>
</g>
<!-- CUT in fence (from inside out) -->
<path d="M400 760 V1040" stroke="#0a0d10" stroke-width="14"/>
<path d="M360 780 L440 900 L360 1020" stroke="#8aa353" stroke-width="4" fill="none"/>
<text x="150" y="900" fill="#8aa353" font-size="30" transform="rotate(-90 150 900)">FENCE — CUT OUTWARD</text>
<!-- gate + cruiser -->
<path d="M1100 1400 H1500" stroke="#8aa353" stroke-width="6" stroke-dasharray="24 14"/>
<text x="1160" y="1470" fill="#8aa353" font-size="28">GATE (OPEN)</text>
<g transform="translate(1300 1230)">
<rect x="-70" y="-120" width="140" height="240" rx="22" fill="none" stroke="#dfe8ee" stroke-width="4"/>
<rect x="-52" y="-92" width="104" height="80" fill="none" stroke="#9fb0b8" stroke-width="3"/>
<rect x="-52" y="18" width="104" height="80" fill="none" stroke="#9fb0b8" stroke-width="3"/>
<!-- open driver door -->
<path d="M-70 -30 L-150 -70 L-150 20 L-70 40" fill="none" stroke="#dfe8ee" stroke-width="4"/>
<text x="0" y="150" fill="#9fb0b8" font-size="24" text-anchor="middle">S.O. CRUISER</text>
<text x="0" y="176" fill="#5c6b73" font-size="18" text-anchor="middle">door open · frozen · engine dead</text>
</g>
<!-- unnatural footprints: driver door into woods, 8ft (~2.4sq) strides -->
<g>
<use href="#print" x="1140" y="1200" transform="rotate(-24 1140 1200)"/>
<use href="#print" x="1010" y="1050" transform="rotate(-24 1010 1050)"/>
<use href="#print" x="880" y="900" transform="rotate(-24 880 900)"/>
<use href="#print" x="750" y="760" transform="rotate(-24 750 760)"/>
<use href="#print" x="600" y="640" transform="rotate(-30 600 640)"/>
<use href="#print" x="470" y="540" transform="rotate(-30 470 540)"/>
<text x="640" y="1000" fill="#8aa353" font-size="24" transform="rotate(-24 640 1000)">STRIDES ~8 FT APART</text>
</g>
<!-- concrete equipment building -->
<g transform="translate(760 720)">
<rect x="0" y="0" width="360" height="260" fill="#0f1418" stroke="#dfe8ee" stroke-width="5"/>
<rect x="20" y="20" width="320" height="220" fill="none" stroke="#3a474e" stroke-width="2"/>
<rect x="150" y="240" width="60" height="20" fill="#8aa353"/>
<text x="180" y="140" fill="#9fb0b8" font-size="26" text-anchor="middle">EQUIPMENT</text>
<text x="180" y="172" fill="#9fb0b8" font-size="26" text-anchor="middle">BUILDING</text>
<text x="180" y="284" fill="#8aa353" font-size="18" text-anchor="middle">DOOR</text>
</g>
<!-- radar dome (geodesic) -->
<g transform="translate(1750 780)">
<circle r="290" fill="#0c1116" stroke="#dfe8ee" stroke-width="6"/>
<g stroke="#5c6b73" stroke-width="2" fill="none" opacity="0.9">
<polygon points="0,-290 168,-236 168,236 0,290 -168,236 -168,-236"/>
<polygon points="0,-160 140,-80 140,80 0,160 -140,80 -140,-80"/>
<path d="M0,-290 L0,290 M-168,-236 L168,236 M168,-236 L-168,236"/>
<path d="M0,-160 L168,-236 M0,-160 L-168,-236 M140,-80 L168,236 M-140,-80 L-168,236 M140,80 L0,290 M-140,80 L0,290"/>
</g>
<circle r="26" fill="#8aa353"/>
<text x="0" y="350" fill="#dfe8ee" font-size="30" text-anchor="middle">RADAR DOME</text>
<text x="0" y="382" fill="#5c6b73" font-size="20" text-anchor="middle">geodesic · catwalk within</text>
</g>
<!-- title block -->
<g transform="translate(40 40)">
<rect width="560" height="120" fill="#0a0d10" stroke="#8aa353" stroke-width="2"/>
<text x="20" y="46" fill="#dfe8ee" font-size="34" letter-spacing="3">FAA RADAR STA. — EXTERIOR</text>
<text x="20" y="80" fill="#8aa353" font-size="22" letter-spacing="2">OPERATION: FALSE ALARM</text>
<text x="20" y="106" fill="#5c6b73" font-size="18">Northern Maine · heavy snow · 1 sq = 5 ft</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

@@ -0,0 +1,66 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000" font-family="Courier New, monospace">
<defs>
<pattern id="grid3" width="100" height="100" patternUnits="userSpaceOnUse">
<path d="M100 0H0V100" fill="none" stroke="#161f24" stroke-width="1"/>
</pattern>
<radialGradient id="ozone" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#1a1420"/>
<stop offset="70%" stop-color="#0a0d10"/>
<stop offset="100%" stop-color="#060809"/>
</radialGradient>
</defs>
<rect width="2000" height="2000" fill="url(#ozone)"/>
<rect width="2000" height="2000" fill="url(#grid3)"/>
<!-- outer dome ring -->
<circle cx="1000" cy="1000" r="880" fill="none" stroke="#dfe8ee" stroke-width="6"/>
<circle cx="1000" cy="1000" r="700" fill="none" stroke="#3a474e" stroke-width="2" stroke-dasharray="10 12"/>
<!-- geodesic struts -->
<g stroke="#2d3a41" stroke-width="2" fill="none">
<path d="M1000 120 V1880 M120 1000 H1880"/>
<path d="M380 380 L1620 1620 M1620 380 L380 1620"/>
<path d="M1000 120 L1620 380 L1880 1000 L1620 1620 L1000 1880 L380 1620 L120 1000 L380 380 Z"/>
</g>
<!-- catwalk ring -->
<circle cx="1000" cy="1000" r="560" fill="none" stroke="#8aa353" stroke-width="10" stroke-dasharray="2 0" opacity="0.9"/>
<circle cx="1000" cy="1000" r="500" fill="none" stroke="#8aa353" stroke-width="10" opacity="0.9"/>
<text x="1000" y="480" fill="#8aa353" font-size="26" text-anchor="middle">UPPER CATWALK (pitch black)</text>
<!-- central transmitter mast -->
<g transform="translate(1000 1000)">
<circle r="150" fill="#0d1216" stroke="#dfe8ee" stroke-width="5"/>
<circle r="150" fill="none" stroke="#d4553c" stroke-width="2" opacity="0.6"/>
<path d="M-150 0 A150 150 0 0 1 150 0" fill="none" stroke="#6b93b8" stroke-width="3"/>
<path d="M0 -150 V150 M-150 0 H150 M-106 -106 L106 106 M106 -106 L-106 106" stroke="#3a474e" stroke-width="2"/>
<circle r="40" fill="#8aa353"/>
<text x="0" y="230" fill="#dfe8ee" font-size="26" text-anchor="middle">CENTRAL TRANSMITTER</text>
<text x="0" y="262" fill="#5c6b73" font-size="18" text-anchor="middle">Vance is rewiring this</text>
</g>
<!-- ladder access down -->
<g transform="translate(1000 1500)">
<rect x="-34" y="-50" width="68" height="120" fill="none" stroke="#8aa353" stroke-width="4"/>
<path d="M-34 -20 H34 M-34 20 H34" stroke="#8aa353" stroke-width="3"/>
<text x="0" y="110" fill="#8aa353" font-size="22" text-anchor="middle">↓ LADDER (from server room)</text>
</g>
<!-- main transformer (climax overload point) -->
<g transform="translate(560 1000)">
<rect x="-90" y="-90" width="180" height="180" fill="#0d1216" stroke="#d4553c" stroke-width="5"/>
<path d="M-30 -50 L10 0 L-10 0 L30 50" fill="none" stroke="#d4553c" stroke-width="6"/>
<text x="0" y="140" fill="#d4553c" font-size="22" text-anchor="middle">MAIN TRANSFORMER</text>
<text x="0" y="166" fill="#5c6b73" font-size="16" text-anchor="middle">manual overload = lethal shock</text>
</g>
<!-- ozone / scorched copper note -->
<text x="1000" y="1560" fill="#7a6b3a" font-size="22" text-anchor="middle" opacity="0.8">smell: ozone · scorched copper</text>
<!-- title -->
<g transform="translate(40 40)">
<rect width="540" height="120" fill="#0a0d10" stroke="#8aa353" stroke-width="2"/>
<text x="20" y="46" fill="#dfe8ee" font-size="32" letter-spacing="2">RADAR DOME — CATWALK</text>
<text x="20" y="80" fill="#8aa353" font-size="22">INTERIOR · TOTAL DARKNESS</text>
<text x="20" y="106" fill="#5c6b73" font-size="18">bring your own light · 1 sq = 5 ft</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

@@ -0,0 +1,68 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1800 1400" font-family="Courier New, monospace">
<defs>
<pattern id="grid2" width="100" height="100" patternUnits="userSpaceOnUse">
<path d="M100 0H0V100" fill="none" stroke="#1d2a30" stroke-width="1"/>
</pattern>
</defs>
<rect width="1800" height="1400" fill="#080a0d"/>
<rect width="1800" height="1400" fill="url(#grid2)"/>
<!-- room shell -->
<rect x="200" y="200" width="1400" height="1000" fill="#0d1216" stroke="#dfe8ee" stroke-width="6"/>
<!-- door bottom -->
<rect x="820" y="1180" width="160" height="24" fill="#8aa353"/>
<text x="900" y="1250" fill="#8aa353" font-size="24" text-anchor="middle">DOOR TO EXTERIOR</text>
<!-- server racks along top wall -->
<g stroke="#9fb0b8" stroke-width="3" fill="none">
<rect x="260" y="240" width="120" height="220"/><rect x="400" y="240" width="120" height="220"/>
<rect x="540" y="240" width="120" height="220"/><rect x="680" y="240" width="120" height="220"/>
<g stroke="#3a474e" stroke-width="2">
<path d="M260 300 H380 M260 360 H380 M260 420 H380"/>
<path d="M400 300 H520 M400 360 H520 M400 420 H520"/>
<path d="M540 300 H660 M540 360 H660 M540 420 H660"/>
<path d="M680 300 H800 M680 360 H800 M680 420 H800"/>
</g>
</g>
<text x="530" y="510" fill="#5c6b73" font-size="22" text-anchor="middle">SERVER RACKS</text>
<!-- main console (center) with body -->
<g transform="translate(900 720)">
<rect x="-260" y="-70" width="520" height="140" rx="10" fill="#0f1519" stroke="#dfe8ee" stroke-width="5"/>
<rect x="-240" y="-54" width="150" height="108" fill="none" stroke="#6b93b8" stroke-width="3"/>
<rect x="-70" y="-54" width="150" height="108" fill="none" stroke="#6b93b8" stroke-width="3"/>
<rect x="100" y="-54" width="150" height="108" fill="none" stroke="#6b93b8" stroke-width="3"/>
<text x="0" y="-96" fill="#9fb0b8" font-size="24" text-anchor="middle">MAIN CONSOLE</text>
<!-- Rostova, frozen, slumped -->
<g opacity="0.95">
<ellipse cx="0" cy="120" rx="70" ry="44" fill="#12303a" stroke="#8fd4e8" stroke-width="3"/>
<circle cx="0" cy="96" r="26" fill="#12303a" stroke="#8fd4e8" stroke-width="3"/>
<path d="M-40 150 L40 150" stroke="#8fd4e8" stroke-width="2"/>
</g>
<text x="0" y="205" fill="#8fd4e8" font-size="22" text-anchor="middle">DR. ROSTOVA — FROZEN SOLID</text>
</g>
<!-- failing backup generator (corner) -->
<g transform="translate(1360 980)">
<rect x="-120" y="-90" width="240" height="180" fill="#0f1519" stroke="#dfe8ee" stroke-width="5"/>
<circle cx="0" cy="0" r="46" fill="none" stroke="#d4553c" stroke-width="4"/>
<path d="M-30 0 L30 0 M0 -30 L0 30" stroke="#d4553c" stroke-width="4"/>
<text x="0" y="130" fill="#d4553c" font-size="22" text-anchor="middle">BACKUP GENERATOR</text>
<text x="0" y="156" fill="#5c6b73" font-size="16" text-anchor="middle">failing · hums unsteadily</text>
</g>
<!-- ladder up to dome catwalk -->
<g transform="translate(300 980)">
<rect x="-30" y="-90" width="60" height="180" fill="none" stroke="#8aa353" stroke-width="4"/>
<path d="M-30 -60 H30 M-30 -20 H30 M-30 20 H30 M-30 60 H30" stroke="#8aa353" stroke-width="3"/>
<text x="0" y="130" fill="#8aa353" font-size="20" text-anchor="middle">LADDER ↑ DOME</text>
</g>
<!-- title -->
<g transform="translate(220 40)">
<rect width="520" height="120" fill="#0a0d10" stroke="#8aa353" stroke-width="2"/>
<text x="20" y="46" fill="#dfe8ee" font-size="32" letter-spacing="2">EQUIPMENT BLDG — INTERIOR</text>
<text x="20" y="80" fill="#8aa353" font-size="22">SERVER ROOM · ~40°F</text>
<text x="20" y="106" fill="#5c6b73" font-size="18">emergency power only · 1 sq = 5 ft</text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

@@ -0,0 +1,25 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<defs>
<radialGradient id="tk" cx="50%" cy="50%" r="50%">
<stop offset="0%" stop-color="#eafaff"/><stop offset="20%" stop-color="#8fd4e8"/>
<stop offset="55%" stop-color="#33245a"/><stop offset="100%" stop-color="#050608"/>
</radialGradient>
<filter id="w2"><feTurbulence type="fractalNoise" baseFrequency="0.02" numOctaves="2" seed="4" result="n"/>
<feDisplacementMap in="SourceGraphic" in2="n" scale="16"/></filter>
</defs>
<rect width="512" height="512" fill="#050608"/>
<g transform="translate(256 256)" filter="url(#w2)">
<circle r="150" fill="url(#tk)"/>
<g stroke="#eafff2" fill="none" stroke-width="2" opacity="0.9">
<polygon points="0,-150 130,-75 130,75 0,150 -130,75 -130,-75"/>
<polygon points="0,-100 87,-50 87,50 0,100 -87,50 -87,-50" transform="rotate(20)"/>
<polygon points="0,-60 52,-30 52,30 0,60 -52,30 -52,-30" transform="rotate(-14)"/>
</g>
<g stroke="#8fd4e8" fill="none" stroke-width="2" opacity="0.7">
<path d="M0 -150 C 60 -210 -70 -250 20 -300"/>
<path d="M130 75 C 210 110 250 30 320 130"/>
<path d="M-130 75 C -210 110 -250 190 -320 120"/>
</g>
<circle r="24" fill="#050608"/><circle r="24" fill="none" stroke="#eafff2" stroke-width="1.5"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

@@ -0,0 +1,21 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<rect width="512" height="512" fill="#08171c"/>
<!-- frost bloom -->
<g stroke="#bfeaf6" stroke-width="1" opacity="0.35">
<path d="M40 40 L110 110 M472 40 L402 110 M40 472 L110 402 M472 472 L402 402"/>
<path d="M256 20 V70 M256 492 V442 M20 256 H70 M492 256 H442"/>
</g>
<g fill="#cfe9f2">
<path d="M256 320c-72 0-120 32-136 92-6 22-9 44-11 100h294c-2-56-5-78-11-100-16-60-64-92-136-92z"/>
<path d="M232 314h48v40h-48z"/>
<path d="M256 118c-46 0-76 34-76 86 0 48 32 88 76 88s76-40 76-88c0-52-30-86-76-86z"/>
</g>
<!-- wide frozen eyes -->
<g fill="#08171c"><circle cx="224" cy="196" r="15"/><circle cx="288" cy="196" r="15"/></g>
<g fill="#8fd4e8"><circle cx="224" cy="196" r="6"/><circle cx="288" cy="196" r="6"/></g>
<!-- lips torn from teeth: exposed grimace -->
<path d="M214 250 Q256 236 298 250 L298 264 Q256 250 214 264 Z" fill="#08171c"/>
<g stroke="#08171c" stroke-width="2"><path d="M226 250 V264 M242 248 V266 M256 247 V266 M270 248 V266 M286 250 V264"/></g>
<!-- ice sheen -->
<path d="M180 150 Q256 120 332 150" stroke="#eafcff" stroke-width="3" fill="none" opacity="0.5"/>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<rect width="512" height="512" fill="#0a0d10"/>
<g fill="#e8ecee">
<!-- deputy shoulders -->
<path d="M256 322c-72 0-120 32-136 92-6 22-9 44-11 98h294c-2-54-5-76-11-98-16-60-64-92-136-92z"/>
<path d="M232 316h48v40h-48z"/>
<!-- head, gaunt -->
<path d="M256 120c-46 0-74 34-74 86 0 30 12 58 30 74l88 0c18-16 30-44 30-74 0-52-28-86-74-86z"/>
</g>
<!-- gouged-out eyes: dark hollows with runnels -->
<g fill="#0a0d10">
<ellipse cx="222" cy="196" rx="20" ry="26"/><ellipse cx="290" cy="196" rx="20" ry="26"/>
</g>
<g stroke="#8b1a12" stroke-width="6" fill="none" opacity="0.9">
<path d="M222 220 C 218 260 214 300 210 340"/>
<path d="M290 220 C 294 260 298 300 302 340"/>
</g>
<!-- UNHINGED JAW: mouth stretched grotesquely open -->
<path d="M210 250 C 230 360 282 360 302 250 C 300 300 286 330 256 330 C 226 330 212 300 210 250Z" fill="#0a0d10"/>
<g stroke="#e8ecee" stroke-width="3">
<path d="M222 262 V300 M238 268 V320 M256 270 V330 M274 268 V320 M290 262 V300"/>
</g>
<!-- static halo -->
<g stroke="#6b93b8" stroke-width="2" fill="none" opacity="0.35">
<circle cx="256" cy="230" r="150"/><circle cx="256" cy="230" r="180"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

+245
View File
@@ -0,0 +1,245 @@
// Build the "Operation FALSE ALARM" adventure compendium (packs/false-alarm).
// Produces a single Adventure document with inline folders, actors, items,
// journal entries and scenes, then compiles it with foundryvtt-cli.
import { compilePack } from "/Users/timevans/Git/VRPG/foundry/vanity/node_modules/@foundryvtt/foundryvtt-cli/index.mjs";
import fs from "fs";
import path from "path";
const REPO = "/Users/timevans/Git/deltagreen2";
const OUT = "/private/tmp/claude-501/-Users-timevans-Library-Application-Support-FoundryVTT/0078d6e6-126b-4f13-b7f4-86feaf7bfbbc/scratchpad/fasrc";
const PACK = `${REPO}/packs/false-alarm`;
const A = "systems/deltagreen2/assets/adventures/false-alarm";
const STATS = { systemId: "deltagreen2", systemVersion: "2.0.0" };
// deterministic 16-char id
function id(seed) {
let h = 0x811c9dc5;
for (let i = 0; i < seed.length; i++) { h ^= seed.charCodeAt(i); h = Math.imul(h, 0x01000193) >>> 0; }
let s = "", x = h; const c = "abcdefghijklmnopqrstuvwxyz0123456789";
for (let i = 0; i < 16; i++) { x = Math.imul(x ^ (x >>> 15), 0x2c1b3c6d) >>> 0; s += c[x % c.length]; }
return s;
}
const GREEN = "#556b2f";
// ---------------------------------------------------------------- folders
function folder(name, type) {
return { _id: id(`fa-folder-${type}-${name}`), name, type, folder: null, sort: 0, color: GREEN, description: "", flags: {}, _stats: STATS };
}
const fActors = folder("Operation FALSE ALARM", "Actor");
const fItems = folder("Operation FALSE ALARM", "Item");
const fJournal = folder("Operation FALSE ALARM", "JournalEntry");
const fScenes = folder("Operation FALSE ALARM", "Scene");
const folders = [fActors, fItems, fJournal, fScenes];
// ---------------------------------------------------------------- items
function skillsAllZero() { return {}; } // rely on schema defaults
let itemSort = 0;
function gearItem(name, icon, expense, desc) {
return {
_id: id(`fa-item-${name}`), name, type: "gear",
img: `systems/deltagreen2/assets/icons/${icon}.svg`,
system: { description: `<p>${desc}</p>`, equipped: false, expense },
folder: fItems._id, sort: (itemSort += 100), ownership: { default: 0 }, flags: {}, effects: [], _stats: STATS,
};
}
function weaponItem(name, icon, sys, desc) {
return {
_id: id(`fa-item-${name}`), name, type: "weapon",
img: `systems/deltagreen2/assets/icons/${icon}.svg`,
system: { description: `<p>${desc}</p>`, skill: "firearms", range: "15M", damage: "1D10", armorPiercing: 0, lethality: 0, isLethal: false, killRadius: "", ammo: 0, ammoMax: 0, expense: "Standard", equipped: false, ...sys },
folder: fItems._id, sort: (itemSort += 100), ownership: { default: 0 }, flags: {}, effects: [], _stats: STATS,
};
}
const items = [
weaponItem("Deputy Vance's Service Pistol", "glock",
{ skill: "firearms", damage: "1D10", ammo: 12, ammoMax: 15, range: "15M" },
"A .40 S&amp;W duty pistol, found in the cruiser footwell. <b>Three rounds are missing and there are no bullet holes in the car.</b> Whatever he fired at, he fired at outside — or at nothing. Evidence."),
weaponItem("Heavy Pipe Wrench", "forcible-entry-tool",
{ skill: "melee_weapons", damage: "1D10+2", range: "0M", ammo: 0, ammoMax: 0 },
"A two-foot steel maintenance wrench. In the Changed Vance's grip it swings with impossible speed. Melee weapon."),
gearItem("Corrupted Data Drive", "software", "Standard",
"The radar facility's primary log drive. Its final frames recorded a &#8220;geometric shadow&#8221; hovering over the dish — not an aircraft. <b>Must be wiped or destroyed</b> as part of the cover-up (see the FAA cloud backup). Computer Science to read; SIGINT/Science to analyse the audio."),
gearItem("Dr. Rostova's Keycard &amp; ID", "papers", "Trivial",
"FAA contractor credentials for Dr. Elena Rostova, sole on-site technician. Opens the equipment building and the console. Her emergency contact is a sister in Vermont — a loose end for the cover-up."),
gearItem("Thermite Charge", "anfo", "Unusual",
"Incendiary supplied for sanitation. Burns hot enough to reduce a frozen body — or a data drive — to slag. Needed to dispose of Dr. Rostova's remains without leaving forensic questions."),
gearItem("Cold-Weather Field Kit", "camping-gear", "Standard",
"Parkas, chemical heat packs, headlamps and rope. The dome interior is pitch black and the whole site is well below freezing; Agents without light and warmth accrue penalties over a long night."),
];
// ---------------------------------------------------------------- actors
function npcActor({ key, name, type, img, token, stats, health, skills, notes, shortDescription, san, weapons }) {
const _id = id(`fa-actor-${key}`);
const sys = {
statistics: Object.fromEntries(Object.entries(stats).map(([k, v]) => [k, { value: v }])),
health: { value: health, min: 0, max: health },
wp: { value: stats.pow, min: 0, max: stats.pow },
skills: Object.fromEntries(Object.entries(skills || {}).map(([k, v]) => [k, { proficiency: v }])),
notes, shortDescription, showUntrainedSkills: false,
};
if (type === "npc") sys.sanity = { value: san ?? 0, currentBreakingPoint: 101 };
else sys.sanity = { notes: "", failedLoss: san?.failed ?? "1D4", successLoss: san?.success ?? "1" };
const embeddedItems = (weapons || []).map((w) => {
const wid = id(`fa-actor-${key}-wpn-${w.name}`);
return { _id: wid, _key: `!actors.items!${_id}.${wid}`, name: w.name, type: "weapon",
img: `systems/deltagreen2/assets/icons/${w.icon}.svg`,
system: { description: "", skill: w.skill, range: w.range || "0M", damage: w.damage, armorPiercing: w.ap || 0, lethality: w.lethality || 0, isLethal: !!w.lethality, killRadius: "", ammo: w.ammo || 0, ammoMax: w.ammoMax || 0, expense: "Standard", equipped: true } };
});
return {
_id, _key: `!actors!${_id}`, name, type, img,
system: sys,
prototypeToken: { name, texture: { src: token, anchorX: 0.5, anchorY: 0.5, fit: "contain", scaleX: 1, scaleY: 1 }, width: 1, height: 1, displayName: 20, disposition: type === "npc" ? 0 : -1, actorLink: false },
items: embeddedItems, effects: [], folder: fActors._id, sort: 0, ownership: { default: 0 }, flags: {}, _stats: STATS,
};
}
const aRostova = npcActor({
key: "rostova", name: "Dr. Elena Rostova", type: "npc",
img: `${A}/token-rostova.svg`, token: `${A}/token-rostova.svg`,
stats: { str: 10, con: 10, dex: 11, int: 15, pow: 12, cha: 11 }, health: 10, san: 0,
skills: { computer_science: 70, science: 0, sigint: 40, first_aid: 30, alertness: 40 },
shortDescription: "FAA facility technician — deceased, flash-frozen.",
notes: "<h2>Dr. Elena Rostova (deceased)</h2><p>The facility's sole on-site technician, found slumped over the main console <b>frozen solid</b> despite the room reading roughly 40&deg;F. Her eyes are wide; her lips are torn away from her teeth in a rictus. There is no external cause of death a coroner could accept.</p><p><b>What she knew:</b> She logged the corrupted radar return and played back the final transmission before she died. The audio killed her — or something reached back down it. She is a clue, not a combatant.</p><p><em>Cover-up:</em> her remains must be burned (Thermite Charge) and her FAA records and next-of-kin contact managed.</p>",
});
const aVance = npcActor({
key: "vance", name: "Deputy Marcus Vance (Changed)", type: "unnatural",
img: `${A}/token-vance.svg`, token: `${A}/token-vance.svg`,
stats: { str: 17, con: 16, dex: 16, int: 6, pow: 15, cha: 3 }, health: 16,
san: { failed: "1D6", success: "1" },
skills: { alertness: 60, athletics: 70, dodge: 50, melee_weapons: 65, unarmed_combat: 55 },
shortDescription: "The blinded deputy — no longer entirely human.",
weapons: [{ name: "Pipe Wrench (frenzied)", icon: "forcible-entry-tool", skill: "melee_weapons", damage: "1D10+2", range: "0M" }],
notes: "<h2>Deputy Marcus Vance — Changed</h2><p>Six hours ago a small-town deputy. Now he is <b>blind — he gouged out his own eyes</b> — yet moves with terrifying, jerky speed, rewiring the central transmitter by feel to broadcast the static loop at maximum amplification. His jaw unhinges abnormally when he screams: he recites the radar's technical manual <b>backward</b>.</p><h3>Combat</h3><ul><li><b>HP</b> 16 &middot; <b>Wrench</b> 1D10+2, attacks at Melee 65%.</li><li><b>Unnatural speed:</b> acts twice per round; blindness gives him no penalty in the pitch-black dome — the Agents' lights do not help him and do not hinder him.</li><li>He is not trying to kill the Agents. He is trying to <b>finish the broadcast.</b> He only attacks those who interpose.</li></ul><h3>Sanity</h3><p>Witnessing the Changed Vance costs <b>1/1D6 SAN</b>. He can still be dropped by gunfire — he is flesh — but doing so does not stop the transmitter he has already wired.</p>",
});
const aTear = npcActor({
key: "tear", name: "The Tear (Static Between Stations)", type: "unnatural",
img: `${A}/token-entity.svg`, token: `${A}/token-entity.svg`,
stats: { str: 20, con: 30, dex: 20, int: 20, pow: 25, cha: 1 }, health: 30,
san: { failed: "1D20", success: "1D6" },
skills: { alertness: 90, dodge: 80 },
shortDescription: "An extradimensional entity that mimics radio frequencies.",
notes: "<h2>The Tear &mdash; the Static Between Stations</h2><p>Not a creature so much as a <b>place that should not touch this one</b>: a shifting, non-Euclidean fractal silhouette that bleeds through the centre of the radar dish when the broadcast peaks. The air turns static-charged; hair stands on end; the temperature crashes.</p><h3>Rules</h3><ul><li><b>Firearms are useless.</b> Bullets pass through it and <b>warp out of shape</b>, doing nothing. Do not roll damage.</li><li><b>Presence:</b> being in its manifested presence costs <b>1D6/1D20 SAN</b>. Prolonged exposure (a round overloading the transformer beside it) is the full 1D20 branch.</li><li>It draws humans toward the freezing dark by <b>mimicking frequencies</b> — the corrupted radar return, the sub-carrier voice whispering the Agents' own names.</li></ul><h3>The only resolution: cut the power</h3><p>The tear cannot be fought, only <b>closed</b> by killing the broadcast:</p><ul><li><b>Blow the backup generator outside</b> &mdash; a hazardous run through the dark and snow (Demolitions, or improvised; STR/Athletics to reach it, exposure to Vance en route), or</li><li><b>Manually overload the main transformer inside the dome</b> &mdash; the Agent doing so takes <b>lethal electrical shock</b> (Lethality; survivors are badly hurt) and the full <b>1D6/1D20 SAN</b> exposure to the entity at arm's length.</li></ul><p>When power dies the tear <b>snaps back</b>, and the vacuum <b>implodes the dome.</b></p>",
});
const actors = [aRostova, aVance, aTear];
// ---------------------------------------------------------------- journals
let jSort = 0;
function textPage(name, html, level = 1) {
return { _id: id(`fa-page-${name}`), name, type: "text", title: { show: true, level },
text: { format: 1, content: html }, image: {}, video: { controls: true, volume: 0.5 }, src: null,
system: {}, sort: 0, ownership: { default: -1 }, flags: {}, _stats: STATS };
}
function imagePage(name, src, caption) {
return { _id: id(`fa-page-img-${name}`), name, type: "image", title: { show: true, level: 1 },
image: { caption: caption || "" }, text: { format: 1, content: "" }, src, system: {}, sort: 0, ownership: { default: -1 }, flags: {}, _stats: STATS };
}
function journal(key, name, pages) {
return { _id: id(`fa-journal-${key}`), name, pages: pages.map((p, i) => ({ ...p, sort: (i + 1) * 100 })),
folder: fJournal._id, sort: (jSort += 100), ownership: { default: 0 }, flags: {}, _stats: STATS,
_key: undefined };
}
const journal_briefing = journal("01-briefing", "① BRIEFING — Operation FALSE ALARM", [
imagePage("Operation File", `${A}/cover.svg`, "TOP SECRET // DELTA GREEN — EYES ONLY"),
textPage("Your Cover &amp; Your Handler",
"<p>You are federal law-enforcement officers and specialists. Your Delta Green handler in <b>A-Cell</b> has pulled you off the books for the night. Cover story: a joint <b>FAA / NWS</b> team responding to a &#8220;severe weather generator failure&#8221; at a remote automated radar station.</p><p>You were briefed by phone. There was no paperwork. There will be none.</p>"),
textPage("The Hook",
"<p>Six hours ago, a standard FAA radar facility in the snowbound wilderness of <b>northern Maine</b> abruptly went offline &mdash; after transmitting a <b>corrupted data loop</b>. A local sheriff's deputy, <b>Marcus Vance</b>, drove out to investigate. He never checked back in.</p>"),
textPage("The Mission",
"<ol><li><b>Erase any unnatural data</b> — the facility log and the FAA's remote cloud backups.</li><li><b>Recover or terminate Deputy Vance.</b></li><li><b>Secure the facility.</b></li><li><b>Cover it up</b> as a severe weather-generator failure.</li></ol><p>Time, weather, and whatever is out there are all against you. Runtime: 3&ndash;4 hours.</p>"),
]);
const journal_radar = journal("02-radar", "② HANDOUT — Radar Terminal Log", [
imagePage("ASR-11 Final Frames", `${A}/handout-radar-log.svg`, "The last thing the dish saw before it crashed."),
textPage("What it shows",
"<p>Show players on a <b>Computer Science</b> success accessing the data log. The radar <b>did not track a physical aircraft.</b> It tracked a <b>&#8220;geometric shadow&#8221;</b> that hovered directly over the dish &mdash; no transponder, no altitude, no motion vector &mdash; and then the system crashed into a corrupted loop.</p>"),
]);
const journal_audio = journal("03-audio", "③ HANDOUT — Final Transmission (Audio)", [
imagePage("Static Capture", `${A}/handout-waveform.svg`, "Rhythmic, oscillating static — a six-second loop."),
textPage("Listening closely",
"<p>Playing back the audio from the final transmission (<b>SIGINT</b> or <b>Science</b>) reveals a rhythmic, oscillating static. <b>Listening to it closely requires a Sanity check (0/1D4).</b></p><p>On a success, the listener notices a hidden, low-frequency <b>human voice</b> buried under the static &mdash; whispering the <b>Agents' own names.</b> (Handler: use the players' real character names. Whisper them.)</p>"),
]);
const journal_tear = journal("04-tear", "④ HANDOUT — THE TEAR (climax only)", [
imagePage("The Manifestation", `${A}/handout-entity.svg`, "DO NOT REVEAL until the broadcast peaks."),
textPage("Reveal notes",
"<p><b>Hold this image back</b> until the radar dish activates and the entity bleeds through. The air is static-charged, everyone's hair stands on end, and a shifting, non-Euclidean <b>fractal silhouette</b> tears open at the centre of the dish. Presence costs <b>1D6/1D20 SAN</b>. Bullets pass through and warp uselessly. See <em>GM — NPCs &amp; The Threat</em>.</p>"),
]);
const journal_gm_scenes = journal("05-gm-scenes", "⑤ GM — Scene Guide &amp; Clues", [
textPage("Scene 1 — The Arrival",
"<p>A fenced compound: one concrete equipment building and a massive geodesic radar dome over the pines. Heavy snow.</p><ul><li>The outer chain-link fence has been <b>cut from the inside out.</b></li><li><b>Deputy's cruiser</b> by the gate: door wide open, engine dead, interior coated in thick frost. <b>Forensics/Search:</b> his service weapon is in the footwell, <b>missing three rounds &mdash; no bullet holes in the car.</b></li><li><b>Survival/Tracking:</b> footprints lead from the driver's side into the woods, spaced <b>~8 feet apart</b> &mdash; as if sprinting, or dragged in massive strides.</li></ul>"),
textPage("Scene 2 — The Server Room",
"<p>The concrete building runs on a failing backup generator that hums unsteadily. <b>Dr. Elena Rostova</b> is dead at the console: <b>frozen solid</b> in a 40&deg;F room, eyes wide, lips torn from her teeth.</p><ul><li><b>Computer Science:</b> the log shows a &#8220;geometric shadow&#8221; over the dish, not an aircraft (Handout ②).</li><li><b>SIGINT/Science:</b> the final transmission is oscillating static. Listening closely = <b>SAN 0/1D4</b>; success hears a voice whispering the Agents' names (Handout ③).</li></ul>"),
textPage("Scene 3 — The Radar Dome",
"<p>The upper catwalk inside the dome is pitch black, reeking of <b>ozone and scorched copper</b>. <b>Deputy Vance is here</b> — blind, self-mutilated, frantically rewiring the transmitter to broadcast the static loop at maximum amplification.</p><ul><li>He is <b>no longer entirely human</b>: unhinged jaw, jerky speed, screaming the tech manual backward. Witnessing him = <b>1/1D6 SAN</b>.</li><li>If interposed, he attacks with a heavy wrench (see <em>GM — NPCs</em>).</li></ul>"),
textPage("The Climax — The Broadcast",
"<p>Whether Vance succeeds or the Agents trigger it during the fight, the dish activates, spins wildly, and projects a beam of invisible localised EM radiation. <b>The Tear</b> bleeds through (Handout ④).</p><p>It cannot be shot. To close it, <b>cut the primary power</b> — blow the backup generator outside, or manually overload the main transformer inside (lethal shock + <b>1D6/1D20 SAN</b>). When power dies, the tear snaps back and the vacuum <b>implodes the dome.</b></p>"),
]);
const journal_gm_threat = journal("06-gm-threat", "⑥ GM — NPCs &amp; The Threat", [
textPage("Dr. Elena Rostova", aRostova.system.notes),
textPage("Deputy Marcus Vance (Changed)", aVance.system.notes),
textPage("The Tear", aTear.system.notes),
]);
const journal_gm_after = journal("07-gm-after", "⑦ GM — Cover-Up &amp; Aftermath", [
textPage("Loose ends",
"<ul><li><b>Burn</b> the frozen remains of Dr. Rostova (Thermite Charge) and manage her FAA record and next of kin.</li><li><b>Stage the cruiser</b> as a horrific wildlife attack or a vehicular accident.</li><li><b>Wipe the FAA's remote cloud backups</b> (Computer Science) as well as the on-site drive.</li><li>The imploded dome sells the &#8220;catastrophic weather-generator failure&#8221; on its own — if nothing contradicts it.</li></ul>"),
textPage("Sanity rewards",
"<p>Eliminating the threat grants <b>+1D4 SAN</b>, minus any losses from collateral damage or a failed cover-up. An Agent who overloaded the transformer by hand carries the worst of it — if they lived.</p>"),
]);
const journalArr = [journal_briefing, journal_radar, journal_audio, journal_tear, journal_gm_scenes, journal_gm_threat, journal_gm_after]
.map((j) => { delete j._key; return j; });
// ---------------------------------------------------------------- scenes
function gridCfg() { return { type: 1, size: 100, style: "solidLines", thickness: 1, color: "#59ff87", alpha: 0.12, distance: 5, units: "ft" }; }
function sceneToken(actor, x, y, hidden = false) {
const tid = id(`fa-tok-${actor._id}-${x}-${y}`);
return { _id: tid, name: actor.name, actorId: actor._id, actorLink: false,
texture: { src: actor.prototypeToken.texture.src, anchorX: 0.5, anchorY: 0.5, scaleX: 1, scaleY: 1, fit: "contain" },
x, y, width: 1, height: 1, hidden, disposition: actor.prototypeToken.disposition, displayName: 20,
sight: { enabled: false }, sort: 0, flags: {} };
}
function scene(key, name, bg, w, h, tokens, notesFlag) {
return {
_id: id(`fa-scene-${key}`), name, active: false, navigation: true, navName: "",
width: w, height: h, padding: 0.25, background: { src: `${A}/${bg}` }, foreground: null,
grid: gridCfg(), initial: { x: Math.round(w / 2), y: Math.round(h / 2), scale: 0.5 },
backgroundColor: "#050708", tokenVision: false, fogExploration: false, globalLight: true, darkness: 0,
tokens, walls: [], lights: [], sounds: [], templates: [], notes: [], drawings: [], tiles: [],
folder: fScenes._id, sort: 0, ownership: { default: 0 }, flags: {}, _stats: STATS,
};
}
const scenes = [
scene("1-arrival", "Scene 1 — The Arrival (Compound)", "map-compound.svg", 2600, 1800, []),
scene("2-server", "Scene 2 — The Server Room", "map-server-room.svg", 1800, 1400, [sceneToken(aRostova, 900, 780)]),
scene("3-dome", "Scene 3 — The Radar Dome", "map-radar-dome.svg", 2000, 2000,
[sceneToken(aVance, 1000, 1000), sceneToken(aTear, 1000, 1000, true)]),
];
// ---------------------------------------------------------------- adventure
const advId = id("fa-adventure-false-alarm");
const adventure = {
_id: advId, _key: `!adventures!${advId}`,
name: "Operation FALSE ALARM",
img: `${A}/cover.svg`,
caption: "A tight 3–4 hour Delta Green shotgun scenario — a remote Maine radar station, and the static between stations.",
description: "<h2>Operation FALSE ALARM</h2><p>A remote automated FAA radar station in snowbound northern Maine has gone dark after transmitting a corrupted data loop, and the deputy who went to look never came back. An extradimensional entity is mimicking radio frequencies, drawing humans into the freezing dark.</p><p><b>Importing this adventure</b> creates four <em>Operation FALSE ALARM</em> folders — Actors, Items, Journals and Scenes. Start players in the <b>① Briefing</b> journal; run the three scenes in order; keep the GM journals (⑤⑥⑦) for yourself. Handout ④ (The Tear) is for the climax only.</p><p><em>3–4 hours · 4–6 Agents.</em></p>",
sort: 0, folder: null,
folders, actors, items, journal: journalArr, scenes,
cards: [], playlists: [], tables: [], macros: [], combats: [],
flags: {}, _stats: STATS,
};
// ---------------------------------------------------------------- compile
fs.rmSync(OUT, { recursive: true, force: true });
fs.mkdirSync(OUT, { recursive: true });
fs.writeFileSync(path.join(OUT, "operation-false-alarm.json"), JSON.stringify(adventure, null, 2));
fs.rmSync(PACK, { recursive: true, force: true });
await compilePack(OUT, PACK, { yaml: false });
console.log(`Compiled adventure: ${actors.length} actors, ${items.length} items, ${journalArr.length} journals, ${scenes.length} scenes, ${folders.length} folders.`);
View File
Binary file not shown.
+1
View File
@@ -0,0 +1 @@
MANIFEST-000002
View File
+5
View File
@@ -0,0 +1,5 @@
2026/07/15-23:34:13.141809 1715e7000 Delete type=3 #1
2026/07/15-23:34:13.143714 171e7f000 Level-0 table #5: started
2026/07/15-23:34:13.144042 171e7f000 Level-0 table #5: 12085 bytes OK
2026/07/15-23:34:13.144148 171e7f000 Delete type=0 #3
2026/07/15-23:34:13.144222 171e7f000 Manual compaction at level-0 from '!adventures!5zf355zpx5s1ux56' @ 72057594037927935 : 1 .. '!adventures!5zf355zpx5s1ux56' @ 0 : 0; will stop at (end)
Binary file not shown.
+12
View File
@@ -125,6 +125,18 @@
}
],
"packs": [
{
"name": "false-alarm",
"label": "Operation FALSE ALARM (Adventure)",
"system": "deltagreen2",
"path": "packs/false-alarm",
"type": "Adventure",
"flags": {},
"ownership": {
"PLAYER": "LIMITED",
"ASSISTANT": "OWNER"
}
},
{
"name": "firearms",
"label": "Firearms",