All pastes #964304 Raw Edit

Miscellany

public lua v1 · immutable
#964304 ·published 2008-03-31 03:33 UTC
rendered paste body
--------------------------------------------------------------------------------------------------------------------------------------------------------------------  file:    unit_clone.lua--  brief:   Adds unit cloneing command--  author:  Dave Rodgers improved by jK----  Copyright (C) 2007.--  Licensed under the terms of the GNU GPL, v2 or later.------------------------------------------------------------------------------------------------------------------------------------------------------------------function gadget:GetInfo()  return {    name      = "UnitClone",    desc      = "Adds unit cloning",    author    = "aegis",    date      = "Apr 24, 2007",    license   = "GNU GPL, v2 or later",    layer     = 0,    enabled   = true  --  loaded by default?  }end--------------------------------------------------------------------------------------------------------------------------------------------------------------------  Proposed Command ID Ranges:----    all negative:  Engine (build commands)--       0 -   999:  Engine--    1000 -  9999:  Group AI--   10000 - 19999:  LuaUI--   20000 - 29999:  LuaCob--   30000 - 39999:  LuaRules--local CMD_CLONE = 33333----------------------------------------------------------------------------------  COMMON--------------------------------------------------------------------------------function isFinished(UnitID)  local _,_,_,_,buildProgress = Spring.GetUnitHealth(UnitID)  if ((buildProgress) and (buildProgress>=1)) then    return true  end  return falseendlocal function GetTechLevel(udid)  local ud = UnitDefs[udid];  return (ud and ud.techLevel) or 0endfunction isFactory(UnitDefID)  return UnitDefs[UnitDefID].isFactory or falseend----------------------------------------------------------------------------------------------------------------------------------------------------------------if (gadgetHandler:IsSyncedCode()) then----------------------------------------------------------------------------------  SYNCED--------------------------------------------------------------------------------include("LuaRules/colors.h.lua")local cloneDefs = {}local cloneUnits = {} -- make it global in Initialize()local stopPenalty  = 1.0local clonePenalty = 1.0local upgradingBuildSpeed = 250local XpScale = 0.50local devolution = true -- remove upgrade capabilities after factory destruction?-- per team techlevel tablelocal teamTechLevel = {}local allyList = Spring.GetAllyTeamList()for _,allyID in ipairs(allyList) do  local teamList = Spring.GetTeamList(allyID)  for _,teamID in ipairs(teamList) do    teamTechLevel[teamID] = 0  endendlocal cloneCmdDesc = {  id     = CMD_CLONE,  type   = CMDTYPE.ICON,  name   = 'Clone',  cursor = 'Clone',  -- add with LuaUI?  action = 'clone',}----------------------------------------------------------------------------------------------------------------------------------------------------------------local function DefCost(paramName, udSrc, udDst)  local pSrc = udSrc[paramName]  local pDst = udDst[paramName]  if ((not pSrc) or (not pDst) or      (type(pSrc) ~= 'number') or      (type(pDst) ~= 'number')) then    return 0  end  local cost = (pDst - pSrc) * clonePenalty  if (cost < 0) then    cost = 0  end  return math.floor(cost)endlocal function ValidateCloneDefs(mds)  local newDefs = {}  for src,cloneData in pairs(mds) do    local udSrc = UnitDefNames[string.lower(src)]    local udDst = UnitDefNames[string.lower(cloneData.into)]    if (not udSrc) then      Spring.Echo('Bad clone src type: ' .. src)    end    if (not udDst) then      Spring.Echo('Bad clone dst type: ' .. cloneData.into)    end    if (udSrc and udDst) then      local unitDef = UnitDefs[udSrc.id]      local newData = {}      newData.into = udDst.id      newData.time = cloneData.time or math.floor(unitDef.buildTime*10/upgradingBuildSpeed)      newData.increment = (1 / (30 * newData.time))      newData.metal  = cloneData.metal  or DefCost('metalCost',  udSrc, udDst)      newData.energy = cloneData.energy or DefCost('energyCost', udSrc, udDst)      newData.resTable = {        m = (newData.increment * newData.metal),        e = (newData.increment * newData.energy)      }      newData.tech = cloneData.tech or 0      newData.xp   = cloneData.xp or 0      newDefs[udSrc.id] = newData    end  end  return newDefsend----------------------------------------------------------------------------------------------------------------------------------------------------------------local function GetCloneToolTip(unitID, cloneDef,teamTech,unitXP)  local ud = UnitDefs[cloneDef.into]  local tt = ''  tt = tt .. WhiteStr  .. 'Clone to a '   .. ud.humanName .. '\n'  tt = tt .. GreenStr  .. 'time: '   .. cloneDef.time     .. '\n'  tt = tt .. CyanStr   .. 'metal: '  .. cloneDef.metal    .. '\n'  tt = tt .. YellowStr .. 'energy: ' .. cloneDef.energy   .. '\n'  if (cloneDef.tech>teamTech) or (cloneDef.xp>unitXP) then    tt = tt .. RedStr .. 'needs'    if (cloneDef.tech>teamTech) then tt = tt .. ' level: ' .. cloneDef.tech  end    if (cloneDef.xp>unitXP)     then tt = tt .. ' xp: ' .. string.format('%.2f',cloneDef.xp) end  end  return ttendlocal function AddCloneCmdDesc(unitID, cloneDef, teamTech)  local unitXP = Spring.GetUnitExperience(unitID)  cloneCmdDesc.tooltip = GetCloneToolTip(unitID, cloneDef,0,0)  cloneCmdDesc.texture = "#" .. cloneDef.into --only works with a patched layout.lua! :(  cloneCmdDesc.disabled= (cloneDef.tech > teamTech)or(cloneDef.xp > unitXP)  Spring.InsertUnitCmdDesc(unitID, cloneCmdDesc)  cloneCmdDesc.tooltip = nil  cloneCmdDesc.texture = nilendlocal function UpdateClonePossibilities(teamID)  local teamTech = teamTechLevel[teamID] or 0  local units = Spring.GetTeamUnitsSorted(teamID)  for unitDefID,unitIDs in pairs(units) do    local cloneDef     = cloneDefs[unitDefID]    if (cloneDef) then      for _,unitID in ipairs(unitIDs) do        local cmdDescID = Spring.FindUnitCmdDesc(unitID, CMD_CLONE)        if (cmdDescID) then          local unitXP = Spring.GetUnitExperience(unitID)          local cloneCmdDesc = {}          cloneCmdDesc.disabled = (cloneDef.tech > teamTech)or(cloneDef.xp > unitXP)          cloneCmdDesc.tooltip  = GetCloneToolTip(unitID, cloneDef, teamTech, unitXP)          Spring.EditUnitCmdDesc(unitID, cmdDescID, cloneCmdDesc)        end      end    end  endend--------------------------------------------------------------------------------local function StartClone(unitID, cloneDef)  -- paralyze the unit  Spring.SetUnitHealth(unitID, { paralyze = 1.0e9 })    -- turns mexes and mm off  Spring.SetUnitResourcing(unitID,"e",0)                -- turns solars off  Spring.GiveOrderToUnit(unitID, CMD.ONOFF, { 0 }, { }) -- turns radars/jammers off    local unitradius = Spring.GetUnitRadius(unitID)  cloneUnits[unitID] = {    def = cloneDef,    progress = 0.0,    increment = cloneDef.increment,	offsetx = unitradius * (1.5 + math.random()), --math.random() is between 0 and 1	offsetz = unitradius * (-0.5 + math.random()),   }endlocal function StopClone(unitID, cloneData)  cloneUnits[unitID] = nil  Spring.SetUnitHealth(unitID, { paralyze = -1})  local scale = cloneData.progress * stopPenalty  local unitDefID = Spring.GetUnitDefID(unitID)  Spring.SetUnitResourcing(unitID,"e", UnitDefs[unitDefID].energyMake)  Spring.GiveOrderToUnit(unitID, CMD.ONOFF, { 1 }, { })  local usedMetal  = cloneData.def.metal  * scale  Spring.AddUnitResource(unitID, 'metal',  usedMetal)  local usedEnergy = cloneData.def.energy * scale  Spring.AddUnitResource(unitID, 'energy', usedEnergy)endlocal function FinishClone(unitID, cloneData)  local udSrc = Spring.GetUnitDefID(unitID)  local udDst = UnitDefs[cloneData.def.into]  local defName = udDst.name  local unitTeam = Spring.GetUnitTeam(unitID)  local px, py, pz = Spring.GetUnitBasePosition(unitID)  --Spring.SetUnitBlocking(unitID, false)  --calculate the new position  px = px + cloneUnits[unitID].offsetx  pz = pz + cloneUnits[unitID].offsetz    cloneUnits[unitID] = nil    --reactivate original unit  Spring.SetUnitHealth(unitID, { paralyze = -1})  local scale = cloneData.progress * stopPenalty  local unitDefID = Spring.GetUnitDefID(unitID)  Spring.SetUnitResourcing(unitID,"e", UnitDefs[unitDefID].energyMake)  Spring.GiveOrderToUnit(unitID, CMD.ONOFF, { 1 }, { })  local newUnit = Spring.CreateUnit(defName, px, py, pz, 0, unitTeam)  local h = Spring.GetUnitHeading(unitID)  Spring.SetUnitRotation(newUnit, 0, -h * math.pi / 32768, 0)  --copy experience  local newXp = Spring.GetUnitExperience(unitID)*XpScale  local nextClone = cloneDefs[cloneData.def.into]  if (nextClone) then    newXp = math.min( newXp, nextClone.xp*0.9)  end  Spring.SetUnitExperience(newUnit, Spring.GetUnitExperience(unitID)*XpScale)  --copy command queue  local cmds = Spring.GetUnitCommands(unitID)  for i = 2, cmds.n do  -- skip the first command (CMD_CLONE)    local cmd = cmds[i]    Spring.GiveOrderToUnit(newUnit, cmd.id, cmd.params, cmd.options.coded)  end  -- copy some state  local states = Spring.GetUnitStates(unitID)  Spring.GiveOrderArrayToUnitArray({ newUnit }, {    { CMD.FIRE_STATE, { states.firestate },             {} },    { CMD.MOVE_STATE, { states.movestate },             {} },    { CMD.REPEAT,     { states['repeat']  and 1 or 0 }, {} },    { CMD.CLOAK,      { states.cloak      and 1 or 0 }, {} },    { CMD.ONOFF,      { 1 }, {} },    { CMD.TRAJECTORY, { states.trajectory and 1 or 0 }, {} },  })--[[  local oldHealth = Spring.GetUnitHealth(unitID)  local newHealth = oldHealth * (udDst.maxHealth / udSrc.maxHealth)  Spring.SetUnitHealth(newUnit, newHealth)--]]  -- FIXME: - re-attach to current transport?  -- update selection  SendToUnsynced("unit_clone", unitID, newUnit)  --Spring.SetUnitBlocking(newUnit, true)  --Spring.DestroyUnit(unitID, false, true) -- selfd = false, reclaim = trueendlocal function UpdateClone(unitID, cloneData)  if (Spring.UseUnitResource(unitID, cloneData.def.resTable)) then    cloneData.progress = cloneData.progress + cloneData.increment  end  if (cloneData.progress >= 1.0) then    FinishClone(unitID, cloneData)    return false -- remove from the list, all done  end  return trueend--------------------------------------------------------------------------------function gadget:Initialize()  --[[  if Spring.IsReplay() then    gadgetHandler:RemoveGadget()    return      end  --]]  _G.cloneUnits = cloneUnits  -- make it global for unsynced access via SYNCED  -- get the cloneDefs  cloneDefs = include("LuaRules/Configs/clone_defs.lua")  if (not cloneDefs) then    gadgetHandler:RemoveGadget()    return  end  gadgetHandler:RegisterCMDID(CMD_CLONE)  cloneDefs = ValidateCloneDefs(cloneDefs)  -- add the Clone command to existing units  for _,unitID in ipairs(Spring.GetAllUnits()) do    local teamID    = Spring.GetUnitTeam(unitID)    local unitDefID = Spring.GetUnitDefID(unitID)    local cloneDef  = cloneDefs[unitDefID]    if (cloneDef) then      local cmdDescID = Spring.FindUnitCmdDesc(unitID, CMD_CLONE)      if (not cmdDescID) then        AddCloneCmdDesc(unitID, cloneDef, teamTechLevel[teamID])      end    end  endendfunction gadget:Shutdown()  for _,unitID in ipairs(Spring.GetAllUnits()) do    local cloneData = cloneUnits[unitID]    if (cloneData) then      StopClone(unitID, cloneData)    end    local cmdDescID = Spring.FindUnitCmdDesc(unitID, CMD_CLONE)    if (cmdDescID) then      Spring.RemoveUnitCmdDesc(unitID, cmdDescID)    end  endendfunction gadget:UnitCreated(unitID, unitDefID, teamID)  local cloneDef = cloneDefs[unitDefID]  if (cloneDef) then    AddCloneCmdDesc(unitID, cloneDef, teamTechLevel[teamID])  end endfunction gadget:UnitDestroyed(unitID, unitDefID, teamID)  cloneUnits[unitID] = nil	RemoveFactory(unitID, unitDefID, teamID)	endfunction gadget:UnitTaken(unitID, unitDefID, oldTeamID, teamID)  AddFactory(unitID, unitDefID, teamID)  self:UnitCreated(unitID, unitDefID, teamID)endfunction gadget:UnitGiven(unitID, unitDefID, newTeamID, teamID)  RemoveFactory(unitID, unitDefID, teamID)endfunction AddFactory(unitID, unitDefID, teamID)  if (isFactory(unitDefID)) then    local unitTechLevel = GetTechLevel(unitDefID)    if (unitTechLevel > teamTechLevel[teamID]) then      teamTechLevel[teamID]=unitTechLevel      UpdateClonePossibilities(teamID)    end  endendfunction RemoveFactory(unitID, unitDefID, teamID)  if (devolution)and(isFactory(unitDefID)) then    -- check all factories and determine team level    local level = 0    for _,unitID2 in ipairs(Spring.GetTeamUnits(teamID)) do      local unitDefID2 = Spring.GetUnitDefID(unitID2)      if (isFactory(unitDefID2) and unitID2 ~= unitID) then         local unitTechLevel = GetTechLevel(unitDefID2)        if (unitTechLevel>level) then level = unitTechLevel end      end    end    if (level ~= teamTechLevel[teamID]) then      teamTechLevel[teamID] = level      UpdateClonePossibilities(teamID)			if (stopCloneOnDevolution) then 				for cloneID, data in pairs(cloneUnits) do 					if (data.def.tech > level and Spring.GetUnitTeam(cloneID) == teamID) then 						StopClone(cloneID, data)					end				end			end    end  endendfunction gadget:UnitFinished(unitID, unitDefID, teamID)  AddFactory(unitID, unitDefID, teamID)endfunction gadget:GameFrame(n)  if ((n+28) % 64)<1 then    local teamIDs = Spring.GetTeamList()    for _,teamID in ipairs(teamIDs) do      UpdateClonePossibilities(teamID)    end  end  if (next(cloneUnits) == nil) then    return  -- no cloneing units  end  local killUnits = {}  for unitID, cloneData in pairs(cloneUnits) do    if (not UpdateClone(unitID, cloneData)) then      killUnits[unitID] = true    end  end  for unitID in pairs(killUnits) do    cloneUnits[unitID] = nil  endendfunction gadget:AllowCommand(unitID, unitDefID, teamID,                             cmdID, cmdParams, cmdOptions)  --do return true end -- FIXME ?--  do  if (cmdID == CMD_CLONE) then    local cloneDef = cloneDefs[unitDefID]    if ((cloneDef) and (cloneDef.tech<=teamTechLevel[teamID]) and (cloneDef.xp<=Spring.GetUnitExperience(unitID))) then      return true    end    return false  end  --return true--  end  local cloneData = cloneUnits[unitID]  if (cloneData) then    if (cmdID == CMD.STOP) then      StopClone(unitID, cloneData)      cloneUnits[unitID] = nil    elseif (cmdID == CMD.ONOFF) then      return false    else      return false    end  end  return trueendfunction gadget:CommandFallback(unitID, unitDefID, teamID,                                cmdID, cmdParams, cmdOptions)  if (cmdID ~= CMD_CLONE) then    return false  -- command was not used  end  local cloneDef = cloneDefs[unitDefID]  if (not cloneDef) then    return true, true  -- command was used, remove it  end  local cloneData = cloneUnits[unitID]  if (not cloneData) then		StartClone(unitID, cloneDef)		return true, true  end  return true, false  -- command was used, do not remove itend----------------------------------------------------------------------------------  SYNCED--------------------------------------------------------------------------------else----------------------------------------------------------------------------------  UNSYNCED------------------------------------------------------------------------------------ speed-ups--local SYNCED = SYNCEDlocal GetUnitTeam         = Spring.GetUnitTeamlocal GetUnitHeading      = Spring.GetUnitHeadinglocal GetUnitBasePosition = Spring.GetUnitBasePositionlocal GetGameFrame        = Spring.GetGameFramelocal GetSpectatingState  = Spring.GetSpectatingStatelocal AddWorldIcon        = Spring.AddWorldIconlocal AddWorldText        = Spring.AddWorldTextlocal glColor      = gl.Colorlocal glPushMatrix = gl.PushMatrixlocal glTranslate  = gl.Translatelocal glRotate     = gl.Rotatelocal glUnitShape  = gl.UnitShapelocal glPopMatrix  = gl.PopMatrix--------------------------------------------------------------------------------local function SelectSwap(cmd, oldID, newID)  local selUnits = Spring.GetSelectedUnits()  for i, unitID in ipairs(selUnits) do    if (unitID == oldID) then      table.insert(selUnits, newID)      Spring.SelectUnitArray(selUnits)      return true    end  end  return trueendfunction gadget:Initialize()  gadgetHandler:AddSyncAction("unit_clone", SelectSwap)endfunction gadget:Shutdown()  gadgetHandler:RemoveSyncAction("unit_clone")endlocal teamColors = {}local function SetTeamColor(teamID,a)  local color = teamColors[teamID]  if (color) then    color[4]=a    glColor(color)    return  end  local _,_,_,_,_,_,r,g,b = Spring.GetTeamInfo(teamID)  if (r and g and b) then    color = { r, g, b }    teamColors[teamID] = color    glColor(color)    return  endendlocal function DrawCloneUnit(unitID, unitCloneInfo, cloneData, localTeamID)  local unitTeam = GetUnitTeam(unitID)  local h = GetUnitHeading(unitID)  if (not h) then    return  -- bonus, heading is only available when the unit is in LOS  end  local px,py,pz = GetUnitBasePosition(unitID)  if (not px) then    return  end  px = px + unitCloneInfo.offsetx  pz = pz + unitCloneInfo.offsetz  local frac = math.mod(GetGameFrame() + unitID, 30) / 30  local alpha = 2.0 * math.abs(0.5 - frac)  --glColor(1.0, 1.0, 1.0, alpha*0.3)  SetTeamColor(unitTeam,alpha)  glPushMatrix()  glTranslate(px, py, pz)  glRotate(h * (360 / 65535), 0, 1, 0)  glUnitShape(cloneData.def.into, unitTeam)  glPopMatrix()  -- cheesy progress indicator  if (localTeamID)and(unitTeam==localTeamID) then    glPushMatrix()    glTranslate(px, py-20, pz)    gl.Billboard()    local progStr = string.format("%.1f%%", 100 * cloneData.progress)    gl.Text(progStr, 0, 0, 7, "oc")    glPopMatrix()  endendfunction gadget:DrawWorld()  local cloneUnits = SYNCED.cloneUnits  if ((not cloneUnits) or (snext(cloneUnits) == nil)) then    return -- no clones to draw  end  gl.Blending(GL.SRC_ALPHA, GL.ONE)  gl.DepthTest(GL.LEQUAL)  local spec, specFullView = GetSpectatingState()  local readTeam  if (specFullView) then    readTeam = Script.ALL_ACCESS_TEAM  else    readTeam = Spring.GetLocalTeamID()  end  CallAsTeam({ ['read'] = readTeam }, function()    for unitID, cloneData in spairs(cloneUnits) do      if (unitID and cloneData) then  -- FIXME: huh?        DrawCloneUnit(unitID, cloneUnits[unitID], cloneData, readTeam)      end    end  end)  gl.DepthTest(false)  gl.Blending(GL.SRC_ALPHA, GL.ONE_MINUS_SRC_ALPHA)end----------------------------------------------------------------------------------  UNSYNCED--------------------------------------------------------------------------------end----------------------------------------------------------------------------------  COMMON--------------------------------------------------------------------------------