Modding Help About the limit on capture pods...

Discussion in 'Starbound Modding' started by ManicRykker, Aug 25, 2017.

  1. ManicRykker

    ManicRykker Phantasmal Quasar

    Hey guys! Been quite a while since I asked a question on the forums, but this really has me stumped...

    Recently, I created a new mod called Pandora's Box (monster related). I want to do a bunch of stuff with em, and got curious about the pod limit.. and wanted to change it (nothing crazy. Wanted to switch it from just one active pet to three)

    I search through some files, and find a "activePodLimit" : 1, bit in the player.config. I think "cool", so I patch that to 3, and test it out...

    No luck. Game still forces second monsters back into their pod when another is sent out...


    Is something else forcing this? The only other thing I thought to check was petspawner.lua, and I'm afraid to change anything myself as I don't quite know what's what/can't find what I'm looking for *exactly*...



    require "/scripts/messageutil.lua"
    require "/scripts/companions/util.lua"

    Pet = {}
    Pet.__index = Pet

    function Pet.new(...)
    local self = setmetatable({}, Pet)
    self:init(...)
    return self
    end

    function Pet:init(podUuid, json)
    self.podUuid = podUuid
    self.name = json.name
    self.rank = json.rank
    self.description = json.description
    self.portrait = json.portrait
    self.spawnConfig = json.config
    self.uniqueId = json.uniqueId
    self.status = json.status
    self.storage = json.storage
    self.collar = json.collar
    self.collisionPoly = json.collisionPoly

    self.persistent = json.persistent or false
    self.spawner = petSpawner
    self.dirtyOnStatusUpdate = true
    self.returnMessage = "pet.returnToPod"
    self.statusRequestMessage = "pet.status"

    if self.uniqueId then
    self.statusTimer = config.getParameter("statusTimer", 2)
    end
    end

    function Pet:store()
    local result = self:toJson()
    if not self.persistent then
    result.uniqueId = nil
    end
    return result
    end

    function Pet:toJson()
    return {
    podUuid = self.podUuid,
    name = self.name,
    rank = self.rank,
    description = self.description,
    portrait = self.portrait,
    config = self.spawnConfig,
    status = self.status,
    storage = self.storage,
    collar = self.collar,
    uniqueId = self.uniqueId,
    collisionPoly = self.collisionPoly,
    persistent = self.persistent
    }
    end

    function Pet:dead()
    return self.status and self.status.dead
    end

    function Pet:_scriptConfig(parameters)
    return parameters
    end

    function Pet:spawn(position, extraParameters)
    if self.uniqueId then return end

    position = position or entity.position()
    if self.collisionPoly then
    position = findCompanionSpawnPosition(position, self.collisionPoly)
    end

    local parameters = {}
    util.mergeTable(parameters, self.spawnConfig.parameters)
    if extraParameters then
    util.mergeTable(parameters, extraParameters)
    end

    local scriptConfig = self:_scriptConfig(parameters)
    parameters.persistent = self.persistent
    scriptConfig.podUuid = self.podUuid
    scriptConfig.ownerUuid = self.spawner.ownerUuid
    scriptConfig.tetherUniqueId = self.spawner.tetherUniqueId

    scriptConfig.initialStatus = copy(self.status) or {}
    scriptConfig.initialStorage = util.mergeTable(scriptConfig.initialStorage or {}, self.storage or {})

    -- Pets level with the player, gaining the effects of the player's armor
    if getPetPersistentEffects then
    -- If the player is spawning us, we gain the effects of their armor, so
    -- ignore the monster's level.
    parameters.level = 1
    end
    if self.spawner.levelOverride then
    -- If a tether is spawning us, we get the level of the world/ship we're on.
    parameters.level = self.spawner.levelOverride
    end

    scriptConfig.initialStatus.persistentEffects = self:getPersistentEffects()

    local damageTeam = self:damageTeam()
    parameters.damageTeamType = damageTeam.type
    parameters.damageTeam = damageTeam.team
    parameters.relocatable = false

    if self.collar and self.collar.parameters then
    util.mergeTable(parameters, self.collar.parameters)
    end

    self.uniqueId = sb.makeUuid()
    scriptConfig.uniqueId = self.uniqueId

    local result = self:_spawn(position, parameters)

    self.spawnedCollarParameters = self.collar and self.collar.parameters
    self.spawning = true
    self:resetStatusTimer(true)

    return result
    end

    function Pet:_spawn(position, parameters)
    return world.spawnMonster(self.spawnConfig.type, position, parameters)
    end

    function Pet:resetStatusTimer(initialRequest)
    if initialRequest then
    self.statusTimer = config.getParameter("initialStatusTimer", config.getParameter("statusTimer", 0.5))
    else
    self.statusTimer = config.getParameter("statusTimer", 2)
    end
    end

    function Pet:damageTeam()
    local damageTeam = entity.damageTeam()
    if self.collar and self.collar.damageTeam then
    util.mergeTable(damageTeam, self.collar.damageTeam)
    end
    return damageTeam
    end

    function Pet:getPersistentEffects()
    local effects = jarray()

    -- The player's armor takes effect if the player is spawning the pet:
    if getPetPersistentEffects then
    util.appendLists(effects, getPetPersistentEffects())
    end

    -- Collars applied to the capturepod also take effect:
    if self.collar and self.collar.effects then
    util.appendLists(effects, self.collar.effects)
    end

    return effects
    end

    function Pet:despawn(callback)
    if not self.uniqueId then return end
    self.statusTimer = nil
    promises:add(world.sendEntityMessage(self.uniqueId, self.returnMessage), function (status)
    self.status = status
    self.uniqueId = nil
    if callback then callback() end
    end)
    end

    function Pet:needsRespawn()
    if not self.uniqueId then return false end
    if not compare(self.spawnedCollarParameters, self.collar and self.collar.parameters) then
    -- The collar changed and it defines parameter overrides for the pet
    return true
    end
    return false
    end

    function Pet:update(dt)
    if self.statusTimer then
    if self:needsRespawn() then
    self:despawn(bind(self.spawn, self))
    self.uniqueId = nil
    return
    end

    if not self.uniqueId then
    self.statusTimer = nil
    return
    end

    self.statusTimer = self.statusTimer - dt
    if self.statusTimer <= 0 then
    self.statusTimer = nil

    self:sendStatusRequest()
    end
    end
    end

    function Pet:statusReady()
    return self.status and not self.spawning
    end

    function Pet:statusFailure()
    if not self.persistent then
    -- Pet entity doesn't exist.
    -- It didn't die, because it sends us its status when it does that.
    -- It just became unloaded. It's not persistent, so respawn it.
    self.uniqueId = nil
    self:spawn()
    else
    -- Companion was tethered to the world (i.e. persistent) and disappeared.
    -- Only possible cause is death
    self.status = self.status or {}
    self.status.dead = true
    self.uniqueId = nil
    end
    end

    function Pet:sendStatusRequest()
    local persistentEffects = self:getPersistentEffects()

    promises:add(world.sendEntityMessage(self.uniqueId, self.statusRequestMessage, persistentEffects, self:damageTeam()), function (state)
    self.status = state.status
    self.storage = state.storage
    if state.portrait then
    self.portrait = state.portrait
    end
    self.spawning = false
    self:resetStatusTimer(false)
    self.statusTimer = config.getParameter("statusTimer", 2)
    if self.dirtyOnStatusUpdate then
    self.spawner:markDirty()
    end
    end, function ()
    if self.spawning then
    -- The pet may simply not have spawned yet. Try requesting status again.
    self:resetStatusTimer(true)
    else
    self:statusFailure()
    end
    end)
    end

    function Pet:setStatus(status, dead)
    self.status = status
    if dead then
    self.status.dead = true
    self.uniqueId = nil
    self.statusTimer = nil
    end
    end

    -- Pods are contain one or more pets. All are released and returned at once.
    -- In the most common case, a pod will be only one pet, but they can contain
    -- more, e.g. in the cases of hemogoblin and nutmidge, which drop smaller
    -- monsters when they die.
    Pod = {}
    Pod.__index = Pod

    function Pod.new(uuid, pets)
    local self = setmetatable({}, Pod)
    self.uuid = uuid
    self.pets = util.map(pets, function (petStore)
    return Pet.new(uuid, petStore)
    end)
    return self
    end

    function Pod:store()
    return {
    uuid = self.uuid,
    pets = util.map(self.pets, Pet.store, jarray())
    }
    end

    function Pod.load(json)
    return Pod.new(json.uuid, json.pets)
    end

    function Pod:dead()
    for _,pet in pairs(self.pets) do
    if not pet:dead() then
    return false
    end
    end
    return true
    end

    function Pod:release(position)
    for _,pet in pairs(self.pets) do
    if not pet:dead() then
    pet:spawn(position)
    end
    end
    end

    function Pod:recall()
    for _,pet in pairs(self.pets) do
    pet:despawn()
    end
    end

    function Pod:update(dt)
    for _,pet in pairs(self.pets) do
    pet:update(dt)
    end
    end

    function Pod:setCollar(collar)
    for _,pet in pairs(self.pets) do
    pet.collar = collar
    end
    end

    function Pod:findPetKey(uniqueId)
    for key,pet in pairs(self.pets) do
    if pet.uniqueId == uniqueId then
    return key
    end
    end
    return nil
    end

    function Pod:findPet(uniqueId)
    local key = self:findPetKey(uniqueId)
    if key then
    return self.pets[key]
    else
    return nil
    end
    end

    function Pod:removePet(uniqueId)
    local key = self:findPetKey(uniqueId)
    if key then
    table.remove(self.pets, key)
    return true
    end
    return false
    end

    function Pod:addPet(pet)
    self.pets[#self.pets+1] = pet
    end

    petSpawner = {}

    function petSpawner:init()
    message.setHandler("pets.updatePet", simpleHandler(bind(petSpawner.updatePet, self)))
    message.setHandler("pets.disassociatePet", simpleHandler(bind(petSpawner.disassociatePet, self)))
    message.setHandler("pets.associatePet", simpleHandler(bind(petSpawner.associatePet, self)))
    message.setHandler("pets.setPodCollar", simpleHandler(bind(petSpawner.setPodCollar, self)))

    -- The values of these should be set by the code using petSpawner.
    self.pods = {}
    self.ownerUuid = nil
    self.tetherUniqueId = nil
    self.levelOverride = nil
    end

    function petSpawner:markDirty()
    -- Mark health bars and other visual status representations as needing an
    -- update.
    self.dirty = true
    end

    function petSpawner:clearDirty()
    -- Indicate that UI elements have been updated
    self.dirty = false
    end

    function petSpawner:isDirty()
    -- Do UI elements need updating?
    return self.dirty
    end

    function petSpawner:updatePet(podUuid, uniqueId, status, dead)
    local pod = self.pods[podUuid]
    if not pod then
    sb.logInfo("Cannot update pet %s from invalid pod %s", uniqueId, podUuid)
    return
    end

    local pet = pod:findPet(uniqueId)
    if not pet then
    sb.logInfo("Cannot update invalid pet %s from pod %s", uniqueId, podUuid)
    return
    end

    pet:setStatus(status, dead)
    self:markDirty()
    end

    function petSpawner:disassociatePet(podUuid, uniqueId)
    local pod = self.pods[podUuid]
    if not pod then
    sb.logInfo("Cannot disassociate pet %s from invalid pod %s", uniqueId, podUuid)
    return
    end

    pod:removePet(uniqueId)
    self:markDirty()
    end

    function petSpawner:associatePet(podUuid, petJson)
    local uniqueId = petJson.uniqueId
    if not uniqueId then
    sb.logInfo("Cannot associate non-existent pet with pod %s", podUuid)
    return
    end

    local pod = self.pods[podUuid]
    if not pod then
    sb.logInfo("Cannot associate pet %s with invalid pod %s", uniqueId, podUuid)
    return
    end

    local pet = Pet.new(podUuid, petJson)

    pod:addPet(pet)
    self:markDirty()
    end

    function petSpawner:setPodCollar(podUuid, collar)
    local pod = self.pods[podUuid]
    if not pod then
    sb.logInfo("Cannot set a collar %s on invalid pod %s", collar and collar.name, podUuid)
    return
    end

    pod:setCollar(collar)
    self:markDirty()
    end


    Does anyone know what it could be?... Thanks in advance.
     
  2. projectmayhem

    projectmayhem Spaceman Spiff

    wonder if changing this would help...

    function Pod:addPet(pet)
    self.pets[#self.pets+1] = pet
    end

    if you put +0, would it let you summon infinite pets?
     
  3. ManicRykker

    ManicRykker Phantasmal Quasar

    Nope, just tried it. The second still gets forced back into it's pod :/
     
  4. Mobius58

    Mobius58 Void-Bound Voyager

    I'm not sure if this would help you, but the crew members are actually based off of the pet monster class. The way starbound gets to spawn the crew is that it has a recruitspawner object that spawns them individually and manages their saved data. so maybe you could try something like that and create a new pod that uses the vanilla pods as members of this larger pod. If you cannot find any other way to get it to work.
     
  5. ManicRykker

    ManicRykker Phantasmal Quasar

    Hmm.. Yeah.

    I still find the presence of the podlimit bit in the player.config so strange though, considering it doesn't have any immediate effects as far as I know..

    I plan on trying other ways to achieve what I was planning, but I'll definitely still be open to any more feedback from folks that know their way around those lua scripts. (as I sure don't.. lol)
     
  6. ManicRykker

    ManicRykker Phantasmal Quasar

    I hate having to rise this question from the grave, but I recently did another search. It would seem I was a doof, and was looking in the wrong place...

    It seems the information I was looking for is located in the player.lua script (in /scripts/companions)


    -- Activate means 'regard this pod as active' but do not release yet.
    -- When the player throws the filledcapturepod, activatePod is called.
    -- When the filledcapturepod hits something, it calls spawnFromPod to release
    -- the monster.
    function activatePod(podUuid)
    local limit = config.getParameter("activePodLimit")
    if limit < 1 then return end
    local pod = petSpawner.pods[podUuid]
    if not pod then
    sb.logInfo("Cannot activate invalid pod %s", podUuid)
    return
    end

    -- If we have too many pets out, call some back
    local overflow = math.max(util.tableSize(storage.activePods) + 1 - limit, 0)
    for uuid,_ in pairs(storage.activePods) do
    deactivatePod(uuid)
    overflow = overflow - 1
    if overflow <= 0 then
    break
    end
    end


    Last night, I did much fiddling. I tried to get some insight over on the Starbound Discord, but didn't have any luck. No matter what I try, the overflow still kicks in as if the limit is 1.

    (In other news, I know for sure this is what I was looking for, as removing the overflow bit allows you to summon unlimited pets. (Still unsure whether I'll use this finding in a side mod.)
    This of course doesn't help my main goal, which is to simply increase the limit from 1 to 3.)


    If anyone has any ideas, or wants to try helping me with this (also note the podlimit I mentioned in the player.config. Still unsure how that plays into this script, as it references it, but the limit doesn't seem to care for increases or decreases...) I would really appreciate (As I mentioned before, I will give credit.)

    In the meantime, I will continue to ask around on the Discord too. This is functionality I would really like to do in my mod...
     

Share This Page