Mission 02: CRATE DIGGERS — timed raid with no base building
Strike force + The Selector against a Stream transcoder outpost; 6-minute countdown shown via SetMissionText, master lacquer is cut (killed) if the timer beats you. Adds the lacquer prop (deliberately untargetable) and ScriptTriggers on all actor templates, without which Trigger.OnKilled throws at map load. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
e11ec402ff
commit
c09da56dbe
BIN
assets_src/m02_outpost.png
Normal file
BIN
assets_src/m02_outpost.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 164 KiB |
BIN
assets_src/map_m02.png
Normal file
BIN
assets_src/map_m02.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 870 KiB |
BIN
assets_src/renders/lacquer.png
Normal file
BIN
assets_src/renders/lacquer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 911 KiB |
BIN
assets_src/renders/lacquer_raw.png
Normal file
BIN
assets_src/renders/lacquer_raw.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 800 KiB |
@ -5,3 +5,7 @@ objective-failed = Objective failed
|
||||
press-first-records = Build a Pressing Plant and bank 2000 records
|
||||
survive-the-probe = Survive the Stream's survey probe
|
||||
keep-the-van-alive = Do not lose the Pop-Up Shop
|
||||
|
||||
destroy-the-transcoder = Destroy the Transcoder before the master is cut
|
||||
keep-the-selector-alive = The Selector must survive
|
||||
wipe-the-outpost = Level the whole outpost
|
||||
|
||||
@ -107,3 +107,5 @@ actor-droneport =
|
||||
.name = Drone Port
|
||||
actor-waxseed =
|
||||
.name = Wax Seep
|
||||
actor-lacquer =
|
||||
.name = Master Lacquer
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
57
mods/vinylgod/maps/m02/m02.lua
Normal file
57
mods/vinylgod/maps/m02/m02.lua
Normal file
@ -0,0 +1,57 @@
|
||||
-- 02: CRATE DIGGERS — timed raid, no base building.
|
||||
|
||||
TIME_LIMIT = DateTime.Minutes(6)
|
||||
Deadline = nil
|
||||
|
||||
WorldLoaded = function()
|
||||
Mrp = Player.GetPlayer("Player")
|
||||
Stream = Player.GetPlayer("Enemy")
|
||||
InitObjectives(Mrp)
|
||||
|
||||
KillTranscoder = Mrp.AddPrimaryObjective("destroy-the-transcoder")
|
||||
KeepSelector = Mrp.AddPrimaryObjective("keep-the-selector-alive")
|
||||
WipeOutpost = Mrp.AddSecondaryObjective("wipe-the-outpost")
|
||||
|
||||
Deadline = DateTime.GameTime + TIME_LIMIT
|
||||
|
||||
Trigger.OnKilled(Transcoder, function()
|
||||
if not Mrp.IsObjectiveCompleted(KillTranscoder) then
|
||||
Mrp.MarkCompletedObjective(KillTranscoder)
|
||||
Mrp.MarkCompletedObjective(KeepSelector)
|
||||
UserInterface.SetMissionText("")
|
||||
end
|
||||
end)
|
||||
|
||||
Trigger.OnKilled(TheSelector, function()
|
||||
Mrp.MarkFailedObjective(KeepSelector)
|
||||
end)
|
||||
|
||||
local outpost = { Transcoder, outpost1, outpost2, turret1, turret2 }
|
||||
Trigger.OnAllKilled(outpost, function()
|
||||
Mrp.MarkCompletedObjective(WipeOutpost)
|
||||
end)
|
||||
|
||||
-- the deadline: the master gets cut and the raid was for nothing
|
||||
Trigger.AfterDelay(TIME_LIMIT, function()
|
||||
if not Mrp.IsObjectiveCompleted(KillTranscoder) then
|
||||
if not MasterLacquer.IsDead then
|
||||
MasterLacquer.Kill()
|
||||
end
|
||||
Media.PlaySpeechNotification(Mrp, "Lose")
|
||||
Mrp.MarkFailedObjective(KillTranscoder)
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
Tick = function()
|
||||
if Deadline ~= nil and not Mrp.IsObjectiveCompleted(KillTranscoder)
|
||||
and not Mrp.IsObjectiveFailed(KillTranscoder) then
|
||||
local left = Deadline - DateTime.GameTime
|
||||
if left < 0 then
|
||||
left = 0
|
||||
end
|
||||
if left % 25 == 0 then
|
||||
UserInterface.SetMissionText("Master cut in " .. Utils.FormatTime(left))
|
||||
end
|
||||
end
|
||||
end
|
||||
BIN
mods/vinylgod/maps/m02/map.bin
Normal file
BIN
mods/vinylgod/maps/m02/map.bin
Normal file
Binary file not shown.
BIN
mods/vinylgod/maps/m02/map.png
Normal file
BIN
mods/vinylgod/maps/m02/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 751 B |
127
mods/vinylgod/maps/m02/map.yaml
Normal file
127
mods/vinylgod/maps/m02/map.yaml
Normal file
@ -0,0 +1,127 @@
|
||||
MapFormat: 12
|
||||
|
||||
RequiresMod: vinylgod
|
||||
|
||||
Title: 02 - Crate Diggers
|
||||
|
||||
Author: Monster Robot Party
|
||||
|
||||
Tileset: VINYLGOD
|
||||
|
||||
MapSize: 50,50
|
||||
|
||||
Bounds: 1,1,48,48
|
||||
|
||||
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
|
||||
Enemies: Player
|
||||
|
||||
Actors:
|
||||
spawn0: mpspawn
|
||||
Owner: Neutral
|
||||
Location: 8,40
|
||||
TheSelector: selector
|
||||
Owner: Player
|
||||
Location: 8,40
|
||||
strike1: stacktank
|
||||
Owner: Player
|
||||
Location: 10,41
|
||||
strike2: spinner
|
||||
Owner: Player
|
||||
Location: 7,42
|
||||
strike3: spinner
|
||||
Owner: Player
|
||||
Location: 11,39
|
||||
strike4: djgrunt
|
||||
Owner: Player
|
||||
Location: 9,43
|
||||
strike5: djgrunt
|
||||
Owner: Player
|
||||
Location: 10,43
|
||||
strike6: djgrunt
|
||||
Owner: Player
|
||||
Location: 8,44
|
||||
strike7: roadie
|
||||
Owner: Player
|
||||
Location: 6,41
|
||||
strike8: boombike
|
||||
Owner: Player
|
||||
Location: 12,42
|
||||
Transcoder: transcoder
|
||||
Owner: Enemy
|
||||
Location: 38,9
|
||||
MasterLacquer: lacquer
|
||||
Owner: Neutral
|
||||
Location: 41,10
|
||||
outpost1: datacentre
|
||||
Owner: Enemy
|
||||
Location: 35,12
|
||||
outpost2: podfarm
|
||||
Owner: Enemy
|
||||
Location: 41,13
|
||||
turret1: adblocker
|
||||
Owner: Enemy
|
||||
Location: 34,9
|
||||
turret2: adblocker
|
||||
Owner: Enemy
|
||||
Location: 38,14
|
||||
guard1: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 36,15
|
||||
guard2: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 37,15
|
||||
guard3: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 39,16
|
||||
guard4: drmwalker
|
||||
Owner: Enemy
|
||||
Location: 34,16
|
||||
guard5: crawler
|
||||
Owner: Enemy
|
||||
Location: 30,20
|
||||
picket1: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 24,28
|
||||
picket2: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 25,29
|
||||
picket3: adblocker
|
||||
Owner: Enemy
|
||||
Location: 22,30
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 12,40
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 26,26
|
||||
waxseed2: waxseed
|
||||
Owner: Neutral
|
||||
Location: 40,20
|
||||
|
||||
Rules: vinylgod|rules/campaign.yaml, rules.yaml
|
||||
|
||||
FluentMessages: vinylgod|fluent/lua.ftl
|
||||
5
mods/vinylgod/maps/m02/rules.yaml
Normal file
5
mods/vinylgod/maps/m02/rules.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
world:
|
||||
LuaScript:
|
||||
Scripts: campaign.lua, m02.lua
|
||||
MissionData:
|
||||
Briefing: A Stream transcoder outpost has found something under the hill: a master lacquer. The original of an original. The only one.\n\nIn six minutes it will be a receipt.\n\nTake the outpost apart. No van, no plant, no reinforcements - what you brought is what you have.\n\nThe Selector does not get to die out here.
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,2 +1,3 @@
|
||||
SIDE A - Monster Robot Party:
|
||||
m01
|
||||
m02
|
||||
|
||||
@ -275,6 +275,7 @@ boombike:
|
||||
# hit them. Upgrade path: dedicated AA armaments + AirborneActor-only targeting.
|
||||
|
||||
^AirUnit:
|
||||
ScriptTriggers:
|
||||
BodyOrientation:
|
||||
QuantizeFacingsFromSequence:
|
||||
RenderSprites:
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
^EconSprite:
|
||||
ScriptTriggers:
|
||||
BodyOrientation:
|
||||
QuantizeFacingsFromSequence:
|
||||
RenderSprites:
|
||||
@ -288,3 +289,26 @@ waxseed:
|
||||
MaxRange: 60
|
||||
MapEditorData:
|
||||
Categories: Resource spawn
|
||||
|
||||
# The master lacquer — mission prop. Deliberately not Targetable: the player
|
||||
# must not be able to shoot the thing they came to rescue.
|
||||
lacquer:
|
||||
ScriptTriggers:
|
||||
BodyOrientation:
|
||||
QuantizeFacingsFromSequence:
|
||||
RenderSprites:
|
||||
WithSpriteBody:
|
||||
Interactable:
|
||||
HiddenUnderShroud:
|
||||
Building:
|
||||
Footprint: x
|
||||
Dimensions: 1,1
|
||||
TerrainTypes: Clear, Wax
|
||||
Tooltip:
|
||||
Name: actor-lacquer.name
|
||||
AppearsOnRadar:
|
||||
HitShape:
|
||||
Health:
|
||||
HP: 1000
|
||||
MapEditorData:
|
||||
Categories: Props
|
||||
|
||||
BIN
mods/vinylgod/sequences/assets/lacquer.png
Normal file
BIN
mods/vinylgod/sequences/assets/lacquer.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.0 KiB |
@ -269,3 +269,7 @@ compressor:
|
||||
waxseed:
|
||||
idle:
|
||||
Filename: sequences/assets/waxseed.png
|
||||
|
||||
lacquer:
|
||||
idle:
|
||||
Filename: sequences/assets/lacquer.png
|
||||
|
||||
118
tools/gen_mission2.py
Normal file
118
tools/gen_mission2.py
Normal file
@ -0,0 +1,118 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate campaign mission 02 — CRATE DIGGERS (timed raid, no base building)."""
|
||||
import os, struct
|
||||
from PIL import Image
|
||||
|
||||
SIZE = 48
|
||||
W = H = SIZE + 2
|
||||
MAP = os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'maps', 'm02')
|
||||
os.makedirs(MAP, exist_ok=True)
|
||||
|
||||
WAX = [(12, 40, 4), (26, 26, 5), (40, 20, 3)]
|
||||
|
||||
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)
|
||||
|
||||
# names used as lua globals must not collide with engine globals
|
||||
ACTORS = [
|
||||
('spawn0', 'mpspawn', 'Neutral', 8, 40),
|
||||
# strike force
|
||||
('TheSelector', 'selector', 'Player', 8, 40),
|
||||
('strike1', 'stacktank', 'Player', 10, 41),
|
||||
('strike2', 'spinner', 'Player', 7, 42),
|
||||
('strike3', 'spinner', 'Player', 11, 39),
|
||||
('strike4', 'djgrunt', 'Player', 9, 43),
|
||||
('strike5', 'djgrunt', 'Player', 10, 43),
|
||||
('strike6', 'djgrunt', 'Player', 8, 44),
|
||||
('strike7', 'roadie', 'Player', 6, 41),
|
||||
('strike8', 'boombike', 'Player', 12, 42),
|
||||
# the outpost
|
||||
('Transcoder', 'transcoder', 'Enemy', 38, 9),
|
||||
('MasterLacquer', 'lacquer', 'Neutral', 41, 10),
|
||||
('outpost1', 'datacentre', 'Enemy', 35, 12),
|
||||
('outpost2', 'podfarm', 'Enemy', 41, 13),
|
||||
('turret1', 'adblocker', 'Enemy', 34, 9),
|
||||
('turret2', 'adblocker', 'Enemy', 38, 14),
|
||||
('guard1', 'bufferbot', 'Enemy', 36, 15),
|
||||
('guard2', 'bufferbot', 'Enemy', 37, 15),
|
||||
('guard3', 'bufferbot', 'Enemy', 39, 16),
|
||||
('guard4', 'drmwalker', 'Enemy', 34, 16),
|
||||
('guard5', 'crawler', 'Enemy', 30, 20),
|
||||
# a picket on the approach
|
||||
('picket1', 'bufferbot', 'Enemy', 24, 28),
|
||||
('picket2', 'bufferbot', 'Enemy', 25, 29),
|
||||
('picket3', 'adblocker', 'Enemy', 22, 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: 02 - Crate Diggers
|
||||
|
||||
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
|
||||
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('m02 written')
|
||||
Loading…
Reference in New Issue
Block a user