Round 5b: SIDE A campaign — mission 01 FIRST PRESSING with scripted objectives, briefing, timed Stream probe; shared campaign.lua, campaign rules, objectives panel, missions registry
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
5255aeeb1f
commit
93ad888c32
7
mods/vinylgod/fluent/lua.ftl
Normal file
7
mods/vinylgod/fluent/lua.ftl
Normal file
@ -0,0 +1,7 @@
|
||||
new-objective = New objective
|
||||
objective-completed = Objective completed
|
||||
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
|
||||
46
mods/vinylgod/maps/m01/m01.lua
Normal file
46
mods/vinylgod/maps/m01/m01.lua
Normal file
@ -0,0 +1,46 @@
|
||||
-- 01: FIRST PRESSING
|
||||
|
||||
TARGET_RECORDS = 2000
|
||||
PROBE_DELAY = DateTime.Minutes(4)
|
||||
|
||||
WorldLoaded = function()
|
||||
-- do NOT shadow the `Player` global: Player.GetPlayer is still needed below
|
||||
Mrp = Player.GetPlayer("Player")
|
||||
Stream = Player.GetPlayer("Enemy")
|
||||
InitObjectives(Mrp)
|
||||
|
||||
PressObjective = Mrp.AddPrimaryObjective("press-first-records")
|
||||
VanObjective = Mrp.AddSecondaryObjective("keep-the-van-alive")
|
||||
|
||||
-- lose the HQ, lose the secondary
|
||||
local hq = Utils.Where(Map.ActorsInWorld, function(a)
|
||||
return a.Owner == Mrp and (a.Type == "popupvan" or a.Type == "popupshop")
|
||||
end)
|
||||
if #hq > 0 then
|
||||
Trigger.OnAllKilled(hq, function()
|
||||
Mrp.MarkFailedObjective(VanObjective)
|
||||
end)
|
||||
end
|
||||
|
||||
-- the probe
|
||||
Trigger.AfterDelay(PROBE_DELAY, function()
|
||||
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
|
||||
|
||||
Tick = function()
|
||||
if PressObjective ~= nil and not Mrp.IsObjectiveCompleted(PressObjective) then
|
||||
local banked = Mrp.Cash + Mrp.Resources
|
||||
local plants = Utils.Where(Map.ActorsInWorld, function(a)
|
||||
return a.Owner == Mrp and a.Type == "pressplant"
|
||||
end)
|
||||
if #plants > 0 and banked >= TARGET_RECORDS then
|
||||
Mrp.MarkCompletedObjective(PressObjective)
|
||||
end
|
||||
end
|
||||
end
|
||||
BIN
mods/vinylgod/maps/m01/map.bin
Normal file
BIN
mods/vinylgod/maps/m01/map.bin
Normal file
Binary file not shown.
BIN
mods/vinylgod/maps/m01/map.png
Normal file
BIN
mods/vinylgod/maps/m01/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 713 B |
94
mods/vinylgod/maps/m01/map.yaml
Normal file
94
mods/vinylgod/maps/m01/map.yaml
Normal file
@ -0,0 +1,94 @@
|
||||
MapFormat: 12
|
||||
|
||||
RequiresMod: vinylgod
|
||||
|
||||
Title: 01 - First Pressing
|
||||
|
||||
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: 9,38
|
||||
van: popupvan
|
||||
Owner: Player
|
||||
Location: 9,38
|
||||
dig: digger
|
||||
Owner: Player
|
||||
Location: 12,38
|
||||
grunt1: djgrunt
|
||||
Owner: Player
|
||||
Location: 11,41
|
||||
grunt2: djgrunt
|
||||
Owner: Player
|
||||
Location: 12,41
|
||||
outpost: transcoder
|
||||
Owner: Enemy
|
||||
Location: 38,9
|
||||
pods: podfarm
|
||||
Owner: Enemy
|
||||
Location: 41,12
|
||||
node: datacentre
|
||||
Owner: Enemy
|
||||
Location: 36,12
|
||||
probe1: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 38,14
|
||||
probe2: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 39,14
|
||||
probe3: bufferbot
|
||||
Owner: Enemy
|
||||
Location: 40,14
|
||||
probe4: crawler
|
||||
Owner: Enemy
|
||||
Location: 37,15
|
||||
waxseed0: waxseed
|
||||
Owner: Neutral
|
||||
Location: 9,43
|
||||
waxseed1: waxseed
|
||||
Owner: Neutral
|
||||
Location: 14,34
|
||||
waxseed2: waxseed
|
||||
Owner: Neutral
|
||||
Location: 25,25
|
||||
waxseed3: waxseed
|
||||
Owner: Neutral
|
||||
Location: 40,14
|
||||
|
||||
Rules: vinylgod|rules/campaign.yaml, rules.yaml
|
||||
|
||||
FluentMessages: vinylgod|fluent/lua.ftl
|
||||
5
mods/vinylgod/maps/m01/rules.yaml
Normal file
5
mods/vinylgod/maps/m01/rules.yaml
Normal file
@ -0,0 +1,5 @@
|
||||
world:
|
||||
LuaScript:
|
||||
Scripts: campaign.lua, m01.lua
|
||||
MissionData:
|
||||
Briefing: The Party wakes up in the landfill with a van, a digger and a wax field.\n\nGet a Pressing Plant running and bank 2000 records before the Stream finishes counting what is down here.\n\nThey will send a probe. They always send a probe.
|
||||
@ -0,0 +1,2 @@
|
||||
SIDE A - Monster Robot Party:
|
||||
m01
|
||||
@ -8,6 +8,7 @@ FileSystem: DefaultFileSystem
|
||||
^EngineDir
|
||||
$vinylgod: vinylgod
|
||||
^EngineDir|mods/common: common
|
||||
vinylgod|scripts
|
||||
|
||||
MapFolders:
|
||||
vinylgod|maps: System
|
||||
|
||||
10
mods/vinylgod/rules/campaign.yaml
Normal file
10
mods/vinylgod/rules/campaign.yaml
Normal file
@ -0,0 +1,10 @@
|
||||
# Applied by campaign maps on top of the standard rules.
|
||||
player:
|
||||
-ConquestVictoryConditions:
|
||||
MissionObjectives:
|
||||
EarlyGameOver: true
|
||||
GameOverDelay: 3000
|
||||
|
||||
world:
|
||||
ObjectivesPanel:
|
||||
PanelName: MISSION_OBJECTIVES
|
||||
@ -46,6 +46,8 @@ world:
|
||||
PathFinder:
|
||||
MapOptions:
|
||||
ScriptTriggers:
|
||||
ObjectivesPanel:
|
||||
PanelName: SKIRMISH_STATS
|
||||
Locomotor@WHEELED:
|
||||
Name: wheeled
|
||||
TerrainSpeeds:
|
||||
|
||||
26
mods/vinylgod/scripts/campaign.lua
Normal file
26
mods/vinylgod/scripts/campaign.lua
Normal file
@ -0,0 +1,26 @@
|
||||
-- Shared campaign helpers for SIDE A. Mirrors the engine's campaign.lua shape.
|
||||
|
||||
InitObjectives = function(player)
|
||||
Trigger.OnObjectiveAdded(player, function(p, id)
|
||||
Media.DisplayMessage(p.GetObjectiveDescription(id),
|
||||
UserInterface.GetFluentMessage("new-objective"))
|
||||
end)
|
||||
Trigger.OnObjectiveCompleted(player, function(p, id)
|
||||
Media.DisplayMessage(p.GetObjectiveDescription(id),
|
||||
UserInterface.GetFluentMessage("objective-completed"))
|
||||
end)
|
||||
Trigger.OnObjectiveFailed(player, function(p, id)
|
||||
Media.DisplayMessage(p.GetObjectiveDescription(id),
|
||||
UserInterface.GetFluentMessage("objective-failed"))
|
||||
end)
|
||||
Trigger.OnPlayerWon(player, function()
|
||||
Trigger.AfterDelay(DateTime.Seconds(1), function()
|
||||
Media.PlaySpeechNotification(player, "Win")
|
||||
end)
|
||||
end)
|
||||
Trigger.OnPlayerLost(player, function()
|
||||
Trigger.AfterDelay(DateTime.Seconds(1), function()
|
||||
Media.PlaySpeechNotification(player, "Lose")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
102
tools/gen_mission.py
Normal file
102
tools/gen_mission.py
Normal file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Generate campaign mission 01 — FIRST PRESSING (map.bin + map.yaml + preview)."""
|
||||
import os, struct
|
||||
from PIL import Image
|
||||
|
||||
SIZE = 48
|
||||
W = H = SIZE + 2
|
||||
MAP = os.path.join(os.path.dirname(__file__), '..', 'mods', 'vinylgod', 'maps', 'm01')
|
||||
os.makedirs(MAP, exist_ok=True)
|
||||
|
||||
WAX = [(14, 34, 5), (9, 43, 4), (25, 25, 5), (40, 14, 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', 9, 38),
|
||||
('van', 'popupvan', 'Player', 9, 38),
|
||||
('dig', 'digger', 'Player', 12, 38),
|
||||
('grunt1', 'djgrunt', 'Player', 11, 41),
|
||||
('grunt2', 'djgrunt', 'Player', 12, 41),
|
||||
('outpost', 'transcoder', 'Enemy', 38, 9),
|
||||
('pods', 'podfarm', 'Enemy', 41, 12),
|
||||
('node', 'datacentre', 'Enemy', 36, 12),
|
||||
('probe1', 'bufferbot', 'Enemy', 38, 14),
|
||||
('probe2', 'bufferbot', 'Enemy', 39, 14),
|
||||
('probe3', 'bufferbot', 'Enemy', 40, 14),
|
||||
('probe4', 'crawler', 'Enemy', 37, 15),
|
||||
]
|
||||
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: 01 - First Pressing
|
||||
|
||||
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))
|
||||
im.resize((W * 3, H * 3), Image.NEAREST).save(os.path.join(MAP, 'map.png'))
|
||||
print('m01 written')
|
||||
Loading…
Reference in New Issue
Block a user