Mission 03: THE DROP — campaign finale
Full base-building map against a live Stream bot holding The Algorithm behind a defended base. Building the Mastering Studio triggers a scripted all-in counterattack; silencing The Algorithm wins. Tick scan throttled to 1Hz. SIDE A is now three missions. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
c09da56dbe
commit
ef432e4182
BIN
assets_src/m03_algorithm.png
Normal file
BIN
assets_src/m03_algorithm.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 275 KiB |
BIN
assets_src/map_m03.png
Normal file
BIN
assets_src/map_m03.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
@ -9,3 +9,8 @@ keep-the-van-alive = Do not lose the Pop-Up Shop
|
|||||||
destroy-the-transcoder = Destroy the Transcoder before the master is cut
|
destroy-the-transcoder = Destroy the Transcoder before the master is cut
|
||||||
keep-the-selector-alive = The Selector must survive
|
keep-the-selector-alive = The Selector must survive
|
||||||
wipe-the-outpost = Level the whole outpost
|
wipe-the-outpost = Level the whole outpost
|
||||||
|
|
||||||
|
build-the-studio = Build a Mastering Studio
|
||||||
|
silence-the-algorithm = Silence The Algorithm
|
||||||
|
keep-the-shop-standing = Keep the Pop-Up Shop standing
|
||||||
|
it-heard-you = THE ALGORITHM HAS NOTICED YOU
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
59
mods/vinylgod/maps/m03/m03.lua
Normal file
59
mods/vinylgod/maps/m03/m03.lua
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
-- 03: THE DROP — finale.
|
||||||
|
|
||||||
|
CounterattackDone = false
|
||||||
|
Ticks = 0
|
||||||
|
|
||||||
|
WorldLoaded = function()
|
||||||
|
Mrp = Player.GetPlayer("Player")
|
||||||
|
Stream = Player.GetPlayer("Enemy")
|
||||||
|
InitObjectives(Mrp)
|
||||||
|
|
||||||
|
StudioObjective = Mrp.AddPrimaryObjective("build-the-studio")
|
||||||
|
AlgorithmObjective = Mrp.AddPrimaryObjective("silence-the-algorithm")
|
||||||
|
ShopObjective = Mrp.AddSecondaryObjective("keep-the-shop-standing")
|
||||||
|
|
||||||
|
Trigger.OnKilled(TheAlgorithm, function()
|
||||||
|
Mrp.MarkCompletedObjective(AlgorithmObjective)
|
||||||
|
end)
|
||||||
|
|
||||||
|
-- losing the HQ costs the secondary, not the mission
|
||||||
|
Trigger.OnKilled(TheVan, function()
|
||||||
|
Trigger.AfterDelay(DateTime.Seconds(2), function()
|
||||||
|
local hq = Utils.Where(Map.ActorsInWorld, function(a)
|
||||||
|
return a.Owner == Mrp and a.Type == "popupshop"
|
||||||
|
end)
|
||||||
|
if #hq == 0 then
|
||||||
|
Mrp.MarkFailedObjective(ShopObjective)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
Tick = function()
|
||||||
|
Ticks = Ticks + 1
|
||||||
|
-- once a second is plenty; scanning every actor every frame is wasteful
|
||||||
|
if Ticks % 25 ~= 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if StudioObjective == nil or Mrp.IsObjectiveCompleted(StudioObjective) then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local studios = Utils.Where(Map.ActorsInWorld, function(a)
|
||||||
|
return a.Owner == Mrp and a.Type == "mastering"
|
||||||
|
end)
|
||||||
|
|
||||||
|
if #studios > 0 then
|
||||||
|
Mrp.MarkCompletedObjective(StudioObjective)
|
||||||
|
if not CounterattackDone then
|
||||||
|
CounterattackDone = true
|
||||||
|
Media.DisplayMessage(UserInterface.GetFluentMessage("it-heard-you"))
|
||||||
|
Media.PlaySpeechNotification(Mrp, "BaseAttack")
|
||||||
|
Utils.Do(Map.ActorsInWorld, function(a)
|
||||||
|
if a.Owner == Stream and a.HasProperty("Hunt") then
|
||||||
|
a.Hunt()
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
BIN
mods/vinylgod/maps/m03/map.bin
Normal file
BIN
mods/vinylgod/maps/m03/map.bin
Normal file
Binary file not shown.
BIN
mods/vinylgod/maps/m03/map.png
Normal file
BIN
mods/vinylgod/maps/m03/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
152
mods/vinylgod/maps/m03/map.yaml
Normal file
152
mods/vinylgod/maps/m03/map.yaml
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
MapFormat: 12
|
||||||
|
|
||||||
|
RequiresMod: vinylgod
|
||||||
|
|
||||||
|
Title: 03 - The Drop
|
||||||
|
|
||||||
|
Author: Monster Robot Party
|
||||||
|
|
||||||
|
Tileset: VINYLGOD
|
||||||
|
|
||||||
|
MapSize: 66,66
|
||||||
|
|
||||||
|
Bounds: 1,1,64,64
|
||||||
|
|
||||||
|
Visibility: MissionSelector
|
||||||
|
|
||||||
|
Categories: Campaign
|
||||||
|
|
||||||
|
LockPreview: True
|
||||||
|
|
||||||
|
Players:
|
||||||
|
PlayerReference@Neutral:
|
||||||
|
Name: Neutral
|
||||||
|
OwnsWorld: True
|
||||||
|
NonCombatant: True
|
||||||
|
Faction: vinylgod
|
||||||
|
PlayerReference@Player:
|
||||||
|
Name: Player
|
||||||
|
Playable: True
|
||||||
|
Required: True
|
||||||
|
AllowBots: False
|
||||||
|
LockFaction: True
|
||||||
|
Faction: vinylgod
|
||||||
|
LockSpawn: True
|
||||||
|
LockTeam: True
|
||||||
|
Enemies: Enemy
|
||||||
|
PlayerReference@Enemy:
|
||||||
|
Name: Enemy
|
||||||
|
Faction: stream
|
||||||
|
Bot: wax
|
||||||
|
Enemies: Player
|
||||||
|
|
||||||
|
Actors:
|
||||||
|
spawn0: mpspawn
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 10,54
|
||||||
|
TheVan: popupvan
|
||||||
|
Owner: Player
|
||||||
|
Location: 10,54
|
||||||
|
dig1: digger
|
||||||
|
Owner: Player
|
||||||
|
Location: 13,54
|
||||||
|
dig2: digger
|
||||||
|
Owner: Player
|
||||||
|
Location: 12,56
|
||||||
|
guard1: stacktank
|
||||||
|
Owner: Player
|
||||||
|
Location: 15,52
|
||||||
|
guard2: spinner
|
||||||
|
Owner: Player
|
||||||
|
Location: 16,55
|
||||||
|
guard3: djgrunt
|
||||||
|
Owner: Player
|
||||||
|
Location: 14,57
|
||||||
|
guard4: djgrunt
|
||||||
|
Owner: Player
|
||||||
|
Location: 15,57
|
||||||
|
TheAlgorithm: algorithm
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 52,8
|
||||||
|
ehub: streamhub
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 48,10
|
||||||
|
eproc1: transcoder
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 44,8
|
||||||
|
eproc2: transcoder
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 55,14
|
||||||
|
enode1: datacentre
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 50,13
|
||||||
|
enode2: datacentre
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 54,11
|
||||||
|
epods: podfarm
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 45,13
|
||||||
|
eprint: printfarm
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 49,16
|
||||||
|
eport: droneport
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 56,18
|
||||||
|
etur1: adblocker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 46,17
|
||||||
|
etur2: adblocker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 52,18
|
||||||
|
etur3: adblocker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 43,11
|
||||||
|
escrap1: scraper
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 47,20
|
||||||
|
escrap2: scraper
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 50,20
|
||||||
|
emob1: drmwalker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 44,20
|
||||||
|
emob2: drmwalker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 53,22
|
||||||
|
emob3: bufferbot
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 46,22
|
||||||
|
emob4: bufferbot
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 47,22
|
||||||
|
emob5: bufferbot
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 48,22
|
||||||
|
mid1: adblocker
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 34,28
|
||||||
|
mid2: bufferbot
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 33,30
|
||||||
|
mid3: bufferbot
|
||||||
|
Owner: Enemy
|
||||||
|
Location: 35,30
|
||||||
|
waxseed0: waxseed
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 12,30
|
||||||
|
waxseed1: waxseed
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 14,50
|
||||||
|
waxseed2: waxseed
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 32,32
|
||||||
|
waxseed3: waxseed
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 50,14
|
||||||
|
waxseed4: waxseed
|
||||||
|
Owner: Neutral
|
||||||
|
Location: 52,34
|
||||||
|
|
||||||
|
Rules: vinylgod|rules/campaign.yaml, rules.yaml
|
||||||
|
|
||||||
|
FluentMessages: vinylgod|fluent/lua.ftl
|
||||||
5
mods/vinylgod/maps/m03/rules.yaml
Normal file
5
mods/vinylgod/maps/m03/rules.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
world:
|
||||||
|
LuaScript:
|
||||||
|
Scripts: campaign.lua, m03.lua
|
||||||
|
MissionData:
|
||||||
|
Briefing: The Algorithm has surfaced. It is sitting in the north-east behind everything it owns, scoring us.\n\nBuild a base. Build a Mastering Studio. Charge the orbital tonearm.\n\nThen put the needle through it.\n\nIt will hear the studio go up, and it will come for you. Let it.
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,4 @@
|
|||||||
SIDE A - Monster Robot Party:
|
SIDE A - Monster Robot Party:
|
||||||
m01
|
m01
|
||||||
m02
|
m02
|
||||||
|
m03
|
||||||
|
|||||||
124
tools/gen_mission3.py
Normal file
124
tools/gen_mission3.py
Normal file
@ -0,0 +1,124 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Generate campaign mission 03 — THE DROP (finale: full base vs a live Stream base)."""
|
||||||
|
import os, struct
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
SIZE = 64
|
||||||
|
W = H = SIZE + 2
|
||||||
|
MAP = os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'maps', 'm03')
|
||||||
|
os.makedirs(MAP, exist_ok=True)
|
||||||
|
|
||||||
|
WAX = [(14, 50, 6), (24, 40, 5), (32, 32, 6), (40, 24, 5), (50, 14, 6), (12, 30, 4), (52, 34, 4)]
|
||||||
|
|
||||||
|
data = bytearray()
|
||||||
|
data += struct.pack('<BHH', 2, W, H)
|
||||||
|
data += struct.pack('<III', 17, 0, 3 * W * H + 17)
|
||||||
|
data += b'\xff\x00\x00' * (W * H)
|
||||||
|
res = bytearray(2 * W * H)
|
||||||
|
for cx, cy, r in WAX:
|
||||||
|
for x in range(cx - r, cx + r + 1):
|
||||||
|
for y in range(cy - r, cy + r + 1):
|
||||||
|
d2 = (x - cx) ** 2 + (y - cy) ** 2
|
||||||
|
if d2 <= r * r and 0 < x < W - 1 and 0 < y < H - 1:
|
||||||
|
o = 2 * (x * H + y)
|
||||||
|
res[o], res[o + 1] = 1, max(2, round(12 * (1 - (d2 ** 0.5) / (r + 1))))
|
||||||
|
data += res
|
||||||
|
open(os.path.join(MAP, 'map.bin'), 'wb').write(data)
|
||||||
|
|
||||||
|
ACTORS = [
|
||||||
|
('spawn0', 'mpspawn', 'Neutral', 10, 54),
|
||||||
|
# the Party
|
||||||
|
('TheVan', 'popupvan', 'Player', 10, 54),
|
||||||
|
('dig1', 'digger', 'Player', 13, 54),
|
||||||
|
('dig2', 'digger', 'Player', 12, 56),
|
||||||
|
('guard1', 'stacktank', 'Player', 15, 52),
|
||||||
|
('guard2', 'spinner', 'Player', 16, 55),
|
||||||
|
('guard3', 'djgrunt', 'Player', 14, 57),
|
||||||
|
('guard4', 'djgrunt', 'Player', 15, 57),
|
||||||
|
# the Algorithm's seat
|
||||||
|
('TheAlgorithm', 'algorithm', 'Enemy', 52, 8),
|
||||||
|
('ehub', 'streamhub', 'Enemy', 48, 10),
|
||||||
|
('eproc1', 'transcoder', 'Enemy', 44, 8),
|
||||||
|
('eproc2', 'transcoder', 'Enemy', 55, 14),
|
||||||
|
('enode1', 'datacentre', 'Enemy', 50, 13),
|
||||||
|
('enode2', 'datacentre', 'Enemy', 54, 11),
|
||||||
|
('epods', 'podfarm', 'Enemy', 45, 13),
|
||||||
|
('eprint', 'printfarm', 'Enemy', 49, 16),
|
||||||
|
('eport', 'droneport', 'Enemy', 56, 18),
|
||||||
|
('etur1', 'adblocker', 'Enemy', 46, 17),
|
||||||
|
('etur2', 'adblocker', 'Enemy', 52, 18),
|
||||||
|
('etur3', 'adblocker', 'Enemy', 43, 11),
|
||||||
|
('escrap1', 'scraper', 'Enemy', 47, 20),
|
||||||
|
('escrap2', 'scraper', 'Enemy', 50, 20),
|
||||||
|
('emob1', 'drmwalker', 'Enemy', 44, 20),
|
||||||
|
('emob2', 'drmwalker', 'Enemy', 53, 22),
|
||||||
|
('emob3', 'bufferbot', 'Enemy', 46, 22),
|
||||||
|
('emob4', 'bufferbot', 'Enemy', 47, 22),
|
||||||
|
('emob5', 'bufferbot', 'Enemy', 48, 22),
|
||||||
|
# forward picket in the middle
|
||||||
|
('mid1', 'adblocker', 'Enemy', 34, 28),
|
||||||
|
('mid2', 'bufferbot', 'Enemy', 33, 30),
|
||||||
|
('mid3', 'bufferbot', 'Enemy', 35, 30),
|
||||||
|
]
|
||||||
|
actors = '\n'.join(f'\t{n}: {t}\n\t\tOwner: {o}\n\t\tLocation: {x},{y}' for n, t, o, x, y in ACTORS)
|
||||||
|
|
||||||
|
open(os.path.join(MAP, 'map.yaml'), 'w').write(f"""MapFormat: 12
|
||||||
|
|
||||||
|
RequiresMod: vinylgod
|
||||||
|
|
||||||
|
Title: 03 - The Drop
|
||||||
|
|
||||||
|
Author: Monster Robot Party
|
||||||
|
|
||||||
|
Tileset: VINYLGOD
|
||||||
|
|
||||||
|
MapSize: {W},{H}
|
||||||
|
|
||||||
|
Bounds: 1,1,{SIZE},{SIZE}
|
||||||
|
|
||||||
|
Visibility: MissionSelector
|
||||||
|
|
||||||
|
Categories: Campaign
|
||||||
|
|
||||||
|
LockPreview: True
|
||||||
|
|
||||||
|
Players:
|
||||||
|
PlayerReference@Neutral:
|
||||||
|
Name: Neutral
|
||||||
|
OwnsWorld: True
|
||||||
|
NonCombatant: True
|
||||||
|
Faction: vinylgod
|
||||||
|
PlayerReference@Player:
|
||||||
|
Name: Player
|
||||||
|
Playable: True
|
||||||
|
Required: True
|
||||||
|
AllowBots: False
|
||||||
|
LockFaction: True
|
||||||
|
Faction: vinylgod
|
||||||
|
LockSpawn: True
|
||||||
|
LockTeam: True
|
||||||
|
Enemies: Enemy
|
||||||
|
PlayerReference@Enemy:
|
||||||
|
Name: Enemy
|
||||||
|
Faction: stream
|
||||||
|
Bot: wax
|
||||||
|
Enemies: Player
|
||||||
|
|
||||||
|
Actors:
|
||||||
|
{actors}
|
||||||
|
|
||||||
|
Rules: vinylgod|rules/campaign.yaml, rules.yaml
|
||||||
|
|
||||||
|
FluentMessages: vinylgod|fluent/lua.ftl
|
||||||
|
""")
|
||||||
|
|
||||||
|
im = Image.new('RGB', (W, H), (55, 50, 52))
|
||||||
|
for cx, cy, r in WAX:
|
||||||
|
for x in range(cx - r, cx + r + 1):
|
||||||
|
for y in range(cy - r, cy + r + 1):
|
||||||
|
if (x - cx) ** 2 + (y - cy) ** 2 <= r * r and 0 <= x < W and 0 <= y < H:
|
||||||
|
im.putpixel((x, y), (255, 45, 150))
|
||||||
|
for _, _, o, x, y in ACTORS:
|
||||||
|
im.putpixel((x, y), (255, 255, 255) if o == 'Player' else (80, 150, 255) if o == 'Enemy' else (240, 210, 90))
|
||||||
|
im.resize((W * 3, H * 3), Image.NEAREST).save(os.path.join(MAP, 'map.png'))
|
||||||
|
print('m03 written')
|
||||||
Loading…
Reference in New Issue
Block a user