game adding metal spot textures on map if missing

game adding metal spot textures on map if missing

Discuss maps & map creation - from concept to execution to the ever elusive release.

Moderator: Moderators

Post Reply
raaar
Metal Factions Developer
Posts: 1095
Joined: 20 Feb 2010, 12:17

game adding metal spot textures on map if missing

Post by raaar »

Some of the more recent maps don't come with metal spot textures. Players have to use the F4 view to see where they are, which is annoying.

I'd like to have a gadget which adds appropriate metal spot textures for a TA-like game on a specific set of maps. Any examples?
User avatar
Forboding Angel
Evolution RTS Developer
Posts: 14673
Joined: 17 Nov 2005, 02:43

Re: game adding metal spot textures on map if missing

Post by Forboding Angel »

A better option would be to draw it with GL on the map itself instead of using textures for it.

Like this:
Image
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Re: game adding metal spot textures on map if missing

Post by smoth »

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.
Attachments
metal3.png
metal3.png (258.82 KiB) Viewed 1949 times
metal2.png
metal2.png (251.56 KiB) Viewed 1949 times
metal1.png
metal1.png (278.37 KiB) Viewed 1949 times
raaar
Metal Factions Developer
Posts: 1095
Joined: 20 Feb 2010, 12:17

Re: game adding metal spot textures on map if missing

Post by raaar »

Thanks for the replies, i'll look into it.

My goal is to have a gadget that:
- only has effects on specific maps from a hard-coded list (I'll update it)
- has several different sets of textures (so I can assign different sets to each map)
- each set has a different subset of textures for spots with : lower-than-75%-median yield, close-to-median yield, greater-than-125%-median yield


I'll use only on maps which lack the textures. Btw, why do they lack the textures?
User avatar
smoth
Posts: 22309
Joined: 13 Jan 2005, 00:46

Re: game adding metal spot textures on map if missing

Post by smoth »

because not every game gives a care about metal spots.
Post Reply

Return to “Map Creation”