Code: Select all
--------------------------------------------------------------------------------
--
-- file: ..
-- brief: Lua metal spots for *A mods. Credits to Niobium for metal finder. Place a .lmf file in map archive to enable support.
--
-- usage:
-- Step 1. Create a [mapname].lmf file in [mapname].sd7/maps/. The first line should be a relative path inside the spring directory in which to search for metal textures in.
-- The last three lines indicate rgb tint in the range of 0.0 to 1.0, and must be present.
-- Example .lmf file:
--
-- line 1: ./maps/textures/
-- line 2: 1.0
-- line 3: 1.0
-- line 4: 1.0
--
-- Step 2. Create a /textures/ folder in the archive and place metal spot textures there. In the example above they should be named "metal_high.png", "metal_medhigh.png", "metal_med.png", "metal_low.png".
--
--
-- Changelog:
-- v3.4
-- * set to use proper rendering function so it doesn't render over effects.
-- v3.3
-- * fixed issues with archive loading
-- v3.2
-- * fixed drawing on sloped terrain
-- * randomness and multiple textures is broken
--
-- v3.1
-- * fixed depth testing issue
--
-- v3
-- * changed to Niobium metal finder with mass metal detection.
--
-- v2
-- * added display lists
-- * added loading metal maps from file
--
--
-- author: Jahziah Wagner
--
-- Copyright (C) 2010.
-- Licensed under the terms of the LGPL
--
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
function widget:GetInfo()
return {
name = "Metal Placer V3.3 Integrated Version for Seths Ravine",
desc = "Lua metal spots for maps that support them.",
author = "Halcyon",
date = "15/12/2010",
license = "LGPL v2",
layer = 5,
enabled = true -- loaded by default?
}
end
local enabled = true -- Spring.GetMapOptions("patches",true)
local floor = math.floor
local gridSize = 4
local mapWidth2 = floor(Game.mapSizeX / gridSize)
local mapHeight2 = floor(Game.mapSizeZ / gridSize)
local metalMap = {}
local spGetGroundInfo = Spring.GetGroundInfo
local threshFraction = 0.4
local metalDataCount = 0
local maxMetalData = 2500000
local maxMetal = 0
local mSpots = {}
local mexRad = Game.extractorRadius > 125 and Game.extractorRadius or 125
local metalSpotWidth = 64
local metalSpotHeight = 64
local randTexture = {}
local rgbr = 0
local rgbg = 0
local rgbb = 0
local searchPath = 0
local pathToSave = "LuaUI/Widgets/MetalMaps/" -- where to store mexmaps
local dl = 0
------------------------------------------------------------
-- Config
------------------------------------------------------------
local gridSize = 16 -- Resolution of metal map
local buildGridSize = 8 -- Resolution of build positions
------------------------------------------------------------
-- Speedups
------------------------------------------------------------
local min, max = math.min, math.max
local floor, ceil = math.floor, math.ceil
local sqrt = math.sqrt
local huge = math.huge
local spGetGroundInfo = Spring.GetGroundInfo
local spGetGroundHeight = Spring.GetGroundHeight
local spTestBuildOrder = Spring.TestBuildOrder
local extractorRadius = Game.extractorRadius
local extractorRadiusSqr = extractorRadius * extractorRadius
local buildmapSizeX = Game.mapSizeX - buildGridSize
local buildmapSizeZ = Game.mapSizeZ - buildGridSize
local buildmapStartX = buildGridSize
local buildmapStartZ = buildGridSize
local metalmapSizeX = Game.mapSizeX - 1.5 * gridSize
local metalmapSizeZ = Game.mapSizeZ - 1.5 * gridSize
local metalmapStartX = 1.5 * gridSize
local metalmapStartZ = 1.5 * gridSize
local mSpots = {}
-- saving metalmap
local function SaveMetalMap(filename)
mSpots = GetSpots()
if #mSpots == 0 then
widgetHandler:RemoveWidget()
return
end
--Spring.Echo("exporting mexmap to ".. filename);
local file = assert(io.open(filename,'w'), "Unable to save mexmap to "..filename)
for k, mex in pairs(mSpots) do
--file:write(k)
--file:write("\n")
file:write(mex.x)
file:write("\n")
file:write(mex.z)
file:write("\n")
--file:write(mex.weight)
--file:write("\n")
--if (Distance(cx,cz,mex.x,mex.z) < cr^2) then -- circle area, slower
--commands[#commands+1] = {x = mex.x, z = mex.z, d = Distance(aveX,aveZ,mex.x,mex.z)}
end
file:close()
Spring.Echo("mexmap exported to ".. filename);
end
local function LoadMetalMap(filename)
--Spring.Echo("importing mexmap from ".. filename);
local file = assert(io.open(filename,'r'), "Unable to load mexmap from "..filename)
index = 1
while true do
l1 = file:read()
if not l1 then break end
l2 = file:read()
--l3 = file:read()
--l4 = file:read()
mSpots[index] = {
x = l1,
z = l2--,
--weight = tonumber(l3)
}
index = index+1
end
Spring.Echo("mexmap imported from ".. filename);
end
------------------------------------------------------------------------------------------------------------------------------------
----------------------------------- NEW METAL FINDER ROUTINE, CREDIT GOES TO NIOBIUM ----------------------------------------------
------------------------------------------------------------------------------------------------------------------------------------
function GetSpots()
-- Main group collection
local uniqueGroups = {}
-- Strip info
local nStrips = 0
local stripLeft = {}
local stripRight = {}
local stripGroup = {}
-- Indexes
local aboveIdx
local workingIdx
-- temp
local stripWorths = {}
local stripIndex = 0
-- Strip processing function (To avoid some code duplication)
local function DoStrip(x1, x2, z, worth)
local assignedTo
for i = aboveIdx, workingIdx - 1 do
if stripLeft[i] > x2 + gridSize then
break
elseif stripRight[i] + gridSize >= x1 then
local matchGroup = stripGroup[i]
if assignedTo then
if matchGroup ~= assignedTo then
for iz = matchGroup.minZ, assignedTo.minZ - gridSize, gridSize do
assignedTo.left[iz] = matchGroup.left[iz]
end
for iz = matchGroup.minZ, matchGroup.maxZ, gridSize do
assignedTo.right[iz] = matchGroup.right[iz]
end
if matchGroup.minZ < assignedTo.minZ then
assignedTo.minZ = matchGroup.minZ
end
assignedTo.maxZ = z
assignedTo.worth = assignedTo.worth + matchGroup.worth
uniqueGroups[matchGroup] = nil
end
else
assignedTo = matchGroup
assignedTo.left[z] = assignedTo.left[z] or x1 -- Only accept the first
assignedTo.right[z] = x2 -- Repeated overwrite gives us result we want
assignedTo.maxZ = z -- Repeated overwrite gives us result we want
assignedTo.worth = assignedTo.worth + worth
end
else
aboveIdx = aboveIdx + 1
end
end
nStrips = nStrips + 1
stripLeft[nStrips] = x1
stripRight[nStrips] = x2
if assignedTo then
stripGroup[nStrips] = assignedTo
else
local newGroup = {
left = {[z] = x1},
right = {[z] = x2},
minZ = z,
maxZ = z,
worth = worth
}
stripGroup[nStrips] = newGroup
uniqueGroups[newGroup] = true
end
end
-- Strip finding
workingIdx = huge
for mz = metalmapStartX, metalmapSizeZ, gridSize do
aboveIdx = workingIdx
workingIdx = nStrips + 1
local stripStart = nil
local stripWorth = 0
for mx = metalmapStartZ, metalmapSizeX, gridSize do
local _, groundMetal = spGetGroundInfo(mx, mz)
if groundMetal > 0 then
stripStart = stripStart or mx
stripWorth = stripWorth + groundMetal
elseif stripStart then
if mx - gridSize - stripStart > extractorRadius then
Spring.Echo('<WG.metalSpots> Mass metal detected. Disabling.')
return {}
end
DoStrip(stripStart, mx - gridSize, mz, stripWorth)
stripStart = nil
stripWorth = 0
end
end
if stripStart then
if metalmapSizeX - stripStart > extractorRadius then
Spring.Echo('<WG.metalSpots> Mass metal detected. Disabling.')
return {}
end
DoStrip(stripStart, metalmapSizeX, mz, stripWorth)
end
end
-- Final processing
local spots = {}
for g, _ in pairs(uniqueGroups) do
local gMinX, gMaxX = huge, -1
local gLeft, gRight = g.left, g.right
for iz = g.minZ, g.maxZ, gridSize do
if gLeft[iz] < gMinX then gMinX = gLeft[iz] end
if gRight[iz] > gMaxX then gMaxX = gRight[iz] end
end
g.minX = gMinX
g.maxX = gMaxX
g.x = (gMinX + gMaxX) * 0.5
g.z = (g.minZ + g.maxZ) * 0.5
g.y = spGetGroundHeight(g.x, g.z)
spots[#spots + 1] = g
end
return spots
end
function GetValidStrips(spot)
local sMinZ, sMaxZ = spot.minZ, spot.maxZ
local sLeft, sRight = spot.left, spot.right
local validLeft = {}
local validRight = {}
local maxZOffset = buildGridSize * floor(extractorRadius / buildGridSize)
for mz = max(sMaxZ - maxZOffset, buildmapStartZ), min(sMinZ + maxZOffset, buildmapSizeZ), buildGridSize do
local vLeft, vRight = buildmapStartX, buildmapSizeX
for sz = sMinZ, sMaxZ, gridSize do
local dz = sz - mz
local maxXOffset = buildGridSize * floor(sqrt(extractorRadiusSqr - dz * dz) / buildGridSize) -- Test for metal being included is dist < extractorRadius
local left, right = sRight[sz] - maxXOffset, sLeft[sz] + maxXOffset
if left > vLeft then vLeft = left end
if right < vRight then vRight = right end
end
validLeft[mz] = vLeft
validRight[mz] = vRight
end
spot.validLeft = validLeft
spot.validRight = validRight
end
------------------------------------------------------------
-- Callins
------------------------------------------------------------
-- checks if the current map uses lua metal spots
local function checkMap()
mapFileName = "maps/" .. Game.mapName .. ".sd7"
if VFS.MapArchive(mapFileName) == true then
--Spring.Echo("Map exists.")
else
Spring.Echo("Map does not exist.")
end
Spring.Echo("Testing map.")
-- check that texture exists, then load it
dirResult = VFS.DirList("maps/")
for i = 1, #dirResult do
--Spring.Echo(dirResult[i])
end
smfFileName = "maps/" .. Game.mapName .. ".lmf"
if VFS.FileExists(smfFileName) then
smfFileData = VFS.LoadFile(smfFileName)
--Spring.Echo("Lmf exists.")
io.output("m_tmp")
io.write(smfFileData)
io.output():close();
io.input( "m_tmp" )
searchPath = io.read() -- line 0 holds searchPath = 'archive' or a relative path
rgbr = io.read() -- line 2 red tint
rgbg = io.read() -- line 3 green tint
rgbb = io.read() -- line 4 blue tint
else -- not used for this map
Spring.Echo("Lmf does not exist.")
widgetHandler:RemoveWidget()
return
end
-- textureFileName = "maps/metal_high.png"
-- if VFS.FileExists(textureFileName) then
-- Spring.Echo("Texture exists.")
-- textureFileData = VFS.LoadFile(textureFileName)
-- else -- not used for this map
-- Spring.Echo("Texture does not exist.")
-- widgetHandler:RemoveWidget()
-- return
-- end
end
local function badInput()
widgetHandler:RemoveWidget()
return
end
local function parseLmf()
rgbr = tonumber(rgbr)
rgbg = tonumber(rgbg)
rgbb = tonumber(rgbb)
if ( assert(type( searchPath )) ~= "string" ) then
badInput()
end
if ( assert(type(rgbr)) ~= "number" or assert(type(rgbg)) ~= "number" or assert(type(rgbb)) ~= "number" or rgbr > 1 or rgbr < 0 or rgbg > 1 or rgbg < 0 or rgbb > 1 or rgbb < 0 ) then
badInput()
end
end
local function randomness()
for i = 1, #mSpots do
randTexture[i] = "LuaUI/Widgets/metalspots/metal"..math.random(1, 5)..".png"
end
end
function createDL()
local x = 0
local y = 0
local z = 0
gl.PushMatrix()
gl.DepthTest(true)
--gl.LoadIdentity()
gl.Color( rgbr, rgbg, rgbb, 1.0)
for i = 1, #mSpots do
gl.Texture(randTexture[i])
--gl.Translate( -mSpots[i].x, 0, -mSpots[i].z )
--gl.Rotate( 1, 0, 0, 1 )
gl.Translate( 0, 1, 0 )
gl.DrawGroundQuad( mSpots[i].x - metalSpotWidth/2, mSpots[i].z - metalSpotHeight/2, mSpots[i].x + metalSpotWidth/2, mSpots[i].z + metalSpotHeight/2, false, 0, 0, 1, 1)
gl.Translate( 0, -1, 0 )
end
gl.DepthTest(false)
gl.PopMatrix()
end
function widget:DrawWorldPreUnit()
--gl.CallList(dl)
createDL()
end
function widget:Initialize()
if (Spring.GetMapOptions().patches == "0") then
widgetHandler:Removewidget()
return
end
if (enabled==true) then
checkMap()
parseLmf()
Spring.CreateDir(pathToSave)
local filename = (pathToSave.. string.lower(string.gsub(Game.mapName, ".smf", "")) .. ".springmexmapv3")
file = io.open(filename,'r')
--io.close(file)
if file ~= nil then -- file exists?
Spring.Echo("Mexmap detected - loading...")
LoadMetalMap(filename)
else
Spring.Echo("Calculating mexmap")
SaveMetalMap(filename)
end
if #mSpots > 0 then
--Spring.Echo( 'worth', mSpots[1].worth )
randomness()
--dl = gl.CreateList(createDL)
end
else
widgetHandler:Removewidget()
end
end
I used this in one of my old maps. Enjoy.