| 1 | ---------------------------------------------------------------- |
|---|
| 2 | --parameters |
|---|
| 3 | ---------------------------------------------------------------- |
|---|
| 4 | local maxMetal = 1 --scale metal by this value; acts like map max metal |
|---|
| 5 | |
|---|
| 6 | ---------------------------------------------------------------- |
|---|
| 7 | --speed-ups |
|---|
| 8 | ---------------------------------------------------------------- |
|---|
| 9 | local mapSizeX = Game.mapSizeX |
|---|
| 10 | local mapSizeZ = Game.mapSizeZ |
|---|
| 11 | local extractorRadius = Game.extractorRadius |
|---|
| 12 | |
|---|
| 13 | local GetGroundInfo = Spring.GetGroundInfo |
|---|
| 14 | local GetUnitPosition = Spring.GetUnitPosition |
|---|
| 15 | |
|---|
| 16 | ---------------------------------------------------------------- |
|---|
| 17 | --constants |
|---|
| 18 | ---------------------------------------------------------------- |
|---|
| 19 | |
|---|
| 20 | local metalMapMaxX = math.floor(mapSizeX / metalMapScale) - 1 |
|---|
| 21 | local metalMapMaxZ = math.floor(mapSizeX / metalMapScale) - 1 |
|---|
| 22 | local extractSearchMax = math.floor(extractorRadius / metalMapScale) |
|---|
| 23 | |
|---|
| 24 | local metalMapScale = 16 |
|---|
| 25 | local metalMapScaleSq = 256 |
|---|
| 26 | |
|---|
| 27 | ---------------------------------------------------------------- |
|---|
| 28 | --locals |
|---|
| 29 | ---------------------------------------------------------------- |
|---|
| 30 | |
|---|
| 31 | --2-D array that stores the metal map |
|---|
| 32 | local metalMap = {} |
|---|
| 33 | |
|---|
| 34 | --3-D array that stores what mexes are lined up to control each particular square on the metal map |
|---|
| 35 | local controlMap = {} |
|---|
| 36 | |
|---|
| 37 | --quarter-circle indicating whether a particular metal map square is within the extractor radius of the origin |
|---|
| 38 | local circleMask = {} |
|---|
| 39 | |
|---|
| 40 | --table of mexes; entries are indexed by unitID and have values equal to the raw metal produced by the mex |
|---|
| 41 | local mexes = {} |
|---|
| 42 | |
|---|
| 43 | --setup metal map, control map |
|---|
| 44 | local function SetupMaps() |
|---|
| 45 | for i = 0, metalMapMaxX do |
|---|
| 46 | metalMap[i] = {} |
|---|
| 47 | controlMap[i] = {} |
|---|
| 48 | for j = 0, metalMapMaxZ do |
|---|
| 49 | _, metalMap[i][j] = GetGroundInfo(metalMapScale * i + 8, metalMapScale * j + 8) * maxMetal |
|---|
| 50 | controlMap[i][j] = {} |
|---|
| 51 | end |
|---|
| 52 | end |
|---|
| 53 | end |
|---|
| 54 | |
|---|
| 55 | --setup circle mask |
|---|
| 56 | local function SetupCircleMask() |
|---|
| 57 | do |
|---|
| 58 | local radiusSq = extractorRadius * extractorRadius |
|---|
| 59 | for i = 0, extractSearchMax do |
|---|
| 60 | for j = 0, extractSearchMax do |
|---|
| 61 | if ((i*i + j*j) * metalMapScaleSq < radiusSq) then |
|---|
| 62 | circleMask[i][j] = true |
|---|
| 63 | end |
|---|
| 64 | end |
|---|
| 65 | end |
|---|
| 66 | end |
|---|
| 67 | end |
|---|
| 68 | |
|---|
| 69 | local function GetTotalGlobalMetal() |
|---|
| 70 | local result = 0 |
|---|
| 71 | for i = 0, metalMapMaxX do |
|---|
| 72 | for j = 0, metalMapMaxZ do |
|---|
| 73 | result = result + metalMap[i][j] |
|---|
| 74 | end |
|---|
| 75 | end |
|---|
| 76 | return result |
|---|
| 77 | end |
|---|
| 78 | |
|---|
| 79 | local function GetMetalMapCoords(posX, posZ) |
|---|
| 80 | return math.floor(posX / metalMapScale), math.floor(posZ / metalMapScale) |
|---|
| 81 | end |
|---|
| 82 | |
|---|
| 83 | local function AddMex(unitID) |
|---|
| 84 | local metal = 0 |
|---|
| 85 | local posX, _, posZ = GetUnitPosition(unitID) |
|---|
| 86 | local mPosX, mPosZ = GetMetalMapCoords(posX, posZ) |
|---|
| 87 | |
|---|
| 88 | --establish how far we are willing to search |
|---|
| 89 | local backX = math.min(extractSearchMax, mPosX) |
|---|
| 90 | local frontX = math.min(extractSearchMax, metalMapMaxX - mPosX) |
|---|
| 91 | local backZ = math.min(extractSearchMax, mPosZ) |
|---|
| 92 | local frontZ = math.min(extractSearchMax, metalMapMaxZ - mPosZ) |
|---|
| 93 | |
|---|
| 94 | --iterate over the search area |
|---|
| 95 | for i = -backX, frontX do |
|---|
| 96 | for j = -backZ, frontZ do |
|---|
| 97 | --check our pre-calculated mask to see if each spot is within the extractor radius |
|---|
| 98 | if (circleMask[math.abs(i)][math.abs(j)]) then |
|---|
| 99 | --position on metal map |
|---|
| 100 | local iPos = mPosX + i |
|---|
| 101 | local jPos = mPosZ + j |
|---|
| 102 | |
|---|
| 103 | --if spot not already claimed, give the metal to the mex |
|---|
| 104 | if (not controlMap[iPos][jPos]) then |
|---|
| 105 | metal = metal + metalMap[iPos][jPos] |
|---|
| 106 | end |
|---|
| 107 | |
|---|
| 108 | --add the mex to the queue of controllers |
|---|
| 109 | table.insert(controlMap[iPos][jPos], unitID) |
|---|
| 110 | end |
|---|
| 111 | end |
|---|
| 112 | end |
|---|
| 113 | mexes[unitID] = metal |
|---|
| 114 | end |
|---|
| 115 | |
|---|
| 116 | local function RemoveMex(unitID) |
|---|
| 117 | local metal = 0 |
|---|
| 118 | local posX, _, posZ = GetUnitPosition(unitID) |
|---|
| 119 | local mPosX, mPosZ = GetMetalMapCoords(posX, posZ) |
|---|
| 120 | |
|---|
| 121 | --establish how far we are willing to search |
|---|
| 122 | local backX = math.min(extractSearchMax, mPosX) |
|---|
| 123 | local frontX = math.min(extractSearchMax, metalMapMaxX - mPosX) |
|---|
| 124 | local backZ = math.min(extractSearchMax, mPosZ) |
|---|
| 125 | local frontZ = math.min(extractSearchMax, metalMapMaxZ - mPosZ) |
|---|
| 126 | |
|---|
| 127 | --iterate over the search area |
|---|
| 128 | for i = -backX, frontX do |
|---|
| 129 | for j = -backZ, frontZ do |
|---|
| 130 | --position on metal map |
|---|
| 131 | local iPos = mPosX + i |
|---|
| 132 | local jPos = mPosZ + j |
|---|
| 133 | |
|---|
| 134 | --if we control this spot, give it to the next in line |
|---|
| 135 | if (controlMap[i][j][1] == unitID) then |
|---|
| 136 | --remove any mexes in line which no longer exist |
|---|
| 137 | local nextID |
|---|
| 138 | |
|---|
| 139 | repeat |
|---|
| 140 | table.remove(controlMap[i][j], 1) |
|---|
| 141 | nextID = controlMap[i][j][1] |
|---|
| 142 | until (not nextID or mexes[nextID]) |
|---|
| 143 | |
|---|
| 144 | --add the metal to the new owner |
|---|
| 145 | if (nextID) then |
|---|
| 146 | mexes[nextID] = mexes[nextID] + metalMap[iPos][jPos] |
|---|
| 147 | end |
|---|
| 148 | end |
|---|
| 149 | end |
|---|
| 150 | end |
|---|
| 151 | mexes[unitID] = nil |
|---|
| 152 | end |
|---|