Initial commit: OpenRA game engine
Fork from OpenRA/OpenRA with one-click launch script (start-ra.cmd) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
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
|
||||
Reference in New Issue
Block a user