Add Negotiations mission (Red Alert)
This commit is contained in:
@@ -14,7 +14,7 @@ actor-crate-name = Crate
|
||||
## campaign-tooltips.yaml
|
||||
actor-technology-center-name = Technology Center
|
||||
|
||||
## allies-05a, allies-05b, allies-05c
|
||||
## allies-05a, allies-05b, allies-05c, negotiations
|
||||
actor-prison-name = Prison
|
||||
|
||||
## fall-of-greece-1-personal-war, situation-critical
|
||||
|
||||
@@ -17,7 +17,7 @@ find-einstein = Find Einstein.
|
||||
einstein-survive = Einstein must survive.
|
||||
protect-civilians = Protect all civilians.
|
||||
|
||||
## allies-01, allies-03, allies-05abc, evacuation
|
||||
## allies-01, allies-03, allies-05abc, evacuation, negotiations
|
||||
tanya-rules-of-engagement = According to the rules of engagement I need your explicit orders to fire, Commander!
|
||||
|
||||
# allies-01, allies-05abc, evacuation
|
||||
@@ -214,6 +214,20 @@ all-engineers-killed = All engineers were killed.
|
||||
kill-stavros = Kill Stavros.
|
||||
sabotage-facility = Sabotage the facility with our engineers.
|
||||
|
||||
## negotiations
|
||||
all-hostages-killed = All hostages were killed.
|
||||
church-destroyed = The church was destroyed.
|
||||
free-hostages = Locate & free the hostages.
|
||||
get-hostages-to-church = Get hostages to church.
|
||||
signal-for-reinforcements = Signal for reinforcements at your drop-off point.
|
||||
guide-thank-you = Thank you! I'll help you get into town!
|
||||
guide-follow-me = Follow me!
|
||||
guide-patrol-coming = Uh oh, a patrol is coming this way.
|
||||
guide-come-this-way = Come this way! Hurry!
|
||||
guide-safe-to-move = It's safe to move now. Let's go.
|
||||
hostage-dies-in = Hostage dies in { $time }
|
||||
keep-all-hostages-alive = Keep all hostages alive.
|
||||
|
||||
## production-disruption, shock-therapy
|
||||
capture-enemy-radar-dome = Capture the enemy radar dome.
|
||||
|
||||
@@ -274,7 +288,7 @@ destroy-soviet-convoy = Destroy all Soviet convoy trucks.
|
||||
destroy-bridges-slow-convoy = Destroy the nearby bridges to slow the
|
||||
convoys down.
|
||||
|
||||
## siberian-conflict-3-wasteland
|
||||
## siberian-conflict-3-wasteland, negotiations
|
||||
destroy-soviet-units-infrastructure = Destroy all Soviet units and structures.
|
||||
|
||||
## situation-critical
|
||||
|
||||
BIN
mods/ra/maps/negotiations/map.bin
Normal file
BIN
mods/ra/maps/negotiations/map.bin
Normal file
Binary file not shown.
BIN
mods/ra/maps/negotiations/map.png
Normal file
BIN
mods/ra/maps/negotiations/map.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 11 KiB |
1346
mods/ra/maps/negotiations/map.yaml
Normal file
1346
mods/ra/maps/negotiations/map.yaml
Normal file
File diff suppressed because it is too large
Load Diff
347
mods/ra/maps/negotiations/negotiations-ai.lua
Normal file
347
mods/ra/maps/negotiations/negotiations-ai.lua
Normal file
@@ -0,0 +1,347 @@
|
||||
--[[
|
||||
Copyright (c) The OpenRA Developers and Contributors
|
||||
This file is part of OpenRA, which is free software. It is made
|
||||
available to you under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version. For more
|
||||
information, see COPYING.
|
||||
]]
|
||||
BuildIntervals =
|
||||
{
|
||||
easy = DateTime.Seconds(25),
|
||||
normal = DateTime.Seconds(15),
|
||||
hard = DateTime.Seconds(10)
|
||||
}
|
||||
StructureRebuildGrace = BuildIntervals[Difficulty]
|
||||
|
||||
InfantryTeams =
|
||||
{
|
||||
{
|
||||
types = { "e1", "e1" }
|
||||
},
|
||||
{
|
||||
types = { "e2", "e2" }
|
||||
},
|
||||
{
|
||||
types = { "e4", "e4" },
|
||||
requirements = { "ftur" }
|
||||
},
|
||||
{
|
||||
types = { "e1", "e1", "e2", "e2" },
|
||||
onBuilt = AttackFromChurch
|
||||
},
|
||||
{
|
||||
types = { "shok", "shok" },
|
||||
requirements = { "ai.hard", "techcenter", "tsla" }
|
||||
}
|
||||
}
|
||||
VehicleTeams =
|
||||
{
|
||||
{
|
||||
types = { "3tnk" },
|
||||
requirements = { "fix" }
|
||||
},
|
||||
{
|
||||
types = { "ttnk", "3tnk" },
|
||||
requirements = { "techcenter", "fix", "tsla" }
|
||||
}
|
||||
}
|
||||
HarvesterTeam =
|
||||
{
|
||||
types = { "harv" },
|
||||
buildTime = Actor.BuildTime("harv"),
|
||||
onBuilt = function(actors)
|
||||
Utils.Do(actors, function(actor)
|
||||
actor.FindResources()
|
||||
end)
|
||||
end
|
||||
}
|
||||
|
||||
PrepareBadGuy = function()
|
||||
BadGuy.Cash = 100000
|
||||
if Difficulty == "hard" then
|
||||
SpawnMiscActor("ai.hard", BadGuy)
|
||||
end
|
||||
PrepareTeamBuildTimes(InfantryTeams)
|
||||
PrepareTeamBuildTimes(VehicleTeams)
|
||||
|
||||
SetBotStructures(
|
||||
{ type = "powr", exists = false, shape = "2x3", location = CPos.New(40, 51) },
|
||||
{ type = "proc", exists = false, shape = "3x4", location = CPos.New(33, 52) },
|
||||
{ type = "barr", exists = false, shape = "2x3", location = CPos.New(40, 55), onBuilt = BuildInfantry, onKilled = CheckNewBase, rebuildSkipped = true },
|
||||
{ type = "powr", exists = false, shape = "2x3", location = CPos.New(30, 50) },
|
||||
{ type = "ftur", exists = false, shape = "1x1", location = CPos.New(39, 58), onKilled = CheckNewBase },
|
||||
{ type = "ftur", exists = false, shape = "1x1", location = CPos.New(43, 57), onKilled = CheckNewBase },
|
||||
{ type = "powr", exists = false, shape = "2x3", location = CPos.New(30, 53) },
|
||||
{ type = "weap", exists = false, shape = "3x3", location = CPos.New(33, 56), onBuilt = BuildVehicles, onKilled = CheckNewBase, rebuildSkipped = true },
|
||||
-- Tesla coil is originally built before factory.
|
||||
-- For convention, coil is swapped (and service depot added).
|
||||
{ type = "tsla", exists = false, shape = "1x1", location = CPos.New(37, 57), onKilled = CheckNewBase },
|
||||
{ type = "fix", exists = false, shape = "3x3", location = CPos.New(30, 56) }
|
||||
)
|
||||
|
||||
Trigger.OnEnteredFootprint( { BuilderRally.Location }, function(actor, id)
|
||||
if actor.Type ~= "fact" then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.RemoveFootprintTrigger(id)
|
||||
ActivateBot()
|
||||
Trigger.OnKilled(actor, CheckNewBase)
|
||||
end)
|
||||
end
|
||||
|
||||
CheckNewBase = function()
|
||||
if IsNewBaseOkay() then
|
||||
return
|
||||
end
|
||||
PrepareTechSale()
|
||||
|
||||
if Difficulty == "hard" then
|
||||
StartFireSale(BadGuy)
|
||||
end
|
||||
end
|
||||
|
||||
IsNewBaseOkay = function()
|
||||
local types = { "fact", "barr", "ftur", "weap", "tsla" }
|
||||
|
||||
return Utils.Any(types, function(type)
|
||||
return BadGuy.HasPrerequisites({ type })
|
||||
end)
|
||||
end
|
||||
|
||||
PrepareTechSale = function()
|
||||
if ForwardTech.IsDead or ForwardTech.Owner == USSR then
|
||||
return
|
||||
end
|
||||
|
||||
ForwardTech.Owner = USSR
|
||||
if ForwardCommand.IsDead then
|
||||
StartFireSale(USSR)
|
||||
end
|
||||
end
|
||||
|
||||
ActivateBot = function()
|
||||
CheckStructures(BadGuy, DateTime.Seconds(5))
|
||||
BadGuy.GrantCondition("ai-enabled")
|
||||
end
|
||||
|
||||
PrepareTeamBuildTimes = function(teamCollection)
|
||||
Utils.Do(teamCollection, function(team)
|
||||
local time = 0
|
||||
|
||||
Utils.Do(team.types, function(type)
|
||||
time = time + Actor.BuildTime(type)
|
||||
end)
|
||||
|
||||
team.buildTime = time
|
||||
end)
|
||||
end
|
||||
|
||||
SelectTeam = function(teamCollection)
|
||||
local teams = Utils.Shuffle(teamCollection)
|
||||
|
||||
for i = 1, #teams do
|
||||
local team = teams[i]
|
||||
if AreTeamRequirementsMet(team.requirements) then
|
||||
return team
|
||||
end
|
||||
end
|
||||
|
||||
return { }
|
||||
end
|
||||
|
||||
AreTeamRequirementsMet = function(requirements)
|
||||
if not requirements then
|
||||
return true
|
||||
end
|
||||
|
||||
return BadGuy.HasPrerequisites(requirements)
|
||||
end
|
||||
|
||||
BuildTeam = function(player, team, nextBuild)
|
||||
if not team.types then
|
||||
-- No teams had their prerequisites.
|
||||
Trigger.AfterDelay(BuildIntervals[Difficulty], nextBuild)
|
||||
return
|
||||
end
|
||||
|
||||
local onBuilt = team.onBuilt or GroupIdleHunt
|
||||
player.Build(team.types, onBuilt)
|
||||
Trigger.AfterDelay(team.buildTime, nextBuild)
|
||||
end
|
||||
|
||||
BuildInfantry = function()
|
||||
if #BadGuy.GetActorsByType("barr") < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local team = SelectTeam(InfantryTeams)
|
||||
|
||||
BuildTeam(BadGuy, team, function()
|
||||
Trigger.AfterDelay(BuildIntervals[Difficulty], BuildInfantry)
|
||||
end)
|
||||
end
|
||||
|
||||
BuildVehicles = function()
|
||||
if #BadGuy.GetActorsByType("weap") < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local team = nil
|
||||
if IsHarvesterNeeded(BadGuy) then
|
||||
team = HarvesterTeam
|
||||
end
|
||||
team = team or SelectTeam(VehicleTeams)
|
||||
|
||||
BuildTeam(BadGuy, team, function()
|
||||
Trigger.AfterDelay(BuildIntervals[Difficulty], BuildVehicles)
|
||||
end)
|
||||
end
|
||||
|
||||
IsHarvesterNeeded = function(player)
|
||||
return player.HasPrerequisites({ "proc" }) and #player.GetActorsByType("harv") < 1
|
||||
end
|
||||
|
||||
AttackFromChurch = function(actors)
|
||||
local path =
|
||||
{
|
||||
BadGuyRally.Location,
|
||||
ForestPatrolStart.Location,
|
||||
GuideHouseReveal.Location,
|
||||
ChurchRally.Location,
|
||||
GuideHouseReveal.Location
|
||||
}
|
||||
GroupTightPatrol(actors, path, false, 0, GroupIdleHunt)
|
||||
end
|
||||
|
||||
--[[
|
||||
These kind of work like the original [BASE] and [STRUCTURES] .INI sections.
|
||||
Check a list every so often and (re)build structures that are missing,
|
||||
in order, and if circumstances allow for it.
|
||||
]]
|
||||
SetBotStructures = function(...)
|
||||
local structures = { ... }
|
||||
|
||||
Utils.Do(structures, function(structure)
|
||||
structure.cost = Actor.Cost(structure.type)
|
||||
structure.buildTime = Actor.BuildTime(structure.type)
|
||||
structure.position = WPos.New(structure.location.X * 1024, structure.location.Y * 1024, 0)
|
||||
end)
|
||||
|
||||
BadGuyStructures = structures
|
||||
end
|
||||
|
||||
CheckStructures = function(player, interval, rushed)
|
||||
if #player.GetActorsByType("fact") < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local buildingScheduled = false
|
||||
|
||||
for _, structure in pairs(BadGuyStructures) do
|
||||
if not structure.exists then
|
||||
buildingScheduled = true
|
||||
ScheduleStructure(player, structure, rushed or structure.buildTime, interval)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if buildingScheduled then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.AfterDelay(interval, function()
|
||||
CheckStructures(player, interval)
|
||||
end)
|
||||
end
|
||||
|
||||
ScheduleStructure = function(player, structure, buildTime, interval)
|
||||
Trigger.AfterDelay(buildTime, function()
|
||||
if #player.GetActorsByType("fact") < 1 then
|
||||
return
|
||||
end
|
||||
|
||||
local success, blocked = BuildStructure(player, structure)
|
||||
if not success and blocked then
|
||||
CheckStructures(player, interval, DateTime.Seconds(2))
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.AfterDelay(interval, function()
|
||||
CheckStructures(player, interval)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
AddRebuildTrigger = function(actor, structure)
|
||||
if structure.rebuildSkipped then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.OnRemovedFromWorld(actor, function()
|
||||
-- Period before the bot can consider replacement.
|
||||
Trigger.AfterDelay(StructureRebuildGrace, function()
|
||||
structure.exists = false
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
BuildStructure = function(player, structure)
|
||||
if structure.cost > player.Cash then
|
||||
return false
|
||||
end
|
||||
|
||||
local blocked = IsStructureAreaBlocked(player, structure.position, structure.shape)
|
||||
if blocked then
|
||||
return false, "blocked"
|
||||
end
|
||||
|
||||
local actor = Actor.Create(structure.type, true, { Owner = player, Location = structure.location })
|
||||
structure.exists = true
|
||||
player.Cash = player.Cash - structure.cost
|
||||
AddRebuildTrigger(actor, structure)
|
||||
|
||||
if structure.onKilled then
|
||||
Trigger.OnKilled(actor, structure.onKilled)
|
||||
end
|
||||
|
||||
if structure.onBuilt then
|
||||
-- Build() will not work properly on producers if called immediately.
|
||||
Trigger.AfterDelay(1, function()
|
||||
structure.onBuilt(actor)
|
||||
end)
|
||||
end
|
||||
|
||||
return actor
|
||||
end
|
||||
|
||||
StructureFootprints =
|
||||
{
|
||||
["1x1"] = WVec.New(1 * 1024, 1 * 1024, 0),
|
||||
["2x3"] = WVec.New(2 * 1024, 3 * 1024, 0),
|
||||
["3x3"] = WVec.New(3 * 1024, 3 * 1024, 0),
|
||||
["3x4"] = WVec.New(3 * 1024, 4 * 1024, 0),
|
||||
}
|
||||
|
||||
IsStructureAreaBlocked = function(player, position, shape)
|
||||
local foot = StructureFootprints[shape]
|
||||
local blockers = Map.ActorsInBox(position, position + foot, function(actor)
|
||||
return actor.CenterPosition.Z == 0 and actor.HasProperty("Health")
|
||||
end)
|
||||
|
||||
if #blockers == 0 then
|
||||
return false
|
||||
end
|
||||
|
||||
ScatterBlockers(player, blockers)
|
||||
return true
|
||||
end
|
||||
|
||||
ScatterBlockers = function(player, actors)
|
||||
Utils.Do(actors, function(actor)
|
||||
if actor.IsIdle and actor.Owner == player and actor.HasProperty("Scatter") then
|
||||
actor.Scatter()
|
||||
end
|
||||
end)
|
||||
end
|
||||
397
mods/ra/maps/negotiations/negotiations-reinforcements.lua
Normal file
397
mods/ra/maps/negotiations/negotiations-reinforcements.lua
Normal file
@@ -0,0 +1,397 @@
|
||||
--[[
|
||||
Copyright (c) The OpenRA Developers and Contributors
|
||||
This file is part of OpenRA, which is free software. It is made
|
||||
available to you under the terms of the GNU General Public License
|
||||
as published by the Free Software Foundation, either version 3 of
|
||||
the License, or (at your option) any later version. For more
|
||||
information, see COPYING.
|
||||
]]
|
||||
ReinforceAllies = function()
|
||||
Media.PlaySpeechNotification(Greece, "ReinforcementsArrived")
|
||||
local proxy = SpawnMiscActor("paradrop.allies", GoodGuy, TanyaDrop.Location, DateTime.Seconds(1))
|
||||
local planes = proxy.TargetParatroopers(TanyaDrop.CenterPosition, Angle.SouthEast)
|
||||
|
||||
Utils.Do(planes, function(plane)
|
||||
Trigger.OnPassengerExited(plane, function(_, passenger)
|
||||
passenger.Owner = Greece
|
||||
end)
|
||||
|
||||
Trigger.OnAllKilled(plane.Passengers, CheckAlliedDestruction)
|
||||
end)
|
||||
|
||||
local demoTypes = { "2tnk", "2tnk", "2tnk", "dtrk" }
|
||||
local artilleryTypes = {"2tnk", "2tnk", "arty", "arty", "mech", "mech", "mech" }
|
||||
local groundPath = { WestRoadEntry.Location, WestRoadRally.Location }
|
||||
|
||||
local demoTeam = Reinforcements.Reinforce(Greece, demoTypes, groundPath)
|
||||
Trigger.OnAllKilled(demoTeam, CheckAlliedDestruction)
|
||||
|
||||
Trigger.AfterDelay(DateTime.Seconds(30), function()
|
||||
if AlliesDefeated then
|
||||
return
|
||||
end
|
||||
|
||||
Media.PlaySpeechNotification(Greece, "ReinforcementsArrived")
|
||||
local artilleryTeam = Reinforcements.Reinforce(Greece, artilleryTypes, groundPath)
|
||||
Trigger.OnAllKilled(artilleryTeam, CheckAlliedDestruction)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceChronoTanks = function(locations)
|
||||
if ChronoTanksReinforced then
|
||||
return
|
||||
end
|
||||
|
||||
ChronoTanksReinforced = true
|
||||
Media.PlaySoundNotification(Greece, "Chronoshift")
|
||||
|
||||
Utils.Do(locations, function(location)
|
||||
local proxy = SpawnMiscActor("powerproxy.chronoshift", GoodGuy, location, DateTime.Seconds(1))
|
||||
local tank = Actor.Create("ctnk", true, { Owner = Greece, Facing = Angle.NorthWest })
|
||||
local payload = { [tank] = location }
|
||||
proxy.Chronoshift(payload)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceTanya = function()
|
||||
local path = { WestRoadEntry.Location, TanyaDrop.Location }
|
||||
local plane = Reinforcements.Reinforce(Greece, { "badr.tanya" }, path)[1]
|
||||
local passengers = { TanyaType }
|
||||
|
||||
Trigger.OnAddedToWorld(plane, function()
|
||||
Utils.Do(passengers, function(type)
|
||||
local passenger = Actor.Create(type, false, { Owner = Greece })
|
||||
plane.LoadPassenger(passenger)
|
||||
Trigger.OnKilled(passenger, OnTanyaKilled)
|
||||
Tanya = passenger
|
||||
AnnounceTanyaRules(passenger)
|
||||
end)
|
||||
|
||||
plane.Paradrop(path[2])
|
||||
end)
|
||||
end
|
||||
|
||||
AnnounceTanyaRules = function(tanya)
|
||||
if Difficulty == "easy" then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.OnAddedToWorld(tanya, function()
|
||||
Trigger.AfterDelay(DateTime.Seconds(1), function()
|
||||
Media.DisplayMessage(UserInterface.Translate("tanya-rules-of-engagement"), tanya.TooltipName)
|
||||
Media.PlaySoundNotification(Greece, "AlertBleep")
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceLongbows = function()
|
||||
if SoutheastTurret.IsDead then
|
||||
return
|
||||
end
|
||||
|
||||
local goals = { LongbowGoal1, LongbowGoal2, LongbowGoal3 }
|
||||
local spawnOffset = 0
|
||||
|
||||
Utils.Do(goals, function(goal)
|
||||
local entry = { LongbowEntry.Location + CVec.New(spawnOffset, 15) }
|
||||
|
||||
Reinforcements.Reinforce(GoodGuy, { "heli" }, entry, 0, function(helicopter)
|
||||
helicopter.Wait(DateTime.Seconds(1))
|
||||
helicopter.AttackMove(goal.Location)
|
||||
|
||||
-- Original behavior was to fire at the ground until out of ammo.
|
||||
Trigger.OnKilled(SoutheastTurret, function()
|
||||
helicopter.Stop()
|
||||
helicopter.Wait(10)
|
||||
helicopter.Move(LongbowExit.Location, 1)
|
||||
helicopter.Destroy()
|
||||
end)
|
||||
end)
|
||||
|
||||
spawnOffset = spawnOffset + 5
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceBadGuy = function()
|
||||
-- If ForwardCommand dies early, the General will be busy hunting.
|
||||
if General.IsDead or ForwardCommand.IsDead then
|
||||
return
|
||||
end
|
||||
|
||||
BadGuyReinforced = true
|
||||
Media.PlaySpeechNotification(Greece, "SignalFlareWest")
|
||||
local flare = SpawnFlare(NorthFlare.Location)
|
||||
|
||||
Trigger.OnKilled(General, function()
|
||||
RemoveActor(flare, DateTime.Seconds(30))
|
||||
end)
|
||||
|
||||
local waterCamera = SpawnPlayerCamera(NorthWaterEntry.Location, -1)
|
||||
local boatPath = { NorthWaterEntry.Location, BuilderBeach.Location }
|
||||
local cargo = Reinforcements.ReinforceWithTransport(BadGuy, "lst", { "mcv" }, boatPath, { boatPath[1] })[2]
|
||||
|
||||
Utils.Do(cargo, function(builder)
|
||||
Trigger.OnKilled(builder, CheckNewBase)
|
||||
|
||||
Trigger.OnAddedToWorld(builder, function()
|
||||
local builderCamera = SpawnPlayerCamera(builder.Location, -1, "camera.small")
|
||||
builder.Move(BuilderRally.Location)
|
||||
|
||||
builder.CallFunc(function()
|
||||
RemoveActor(waterCamera)
|
||||
RemoveActor(builderCamera)
|
||||
end)
|
||||
|
||||
builder.Deploy()
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceNorthChinook = function()
|
||||
local delay = DateTime.Seconds(4)
|
||||
local camera = SpawnPlayerCamera(NorthChinookUnload.Location, -1, "camera.small", delay + DateTime.Seconds(2))
|
||||
|
||||
Trigger.AfterDelay(delay, function()
|
||||
local transport = Actor.Create("tran.north", true, { Owner = USSR, Location = NorthChinookEntry.Location, Facing = Angle.South })
|
||||
local passengers = transport.Passengers
|
||||
|
||||
transport.UnloadPassengers(NorthChinookUnload.Location)
|
||||
transport.Move(SouthChinookExit.Location)
|
||||
transport.Destroy()
|
||||
|
||||
Trigger.OnPassengerExited(transport, function()
|
||||
if transport.PassengerCount > 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Original unload is slow. Mimic the timing with a short pause.
|
||||
Trigger.AfterDelay(DateTime.Seconds(3), function()
|
||||
OrderNorthChinookSoldiers(passengers, camera)
|
||||
end)
|
||||
end)
|
||||
|
||||
Trigger.OnAnyKilled(passengers, function()
|
||||
RemoveActor(camera, DateTime.Seconds(1))
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
OrderNorthChinookSoldiers = function(soldiers, camera)
|
||||
local patrolPath =
|
||||
{
|
||||
VillageNortheast.Location,
|
||||
VillageNorthwest.Location,
|
||||
VillageSouthwest.Location,
|
||||
VillageSoutheast.Location
|
||||
}
|
||||
local liveSoldiers = Utils.Where(soldiers, IsAlive)
|
||||
|
||||
Utils.Do(liveSoldiers, function(soldier)
|
||||
soldier.AttackMove(NorthChinookRally.Location)
|
||||
|
||||
soldier.CallFunc(function()
|
||||
Trigger.AfterDelay(1, function()
|
||||
if not AreAllIdleOrDead(liveSoldiers) then
|
||||
return
|
||||
end
|
||||
RemoveActor(camera, DateTime.Seconds(1))
|
||||
GroupAttackMove(liveSoldiers, VillageBridgeCenter.Location, 2)
|
||||
GroupTightPatrol(liveSoldiers, patrolPath, true)
|
||||
GroupHuntOnDamaged(liveSoldiers, Greece)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceVillagePatrol = function()
|
||||
if VillagePatrolSent then
|
||||
return
|
||||
end
|
||||
|
||||
VillagePatrolSent = true
|
||||
|
||||
local path =
|
||||
{
|
||||
VillageSoutheast.Location,
|
||||
VillageSouthwest.Location,
|
||||
VillageNorthwest.Location,
|
||||
VillageNortheast.Location
|
||||
}
|
||||
local types = { "e1", "e1", "e2", "dog" }
|
||||
local origin = { VillagePatrolEntry.Location }
|
||||
local group = Reinforcements.Reinforce(USSR, types, origin, 0)
|
||||
GroupTightPatrol(group, path, true)
|
||||
end
|
||||
|
||||
ReinforceGuardHouse = function(intruderType)
|
||||
if GuardHouse.IsDead then
|
||||
return
|
||||
end
|
||||
|
||||
local goal = GuideBarrelGoal.Location
|
||||
if intruderType == TanyaType then
|
||||
goal = PrisonReveal.Location
|
||||
end
|
||||
|
||||
for i = 1, 4 do
|
||||
Trigger.AfterDelay(i * 5, function()
|
||||
local guard = Actor.Create("e1", true, { Owner = USSR, Location = GuardHouseSpawn.Location, SubCell = 4, Facing = Angle.SouthWest })
|
||||
guard.Move(GuardHouseExit.Location)
|
||||
guard.AttackMove(goal)
|
||||
IdleHunt(guard)
|
||||
end)
|
||||
end
|
||||
end
|
||||
|
||||
ReinforceHardTeams = function()
|
||||
if Difficulty ~= "hard" then
|
||||
return
|
||||
end
|
||||
|
||||
ReinforceBaseDefenders()
|
||||
ReinforceNorthBeachGuards()
|
||||
ReinforceV2Team()
|
||||
ReinforceNorthEdgeGuards()
|
||||
PrepareSouthChinook()
|
||||
ReinforceHardDogs()
|
||||
end
|
||||
|
||||
ReinforceBaseDefenders = function()
|
||||
local origin = BadGuyRally.Location + CVec.New(2, 2)
|
||||
local types = { "3tnk", "3tnk" }
|
||||
local defenders = Reinforcements.Reinforce(USSR, types, { origin }, 0, function(actor)
|
||||
actor.Wait(1)
|
||||
actor.Scatter()
|
||||
end)
|
||||
GroupHuntOnDamaged(defenders, Greece)
|
||||
|
||||
local structures = { RoadTurretWest, RoadTurretEast, ForwardPower, ForwardCommand, ForwardTech }
|
||||
Trigger.OnAnyKilled(structures, function()
|
||||
GroupIdleHunt(defenders)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceNorthBeachGuards = function()
|
||||
local types = { "e1", "e1", "e2", "e1" }
|
||||
local patrolPath = { VillageNortheast.Location, VillageSouthwest.Location }
|
||||
local soldiers = Reinforcements.Reinforce(USSR, types, { NorthFlare.Location }, 0, function(actor)
|
||||
actor.Scatter()
|
||||
end)
|
||||
|
||||
Trigger.OnEnteredFootprint({ GeneralRally.Location }, function(actor, id)
|
||||
if actor.Type ~= "gnrl" then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.RemoveFootprintTrigger(id)
|
||||
GroupHuntOnDamaged(soldiers, Greece)
|
||||
|
||||
Utils.Do(soldiers, function(soldier)
|
||||
if soldier.IsDead then
|
||||
return
|
||||
end
|
||||
|
||||
soldier.AttackMove(BadGuyRally.Location)
|
||||
soldier.AttackMove(GuideBarrelGoal.Location)
|
||||
soldier.AttackMove(VillageNorthwest.Location)
|
||||
soldier.Patrol(patrolPath, true)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceV2Team = function()
|
||||
local rocket = Actor.Create("v2rl", true, { Owner = USSR, Facing = Angle.West, Location = SamRocketEntry.Location })
|
||||
Reinforcements.Reinforce(USSR, { "e1" }, { SamFenceReveal.Location }, 0, function(actor)
|
||||
actor.Guard(rocket)
|
||||
end)
|
||||
end
|
||||
|
||||
PrepareSouthChinook = function()
|
||||
local startPosition = SouthChinookEntry.CenterPosition + WVec.New(0, 0, Actor.CruiseAltitude("tran"))
|
||||
|
||||
Trigger.OnEnteredProximityTrigger(SouthChinookProximity.CenterPosition, WDist.FromCells(8), function(actor, id)
|
||||
if actor.Type ~= TanyaType or not EscortFinished then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.RemoveProximityTrigger(id)
|
||||
|
||||
local transport = Actor.Create("tran.south", true, { Owner = USSR, CenterPosition = startPosition, Facing = Angle.East })
|
||||
transport.UnloadPassengers(SouthChinookEntry.Location)
|
||||
transport.Move(SouthChinookExit.Location)
|
||||
transport.Destroy()
|
||||
|
||||
Utils.Do(transport.Passengers, function(passenger)
|
||||
Trigger.OnAddedToWorld(passenger, function()
|
||||
passenger.Wait(DateTime.Seconds(1))
|
||||
IdleHunt(passenger)
|
||||
end)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceNorthEdgeGuards = function()
|
||||
local types = { "e1", "e1", "e2" }
|
||||
local guards = Reinforcements.Reinforce(USSR, types, { NorthEdgeGuardEntry.Location }, 0, function(actor)
|
||||
actor.Scatter()
|
||||
end)
|
||||
GroupHuntOnDamaged(guards, Greece)
|
||||
end
|
||||
|
||||
ReinforceStartSoldiers = function()
|
||||
local teams =
|
||||
{
|
||||
{
|
||||
entry = { WestRoadEntry.Location },
|
||||
types = { "e2", "e2", "e1" }
|
||||
},
|
||||
{
|
||||
entry = { RoadTurretRifle2.Location },
|
||||
types = { "e1", "e2", "e4" }
|
||||
}
|
||||
}
|
||||
|
||||
Utils.Do(teams, function(team)
|
||||
local soldiers = Reinforcements.Reinforce(USSR, team.types, team.entry, 15, function(actor)
|
||||
actor.AttackMove(TanyaDrop.Location)
|
||||
actor.Hunt()
|
||||
end)
|
||||
|
||||
Trigger.OnAnyKilled(soldiers, function()
|
||||
IdleHunt(StartSoldier)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
|
||||
ReinforceHardDogs = function()
|
||||
local startDog = Actor.Create("dog", true, { Owner = USSR, Location = WestRoadEntry.Location, Facing = Angle.South })
|
||||
startDog.Move(WestRoadRally.Location + CVec.New(-1, 0))
|
||||
PrepareStartDogAttack(startDog)
|
||||
|
||||
local turretDog = Actor.Create("dog.areaguard", true, { Owner = USSR, Location = LongbowGoal2.Location, Facing = Angle.SouthWest })
|
||||
turretDog.AddTag("TurretDog")
|
||||
|
||||
Trigger.OnKilled(turretDog, function()
|
||||
GroupIdleHunt(SoutheastTurretGuards)
|
||||
end)
|
||||
end
|
||||
|
||||
PrepareStartDogAttack = function(dog)
|
||||
-- Tanya will take about 100 ticks/4 seconds to touch the ground.
|
||||
local delay = DateTime.Seconds(5) + 5
|
||||
|
||||
Trigger.OnEnteredFootprint({ TanyaDrop.Location }, function(actor, id)
|
||||
if actor.Type ~= TanyaType then
|
||||
return
|
||||
end
|
||||
|
||||
Trigger.RemoveFootprintTrigger(id)
|
||||
Trigger.AfterDelay(delay, function()
|
||||
if dog.IsDead or actor.IsDead then
|
||||
return
|
||||
end
|
||||
|
||||
dog.Attack(actor)
|
||||
end)
|
||||
end)
|
||||
end
|
||||
1265
mods/ra/maps/negotiations/negotiations.lua
Normal file
1265
mods/ra/maps/negotiations/negotiations.lua
Normal file
File diff suppressed because it is too large
Load Diff
6
mods/ra/maps/negotiations/notifications.yaml
Normal file
6
mods/ra/maps/negotiations/notifications.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
Sounds:
|
||||
Notifications:
|
||||
HostageShot: gun27
|
||||
DogWhine: dogw5
|
||||
GuideOkay: guyokay1
|
||||
Chronoshift: chrono2
|
||||
196
mods/ra/maps/negotiations/rules.yaml
Normal file
196
mods/ra/maps/negotiations/rules.yaml
Normal file
@@ -0,0 +1,196 @@
|
||||
World:
|
||||
LuaScript:
|
||||
Scripts: campaign.lua, utils.lua, negotiations.lua, negotiations-ai.lua, negotiations-reinforcements.lua
|
||||
MissionData:
|
||||
Briefing: A Soviet force has holed up in a small town, threatening to kill a hostage every few minutes until their demands are met. We do not negotiate with terrorists -- explain this to them.\n\nLocate the hostages, free as many of them as you can, and get them safely to a nearby abandoned church. Once done, return to your drop-off point, signal for reinforcements, and finish off the Soviet forces.
|
||||
WinVideo: allymorf.vqa
|
||||
LossVideo: battle.vqa
|
||||
TimeLimitManager:
|
||||
SkipTimerExpiredNotification: true
|
||||
StartGameNotification:
|
||||
Notification: TimerStarted
|
||||
ScriptLobbyDropdown@difficulty:
|
||||
ID: difficulty
|
||||
Label: dropdown-difficulty.label
|
||||
Description: dropdown-difficulty.description
|
||||
Values:
|
||||
easy: options-difficulty.easy
|
||||
normal: options-difficulty.normal
|
||||
hard: options-difficulty.hard
|
||||
Default: normal
|
||||
|
||||
Player:
|
||||
ExternalCondition@BadGuy:
|
||||
Condition: ai-enabled
|
||||
HarvesterBotModule:
|
||||
RequiresCondition: ai-enabled
|
||||
BuildingRepairBotModule:
|
||||
RequiresCondition: ai-enabled
|
||||
MissionObjectives:
|
||||
Cooperative: true
|
||||
|
||||
ai.hard:
|
||||
ProvidesPrerequisite:
|
||||
Prerequisite: ai.hard
|
||||
Interactable:
|
||||
AlwaysVisible:
|
||||
|
||||
paradrop.allies:
|
||||
Inherits: powerproxy.paratroopers
|
||||
ParatroopersPower:
|
||||
DropItems: e1,e1,e3,e3,e3,e3,e1,e1,e1,medi
|
||||
SquadSize: 2
|
||||
DisplayBeacon: false
|
||||
|
||||
powerproxy.chronoshift:
|
||||
AlwaysVisible:
|
||||
ChronoshiftPower:
|
||||
Dimensions: 1, 1
|
||||
Footprint: x
|
||||
|
||||
CAMERA.tiny:
|
||||
Inherits: CAMERA
|
||||
RevealsShroud:
|
||||
Range: 2c0
|
||||
|
||||
CAMERA.small:
|
||||
Inherits: CAMERA
|
||||
RevealsShroud:
|
||||
Range: 4c0
|
||||
|
||||
CTNK:
|
||||
Chronoshiftable:
|
||||
ChronoshiftSound:
|
||||
|
||||
V2RL:
|
||||
AutoTarget:
|
||||
# A mobile V2 can be annoying to chase, or too much with the dog attack.
|
||||
InitialStanceAI: Defend
|
||||
|
||||
# This type will mimic the wide guard range sometimes given to RA '96 dogs by
|
||||
# Area Guard orders. An extra dog on Hard difficulty will also use this.
|
||||
DOG.areaguard:
|
||||
Inherits: DOG
|
||||
RenderSprites:
|
||||
Image: dog
|
||||
AutoTarget:
|
||||
ScanRadius: 9
|
||||
RevealsShroud:
|
||||
Range: 9c0
|
||||
ScriptTags:
|
||||
|
||||
# Keep SAMs online if the nearby truck blows; original SAMs require no power.
|
||||
# This does not affect the MCV-built base.
|
||||
APWR:
|
||||
PowerMultiplier:
|
||||
Modifier: 200
|
||||
|
||||
MISS:
|
||||
Tooltip:
|
||||
Name: actor-prison-name
|
||||
|
||||
STEK:
|
||||
ProvidesPrerequisite@TeslaTanks:
|
||||
Prerequisite: vehicles.russia
|
||||
ProvidesPrerequisite@ShockTroopers:
|
||||
Prerequisite: infantry.russia
|
||||
Power:
|
||||
Amount: 0
|
||||
SpawnActorsOnSell:
|
||||
# Swap out the normal Technician that resembles one of the civilians.
|
||||
# Swap Engineers as well because they're useless for hunting.
|
||||
ActorTypes: e1,tecn2,tecn2,c8,c9
|
||||
|
||||
V08:
|
||||
# Keep close to original death time of ~10s (at normal 5/7).
|
||||
DamageMultiplier:
|
||||
Modifier: 250
|
||||
|
||||
C1:
|
||||
ExternalCondition@untargetable:
|
||||
Condition: untargetable
|
||||
Targetable:
|
||||
RequiresCondition: !untargetable
|
||||
Infiltrates:
|
||||
Types: SpyInfiltrate
|
||||
ValidRelationships: Ally, Neutral
|
||||
-Wanders:
|
||||
|
||||
V01:
|
||||
InfiltrateForPowerOutage:
|
||||
Types: SpyInfiltrate
|
||||
Targetable@Guide:
|
||||
TargetTypes: SpyInfiltrate
|
||||
Cargo:
|
||||
MaxWeight: 5
|
||||
Types: c2,c3,c4,c5,c6
|
||||
LoadedCondition: loaded
|
||||
RevealsShroud:
|
||||
RequiresCondition: !loaded
|
||||
Range: 4c0
|
||||
Type: CenterPosition
|
||||
ValidRelationships: Ally, Neutral
|
||||
|
||||
^Hostage:
|
||||
# Not hostile to Soviets but will "guard" Tanya as she returns to the church.
|
||||
Inherits@Armed: ^ArmedCivilian
|
||||
Guard:
|
||||
TargetLineColor: 00000000
|
||||
RevealsShroud@Imprisoned:
|
||||
Range: 1c512
|
||||
ValidRelationships: Neutral
|
||||
|
||||
C2:
|
||||
Inherits@Hostage: ^Hostage
|
||||
|
||||
C3:
|
||||
Inherits@Hostage: ^Hostage
|
||||
|
||||
C4:
|
||||
Inherits@Hostage: ^Hostage
|
||||
|
||||
C5:
|
||||
Inherits@Hostage: ^Hostage
|
||||
|
||||
C6:
|
||||
Inherits@Hostage: ^Hostage
|
||||
|
||||
C8:
|
||||
Inherits@Armed: ^ArmedCivilian
|
||||
|
||||
C9:
|
||||
Inherits@Armed: ^ArmedCivilian
|
||||
|
||||
GNRL:
|
||||
SpeedMultiplier:
|
||||
Modifier: 150
|
||||
AutoTarget:
|
||||
InitialStanceAI: Defend
|
||||
|
||||
BADR.tanya:
|
||||
Inherits: BADR
|
||||
RenderSprites:
|
||||
Image: badr
|
||||
ParaDrop:
|
||||
# Avoid dropping Tanya short of her target waypoint (and any triggers).
|
||||
DropRange: 0c512
|
||||
|
||||
HELI:
|
||||
# Make it less obvious that the Guide House rifles do nothing until revealed.
|
||||
RevealsShroudMultiplier:
|
||||
Modifier: 70
|
||||
|
||||
TRAN.north:
|
||||
Inherits: TRAN
|
||||
RenderSprites:
|
||||
Image: tran
|
||||
Cargo:
|
||||
InitialUnits: e2,e2,e3,shok,shok
|
||||
|
||||
TRAN.south:
|
||||
Inherits: TRAN
|
||||
RenderSprites:
|
||||
Image: tran
|
||||
Cargo:
|
||||
InitialUnits: e1,e1,e4,e2,e2
|
||||
AfterUnloadDelay: 0
|
||||
12
mods/ra/maps/negotiations/weapons.yaml
Normal file
12
mods/ra/maps/negotiations/weapons.yaml
Normal file
@@ -0,0 +1,12 @@
|
||||
# Like original, avoid hitting targets besides the southeast flame turret.
|
||||
HellfireAG:
|
||||
ValidTargets: Structure
|
||||
|
||||
# The default explosion can kill the prison barrels, freeing all hostages.
|
||||
# The original spread seems closer to half the default.
|
||||
# Clipped the last two damage warheads to avoid this.
|
||||
MiniNuke:
|
||||
Warhead@7Dam_areanuke2: SpreadDamage
|
||||
Spread: 2c512
|
||||
Warhead@10Dam_areanuke3: SpreadDamage
|
||||
Spread: 2c512
|
||||
@@ -50,6 +50,7 @@ Aftermath Allied Missions:
|
||||
in-the-nick-of-time
|
||||
production-disruption
|
||||
monster-tank-madness
|
||||
negotiations
|
||||
Aftermath Soviet Missions:
|
||||
shock-therapy
|
||||
situation-critical
|
||||
|
||||
Reference in New Issue
Block a user