Files
OpenRA/mods/ra/maps/negotiations/negotiations-ai.lua
let5sne.win10 9cf6ebb986
Some checks failed
Continuous Integration / Linux (.NET 8.0) (push) Has been cancelled
Continuous Integration / Windows (.NET 8.0) (push) Has been cancelled
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>
2026-01-10 21:46:54 +08:00

348 lines
8.3 KiB
Lua

--[[
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