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>
@@ -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: “GEOMETRIC SHADOW” · 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">“ . . . it is saying our names . . . ”</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 |
@@ -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&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 “geometric shadow” 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 & 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°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 · <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 — 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> — 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> — 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 & 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 “severe weather generator failure” 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 — 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–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>“geometric shadow”</b> that hovered directly over the dish — no transponder, no altitude, no motion vector — 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 — 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 & The Threat</em>.</p>"),
|
||||
]);
|
||||
|
||||
const journal_gm_scenes = journal("05-gm-scenes", "⑤ GM — Scene Guide & 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 — 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> — 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°F room, eyes wide, lips torn from her teeth.</p><ul><li><b>Computer Science:</b> the log shows a “geometric shadow” 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 & 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 & 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 “catastrophic weather-generator failure” 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.`);
|
||||
@@ -0,0 +1 @@
|
||||
MANIFEST-000002
|
||||
@@ -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)
|
||||
@@ -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",
|
||||
|
||||