Jump to content

Spawning something out of screen ?


Recommended Posts

I want to spawn something near the player when doing a certain action. Triggering the event is ok, but i don't know how to spawn my thing near the player but offscreen if possible, at least not "on" the player.

I guess it implies code similar for warg calling help, probably like this :



local function NumHoundsToSpawn(inst)
    local numHounds = TUNING.WARG_BASE_HOUND_AMOUNT

    local pt = Vector3(inst.Transform:GetWorldPosition())
    local ents = TheSim:FindEntities(pt.x, pt.y, pt.z, TUNING.WARG_NEARBY_PLAYERS_DIST, {"player"}, {"playerghost"})
    for i,player in ipairs(ents) do
        local playerAge = player.components.age:GetAgeInDays()
        local addHounds = math.clamp(Lerp(1, 4, playerAge/100), 1, 4)
        numHounds = numHounds + addHounds
    end
    local numFollowers = inst.components.leader:CountFollowers()
    local num = math.min(numFollowers+numHounds/2, numHounds) -- only spawn half the hounds per howl
    num = (math.log(num)/0.4)+1 -- 0.4 is approx log(1.5)

    num = RoundToNearest(num, 1)

    return num - numFollowers
end

But i'm confused about what to use and how. If someone have a clue it will be welcome :)

Link to comment
Share on other sites

So you have a certain action the player does and you have a function that is called when this action is made, right?
And now you need code within this function, to spawn a prefab somewhere around the player, but not at exactly the same point? Is offscreen necessary?

In the code you wrote, there is much more than just spawning something. Do you need all the stuff in this code? For example making them follow the leader, playerage and such stuff?

Edited by Serpens
Link to comment
Share on other sites

1 minute ago, Serpens said:

So you have a certain action the player does and you have a function that is called when this action is made, right?

Yes, this part i could do.

1 minute ago, Serpens said:

And now you need code within this function, to spawn a prefab somewhere around the player, but not at exactly the same point? Is offscreen necessary?

Offscreen is probably more "clean", but anything that isn't "all in the same spot" will be better than now.

Here is one example i plan to use it.


function WargPrefabPostInit(inst)
	if not GLOBAL.TheWorld.ismastersim then
		return inst
	end
	
	local function SeasonHound(inst)

		if GLOBAL.TheWorld.state.isspring then
				inst.components.lootdropper:SpawnLootPrefab("hound_yellow")
				inst.components.lootdropper:SpawnLootPrefab("hound_yellow")
				local num = math.random(1, 3)
				if num == 3 then
					inst.components.lootdropper:SpawnLootPrefab("hound_yellow")

				end
		elseif GLOBAL.TheWorld.state.issummer then
				inst.components.lootdropper:SpawnLootPrefab("hound_orange")
				inst.components.lootdropper:SpawnLootPrefab("hound_orange")
				local num = math.random(1, 3)
				if num == 3 then
					inst.components.lootdropper:SpawnLootPrefab("hound_orange")

				end
		elseif GLOBAL.TheWorld.state.isautumn then
				inst.components.lootdropper:SpawnLootPrefab("hound_green")
				inst.components.lootdropper:SpawnLootPrefab("hound_green")
				local num = math.random(1, 3)
				if num == 3 then
					inst.components.lootdropper:SpawnLootPrefab("hound_green")

				end
		elseif GLOBAL.TheWorld.state.iswinter then
				inst.components.lootdropper:SpawnLootPrefab("hound_purple")
				inst.components.lootdropper:SpawnLootPrefab("hound_purple")
				local num = math.random(1, 3)
				if num == 3 then
					inst.components.lootdropper:SpawnLootPrefab("hound_purple")

				end
		end

			
	end
	inst:ListenForEvent("death", function() SeasonHound(inst) end)
end
	
AddPrefabPostInit("warg", WargPrefabPostInit)

When a warg is killed, i want to spawn hounds, i manage to do it now with "spawnlootprefab" but it's not "clean" and all the hounds are spawned "on" the warg. I would like to spawn them "around" it, and if possible, far away enough so it seems that the warg "called" them. Something more natural.

But anything will be better than the current state.

 

I plan to use something similar to "spawn" a creature when the player interact with a prefab (similar to koalefant track but a LOT much simpler), with if possible the same "off screen if possible" but anything will be better than "right under the feet of the player".


I hope i'm more clear. Thanks for reading.

Link to comment
Share on other sites

I already made a nice function for spawning stuff near someone/something for my teleportato mod (chests and enemies spawning around the teleportato)
Maybe it is exactly what you are looking for?
 

Spoiler

