Modding Help Melee Weapon that Changes Elements

Discussion in 'Starbound Modding' started by I Said No, Nov 4, 2017.

  1. I Said No

    I Said No Cosmic Narwhal

    Hey there, i'm currently trying to create a melee weapon that can cycle between elements similarly to the Adaptable Crossbow. The problem i'm running into is that I have it mostly finished - the sword cycles properly between elements on a right click, and emits the correct particles and such when being slashed, but seems to always apply physical damage no matter which element it has been switched to. As i'm rather unfamiliar with more involved code-work, I decided to ask for help here.

    Here's the json file for the item:
    {
    "itemName" : "isn_prism_blade",
    "price" : 7500,
    "level" : 4,
    "maxStack" : 1,
    "rarity" : "Legendary",
    "description" : "Focuses a short ray of deadly light through a prism.",
    "shortdescription" : "Prism Blade",
    "tooltipKind" : "sword",
    "category" : "broadsword",
    "twoHanded" : true,
    "itemTags" : ["weapon","melee","broadsword"],

    "inventoryIcon" : "isn_prism_blade.png",

    "animation" : "/items/active/weapons/melee/broadsword/combobroadsword.animation",
    "animationParts" : {
    "handle" : "",
    "blade" : "isn_prism_blade.png"
    },
    "animationCustom" : {
    "sounds" : {
    "typeswitch" : [ "/sfx/objects/ancientlightplatform_on.ogg" ]
    }
    },

    "scripts" : ["/items/active/weapons/melee/meleeweapon.lua"],

    "elementalType" : "physical",
    "projectileOffset" : [0,0],

    "primaryAbilityType" : "isn_prism_blade_lmb",

    "builder" : "/items/buildscripts/buildunrandweapon.lua"
    }

    The json file for the weaponability it uses:
    {
    "animationParts" : { },
    "animationCustom" : {
    "sounds" : {
    "fire" : [ "/sfx/melee/mech_energysword_swing1.ogg" ],
    "fire2" : [ "/sfx/melee/mech_energysword_swing2.ogg" ],
    "fire3" : [ "/sfx/melee/mech_energysword_swing3.ogg" ]
    }
    },

    "ability" : {
    "name" : "Prism Combo Slash",
    "type" : "isn_prism_blade_lmb",
    "scripts" : ["/items/active/isn_prism_blade/isn_prism_blade.lua"],
    "class" : "PrismCombo",

    "comboSteps" : 3,

    "flashTime" : 0.15,
    "flashDirectives" : "fade=FFFFFFFF=0.15",

    "swooshOffsetRegions" : [
    [0.75, 0.0, 4.25, 5.0],
    [3.0, -0.5, 6.5, 2.0],
    [1.5, -1.0, 5.5, 1.0]
    ],

    // cooldown time multiplier for steps after the first, compounded per combo step
    "comboSpeedFactor" : 0.8,

    "edgeTriggerGrace" : 0.1,

    "fireTime" : 0.8,
    "baseDps" : 7.0,

    "damageConfig" : {
    "damageSourceKind" : "broadsword",
    "statusEffects" : [ ],
    "knockbackMode" : "facing",
    "timeout" : 0.5
    },
    "stepDamageConfig" : [
    {
    "baseDamageFactor" : 1.0,
    "knockback" : 15
    },
    {
    "baseDamageFactor" : 0.5,
    "knockback" : 10
    },
    {
    "baseDamageFactor" : 1.0,
    "knockback" : 25
    }
    ],

    "stances" : {
    "idle" : {
    "armRotation" : -90,
    "weaponRotation" : -10,
    "allowRotate" : false,
    "allowFlip" : true
    },
    "windup1" : {
    "duration" : 0.05,
    "armRotation" : 90,
    "weaponRotation" : -10,
    "twoHanded" : true,

    "allowRotate" : false,
    "allowFlip" : true
    },
    "preslash1" : {
    "duration" : 0.025,
    "armRotation" : 55,
    "weaponRotation" : -45,
    "twoHanded" : true,

    "allowRotate" : false,
    "allowFlip" : false
    },
    "fire1" : {
    "duration" : 0.075,
    "armRotation" : -45,
    "weaponRotation" : -55,
    "twoHanded" : true,

    "allowRotate" : false,
    "allowFlip" : false
    },
    "wait1" : {
    "duration" : 0.05,
    "armRotation" : -45,
    "weaponRotation" : -55,
    "allowRotate" : false,
    "allowFlip" : true,
    "twoHanded" : true
    },
    "windup2" : {
    "duration" : 0.05,
    "armRotation" : -15,
    "weaponRotation" : -60,
    "weaponOffset" : [0, 0],
    "twoHanded" : true,
    "allowFlip" : true,
    "allowRotate" : false
    },
    "fire2" : {
    "duration" : 0.1,
    "armRotation" : -150,
    "weaponRotation" : 55,
    "weaponOffset" : [0, 0],
    "twoHanded" : true,
    "allowFlip" : true,
    "allowRotate" : false
    },
    "wait2" : {
    "duration" : 0.05,
    "armRotation" : -150,
    "weaponRotation" : 55,
    "weaponOffset" : [0, 0],
    "allowRotate" : false,
    "allowFlip" : true,
    "twoHanded" : true
    },
    "windup3" : {
    "duration" : 0.05,
    "armRotation" : -150,
    "weaponRotation" : 55,
    "twoHanded" : true,

    "allowRotate" : false,
    "allowFlip" : true
    },
    "fire3" : {
    "duration" : 0.03,
    "armRotation" : 0,
    "weaponRotation" : -90,
    "twoHanded" : true,

    "allowRotate" : false,
    "allowFlip" : true
    }
    }
    }
    }

    And the lua file the weaponability runs on:
    -- Melee primary ability
    PrismCombo = WeaponAbility:new()

    function PrismCombo:init()
    self.comboStep = 1

    self.scriptdamageconfig = self.damageConfig

    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.clickprevention = false
    self.flashtime = 0

    self.weapon.onLeaveAbility = function()
    self.weapon:setStance(self.stances.idle)
    end
    end

    -- Ticks on every update regardless if this is the active ability
    function PrismCombo: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

    if self.fireMode == "alt" then
    if self.clickprevention == false then
    animator.playSound("typeswitch")
    animator.setGlobalTag("bladeDirectives", "fade=FFFFFFFF=0.3")
    self.flashtime = 5
    if self.scriptdamageconfig.damageSourceKind == "broadsword" then
    self.scriptdamageconfig.damageSourceKind = "firebroadsword"
    self.elementalType = "fire"
    self.weapon.elementalType = "fire"
    elseif self.scriptdamageconfig.damageSourceKind == "firebroadsword" then
    self.scriptdamageconfig.damageSourceKind = "icebroadsword"
    self.elementalType = "ice"
    self.weapon.elementalType = "ice"
    elseif self.scriptdamageconfig.damageSourceKind == "icebroadsword" then
    self.scriptdamageconfig.damageSourceKind = "poisonbroadsword"
    self.elementalType = "poison"
    self.weapon.elementalType = "poison"
    elseif self.scriptdamageconfig.damageSourceKind == "poisonbroadsword" then
    self.scriptdamageconfig.damageSourceKind = "electricbroadsword"
    self.elementalType = "electric"
    self.weapon.elementalType = "electric"
    else
    self.scriptdamageconfig.damageSourceKind = "broadsword"
    self.elementalType = "physical"
    self.weapon.elementalType = "physical"
    end
    end
    self.clickprevention = true
    else
    self.clickprevention = false
    end

    self.flashtime = self.flashtime - 1
    if self.flashtime < 1 then
    animator.setGlobalTag("bladeDirectives", "")
    end
    end

    -- State: windup
    function PrismCombo:windup()
    local stance = self.stances["windup"..self.comboStep]

    self.weapon:setStance(stance)

    self.edgeTriggerTimer = 0

    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 PrismCombo:wait()
    local stance = self.stances["wait"..(self.comboStep - 1)]

    self.weapon:setStance(stance)

    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 PrismCombo:preslash()
    local stance = self.stances["preslash"..self.comboStep]

    self.weapon:setStance(stance)
    self.weapon:updateAim()

    util.wait(stance.duration)

    self:setState(self.fire)
    end

    -- State: fire
    function PrismCombo:fire()
    local stance = self.stances["fire"..self.comboStep]

    self.weapon:setStance(stance)
    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 PrismCombo: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 PrismCombo:readyFlash()
    animator.setGlobalTag("bladeDirectives", self.flashDirectives)
    self.flashTimer = self.flashTime
    end

    function PrismCombo: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 = util.mergeTable(copy(self.scriptdamageconfig), self.stepDamageConfig)
    self.stepDamageConfig.timeoutGroup = "primary"..i

    local damageFactor = self.stepDamageConfig.baseDamageFactor
    self.stepDamageConfig.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 PrismCombo:uninit()
    self.weapon:setDamage()
    end


    Any help you can offer is greatly appreciated. =)
     
  2. I Said No

    I Said No Cosmic Narwhal

    Aha, I managed to fix it on my own. Turns out I was thinking along the right lines altering the damageconfig, but was altering the wrong one. Broadswords that swing in a combo have a seperate damageconfig for each slash that needs to have its damagesource changed to the new element, like so:

    This line in init gives me easier access to the first step's damageconfig:
    self.stepone = self.stepDamageConfig[1]

    Then in the code that changes elements, I do this:
    self.stepone.damageSourceKind = "firebroadsword"

    Et voila, the first slash now does fire elemental damage. You could also probably set up a sword that does different elemental damage on each swing with this too, if you wanted.
     

Share This Page