Skip to content

Commit 282c7c5

Browse files
author
efrec
committed
mostly replaced unit_continuous_aim.lua for dynamic maxunits
This adds support for give/take/capture and dynamic team maxunits. Non-player teams get a very large reference value for their maxunits, making them less prone to reaimTime penalties and keeping a more smooth difficulty scaling as they produce more units. Player teams scale vs. a reference amount, 2400 (2000 maxunits + 400 default spam rating for padding), so unit control is never noticeably different due to game mode. It also allows spam score to decrease over time, treating this as a performance tradeoff rather than a penalty for making units, and spreads out the reaimTime penalty to all unitdefs on that team, not only the source "spam-prone" unit. Finally, all units are "spammable" to some degree, now, and all values have been adjusted to account for this.
1 parent 508b1bc commit 282c7c5

File tree

1 file changed

+79
-217
lines changed

1 file changed

+79
-217
lines changed

luarules/gadgets/unit_continuous_aim.lua

Lines changed: 79 additions & 217 deletions
Original file line numberDiff line numberDiff line change
@@ -7,248 +7,110 @@ function gadget:GetInfo()
77
author = "Doo, Beherith",
88
date = "April 2018",
99
license = "GNU GPL, v2 or later",
10-
layer = 0,
11-
enabled = true, -- When we will move on 105 :)
10+
layer = 0, -- after game_dynamic_maxunits for accurate unit caps
11+
enabled = tonumber(Engine.versionMajor) >= 105,
1212
}
1313
end
1414

1515
if not gadgetHandler:IsSyncedCode() then
1616
return
1717
end
1818