local function SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin) -- xmin and zmin are the mind distance it should have to the given position
    -- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
    local pos = nil
    if loc.prefab then    
        pos = loc:GetPosition()
    else -- loc can also be a position already
        pos = loc
    end
    x = x or 5
    y = y or 0
    z = z or 5
    xmin = xmin or 3
    zmin = zmin or 3
    local xn = 0
    local zn = 0
    times = times or 1
    times = math.ceil(times)
    local found = false
    local tp_pos
    local attempts = 100
    local spawn = nil
    for i=1,times,1 do 
        found = false
        attempts = 100 --try multiple times to get a spot on ground before giving up so we don't infinite loop
        while attempts > 0 do
            xn = GetRandomWithVariance(0,x)
            zn = GetRandomWithVariance(0,z)
            xn = (xn>=0 and xn<=xmin and xn+xmin) or (xn<0 and xn>=-xmin and xn-xmin) or xn -- dont be in range of xmin zmin, cause this is too near
            zn = (zn>=0 and zn<=zmin and zn+zmin) or (zn<0 and zn>=-zmin and zn-zmin) or zn
            tp_pos = pos + Vector3(xn ,GetRandomWithVariance(0,y) ,zn  )
            if TheWorld.Map:IsAboveGroundAtPoint(tp_pos:Get()) then
                found = true
                break
            end
            attempts = attempts - 1
        end
        spawn = nil
        if found then
            spawn = SpawnPrefab(prefab)
            if spawn then
                spawn.Transform:SetPosition(tp_pos:Get())
                SpawnPrefab("collapse_small").Transform:SetPosition(tp_pos:Get()) -- a small puff effect
            else
                print("Adventure: Spawn of "..tostring(prefab).." failed...")
            end
            -- print("spawned "..tostring(prefab).." at "..tostring(tp_pos).." times: "..tostring(times).." spawn: "..tostring(spawn))
        end
    end
    return spawn -- does only return the last spawn, if times is higher than 1
end

- The prefab is what you want to spawn.
- The "loc" is either a position where to spawn or an instance near to spawn.
- times is the number of prefabs you want to spawn around the position/instance
- x,y,z are the range within the prefab should spawn. Since y is the height, this is usually 0.
- xmin and zmin are the min distance between position/instance and the prefab.

This function already tests, if the random point to spawn is land. If it is blocked or water it is trying another point withing range, up to 100 times before giving up.
And it also makes a small "puff" effect.
Of course you can remove/change every "print" statement. They are mainly for debugging.

Edited by Serpens
Link to comment
Share on other sites

@Serpens

Thanks, it works fine. My first tests got crashes because i wasn't in global (this specific code is in modmain), i didn't got time to make tests until today, now i added the GLOBAL. all is working fine, i just have to tune values to see what distance will be great.

Anyway, thanks because it was what i needed so it's great :)

Link to comment
Share on other sites

Hello. May I ask some help here, too? xD

 

I'm trying to do something similar around my character. 

 

The line I'm trying to use is:

 

Quote

-- Spawning hounds around you!
inst:DoPeriodicTask(1, function()    
local function SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin) -- xmin and zmin are the mind distance it should have to the given position
    -- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
    local pos = nil
    if loc.prefab then    
        pos = loc:GetPosition()
    else -- loc can also be a position already
        pos = loc
    end
    x = x or 10
    y = y or 0
    z = z or 10
    xmin = xmin or 6
    zmin = zmin or 6
    local xn = 0
    local zn = 0
    times = times or 1
    times = math.ceil(times)
    local found = false
    local tp_pos
    local attempts = 100
    local spawn = nil
    for i=1,times,1 do 
        found = false
        attempts = 100 --try multiple times to get a spot on ground before giving up so we don't infinite loop
        while attempts > 0 do
            xn = GetRandomWithVariance(0,x)
            zn = GetRandomWithVariance(0,z)
            xn = (xn>=0 and xn<=xmin and xn+xmin) or (xn<0 and xn>=-xmin and xn-xmin) or xn -- dont be in range of xmin zmin, cause this is too near
            zn = (zn>=0 and zn<=zmin and zn+zmin) or (zn<0 and zn>=-zmin and zn-zmin) or zn
            tp_pos = pos + Vector3(xn ,GetRandomWithVariance(0,y) ,zn  )
            if TheWorld.Map:IsAboveGroundAtPoint(tp_pos:Get()) then
                found = true
                break
            end
            attempts = attempts - 1
        end
        spawn = nil
        if found then
            spawn = SpawnPrefab("hound")
            if spawn then
                spawn.Transform:SetPosition(tp_pos:Get())
                SpawnPrefab("collapse_small").Transform:SetPosition(tp_pos:Get()) -- a small puff effect
            else
                print("Adventure: Spawn of "..tostring(prefab).." failed...")
            end
            -- print("spawned "..tostring(prefab).." at "..tostring(tp_pos).." times: "..tostring(times).." spawn: "..tostring(spawn))
        end
    end
    return spawn -- does only return the last spawn, if times is higher than 1
