playerBonusConfig = {
--[[
type: Unique identifier for the bonus, will automatically be configured to a constant as well (e.g. PLAYERBONUS_ARMOR)
readableString: String used when showing the player they received the bonus
roll: Obvious, but math.random(min, max)
loadOnLogin: Load the bonus when player logs in, since it's not saved to database
callback: Callback for when you require special functionality (aka when basic storages aren't enough)
]]
bonuses = {
{
type = "armor",
readableString = "Armor",
loadOnLogin = true,
roll = {min = 5, max = 10},
-- We don't need a callback for armor since it's handled by storage value in onHealth/onManaChange events
},
{
type = "attackspeed",
readableString = "Attack Speed",
roll = {min = 5, max = 10},
loadOnLogin = true,
callback = function(player, currentBonus, modifier)
player:setAttackSpeed(player:getAttackSpeed() + modifier)
end
},
{
type = "health",
readableString = "Maximum Health",
roll = {min = 5, max = 10},
loadOnLogin = true,
callback = function(player, currentBonus, modifier)
player:setMaxHealth(player:getMaxHealth() + modifier)
player:addHealth(modifier)
end
},
{
type = "mana",
readableString = "Maximum Mana",
roll = {min = 5, max = 10},
loadOnLogin = true,
callback = function(player, currentBonus, modifier)
player:setMaxMana(player:getMaxMana() + modifier)
player:addMana(modifier)
end
},
{
type = "criticalChance",
readableString = "Critical Hit Chance",
roll = {min = 5, max = 10},
loadOnLogin = true,
callback = function(player, currentBonus, modifier)
player:addSpecialSkill(SPECIALSKILL_CRITICALHITCHANCE, modifier)
end
},
{
type = "criticalAmount",
readableString = "Critical Hit Amount",
roll = {min = 5, max = 10},
loadOnLogin = true,
callback = function(player, currentBonus, modifier)
player:addSpecialSkill(SPECIALSKILL_CRITICALHITAMOUNT, modifier)
end
},
{
type = "healthRegeneration",
readableString = "Health Regeneration",
roll = {min = 5, max = 10},
loadOnLogin = true,
condition = Condition(CONDITION_REGENERATION, CONDITIONID_DEFAULT),
callback = function(player, currentBonus, modifier)
local subId = 501
local condition = player:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT, subId)
if condition then
condition:setParameter(CONDITION_PARAM_HEALTHGAIN, currentBonus + modifier)
else
condition = playerBonusConfig.bonuses[6].condition
condition:setTicks(-1)
condition:setParameter(CONDITION_PARAM_SUBID, subId)
condition:setParameter(CONDITION_PARAM_HEALTHTICKS, 1000)
condition:setParameter(CONDITION_PARAM_HEALTHGAIN, currentBonus + modifier)
player:addCondition(condition)
end
end
},
{
type = "manaRegeneration",
readableString = "Mana Regeneration",
roll = {min = 5, max = 10},
loadOnLogin = true,
condition = Condition(CONDITION_REGENERATION, CONDITIONID_DEFAULT),
callback = function(player, currentBonus, modifier)
local subId = 502
local condition = player:getCondition(CONDITION_REGENERATION, CONDITIONID_DEFAULT, subId)
if condition then
condition:setParameter(CONDITION_PARAM_MANAGAIN, currentBonus + modifier)
else
condition = playerBonusConfig.bonuses[7].condition
condition:setTicks(-1)
condition:setParameter(CONDITION_PARAM_SUBID, subId)
condition:setParameter(CONDITION_PARAM_MANATICKS, 1000)
condition:setParameter(CONDITION_PARAM_MANAGAIN, currentBonus + modifier)
player:addCondition(condition)
end
end
}
},
baseStorage = 12345, -- Base storage key to start from, the rest are assigned based on baseStorage and adds + 1 for each bonus in the config
perLevel = 5, -- Random bonus applied at each level interval defined here
msg = {
type = MESSAGE_STATUS_CONSOLE_ORANGE,
--[[
%s : readableString
%d (1): Bonus modifier (current + >>modifier<<)
%d (2): Total bonus (value of current + modifier)
]]
string = "Your %s has been increased by %d, your total is now %d."
}
}
for i, bonus in ipairs(playerBonusConfig.bonuses) do
-- Add constants for bonus types, e.g. PLAYERBONUS_ARMOR -> "armor"
_G["PLAYERBONUS_" .. bonus.type:upper()] = bonus.type
-- Automatically assign storage keys because fuck doing them all manually
playerBonusConfig.bonuses[i].storage = playerBonusConfig.baseStorage + i
end
-- Lib functions
function getBonusByType(bonusType)
for _, bonus in ipairs(playerBonusConfig.bonuses) do
if bonus.type == bonusType then
return bonus
end
end
return nil
end
function Player.addRandomBonus(self)
local bonus = playerBonusConfig.bonuses[math.random(#playerBonusConfig.bonuses)]
return self:addBonus(bonus.type, math.random(bonus.roll.min, bonus.roll.max))
end
function Player.addBonus(self, bonusType, modifier)
local bonus = bonusType
if type(bonus) == "string" then
bonus = getBonusByType(bonus)
end
if not bonus then
print("[Player.addBonus] Invalid bonus type given (".. tostring(bonusType) ..")")
return false
end
-- Add modifier to the cumulative bonus
local currentBonus = self:getBonus(bonus)
self:setStorageValue(bonus.storage, currentBonus + modifier)
if bonus.callback then
bonus.callback(self, currentBonus, modifier)
end
-- bonus, modifier, total
return bonus, modifier, currentBonus + modifier
end
function Player.getBonus(self, bonusType)
local bonus = bonusType
if type(bonus) == "string" then
bonus = getBonusByType(bonus)
end
if not bonus then
print("[Player.getBonus] Invalid bonus type given (".. tostring(bonusType) ..")")
return 0
end
local value = self:getStorageValue(bonus.storage)
return value == -1 and 0 or value
end
-- Below are all events, no touchy touchy
local function armorFormula(player, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
-- Make sure we're being hit and the damage is negative (avoid the formula on healing)
if not attacker or primaryType == COMBAT_HEALING then
return primaryDamage, secondaryDamage
end
local armor = player:getBonus(PLAYERBONUS_ARMOR)
if armor == 0 then
return primaryDamage, secondaryDamage
end
-- Apply default armor formula from Creature::blockHit
if armor > 3 then
primaryDamage = primaryDamage - (math.random(armor / 2, armor - (armor % 2 + 1)))
secondaryDamage = secondaryDamage - (math.random(armor / 2, armor - (armor % 2 + 1)))
else
primaryDamage = primaryDamage - 1
secondaryDamage = secondaryDamage - 1
end
return math.max(0, primaryDamage), math.max(0, secondaryDamage)
end
-- HealthChange Armor mitigation event
local eventHpChange = CreatureEvent("bonuses_armorHpEvent")
eventHpChange:type("healthChange")
function eventHpChange.onHealthChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
primaryDamage, secondaryDamage = armorFormula(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
eventHpChange:register()
-- ManaChange Armor mitigation event
local eventMpChange = CreatureEvent("bonuses_armorMpEvent")
eventMpChange:type("manaChange")
function eventMpChange.onManaChange(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType, origin)
primaryDamage, secondaryDamage = armorFormula(creature, attacker, primaryDamage, primaryType, secondaryDamage, secondaryType)
return primaryDamage, primaryType, secondaryDamage, secondaryType
end
eventMpChange:register()
-- Advance event to give random bonuses to the player
local eventAdvance = CreatureEvent("bonuses_onAdvance")
eventAdvance:type("advance")
function eventAdvance.onAdvance(player, skill, oldLevel, newLevel)
if skill ~= SKILL_LEVEL or newLevel < oldLevel then
return true
end
-- Apply a new random bonus for each level interval reached
local levelGain = newLevel - oldLevel
while true do
levelGain = levelGain - playerBonusConfig.perLevel
if levelGain < 0 then
break
end
local bonus, modifier, totalBonus = player:addRandomBonus()
if bonus and modifier and totalBonus then
player:sendTextMessage(playerBonusConfig.msg.type, string.format(playerBonusConfig.msg.string, bonus.readableString, modifier, totalBonus))
end
end
return true
end
eventAdvance:register()
-- Login event to register above events
local eventLogin = CreatureEvent("bonuses_eventRegister")
eventLogin:type("login")
function eventLogin.onLogin(player)
local events = {"bonuses_armorHpEvent", "bonuses_armorMpEvent", "bonuses_onAdvance"}
for _, eventName in ipairs(events) do
player:registerEvent(eventName)
end
for _, bonus in ipairs(playerBonusConfig.bonuses) do
if bonus.loadOnLogin then
-- Loads the storage value of the bonus and applies it to the player
bonus.callback(player, 0, player:getBonus(bonus.type))
end
end
return true
end
eventLogin:register()