19+
local reaimTimeMax = 0.5 ---@type number Caps dynamic reaim times and filters unitDefs. In seconds.
20+
local spamRatingBase = 400 ---@type integer Each team's reaimtime increases by 1 each X units made.
21+
local spamRatingMax = 1000 ---@type integer Sets the minimum performance penalty for spammed units.
22+
local unitCapDefault = 2400 ---@type integer The per-team unit cap we assume in these spam ratings.
23+
24+
local math_floor = math.floor
25+
local math_max = math.max
26+
local math_clamp = math.clamp
27+
28+
local spGetTeamLuaAI = Spring.GetTeamLuaAI
29+
local spGetTeamMaxUnits = Spring.GetTeamMaxUnits
1930
local spSetUnitWeaponState = Spring.SetUnitWeaponState
20-
local tableCopy = table.copy
21-
22-
23-
local convertedUnitsNames = {
24-
-- value is reaimtime in frames, engine default is 15
25-
['armfav'] = 3,
26-
['armbeamer'] = 3,
27-
['armpw'] = 2,
28-
['armpwt4'] = 2,
29-
['armflea'] = 2,
30-
['armrock'] = 2,
31-
['armham'] = 2,
32-
['armwar'] = 6,
33-
['armjeth'] = 2,
34-
['corfav'] = 3,
35-
['corak'] = 2,
36-
['corthud'] = 2,
37-
['corstorm'] = 2,
38-
['corcrash'] = 5,
39-
['legkark'] = 2,
40-
['corkark'] = 2,
41-
['cordeadeye'] =2,
42-
['armsnipe'] = 2,
43-
['armfido'] = 3,
44-
['armfboy'] = 2,
45-
['armfast'] = 2,
46-
['armamph'] = 3,
47-
['armmav'] = 2,
48-
['armspid'] = 3,
49-
['armsptk'] = 5,
50-
['armzeus'] = 3,
51-
['coramph'] = 3,
52-
['corcan'] = 2,
53-
['corhrk'] = 5,
54-
['cormando'] = 2,
55-
['cormort'] = 2,
56-
['corpyro'] = 2,
57-
['cortermite'] = 2,
58-
['armraz'] = 1,
59-
['armmar'] = 3,
60-
['armbanth'] = 1,
61-
['corkorg'] = 1,
62-
['armvang'] = 3,
63-
['armcrus'] = 5,
64-
['corsala'] = 6,
65-
['corsiegebreaker'] = 5,
66-
['legerailtank'] = 9,
67-
68-
-- the following units get a faster reaimtime to counteract their turret acceleration
69-
['armthor'] = 4,
70-
['armflash'] = 6,
71-
['corgator'] = 6,
72-
['armdecade'] = 6,
73-
['coresupp'] = 6,
74-
['corhlt'] = 5,
75-
['corfhlt'] = 5,
76-
['cordoom'] = 5,
77-
['corshiva'] = 5,
78-
['corcat'] = 5,
79-
['corkarg'] = 5,
80-
['corbhmth'] = 5,
81-
['armguard'] = 5,
82-
['armamb'] = 5,
83-
['corpun'] = 5,
84-
['cortoast'] = 5,
85-
['corbats'] = 5,
86-
['corblackhy'] = 6,
87-
['corscreamer'] = 5,
88-
['corcom'] = 5,
89-
['armcom'] = 5,
90-
['cordecom'] = 5,
91-
['armdecom'] = 5,
92-
['legcom'] = 5,
93-
['legdecom'] = 5,
94-
['legcomlvl2'] = 5,
95-
['legcomlvl3'] = 5,
96-
['legcomlvl4'] = 5,
97-
['legcomlvl5'] = 5,
98-
['legcomlvl6'] = 5,
99-
['legcomlvl7'] = 5,
100-
['legcomlvl8'] = 5,
101-
['legcomlvl9'] = 5,
102-
['legcomlvl10'] = 5,
103-
['legah'] = 5,
104-
['legbal'] = 5,
105-
['legbastion'] = 5,
106-
['legcen'] = 3,
107-
['legfloat'] = 5,
108-
['leggat'] = 5,
109-
['leggob'] = 5,
110-
['leginc'] = 1,
111-
['cordemon'] = 6,
112-
['corcrwh'] = 7,
113-
['leglob'] = 5,
114-
['legmos'] = 5,
115-
['leghades'] = 5,
116-
['leghelios'] = 5,
117-
['legheavydrone'] = 5,
118-
['legkeres'] = 5,
119-
['legrail'] = 5,
120-
['legbar'] = 5,
121-
['legcomoff'] = 5,
122-
['legcomt2off'] = 5,
123-
['legcomt2com'] = 5,
124-
['legstr'] = 3,
125-
['legamph'] = 4,
126-
['legbart'] = 5,
127-
['legmrv'] = 5,
128-
['legsco'] = 5,
129-
['leegmech'] = 5,
130-
['legionnaire'] = 5,
131-
['legafigdef'] = 5,
132-
['legvenator'] = 5,
133-
['legmed'] = 5,
134-
['legaskirmtank'] = 5,
135-
['legaheattank'] = 3,
136-
['legeheatraymech'] = 1,
137-
['legtriariusdrone'] = 1,
138-
['legnavydestro'] = 4,
139-
['legeheatraymech_old'] = 1,
140-
['legbunk'] = 3,
141-
['legrwall'] = 4,
142-
['legjav'] = 1,
143-
['legeshotgunmech'] = 3,
144-
['legehovertank'] = 4,
145-
['armanavaldefturret'] = 4,
146-
}
147-
--add entries for scavboss
148-
local scavengerBossV4Table = {'scavengerbossv4_veryeasy', 'scavengerbossv4_easy', 'scavengerbossv4_normal', 'scavengerbossv4_hard', 'scavengerbossv4_veryhard', 'scavengerbossv4_epic',
149-
'scavengerbossv4_veryeasy_scav', 'scavengerbossv4_easy_scav', 'scavengerbossv4_normal_scav', 'scavengerbossv4_hard_scav', 'scavengerbossv4_veryhard_scav', 'scavengerbossv4_epic_scav'}
150-
for _, name in pairs(scavengerBossV4Table) do
151-
convertedUnitsNames[name] = 4
152-
end
153-
--if Spring.GetModOptions().emprework then
154-
--convertedUnitsNames['armdfly'] = 50
155-
--end
156-
-- convert unitname -> unitDefID
157-
local convertedUnits = {}
158-
for name, params in pairs(convertedUnitsNames) do
159-
if UnitDefNames[name] then
160-
convertedUnits[UnitDefNames[name].id] = params
31+
32+
local reaimFramesMax = math.round(reaimTimeMax * Game.gameSpeed)
33+
local unitCapReference = math_max(Spring.GetModOptions().maxunits, unitCapDefault)
34+
local unitCapNonPlayer = math_max(6000, unitCapReference) -- for one team vs. many
35+
36+
local unitReaimTimes = {}
37+
local unitSpamRating = {}
38+
local unitWeaponCount = {}
39+
40+
-- Unit scripts have to manually check in script if it is at the desired heading.
41+
-- See: https://springrts.com/phpbb/viewtopic.php?t=36654
42+
for unitDefID, unitDef in pairs(UnitDefs) do
43+
if #unitDef.weapons >= 1 then
44+
if tonumber(unitDef.customParams.continuous_aim_time) then
45+
local reaimTime = tonumber(unitDef.customParams.continuous_aim_time)
46+
local reaimFrames = math_max(math.round(reaimTime * Game.gameSpeed), 1)
47+
if reaimFrames < reaimFramesMax then
48+
unitReaimTimes[unitDefID] = reaimFrames
49+
unitWeaponCount[unitDefID] = #unitDef.weapons
50+
end
51+
end
52+
local spamCount = tonumber(unitDef.customParams.continuous_aim_spam) or spamRatingBase
53+
local spamScore = 1 / math.clamp(spamCount, 1, spamRatingMax) -- as reaimTime per unit
54+
unitSpamRating[unitDefID] = spamScore * unitCapDefault -- as reaimTime/unit/max units
16155
end
16256
end
163-
convertedUnitsNames = nil
164-
165-
166-
local spamUnitsTeamsNames = { --{unitDefID = {teamID = totalcreated,...}}
167-
['armpw'] = {},
168-
['armflea'] = {},
169-
['armfav'] = {},
170-
['corak'] = {},
171-
['corfav'] = {},
172-
}
173-
-- convert unitname -> unitDefID
174-
local spamUnitsTeams = {}
175-
for name, params in pairs(spamUnitsTeamsNames) do
176-
if UnitDefNames[name] then
177-
spamUnitsTeams[UnitDefNames[name].id] = params
178-
end
57+
58+
local teamMaxUnits = {}
59+
local teamReaimTimes = {}
60+
61+
local function isNonPlayerEnemyTeam(teamID)
62+
local luaAI = spGetTeamLuaAI(teamID)
63+
return luaAI and (luaAI:find("Scavenger") or luaAI:find("Raptor")) -- seems not-clean tbh
17964
end
180-
spamUnitsTeamsNames = nil
18165