end

end)

Basically I tried to use a function that every "x" time, spawns a hound near you. I tried to do 1 second for test purposes.

The thing is... It is not spawning the hound. The game lags for a bit, and nothing happens.

 

Any ideas?

Thanks!

Link to comment
Share on other sites

@diovbc your code is wrong, I mean this part:
 

inst:DoPeriodicTask(1, function()    
local function SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin)
-- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
--...

Please try notepad++ and do search for "inst:DoPeriodicTask" in the game files, to get some examples how to use the function. It is described here how to search:
http://forums.kleientertainment.com/topic/73989-tutorial-basics-what-to-use-to-open-lua-files-and-why/#comment-865955

There are two way how it could look correct:
1)

inst:DoPeriodicTask(1, function(prefab,loc,x,y,z,times,xmin,zmin)    
-- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
--...
--..
end, nil, prefab,loc,...your values)

2)


local function SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin)
-- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
--...
--..
end

inst:DoPeriodicTask(1, SpawnPrefabAtLandPlotNearInst,nil,prefab,loc,...your values)    

Of course you also need to know where to put the code. You can't put it anywhere, you need to know when this should happen. So to tell you where to put it, I would need the rest of your code or explanation when it should happen.


And of course you need to stop that task sometime. This is another peace of code you have to add.

 

Edited by Serpens
Link to comment
Share on other sites

@Serpens: i'm trying to use this code to spawn something when investigate a custom dirt pile, the code itself works fine but my problem is that i can't manage to remove the custom dirt pile itself when using it.

local assets =
{
    Asset("ANIM", "anim/koalefant_tracks.zip"),
    Asset("ANIM", "anim/smoke_puff_small.zip"),
}

local prefabs =
{
    "small_puff",
	"hound_purple"
}

local AUDIO_HINT_MIN = 10
local AUDIO_HINT_MAX = 60

local function GetVerb()
    return "INVESTIGATE"
end

local function SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin) -- xmin and zmin are the mind distance it should have to the given position
		prefab = "hound_purple"
    -- print("Spawn "..tostring(prefab).." near "..tostring(loc).." times: "..tostring(times))
    if prefab==nil or loc==nil or type(prefab)~="string" then
        print("Adventure: Spawnprefab is "..tostring(prefab).." instead of a prefab string... and loc is "..tostring(loc))
        return nil 
    end
    local pos = nil
    if loc.prefab then    
        pos = loc:GetPosition()
    else -- loc can also be a position already
        pos = loc
    end
    x = x or 5
    y = y or 0
    z = z or 5
    xmin = xmin or 3
    zmin = zmin or 3
    local xn = 0
    local zn = 0
    times = times or 1
    times = math.ceil(times)
    local found = false
    local tp_pos
    local attempts = 100
    local spawn = nil
    for i=1,times,1 do 
        found = false
        attempts = 100 --try multiple times to get a spot on ground before giving up so we don't infinite loop
        while attempts > 0 do
            xn = GetRandomWithVariance(0,x)
            zn = GetRandomWithVariance(0,z)
            xn = (xn>=0 and xn<=xmin and xn+xmin) or (xn<0 and xn>=-xmin and xn-xmin) or xn -- dont be in range of xmin zmin, cause this is too near
            zn = (zn>=0 and zn<=zmin and zn+zmin) or (zn<0 and zn>=-zmin and zn-zmin) or zn
            tp_pos = pos + Vector3(xn ,GetRandomWithVariance(0,y) ,zn  )
            if TheWorld.Map:IsAboveGroundAtPoint(tp_pos:Get()) then
                found = true
                break
            end
            attempts = attempts - 1
        end
        spawn = nil
        if found then
            spawn = SpawnPrefab(prefab)
            if spawn then
                spawn.Transform:SetPosition(tp_pos:Get())
                SpawnPrefab("collapse_small").Transform:SetPosition(tp_pos:Get()) -- a small puff effect
            else
                print("Adventure: Spawn of "..tostring(prefab).." failed...")
            end
            -- print("spawned "..tostring(prefab).." at "..tostring(tp_pos).." times: "..tostring(times).." spawn: "..tostring(spawn))
        end
    end
    return spawn -- does only return the last spawn, if times is higher than 1

end


-- local function OnInvestigated(inst)
    -- print("dirtpile - OnInvestigated")
	SpawnPrefabAtLandPlotNearInst
    PlayFX(pt, "small_puff", "smoke_puff_small", "puff", "dontstarve/common/deathpoof", nil, Vector3(216/255, 154/255, 132/255))
    -- inst:Remove()
-- end





