I have been trying t make a mobile factory, but for some reason instead of being a factory its a builder unit, how do I change it to a mobile factory?
Hello nightovizard, 
Spring doesn't have mobile factory, but modder have tried a lot of hax to make mobile factory.
The following is my method,
First, you must intercept build command issued by player, this is done by Widget or probably in Gadget Unsynced (did not test the later), like this:
Code: Select all
function widget:CommandNotify(cmdID, cmdParams, cmdOptions)
	if haveRelevantUnit == 0 or cmdID >= 0 or (cmdParams[1] and cmdParams[2] and cmdParams[3]) then --can't handle other command.
		return false
	end
	local selectedUnits = Spring.GetSelectedUnits()
	for i=1, #selectedUnits do
		if constructionShipDef[Spring.GetUnitDefID(selectedUnits[i])] then
			BuildThisUnit(-1*cmdID)
			return true
		end
	end
	return false
end
*I copied pasted from my WIP Widget, some variable could be undefined in that code snippet, but the important thing is the concept*
The "BuildThisUnit()" function will send LUA message to Gadget which will spawn the unit, here's the detail:
Code: Select all
function widget:GameFrame()
	if haveRelevantUnit>0 then
		rmbG = rmbG2
		_,_, _, _, rmbG2 = Spring.GetMouseState()
	end
end
function BuildThisUnit(unitDefID)
	local alt, ctrl, meta, shift = Spring.GetModKeyState()
	local _,_, lmb, mmb, rmb = Spring.GetMouseState()
	local selectedUnits = Spring.GetSelectedUnits()
	if #selectedUnits == 0 then
		return
	end
	local selectedUnitsString = '{'
	for i=1,#selectedUnits do
		local mobile_factory = constructionShipDef[Spring.GetUnitDefID(selectedUnits[i])]
		if mobile_factory and mobile_factory.buildOptions[unitDefID] then
			selectedUnitsString = selectedUnitsString .. '[' .. i .. '] =' .. selectedUnits[i] .. ',' 
		end
	end
	selectedUnitsString = selectedUnitsString .. '}' --eg: {unitID1,unitID2,unitID3,}
	local options = '{'
	local count = 1
	if alt then options = options .. '[' .. count .. '] = \"alt\",'; count= count + 1 end
	if ctrl then options = options .. '[' .. count .. '] = \"ctrl\",'; count= count + 1 end
	if meta then options = options .. '[' .. count .. '] = \"meta\",'; count= count + 1 end
	if shift then options = options .. '[' .. count .. '] = \"shift\",'; count= count + 1 end
	if rmb or rmbG then options = options .. '[' .. count .. '] = \"right\",' end
	options = options .. '}'  --eg: {'shift','meta','ctrl',}
	
	Spring.SendLuaRulesMsg('mbfc' .. '{[1] =' .. selectedUnitsString .. ',[2] =' .. unitDefID .. ',[3] =' .. options .. ',}' )
	Spring.Echo('{'.. selectedUnitsString .. ',' .. unitDefID .. ',' .. options .. ',' .. '}' )