66+
local function getTeamMaxUnits(teamID)
67+
local actual = spGetTeamMaxUnits(teamID)
68+
local reference = isNonPlayerEnemyTeam(teamID) and unitCapNonPlayer or unitCapReference
69+
return actual and (math_max(actual, reference) + reference) * 0.5 or reference
70+
end
18271

183-
local spamUnitsTeamsReaimTimes = {} --{unitDefID = {teamID = currentReAimTime,...}}
72+
function gadget:MetaUnitAdded(unitID, unitDefID, unitTeam)
73+
local unitReaimTime = unitReaimTimes[unitDefID]
18474

75+
if unitReaimTime then
76+
local teamReaimTime = teamReaimTimes[unitTeam]
77+
local addSpamRating = unitSpamRating[unitDefID] / teamMaxUnits[unitTeam]
18578

186-
-- for every spamThreshold'th spammable unit type built by this team, increase reaimtime by 1 for that team
187-
local spamThreshold = 100
188-
local maxReAimTime = 15
79+
teamReaimTime = teamReaimTime + addSpamRating
80+
teamReaimTimes[unitTeam] = teamReaimTime
81+
unitReaimTime = math_clamp(math_floor(unitReaimTime + teamReaimTime), 1, reaimFramesMax)
18982

190-
-- add for scavengers copies
191-
local convertedUnitsCopy = tableCopy(convertedUnits)
192-
for id, v in pairs(convertedUnitsCopy) do
193-
if UnitDefNames[UnitDefs[id].name..'_scav'] then
194-
convertedUnits[UnitDefNames[UnitDefs[id].name..'_scav'].id] = v
83+
for weaponNum = 1, unitWeaponCount[unitDefID] do
84+
-- NOTE: this will prevent unit from firing if it does not IMMEDIATELY return from AimWeapon (no sleeps, not wait for turns!)
85+
-- So you have to manually check in script if it is at the desired heading
86+
spSetUnitWeaponState(unitID, weaponNum, "reaimTime", unitReaimTime)
87+
end
19588
end
19689
end
19790

