Modding Help A few questions about combos and special attacks

Discussion in 'Starbound Modding' started by Vinderex, Jan 18, 2017.

  1. Vinderex

    Vinderex Pangalactic Porcupine

    Hi everyone. I'm needing a little bit of help on how to do a few things I've got planned for my Classical Weaponry mod.

    So anyways...
    1. How would I go about flipping the direction a weapon is facing for certain attacks in a combo. (for example, first slash is downward, then the second is upward, so the weapon sprite must be flipped to put its bladed edge up instead of down, and then the third is downward again and returns the weapon to its regular orientation). Is it at simple as setting a property in the respective section of the animation or weaponability files, or would it require something more complex like a script?

    2. I want a certain special attack to temporarily lock all motion and controls for a short while after it is launched. That is, the player will be completely stopped and locked in place, even if in mid-air, being pushed, or in any other instance where movement is in some way forced on the player; and all control input will be disabled, including weapon/item selection, teleporting, and opening menus. Any idea how to pull this off? What I'm intending to use this for is an attack that's capable of instant killing most same-tier enemies and maybe even some of the weaker next-tier enemies, but it leaves you wide open and completely unable to defend yourself if you miss or fail to kill in one shot.

    3. Is there a way to change the names (just displayed names, not ID) of vanilla items? More specifically, I'm planning on making a set of katana type weapons, one of which will be a Solus katana, so I'd like to change the name of Asra Nox's weapon to something like "Nox's Katana" or "Solus Blade".
     
    Last edited: Jan 19, 2017
  2. bk3k

    bk3k Oxygen Tank

    1. Don't know off hand. Probably some animation function for this. If nothing else, a change of frame should work.
    2. I don't think you can prevent opening menus etc as far as I know. I believe you can lock out controls though. I couldn't tell you off the top of my head but I could look it up.What you need you can find in \starbound\doc\lua\actormovementcontroller.md and you can find examples of that code in use with something like Notepad++'s "find in files" feature(pointed at your unpacked asset folder).
    3. Yes that's a simple patch right there. Make a soluskatana.activeitem.patch file(maintain the same path).
    Code:
    [
      {
        "op" : "replace",
        "path" : "/shortdescription",
        "value" : "Nox's Katana"
      }
    ]
     
    Last edited: Jan 19, 2017
  3. Chofranc

    Chofranc Pangalactic Porcupine

    1.- In the primary ability file you will find the value "stances", there you can change the position of the weapon and the position of the arm. Look for example the file \items\active\weapons\melee\abilities\shortsword\shortswordcombo.weaponability.

    Advice: If you are going to create new custom combos you need to patch the weaponabilities.config file in /items/buildscripts/ folder adding by adding the path of your new combo.

    2.- You can only reduce or disable the movement of the player, the rest i think that is not possible to disable it.
     
  4. Inf_Wolf14

    Inf_Wolf14 Parsec Taste Tester

    Code:
    animation.setFlipped(true)
    
    This can be applied to weapon abilities to flip the weapon sprite left-to-right.

    You just have to make sure that you set back the property whenever your combo step is not the desired, flipped step.
    (I'll post a snippet from my own code when I get on for a dirty example of how I used it.)



    Edit:
    Code:
    if self.comboStep > 1 then
      animator.setFlipped(true)
    else
      animator.setFlipped(false)
    end
    
    Simple, basic, but it does what it needs to do.
    This section I positioned in the wind-up function of my weapon combo script.
    In my case, all stances past the first will display the flipped sprite.

    Default Sprite:
    basicscythe.png
    Flipped Sprite, Rendered In-Game:
    basicscythe2.png

    (There is very easily a better way to do this, but I have it hardcoded because I don't need to use it anywhere else in my mod.)
     
    Last edited: Jan 19, 2017
  5. Vinderex

    Vinderex Pangalactic Porcupine

    Thanks everyone!

    For anyone interested, I made a little edit to the meleecombo.lua script so that flipping can be designated in the stances of the weaponability. ^.^

    Code:
    --Edited meleecombo script, adding an extra parameter to stances which allows flipping the direction of a weapon.
    --Original by Chucklefish. Edited by Vinderex.
    
    --To use: add the "flipx" parameter to stances using this script, and set it to either true or false. 
    --Note that this will also flip any animations and damage areas associated with the stance.
    
    -- Melee primary ability
    MeleeCombo = WeaponAbility:new()
    
    function MeleeCombo:init()
      self.comboStep = 1
    
      self.energyUsage = self.energyUsage or 0
    
      self:computeDamageAndCooldowns()
    
      self.weapon:setStance(self.stances.idle)
    
      self.edgeTriggerTimer = 0
      self.flashTimer = 0
      self.cooldownTimer = self.cooldowns[1]
    
      self.animKeyPrefix = self.animKeyPrefix or ""
    
      self.weapon.onLeaveAbility = function()
        animator.setFlipped(false)
        self.weapon:setStance(self.stances.idle)
      end
    end
    
    -- Ticks on every update regardless if this is the active ability
    function MeleeCombo:update(dt, fireMode, shiftHeld)
      WeaponAbility.update(self, dt, fireMode, shiftHeld)
    
      if self.cooldownTimer > 0 then
        self.cooldownTimer = math.max(0, self.cooldownTimer - self.dt)
        if self.cooldownTimer == 0 then
          self:readyFlash()
        end
      end
    
      if self.flashTimer > 0 then
        self.flashTimer = math.max(0, self.flashTimer - self.dt)
        if self.flashTimer == 0 then
          animator.setGlobalTag("bladeDirectives", "")
        end
      end
    
      self.edgeTriggerTimer = math.max(0, self.edgeTriggerTimer - dt)
      if self.lastFireMode ~= (self.activatingFireMode or self.abilitySlot) and fireMode == (self.activatingFireMode or self.abilitySlot) then
        self.edgeTriggerTimer = self.edgeTriggerGrace
      end
      self.lastFireMode = fireMode
    
      if not self.weapon.currentAbility and self:shouldActivate() then
        self:setState(self.windup)
      end
    end
    
    -- State: windup
    function MeleeCombo:windup()
      local stance = self.stances["windup"..self.comboStep]
    
      self.weapon:setStance(stance)
    
      self.edgeTriggerTimer = 0
    
      if stance.flipx==true then
        animator.setFlipped(true)
        else
        animator.setFlipped(false)
        end
     
      if stance.hold then
        while self.fireMode == (self.activatingFireMode or self.abilitySlot) do
          coroutine.yield()
        end
      else
        util.wait(stance.duration)
      end
    
      if self.energyUsage then
        status.overConsumeResource("energy", self.energyUsage)
      end
    
      if self.stances["preslash"..self.comboStep] then
        self:setState(self.preslash)
      else
        self:setState(self.fire)
      end
    end
    
    -- State: wait
    -- waiting for next combo input
    function MeleeCombo:wait()
      local stance = self.stances["wait"..(self.comboStep - 1)]
    
      self.weapon:setStance(stance)
    
      if stance.flipx==true then
        animator.setFlipped(true)
        else
        animator.setFlipped(false)
        end
     
      util.wait(stance.duration, function()
        if self:shouldActivate() then
          self:setState(self.windup)
          return
        end
      end)
    
      self.cooldownTimer = math.max(0, self.cooldowns[self.comboStep - 1] - stance.duration)
      self.comboStep = 1
    end
    
    -- State: preslash
    -- brief frame in between windup and fire
    function MeleeCombo:preslash()
      local stance = self.stances["preslash"..self.comboStep]
    
      self.weapon:setStance(stance)
     
      if stance.flipx==true then
        animator.setFlipped(true)
        else
        animator.setFlipped(false)
        end
     
      self.weapon:updateAim()
    
      util.wait(stance.duration)
    
      self:setState(self.fire)
    end
    
    -- State: fire
    function MeleeCombo:fire()
      local stance = self.stances["fire"..self.comboStep]
    
      self.weapon:setStance(stance)
     
      if stance.flipx==true then
        animator.setFlipped(true)
        else
        animator.setFlipped(false)
        end
     
      self.weapon:updateAim()
    
      local animStateKey = self.animKeyPrefix .. (self.comboStep > 1 and "fire"..self.comboStep or "fire")
      animator.setAnimationState("swoosh", animStateKey)
      animator.playSound(animStateKey)
    
      local swooshKey = self.animKeyPrefix .. (self.elementalType or self.weapon.elementalType) .. "swoosh"
      animator.setParticleEmitterOffsetRegion(swooshKey, self.swooshOffsetRegions[self.comboStep])
      animator.burstParticleEmitter(swooshKey)
    
      util.wait(stance.duration, function()
        local damageArea = partDamageArea("swoosh")
        self.weapon:setDamage(self.stepDamageConfig[self.comboStep], damageArea)
      end)
    
      if self.comboStep < self.comboSteps then
        self.comboStep = self.comboStep + 1
        self:setState(self.wait)
      else
        self.cooldownTimer = self.cooldowns[self.comboStep]
        self.comboStep = 1
      end
    end
    
    function MeleeCombo:shouldActivate()
      if self.cooldownTimer == 0 and (self.energyUsage == 0 or not status.resourceLocked("energy")) then
        if self.comboStep > 1 then
          return self.edgeTriggerTimer > 0
        else
          return self.fireMode == (self.activatingFireMode or self.abilitySlot)
        end
      end
    end
    
    function MeleeCombo:readyFlash()
      animator.setGlobalTag("bladeDirectives", self.flashDirectives)
      self.flashTimer = self.flashTime
    end
    
    function MeleeCombo:computeDamageAndCooldowns()
      local attackTimes = {}
      for i = 1, self.comboSteps do
        local attackTime = self.stances["windup"..i].duration + self.stances["fire"..i].duration
        if self.stances["preslash"..i] then
          attackTime = attackTime + self.stances["preslash"..i].duration
        end
        table.insert(attackTimes, attackTime)
      end
    
      self.cooldowns = {}
      local totalAttackTime = 0
      local totalDamageFactor = 0
      for i, attackTime in ipairs(attackTimes) do
        self.stepDamageConfig[i] = util.mergeTable(copy(self.damageConfig), self.stepDamageConfig[i])
        self.stepDamageConfig[i].timeoutGroup = "primary"..i
    
        local damageFactor = self.stepDamageConfig[i].baseDamageFactor
        self.stepDamageConfig[i].baseDamage = damageFactor * self.baseDps * self.fireTime
    
        totalAttackTime = totalAttackTime + attackTime
        totalDamageFactor = totalDamageFactor + damageFactor
    
        local targetTime = totalDamageFactor * self.fireTime
        local speedFactor = 1.0 * (self.comboSpeedFactor ^ i)
        table.insert(self.cooldowns, (targetTime - totalAttackTime) * speedFactor)
      end
    end
    
    function MeleeCombo:uninit()
      self.weapon:setDamage()
    end
    
     
  6. bk3k

    bk3k Oxygen Tank

    One thing about LUA is that it handles boolean comparisons in an interesting way. Let me show you.
    You don't necessarily need to do this
    Code:
    if stance.flipx==true then
    because you could do this
    Code:
    if stance.flipx then
    LUA will treat all values which are not expressly false or nil as true.
    So if you tried to reference a nil value, the expression would simply be false... well not literally false but perhaps more like not true

    And that means you could optionally turn this
    Code:
      if stance.flipx==true then
        animator.setFlipped(true)
      else
        animator.setFlipped(false)
      end
    
    into this
    Code:
      animator.setFlipped(stance.flipx)
    
    ...except we wouldn't be catching the scenario of stance.flipx being nil. If you knew that would certainly not be nil, you'd be alright. In this case we aren't assured that, but this can be solved elegantly like so
    Code:
      animator.setFlipped(stance.flipx or false)
    
    or false is only there to catch scenarios of stance.flipx being nil. The expression to the right of or will not be evaluated at all if the expression on the left is true in which case you don't have to worry about running excessive code. Less code to do the same job is preferable, no?

    Actually you don't have to change it as I assume your code works fine as is. I just wanted to show you another way.

    Another consideration is you don't need to replace that script. You can just make save your alterned version as under a different name and just patch
    \items\active\weapons\melee\abilities\broadsword\broadswordcombo.weaponability
    and
    \items\active\weapons\melee\abilities\shortsword\shortswordcombo.weaponability
    That way your changes are incorporated.
     
  7. Vinderex

    Vinderex Pangalactic Porcupine

    Yeah, I know that part, and I'm not replacing it. I'm only using it for the new weapons in my mod that need it.

    As for the boolean stuff, good to know. ^.^ I kind of figured it'd work that way, but I'm a complete noob with lua (only ever worked with C, Java, and LSL before) and wasn't sure my edit would even work, so I figured better safe than sorry. lol
     
  8. Vinderex

    Vinderex Pangalactic Porcupine

    Bump. Still needing some tips on #2. Just disabling movement would be good enough if the other stuff isn't possible.
     
  9. Inf_Wolf14

    Inf_Wolf14 Parsec Taste Tester

    You could try the 'actormovementcontroller' branch:
    Code:
    mcontroller.controlModifiers({
      speedModifier = 0.0,
      jumpModifier = 0.0,
      airJumpModifier = 0.0
    })
    
    Check the Lua docs concerning "actormovementcontroller". It's full of function pertaining to entity movement, while also accessible from activeitem scripts.

    From the sounds of what you're looking for, these functions should be right up your alley.
     
  10. EmissaryOfInfinity

    EmissaryOfInfinity Subatomic Cosmonaut

    For the issue of freezing the player, you can use mcontroller.clearControls(), which prevents the player from using any inputs whatsoever. If you want to freeze them in the air, you could also use mcontroller.setYVelocity(0), which would remove all movement from them until you specify otherwise. So as an example code...
    Code:
    function init()
    self.frozenTime = 3
    end
    
    function update(args)
    if self.frozenTime > 0
    mcontroller.clearControls()
    mcontroller.setYVelocity(0)
    mcontroller.setYVelocity(0)
    unfreeze(arg.dt)
    end
    end
    
    function unfreeze(dt)
    self.frozenTime = self.frozenTime - dt
    end
    
    This would freeze the player for three seconds, disabling all controls and instantly stopping all movement until the freeze timer has ended.
     

Share This Page