end
Then, the gadget must read this message, decipher it, and perform any synced action like spawning unit, or consume resources (to emulate mobile factory), like this: (NOTE this is in Synced portion of gadget)
Code: Select all
	function gadget:RecvLuaMsg(msg, playerID)
		if msg:sub(1,4) == 'mbfc' then
			local tableString = msg:sub(5)
			local chunk, err = loadstring("return "..tableString) --This code is from cawidgets.lua
			local cmdTable = chunk and chunk() or {}
			if #cmdTable == 3 and
			type(cmdTable[1]) == 'table' and
			type(cmdTable[2]) == 'number' and
			type(cmdTable[3]) == 'table' then
				local unitDefID = cmdTable[2]
				if UnitDefs[unitDefID] then --valid unitDefID
					local shift, meta, ctrl, alt, right
					local options = cmdTable[3]
					for i=1, #options do
						local option = options[i]
						if option == "shift" then
							shift = true
						elseif option == "alt" then
							alt = true
						elseif option == "ctrl" then
							ctrl = true
						elseif option == "right" then
							right = true
						elseif option == "meta" then
							meta = true
						end
					end
					local playerTeamID = select(4,Spring.GetPlayerInfo(playerID))
					local selectedUnits = cmdTable[1]
					for i=1, #selectedUnits do
						local unitID = selectedUnits[i]
						if Spring.GetUnitTeam(unitID) ~= playerTeamID then --is giving order to enemy unit?
							break;
						end
						local consShipData = constructionShipData[unitID]
						if consShipData and consShipData.typedata.buildOptions[unitDefID] then --buildable unitDefID
							if right then
								if shift then
									RemoveTargettedUnitDefIDFromQueue(unitDefID,consShipData,unitID,5)
								elseif ctrl then
									RemoveTargettedUnitDefIDFromQueue(unitDefID,consShipData,unitID,20)
								else
									RemoveTargettedUnitDefIDFromQueue(unitDefID,consShipData,unitID,1)
								end
							else
								local consShipQueue = consShipData.status.buildQueue
								if shift then
									local tableIndex = #consShipQueue
									for i=1,5 do
										consShipQueue[tableIndex+i] = unitDefID
									end
								elseif ctrl then
									local tableIndex = #consShipQueue
									for i=1,20 do
										consShipQueue[tableIndex+i] = unitDefID
									end
								else
									consShipQueue[#consShipQueue+1] = unitDefID
								end
							end
						end
					end
				end
			end
		end
	end
	function RemoveTargettedUnitDefIDFromQueue(unitDefID, consShipData,consShipID, count)
		Spring.Echo("REMOVING QUEUE " .. count)
		local consShipQueue = consShipData.status.buildQueue
		local isBuildingSame = (consShipData.status.isBuildingA[2] == unitDefID)
		local isBuildingIndex
		local countRemove = count
		local countTableIndex = 1
		local toRemoveIndex = {nil}
		--Remove all targetted UnitDefID excluding currently built unit:
		for j=1,#consShipQueue do
			if consShipQueue[j] ==unitDefID then
				if not isBuildingIndex and isBuildingSame then
					isBuildingIndex = j
					Spring.Echo("Current build to be cancelled")
				else
					toRemoveIndex[countTableIndex] = j
					countTableIndex = countTableIndex + 1
					countRemove = countRemove - 1
					if countRemove == 0 then
						break;
					end
				end
			end
		end
		for j=#toRemoveIndex, 1,-1 do  --remove from topmost index first
			table.remove(consShipQueue,toRemoveIndex[j])
		end
		--If not reached targetted quota, remove currently built unit too:
		if countRemove>0 and isBuildingIndex then
			table.remove(consShipQueue,isBuildingIndex)
			if constructionShipData[consShipID].status.isBuildingA[1] then
				CancelNanoframeConstruction(constructionShipData[consShipID].status.isBuildingA[1],consShipID)
			end
		end
	end
Note, the code added unitDefID to a table called "consShipQueue", this can be a global table (but in my case I put it in some other table to index it by UnitID), this table can be read later in "function gadget:GameFrame()" which loop every frame. Here you can do anything, like spawning unit or consume resource, like this:
Code: Select all
	function gadget:GameFrame(n)
....<some other messy stuff>.....
			for unitID,buildInfo in pairs(consShipStatus.unitsOnPadInfo) do
				local health,maxHealth,_,_,buildProgress = Spring.GetUnitHealth(unitID)
				if buildProgress and  buildProgress <1 then
					consShipStatus.isBuildingA[1] = unitID
					consShipStatus.isBuildingA[2] = buildInfo.unitDefID
					Spring.SetUnitRulesParam(consShipID,"currentlyBuilding",unitID) --hint widgets
					if isWait then
						break
					end
					local metalCost = consShipTypedata.buildOptions[buildInfo.unitDefID].metalCost
					local maxMetalPerFrame = (consShipTypedata.buildPower/30)
					local maxEnergyPerFrame = maxMetalPerFrame
					local buildProgressPerFrame = maxMetalPerFrame/metalCost
					local remainingBuildProgress = 1-buildProgress
					if remainingBuildProgress > buildProgressPerFrame then
							consShipStatus.constructing = true
						local healthPerFrame = buildProgressPerFrame*maxHealth
							Spring.SetUnitHealth(unitID,{ health = health+healthPerFrame, build = buildProgress+buildProgressPerFrame })
							Spring.UseUnitResource(consShipID, 'energy',  maxEnergyPerFrame)-- + energyMake - energyUse)
							Spring.UseUnitResource(consShipID, 'metal',  maxMetalPerFrame)-- + metalMake - metalUse)
							consShipStatus.unitsOnPadInfo[unitID].usedEnergy = maxEnergyPerFrame + buildInfo.usedEnergy
							consShipStatus.unitsOnPadInfo[unitID].usedMetal = maxMetalPerFrame + buildInfo.usedMetal
					else --FINISH BUILD:
						local remainingMetalToSpend = remainingBuildProgress*metalCost
						local remainingEnergyToSpend = remainingMetalToSpend
						local remainingHealthToAdd =  remainingBuildProgress*maxHealth
							Spring.SetUnitHealth(unitID,{ health = health+remainingHealthToAdd, build = 1.0 })
							Spring.UseUnitResource(consShipID, 'energy', remainingEnergyToSpend)-- + energyMake - energyUse)
							Spring.UseUnitResource(consShipID, 'metal', remainingMetalToSpend)-- + metalMake - metalUse)
							consShipStatus.unitsOnPadInfo[unitID].usedEnergy = remainingEnergyToSpend + buildInfo.usedEnergy
							consShipStatus.unitsOnPadInfo[unitID].usedMetal = remainingMetalToSpend + buildInfo.usedMetal
							GG.StopMiscPriorityResourcing(consShipID,teamID) --is using unit_priority.lua gadget to handle priority.
							consShipStatus.constructing = false
							consShipStatus.isBuildingA[1] = nil
							consShipStatus.isBuildingA[2] = nil
							local isRepeat = Spring.GetUnitStates(consShipID)["repeat"]
							if isRepeat then
								consShipStatus.buildQueue[#consShipStatus.buildQueue+1] = consShipStatus.buildQueue[1]
							end
							table.remove(consShipStatus.buildQueue,1)
							Spring.SetUnitRulesParam(consShipID,"currentlyBuilding",-1) --hint widgets
							if UnitDefs[buildInfo.unitDefID].canFly then --if unit can fly, then undock immediately..
								DetachUnit(unitID, consShipID)
								Spring.GiveOrderToUnit(unitID, CMD.IDLEMODE, {0}, {}) --fly away
							end
					end
					break; --only build 1 unit at a time
				end
			end
	end
There are other misc stuff to make mobile factory not look silly, like constantly repositioning nanoframe to appear on the unit and add command to allow undocking, that make a lot more codes. Currently my gadget is WIP, its not yet added to ZK mod (which I intended for)
-----
In summary, my method were:
a) intercept build command using widget
b) send build command to gadget using LUArule message
c) read LUArule message and spawn unit
EDIT:
Knorker method is to attach factory to a unit, this could work too!
Instead of spawning unit, just give build order to the factory attached to a mobile unit.