198-
local spamUnitsTeamsCopy = tableCopy(spamUnitsTeams)
199-
for id,v in pairs(spamUnitsTeamsCopy) do
200-
if UnitDefNames[UnitDefs[id].name..'_scav'] then
201-
spamUnitsTeams[UnitDefNames[UnitDefs[id].name..'_scav'].id] = {}
91+
function gadget:MetaUnitRemoved(unitID, unitDefID, teamID)
92+
if unitSpamRating[unitDefID] then
93+
teamReaimTimes[teamID] = teamReaimTimes[teamID] - unitSpamRating[unitDefID]
20294
end
20395
end
20496

205-
for unitDefID, _ in pairs(spamUnitsTeams) do
206-
spamUnitsTeamsReaimTimes[unitDefID] = {}
207-
end
208-
209-
local unitWeapons = {}
210-
for unitDefID, _ in pairs(convertedUnits) do
211-
local unitDef = UnitDefs[unitDefID]
212-
if unitDef then
213-
local weapons = unitDef.weapons
214-
if #weapons > 0 then
215-
unitWeapons[unitDefID] = {}
216-
for id, _ in pairs(weapons) do
217-
unitWeapons[unitDefID][id] = true -- no need to store weapondefid
218-
end
219-
else
220-
-- units with no weapons shouldnt even be here
221-
convertedUnits[unitDefID] = nil
222-
end
97+
function gadget:TeamDied(deadTeamID)
98+
-- For now, this is our only source of TransferTeamMaxUnits after init.
99+
for _, teamID in pairs(Spring.GetTeamList() or {}) do
100+
teamMaxUnits[teamID] = getTeamMaxUnits(teamID)
223101
end
224102
end
225103

226-
function gadget:UnitCreated(unitID, unitDefID, teamID)
227-
if convertedUnits[unitDefID] then
228-
local currentReaimTime = convertedUnits[unitDefID]
229-
230-
if spamUnitsTeams[unitDefID] then
231-
if not spamUnitsTeams[unitDefID][teamID] then
232-
-- initialize for this team at base defaults
233-
spamUnitsTeams[unitDefID][teamID] = 1
234-
spamUnitsTeamsReaimTimes[unitDefID][teamID] = convertedUnits[unitDefID]
235-
else
236-
local spamCount = spamUnitsTeams[unitDefID][teamID] + 1
237-
spamUnitsTeams[unitDefID][teamID] = spamCount
238-
currentReaimTime = spamUnitsTeamsReaimTimes[unitDefID][teamID]
239-
if spamCount % spamThreshold == 0 and currentReaimTime < maxReAimTime then
240-
spamUnitsTeamsReaimTimes[unitDefID][teamID] = currentReaimTime + 1
241-
--Spring.Echo("Unit type", unitDefID,'has been built', spamCount, 'times by team', teamID,'increasing reaimtime to ', currentReaimTime + 1)
242-
end
243-
end
244-
end
245-
if currentReaimTime < 15 then
246-
for id, _ in pairs(unitWeapons[unitDefID]) do
247-
-- NOTE: this will prevent unit from firing if it does not IMMEDIATELY return from AimWeapon (no sleeps, not wait for turns!)
248-
-- So you have to manually check in script if it is at the desired heading
249-
-- https://springrts.com/phpbb/viewtopic.php?t=36654
250-
spSetUnitWeaponState(unitID, id, "reaimTime", currentReaimTime)
251-
end
104+
function gadget:Initialize()
105+
teamMaxUnits = {}
106+
teamReaimTimes = {}
107+
108+
for _, teamID in pairs(Spring.GetTeamList() or {}) do
109+
teamMaxUnits[teamID] = getTeamMaxUnits(teamID)
110+
teamReaimTimes[teamID] = 0
111+
112+
for _, unitID in pairs(Spring.GetTeamUnits(teamID) or {}) do
113+
gadget:MetaUnitAdded(unitID, Spring.GetUnitDefID(unitID), teamID)
252114
end
253115
end
254116
end

0 commit comments

Comments
 (0)