local function create()
    --print("dirtpile - create")

    local inst = CreateEntity()

    inst.entity:AddTransform()
    inst.entity:AddAnimState()
    --inst.entity:AddSoundEmitter()
    inst.entity:AddNetwork()

    MakeInventoryPhysics(inst)

    inst:AddTag("dirtpile")

    inst.AnimState:SetBank("track")
    inst.AnimState:SetBuild("koalefant_tracks")
    --inst.AnimState:SetOrientation( ANIM_ORIENTATION.OnGround )
    --inst.AnimState:SetLayer( LAYER_BACKGROUND )
    --inst.AnimState:SetSortOrder( 3 )
    inst.AnimState:SetRayTestOnBB(true)
    inst.AnimState:PlayAnimation("idle_pile")

    --inst.Transform:SetRotation(math.random(360))

    inst.GetActivateVerb = GetVerb

    inst.entity:SetPristine()

    if not TheWorld.ismastersim then
        return inst
    end

    inst:AddComponent("inspectable")
    --inst.components.inspectable.getstatus = GetStatus

    inst:AddComponent("activatable")    

    --set required
    inst.components.activatable.OnActivate = SpawnPrefabAtLandPlotNearInst
    -- inst.components.activatable.OnActivate = OnInvestigated
    inst.components.activatable.inactive = true

    -- inst:AddComponent("hauntable")


    --inst:DoTaskInTime(1, OnAudioHint)

    inst.persists = false
    return inst
end

return Prefab("customdirtpile", create, assets, prefabs)

 

Here is the code i'm using. I tried to add an "inst:remove()" in the spawn code, but the variable isn't declared. If i add "inst" in the " SpawnPrefabAtLandPlotNearInst(prefab,loc,x,y,z,times,xmin,zmin)" part it doesn't work. So maybe you have an advice, even if it's not the initial use for your code ?

Or another solution ? What i want is being able to spawn monter around the player when investigate a dirtpile, but without managing all the complex components of the initial koalefants tracks.


But of course i could search to do something not really possible here.

Edited by Lumina
Link to comment
Share on other sites

I would have to test it myself, but dont have time to do this at the momemt.

But I would not advice to do

inst.components.activatable.OnActivate = SpawnPrefabAtLandPlotNearInst

The way with "OnInvestigated" is better.

So simplify the problem:
If you only have "inst.Remove()" in OnInvestigate, it is not removed?
maybe try "return inst:Remove()".
And if this also does not work use DoTaskIntime with a timer of 0.01.

Link to comment
Share on other sites

If i use " OnInvestigated", the custom dirt pile is removed, but i can't spawn the monster.

If i use " SpawnPrefabAtLandPlotNearInst " i can spawn the monster but can't remove the custom dirt pile.


I could do one, i could do the other, but i fail to do both at the same time.

 

I tried to call the "SpawnPrefabAtLandPlotNearInst" function into the "OnInvestigated" function without success, i probably wasn't doing it right.

Link to comment
Share on other sites

15 hours ago, Lumina said:

I tried to call the "SpawnPrefabAtLandPlotNearInst" function into the "OnInvestigated" function without success, i probably wasn't doing it right.

In case your OnInvestigate function really looks like this, it of course does not work:
 

local function OnInvestigated(inst)
    print("dirtpile - OnInvestigated")
    SpawnPrefabAtLandPlotNearInst
    PlayFX(pt, "small_puff", "smoke_puff_small", "puff", "dontstarve/common/deathpoof", nil, Vector3(216/255, 154/255, 132/255))
    inst:Remove()
end

SpawnPrefab... is a function, so of course you have to call it like that:
 

SpawnPrefabAtLandPlotNearInst("prefab",inst,...)

replace prefab with your prefab (and remove your prefab change in the function itself), and replace ... with whatever you want.

Link to comment
Share on other sites

Also, i changed prefab this way because i plan to change prefab depending of season and the only way i found at the moment is to change it with a "if season is = x then, elseif". There is probably a better way, but in the meantime this is the reason why i change it this way.

Link to comment
Share on other sites

1 hour ago, Lumina said:

Also, i changed prefab this way because i plan to change prefab depending of season and the only way i found at the moment is to change it with a "if season is = x then, elseif". There is probably a better way, but in the meantime this is the reason why i change it this way.


yes, but it is better readable and more professional and everything, if you do it outside of the spawn function, so eg. like this:

local function OnInvestigated(inst)
    print("dirtpile - OnInvestigated")
    prefab = "hound"
    if itiswinter then
        prefab = "winterhound"
    end
    SpawnPrefabAtLandPlotNearInst(prefab,inst,...)
    PlayFX(pt, "small_puff", "smoke_puff_small", "puff", "dontstarve/common/deathpoof", nil, Vector3(216/255, 154/255, 132/255))
    inst:Remove()
end

The order of the things you pass to the spawn function must be exactly the same order like you see in the bracket of the spawnfunction, so:
(prefab,loc,x,y,z,times,xmin,zmin)

 

Edited by Serpens
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

×
  • Create New...