root/trunk/mods/ca/LuaRules/Gadgets/unit_metal_hax.lua

Revision 3171, 10.2 KB (checked in by det, 2 months ago)

Reverting [3165], commit 2/2 (I lied)

  • Property svn:eol-style set to native
  • Property license set to GPL
  • Property svn:keywords set to Id
Line 
1-- $Id$
2----------------------------------------------------------------
3--Metal Hax
4----------------------------------------------------------------
5
6function gadget:GetInfo()
7  return {
8    name      = "Mex Hax",
9    desc      = "Takes over mexes from the engine.",
10    author    = "Evil4Zerggin",
11    date      = "24 May 2008",
12    license   = "GNU GPL, v2 or later",
13    layer     = 0,
14    enabled   = false  --  loaded by default?
15  }
16end
17
18if (gadgetHandler:IsSyncedCode()) then
19
20----------------------------------------------------------------
21--parameters
22----------------------------------------------------------------
23local maxMetal = 0.001 --scale metal by this value; acts like extractsMetal
24local desiredEnergy = 0.8 -- on which percentage to hover
25local storedEnergyWeight = 0.2 -- how much to weight the energy to spend on how much energy is stored
26local deltaEWeight = 0.2 --how much to weight the energy to spend on how quickly energy is changing
27
28----------------------------------------------------------------
29--speed-ups
30----------------------------------------------------------------
31local mapSizeX = Game.mapSizeX
32local mapSizeZ = Game.mapSizeZ
33local extractorRadius = Game.extractorRadius
34
35local GetGroundInfo = Spring.GetGroundInfo
36local GetUnitPosition = Spring.GetUnitPosition
37local SetUnitResourcing = Spring.SetUnitResourcing
38local SetUnitMetalExtraction = Spring.SetUnitMetalExtraction
39local GetTeamResources = Spring.GetTeamResources
40local GetUnitTeam = Spring.GetUnitTeam
41
42----------------------------------------------------------------
43--constants
44----------------------------------------------------------------
45
46local metalMapScale = 16
47local metalMapScaleSq = 256
48
49local metalMapMaxX = math.floor(mapSizeX / metalMapScale) - 1
50local metalMapMaxZ = math.floor(mapSizeZ / metalMapScale) - 1
51local extractSearchMax = math.floor(extractorRadius / metalMapScale)
52
53----------------------------------------------------------------
54--locals
55----------------------------------------------------------------
56
57--2-D array that stores the metal map
58local metalMap = {}
59
60--3-D array that stores what mexes are lined up to control each particular square on the metal map
61local controlMap = {}
62
63--circle indicating whether a particular metal map square is within the extractor radius of the origin
64local circleMask = {}
65
66--table of mexes; entries are {amount of metal that the mex is over, shares of energy the mex takes}
67local mexes = {}
68
69--indexed by teams, entries are {table of mexes, autoManage, doUpdate, energy, energy shares, }
70local teamInfos = {}
71
72----------------------------------------------------------------
73--initialization
74----------------------------------------------------------------
75
76--setup metal map, control map
77local function SetupMaps()
78  for i = 0, metalMapMaxX do
79    metalMap[i] = {}
80    controlMap[i] = {}
81    for j = 0, metalMapMaxZ do
82      local metal
83      _, metal, _, _, _, _, _, _ = GetGroundInfo(metalMapScale * i + 8, metalMapScale * j + 8)
84      metalMap[i][j] = metal * maxMetal
85      controlMap[i][j] = {}
86    end
87  end
88end
89
90--setup circle mask
91local function SetupCircleMask()
92  do
93    local radiusSq = extractorRadius * extractorRadius
94    for i = 0, extractSearchMax do
95      circleMask[i] = {}
96      circleMask[-i] = {}
97      for j = 0, extractSearchMax do
98        if ((i*i + j*j) * metalMapScaleSq < radiusSq) then
99          circleMask[i][j] = true
100          circleMask[-i][j] = true
101          circleMask[i][-j] = true
102          circleMask[-i][-j] = true
103        end
104      end
105    end
106  end
107end
108
109local function SetupTeamList()
110  local teamList = Spring.GetTeamList()
111  for i=1,#teamList do
112    local teamID = teamList[i]
113    teamInfos[teamID] = {{}, true, false, 0, 0,}
114  end
115end
116
117----------------------------------------------------------------
118--functions
119----------------------------------------------------------------
120
121local function GetMexMod(energy, baseMetal)
122  if (baseMetal <= 0) then return 0 end
123 
124  return 1 + math.sqrt(0.2 * energy / baseMetal)
125end
126
127local function GetMexShares(baseMetal)
128  return baseMetal
129end
130
131local function GetOverdriveEnergy(teamID)
132  local teamInfo = teamInfos[teamID]
133  if (teamInfo) then
134    return teamInfo[4]
135  else
136    return nil
137  end
138end
139
140local function GetOverdriveMetal(teamID)
141  local teamInfo = teamInfos[teamID]
142  if (teamInfo) then
143    return math.sqrt(0.2 * teamInfo[4] * teamInfo[5])
144  else
145    return nil
146  end
147end
148
149local function GetDifferentialEnergyPerMetal(teamID)
150  local teamInfo = teamInfos[teamID]
151  if (teamInfo and teamInfo[5] > 0) then
152    return math.sqrt(20 * teamInfo[4] / teamInfo[5])
153  else
154    return nil
155  end
156end
157
158local function GetTotalGlobalMetal()
159  local result = 0
160  for i = 0, metalMapMaxX do
161    for j = 0, metalMapMaxZ do
162      result = result + metalMap[i][j]
163    end
164  end
165  return result
166end
167
168local function GetMetalMapCoords(posX, posZ)
169  return math.floor(posX / metalMapScale), math.floor(posZ / metalMapScale)
170end
171
172local function AddMex(unitID, unitDefID, unitTeam)
173  local unitDef = UnitDefs[unitDefID]
174  if ((unitDef.speed or 0) ~= 0) then return end
175
176  local metal = 0
177  local posX, _, posZ = GetUnitPosition(unitID)
178  local mPosX, mPosZ = GetMetalMapCoords(posX, posZ)
179 
180  --establish how far we are willing to search
181  local backX = math.min(extractSearchMax, mPosX)
182  local frontX = math.min(extractSearchMax, metalMapMaxX - mPosX)
183  local backZ = math.min(extractSearchMax, mPosZ)
184  local frontZ = math.min(extractSearchMax, metalMapMaxZ - mPosZ)
185 
186  --iterate over the search area
187  for i = -backX, frontX do
188    for j = -backZ, frontZ do
189      --check our pre-calculated mask to see if each spot is within the extractor radius
190      if (circleMask[i][j]) then
191        --position on metal map
192        local iPos = mPosX + i
193        local jPos = mPosZ + j
194       
195        --if spot not already claimed, give the metal to the mex
196        if (not controlMap[iPos][jPos][1]) then
197          metal = metal + metalMap[iPos][jPos]
198        end
199       
200        --add the mex to the queue of controllers
201        table.insert(controlMap[iPos][jPos], unitID)
202      end
203    end
204  end
205  mexes[unitID] = {metal, GetMexShares(metal),}
206  --doUpdate
207  teamInfos[unitTeam][1][unitID] = true
208  teamInfos[unitTeam][3] = true
209end
210
211local function RemoveMex(unitID, unitTeam)
212  if (not mexes[unitID]) then return end
213 
214  local metal = 0
215  local posX, _, posZ = GetUnitPosition(unitID)
216  local mPosX, mPosZ = GetMetalMapCoords(posX, posZ)
217 
218  --establish how far we are willing to search
219  local backX = math.min(extractSearchMax, mPosX)
220  local frontX = math.min(extractSearchMax, metalMapMaxX - mPosX)
221  local backZ = math.min(extractSearchMax, mPosZ)
222  local frontZ = math.min(extractSearchMax, metalMapMaxZ - mPosZ)
223 
224  --iterate over the search area
225  for i = -backX, frontX do
226    for j = -backZ, frontZ do
227      --position on metal map
228      local iPos = mPosX + i
229      local jPos = mPosZ + j
230     
231      --if we control this spot, give it to the next in line
232      if (controlMap[iPos][jPos][1] == unitID) then
233        --remove any mexes in line which no longer exist
234        local nextID
235       
236        repeat
237          table.remove(controlMap[iPos][jPos], 1)
238          nextID = controlMap[iPos][jPos][1]
239        until (not nextID or mexes[nextID])
240       
241        --add the metal to the new owner
242        if (nextID) then
243          mexes[nextID][1] = mexes[nextID][1] + metalMap[iPos][jPos]
244          mexes[nextID][2] = GetMexShares(mexes[nextID][1])
245          teamInfos[GetUnitTeam(nextID)][3] = true
246        end
247      end
248    end
249  end
250  --remove mex
251  teamInfos[unitTeam][1][unitID] = nil
252  mexes[unitID] = nil
253  --doUpdate
254  teamInfos[unitTeam][3] = true
255end
256
257----------------------------------------------------------------
258--callins
259----------------------------------------------------------------
260
261function gadget:Initialize()
262  SetupMaps()
263  SetupCircleMask()
264  Spring.SendMessage("Total Global Metal:" .. GetTotalGlobalMetal())
265  SetupTeamList()
266end
267
268function gadget:UnitFinished(unitID, unitDefID, unitTeam)
269  AddMex(unitID, unitDefID, unitTeam)
270end
271
272function gadget:UnitDestroyed(unitID, unitDefID, unitTeam)
273  RemoveMex(unitID, unitTeam)
274end
275
276function gadget:UnitTaken(unitID, unitDefID, oldTeamID, teamID)
277  RemoveMex(unitID, oldTeamID)
278  AddMex(unitID, unitDefID, teamID)
279end
280
281function gadget:UnitGiven(unitID, unitDefID, teamID, oldTeamID)
282  RemoveMex(unitID, oldTeamID)
283  AddMex(unitID, unitDefID, teamID)
284end
285
286function gadget:GameFrame(n)
287  if (((n+30) % 32) > 0.1) then return end
288 
289  for teamID, teamInfo in pairs(teamInfos) do
290 
291    --cleanup
292    if (teamInfo[4] < 0) then
293      teamInfo[4] = 0
294    end
295    if (teamInfo[5] < 0) then
296      teamInfo[5] = 0
297    end
298   
299    if (teamInfo[2] or teamInfo[3]) then
300      --automanage
301      if (teamInfo[2]) then
302        --determine how much to spend
303        if (teamInfo[5] > 0) then
304          local eCur, eMax, ePull, eInc, eExp, _, eSent, eRec = GetTeamResources(teamID, "energy")
305          if (eCur == nil) then return end
306          local deltaE = eInc - eExp + eRec - eSent
307          local eChange = storedEnergyWeight * (eCur - eMax * desiredEnergy) + deltaEWeight * deltaE
308          teamInfo[4] = teamInfo[4] + eChange
309         
310          if (teamInfo[4] < 0) then
311            teamInfo[4] = 0
312          end
313        else
314          --if no mexes, reset energy budget to zero
315          teamInfo[4] = 0
316        end
317      end
318     
319      --update
320      if (teamInfo[3]) then
321        --recalculate shares
322        local shares = 0
323        for unitID, _ in pairs(teamInfo[1]) do
324          shares = shares + mexes[unitID][2]
325        end
326        teamInfo[5] = shares
327       
328        --done
329        teamInfo[3] = false
330      end
331     
332      Spring.SendMessage("Player " .. teamID .. " Energy Budget: " .. teamInfo[4] .. " Shares: " .. teamInfo[5])
333     
334      --pump the energy
335      for unitID, _ in pairs(teamInfo[1]) do
336        local energyThisMex = teamInfo[4] * mexes[unitID][2] / teamInfo[5]
337        local metalThisMex = GetMexMod(energyThisMex, mexes[unitID][1]) * mexes[unitID][1]
338        Spring.SetUnitResourcing(unitID, "cue", energyThisMex * 2)
339        Spring.SetUnitResourcing(unitID, "cmm", metalThisMex * 2)
340      end
341    end
342  end
343end
344
345----------------------------------------------------------------
346--UNSYNCED
347----------------------------------------------------------------
348else
349
350end
Note: See TracBrowser for help on using the browser.