funny dance missiles with SetProjectileTarget (2020)

funny dance missiles with SetProjectileTarget (2020)

Discuss Lua based Spring scripts (LuaUI widgets, mission scripts, gaia scripts, mod-rules scripts, scripted keybindings, etc...)

Moderator: Moderators

Post Reply
User avatar
PicassoCT
Journeywar Developer & Mapper
Posts: 10450
Joined: 24 Jan 2006, 21:12

funny dance missiles with SetProjectileTarget (2020)

Post by PicassoCT »

I made a better version of this.. using event streams..

Code: Select all

function gadget:GetInfo()
    return {
        name = "CruiseMissile Management",
        desc = "SetProjectileTarget etc",
        author = "PicassoCT",
        date = "Mar 2020",
        license = "later horses dont be mean.",
        layer = 0,
        enabled = true --      loaded by default?
    }
end

if (not gadgetHandler:IsSyncedCode()) then
    return
end

VFS.Include("scripts/lib_OS.lua")
VFS.Include("scripts/lib_UnitScript.lua")
VFS.Include("scripts/lib_Animation.lua")
VFS.Include("scripts/lib_Build.lua")
VFS.Include("scripts/lib_mosaic.lua")
GameConfig = getGameConfig()

local cruiseMissileWeapons = {}
cruiseMissileWeapons[WeaponDefNames["cm_airstrike"].id] = true
cruiseMissileWeapons[WeaponDefNames["cm_walker"].id] = true
cruiseMissileWeapons[WeaponDefNames["cm_antiarmor"].id] = true
cruiseMissileWeapons[WeaponDefNames["cm_turret_ssied"].id] = true

onImpact = {
    [WeaponDefNames["cm_airstrike"].id] = function(projID, tx, ty, tz)
        px, py, pz = Spring.GetProjectilePosition(projID)
        teamID = Spring.GetProjectileTeamID(projID)

        for i = 1, 4 do
            GG.UnitsToSpawn:PushCreateUnit("air_copter_ssied", px, py, pz, 1, teamID)
        end

        Spring.SetProjectileTarget(projID, tx, Spring.GetGroundHeight(tx, tz), tz)
    end,
    [WeaponDefNames["cm_walker"].id] = function(projID, tx, ty, tz)
        px, py, pz = Spring.GetProjectilePosition(projID)
        teamID = Spring.GetProjectileTeamID(projID)
        for i = 1, 2, 1 do
            unitID = Spring.CreateUnit("ground_walker_mg", px, py, pz, 1, teamID)
            giveParachutToUnit(unitID, px, py, pz)
        end
        Spring.DeleteProjectile(projID)
    end,
    [WeaponDefNames["cm_antiarmor"].id] = function(projID, tx, ty, tz)
        px, py, pz = Spring.GetProjectilePosition(projID)
        teamID = Spring.GetProjectileTeamID(projID)
      
        for i = 1, 6 do
            Spring.SpawnProjectile(
                WeaponDefNames["javelinrocket"].id,
                {
                    pos = {px+math.random(-2,2), py+math.random(0,5), pz+math.random(-2,2)},
                    ["end"] = {tx, ty, tz},
                    -- speed = {number x, number y, number z},
                    spread = {10,10,10},
                    -- owner = pOwner,
                    team = teamID,
                    ttl = 30 * 30,
                    error = {0, 5, 0},
                    maxRange = 1200,
                    gravity = Game.gravity,
                    startAlpha = 1,
                    endAlpha = 1,
                    model = "air_copter_antiarmor_projectile.s3o"
                }
            )
        end
        Spring.DeleteProjectile(projID, tx, ty, tz)
    end,
    [WeaponDefNames["cm_turret_ssied"].id] = function(projID)
        px, py, pz = Spring.GetProjectilePosition(projID)
        teamID = GetProjectileTeamID(projID)
        unitID = Spring.CreateUnit("ground_turret_mg", px, py, pz, 1, teamID)
        giveParachutToUnit(unitID, px, py, pz)

        Spring.DeleteProjectile(projID)
    end
}

onLastPointBeforeImpactSetTargetTo = {
    [WeaponDefNames["cm_airstrike"].id] = function(projID)
    end,
    [WeaponDefNames["cm_walker"].id] = function(projID)
    end,
    [WeaponDefNames["cm_antiarmor"].id] = function(projID)
    end,
    [WeaponDefNames["cm_turret_ssied"].id] = function(projID)
    end
}

function getWeapondefByName(name)
    return WeaponDefs[WeaponDefNames[name].id]
end

local SSied_Def = getWeapondefByName("ssied")
local CM_Def = getWeapondefByName("cruisemissile")

assert(CM_Def)
assert(CM_Def.range)
assert(CM_Def.projectilespeed)

local redirectProjectiles = {} -- [frame][projectileID] = table with .targetType .targetX .targetY .targetZ .targetID

function gadget:Initialize()
    for id, boolActive in pairs(cruiseMissileWeapons) do
        Script.SetWatchWeapon(id, true)
    end
end

cruiseMissileFunction = function(evtID, frame, persPack, startFrame)
    projID = persPack.projID
    px, py, pz = Spring.GetProjectilePosition(projID)

    if not px then
       -- echo("Projectile died")
        return nil, persPack
    end

    --check if close to next target
    nextTarget = persPack.redirectList[persPack.redirectIndex]
    dist = distance(px, py, pz, nextTarget.targetX, nextTarget.targetY, nextTarget.targetZ)

    -- if close to target
    if dist < 50 then
        --if lasttarget
        if persPack.redirectIndex == #persPack.redirectList then
            --if pre last target
            persPack.on_Impact(projID, nextTarget.targetX, nextTarget.targetY, nextTarget.targetZ)
           -- echo("Projectile ready to die")
            return nil, persPack
        elseif persPack.redirectIndex + 1 == #persPack.redirectList then
            persPack.on_LastPointBeforeImpactSetTargetTo(projID)
        end

        persPack.redirectIndex = math.min(#persPack.redirectList, persPack.redirectIndex + 1)
       -- echo("Setting next target:" .. frame .. " to target " .. persPack.redirectIndex)
        nextTarget = persPack.redirectList[persPack.redirectIndex]
        dist = distance(px, py, pz, nextTarget.targetX, nextTarget.targetY, nextTarget.targetZ)
    end

    FramesToTarget = math.max(2, math.ceil(dist / CM_Def.projectilespeed) - 2)
    setTargetTable(projID, persPack.redirectList[persPack.redirectIndex])
   -- echo("game_cruiseMissiles:" .. (FramesToTarget / 30) .. " seconds till waypoint " .. persPack.redirectIndex)
    return frame + FramesToTarget, persPack
end

redirectedProjectiles = {}
function gadget:ProjectileCreated(proID, proOwnerID, proWeaponDefID)
    if (cruiseMissileWeapons[proWeaponDefID] or cruiseMissileWeapons[Spring.GetProjectileDefID(proID)]) then
       -- echo("Cruise Missile registered")
        redirectedProjectiles[proID] = proWeaponDefID

        local tx, ty, tz = getProjectileTargetXYZ(proID)
        local x, y, z = Spring.GetUnitPosition(proOwnerID)
        local resolution = 10
        local preCog = 1
        redirectList = {}

        for i = 1, resolution - 1, 1 do
            rx, rz = mix(tx, x, i / resolution), mix(tz, z, i / resolution)
            interpolate_Y = 0
            for add = 0, preCog, 1 do
                it = math.max(1, math.min(resolution, i + add))
                ix, iz = mix(tx, x, it / resolution), mix(tz, z, it / resolution)
                interpolate_Y = math.max(Spring.GetSmoothMeshHeight(ix, iz), interpolate_Y)
            end

            redirectList[#redirectList + 1] = {
                targetX = rx,
                targetY = interpolate_Y + GameConfig.CruiseMissilesHeightOverGround,
                targetZ = rz,
                targetType = string.byte("g")
            }
        end

        GG.EventStream:CreateEvent(
            cruiseMissileFunction,
            {
                --persistance Pack
                redirectIndex = 1,
                redirectList = redirectList,
                projID = proID,
                weaponDefID = proWeaponDefID,
                on_Impact = onImpact[proWeaponDefID],
                on_LastPointBeforeImpactSetTargetTo = onLastPointBeforeImpactSetTargetTo[proWeaponDefID]
            },
            Spring.GetGameFrame() + 1
        )

        return true
    end
end

function getProjectileTargetXYZ(proID)
    local targetTypeInt, target = Spring.GetProjectileTarget(proID)
    if targetTypeInt == string.byte("g") then
        return target[1], target[2], target[3]
    end
    if targetTypeInt == string.byte("u") then
        return Spring.GetUnitPosition(target)
    end
    if targetTypeInt == string.byte("f") then
        return Spring.GetFeaturePosition(target)
    end
    if targetTypeInt == string.byte("p") then
        return Spring.GetProjectilePosition(target)
    end
end

function addProjectileRedirect(proID, targetTable, delay, boolImpact)
    local f = Spring.GetGameFrame() + delay
    if not redirectProjectiles[f] then
        redirectProjectiles[f] = {}
    end

    redirectProjectiles[f][proID] = targetTable
end

function setTargetTable(proID, targetTable)
    if targetTable.targetType == string.byte("g") then
        Spring.SetProjectileTarget(proID, targetTable.targetX, targetTable.targetY, targetTable.targetZ)
    else
        Spring.SetProjectileTarget(proID, targetTable.targetID, targetTable.targetType)
    end
end
Ah Eventstream is basically a gadget thread, that decides for itself when its time to die..

Code: Select all

function gadget:GetInfo()
    return {
        name = "EventStream",
        desc = "This gadget streams eventsfunctions until they get deactivated or remove themselves",
        author = "This one, no, this one shall not pass. He shall remain outside, for he is evil, mending riddles to problems that need no solving. Answering questions we did not have.",
        date = "Sep. 2014",
        license = "GNU GPL, v2 or later",
        layer = 0,
        enabled = true,
    }
end

--A Explanation:
--[[
Eventstreams are a attempt to optimize the number of necessary lua calls. Without the resorting to cumbersome if frame % magicnumber comparisons.
The Idea is simply- in every interesting case, there is a event that started it. And it knows best how to handle itself, 
what data to store, and when to remove itself from the world.

So for every event there is only a basic package needed - a function, a persistance table, and the frame in which it wants to be called..
	--Expected Tableformat:
	--GG.EventStream[nr] which contains Tables in the shape of"..
	--{id=id, Action = function(id,frame, persPack), persPack}"..
	-- 	Action handles the actual action, for example validation a unit still exists.
	--	It always returns a frameNr when it wants to be called Next, and the Persistance Package
	-- If it does not, the Action is considered done and is deleted after calling Final if that is defined -> Final(id, frame, PersPackage, startFrame)
	--adding the id of the action to GG.EventStreamDeactivate deletes the Action
	
	Once the function does not return a frame - the gadget recognizes the event as complete and delete the event. EventStreams are selfcontained and responsible for what they alter in the game world.
		
		Pros: Dezentralized and therefore Distributed Event management
		Cons: Not ideal for Situations where many Units have to interact with one another- in that case you need to write a manager function which 
			]]

if (gadgetHandler:IsSyncedCode()) then
    local Events = {}
    GG.EventStreamID = 0

    local function DeactivateEvent(self, evtID)
        boolRemovedFunction = false

        for frames, EventTables in ipairs(Events) do
            for i = #EventTables, 1, -1 do
                if EventTables[i] == evtID then
                    table.remove(Events[frames], evtID)
                    boolRemovedFunction = true
                end
            end
        end
        return boolRemovedFunction
    end


    local function CreateEvent(self, action, persPack, startFrame)
	
        startFrame = math.max(startFrame, Spring.GetGameFrame())
        --	Spring.Echo("Create event "..(GG.EventStreamID+1).. "waiting for frame  "..startFrame)
        myID = GG.EventStreamID
        GG.EventStreamID = GG.EventStreamID + 1
        self[myID] = { id = myID, action = action, persPack = persPack, startFrame = startFrame }
        if not Events[startFrame] then Events[startFrame] = {} end
        Events[startFrame][#Events[startFrame] + 1] = myID

        return myID
    end

    local function InjectCommand(self, ...)
        self[#self + 1] = { ... }
    end

    if GG.EventStream == nil then GG.EventStream = { CreateEvent = CreateEvent, DeactivateEvent = DeactivateEvent } end
    if GG.EventStreamDeactivate == nil then GG.EventStreamDeactivate = {} end

    function gadget:GameFrame(frame)

        if Events[frame] then
            for i = 1, #Events[frame] do
                evtID = Events[frame][i]

                if GG.EventStream[evtID] then

                    nextFrame, GG.EventStream[evtID].persPack = GG.EventStream[evtID].action(evtID, frame, GG.EventStream[evtID].persPack, GG.EventStream[evtID].startFrame)

                    if nextFrame then
                        if not Events[nextFrame] then Events[nextFrame] = {} end
                        Events[nextFrame][#Events[nextFrame] + 1] = evtID
                    else
                        --Spring.Echo("Event "..evtID .." is completed" )
                        GG.EventStream[evtID] = nil
                    end
                end
            end
        end

        --handle EventStream
        Events[frame] = nil
    end
end
Yeah my code is evil, and if you dont release on steam, you are not a real dev, but hey, it might solve someone elses problem with the original knorkery.

viewtopic.php?t=31799
User avatar
FLOZi
MC: Legacy & Spring 1944 Developer
Posts: 6240
Joined: 29 Apr 2005, 01:14

Re: funny dance missiles with SetProjectileTarget (2020)

Post by FLOZi »

gif or it didn't happen
User avatar
PicassoCT
Journeywar Developer & Mapper
Posts: 10450
Joined: 24 Jan 2006, 21:12

Re: funny dance missiles with SetProjectileTarget (2020)

Post by PicassoCT »

User avatar
PicassoCT
Journeywar Developer & Mapper
Posts: 10450
Joined: 24 Jan 2006, 21:12

Re: funny dance missiles with SetProjectileTarget (2020)

Post by PicassoCT »

Tis scared the flozi..
How does one make vid to gif?
Post Reply

Return to “Lua Scripts”