Difference between revisions of "KarmaSystem"

From Legends of Aria Admin and Modding Wiki
Jump to: navigation, search
(NegativeActions)
(Karma Actions)
Line 3: Line 3:
 
   welcome this will discuss the contents of the globals\static_data\karma\main.lua
 
   welcome this will discuss the contents of the globals\static_data\karma\main.lua
 
   this documentation is correct as of version 6.0 PRECB1 Release  
 
   this documentation is correct as of version 6.0 PRECB1 Release  
 +
''
 +
''
 +
  --[[ 
 +
    KarmaActionName = {
 +
        -- the amount to adjust
 +
        Adjust = -10,
 +
        -- the max amount this karma action can affect
 +
        UpTo = -500,
 +
        -- These only apply to NEGATIVE mods. (Adjust < 0)
 +
        -- if the action is being performed on a NPC (not a player character), it's multiplied by this amount
 +
        NpcModifier = 0.1
 +
    },
 +
  ]]
 +
  KarmaActions = {}
 +
  require 'globals.static_data.karma.negative_actions'
 +
  require 'globals.static_data.karma.positive_actions'
 
''
 
''
 
=== NegativeActions ===
 
=== NegativeActions ===

Revision as of 09:44, 27 January 2018

Karma Actions

 welcome this will discuss the contents of the globals\static_data\karma\main.lua
 this documentation is correct as of version 6.0 PRECB1 Release 

 --[[  
   KarmaActionName = {
       -- the amount to adjust
       Adjust = -10,
       -- the max amount this karma action can affect
       UpTo = -500,
       -- These only apply to NEGATIVE mods. (Adjust < 0)
       -- if the action is being performed on a NPC (not a player character), it's multiplied by this amount
       NpcModifier = 0.1
   },
 ]]
 KarmaActions = {}
 require 'globals.static_data.karma.negative_actions'
 require 'globals.static_data.karma.positive_actions'

NegativeActions

 welcome this will discuss the contents of the globals\static_data\karma\negative_actions.lua
 this documentation is correct as of version 6.0 PRECB1 Release 

 --[[
   Negative actions, obviously, are only applicable to 'bad' player choices.
   (ie LootContainer should never apply when looting something from your bank or a mob you killed for example, but should apply if the thing you're looting has positive Karma)
 ]]
 KarmaActions.Negative = {
   Attack = {
       Adjust = -50,
       NpcModifier = 0.5,
   },
   -- This applies to looting players bodies as well as anything with ObjVar Karma
   LootContainer = {
       Adjust = -10,
       NpcModifier = 0.25,
   },
   LootUnownedKill = {
       Adjust = -25,
       NpcModifier = 1, -- set to 1 to negate the effects of this modifer for this action since this only applies to npcs (corpses n such)
   },
   Murder = {
       Adjust = -500,
       NpcModifier = 0.1,
   },
   -- applies when a player performs a benefical act on a karma level set to punish beneficial acts
   PunishForBeneficial = {
       Adjust = -50,
       NpcModifier = 0.1,
   },
 }

PositiveActions

 welcome this will discuss the contents of the globals\static_data\karma\positive_actions.lua
 this documentation is correct as of version 6.0 PRECB1 Release 

Karma Functions

 welcome this will discuss the contents of the globals\helpers\karma.lua
 this documentation is correct as of version 6.0 PRECB1 Release 

AdjustKarma

 -- Adjust the karma for a mobile, a system message pertaining to the change is sent.
 -- @param mobile(mobileObj)
 -- @param amount(number) The amount to adjust by.
 -- @return none
 function AdjustKarma(mobile, amount)
   if ( amount == 0 or amount == nil ) then return end
   local karma = GetKarma(mobile)
   -- cache the karma level so we can check if it changed.
   local karmaLevel = GetKarmaLevel(karma)
   karma = math.floor(karma + amount + 0.5)
   SetKarma(mobile, karma)
   if ( amount > 0 ) then
       mobile:SystemMessage("You have gained"..GetKarmaStringAmount(amount).."karma.", "info")
   else
       mobile:SystemMessage("You have lost"..GetKarmaStringAmount(math.abs(amount)).."karma.", "info")
   end
   if ( karmaLevel.Name ~= GetKarmaLevel(karma).Name ) then
       -- Karma was changed, update the name and whatever else, maybe a message?
       mobile:SendMessage("UpdateName")
   end
 end

AlterKarmaAction

 -- convenience function to alter a karma action's amount before applying it
 -- @param karmaAction Lua table karma action
 -- @param newAmount(number)
 -- @return the altered karma action
 function AlterKarmaAction(karmaAction, newAmount)
   karmaAction = deepcopy(karmaAction)
   karmaAction.Adjust = newAmount
   return karmaAction
 end

CheckKarmaBeneficialAction

 -- Check when a player performs a beneficial action on a mobile(player/npc/etc)
 -- @param player(playerObj) DOES NOT ENFORCE IsPlayerCharacter()
 -- @param mobile(mobileObj) Anything with a karma level
 -- @return none
 function CheckKarmaBeneficialAction(player, mobileB)
   -- beneficial actions are only bad against some
   local karmaLevelB = GetKarmaLevel(GetKarma(mobileB))
   local isPlayerB = IsPlayerCharacter(mobileB)
   if (
       (isPlayerB and karmaLevelB.PunishBeneficialToPlayer)
       or
       (not isPlayerB and karmaLevelB.PunishBeneficialToNPC)
   ) then
       ExecuteKarmaAction(player, KarmaActions.Negative.PunishForBeneficial, mobileB)
   end
   if ( isPlayerB ) then
       -- when players benefit players, they inherit their aggressive conflict relations.
       InheritAggressivePlayerConflicts(mobileB, player)
   end
 end

CheckKarmaLoot

 -- Check player against a container on a loot (item removed from a container)
 -- @param player(playerObj) Player doing the looting
 -- @param container(Container) Container being looted from (mobileB:TopmostContainer())
 -- @return none (executes karma actions internally)
 function CheckKarmaLoot(player, container)
   if ( container == nil ) then return end
   -- can't get in trouble doing stuff to yourself..
   if ( player == container ) then return end
   -- only mobiles supported right now
   if not( container:IsMobile() ) then return end
   local containerOwner = nil
   -- re-assign the container to the owner if applicable.
   if ( container:GetCreationTemplateId() == "player_corpse" ) then
       containerOwner = container:GetObjVar("BackpackOwner")
   else
       if ( IsPet(container) ) then containerOwner = container:GetObjectOwner() or container end
   end
   -- can't get in trouble doing stuff to yourself..
   if ( containerOwner ~= nil and player == containerOwner ) then return end
   local isPlayer = false
   if ( containerOwner ~= nil ) then
       isPlayer = IsPlayerCharacter(containerOwner)
   else
       isPlayer = IsPlayerCharacter(container)
   end
   if ( isPlayer ) then
       -- TODO: It's ok to loot players in your group?
       -- if looting a player owned container that's not theirs, advance the conflict relation with no warning, no karma action
       AdvanceConflictRelation(player, containerOwner, true, KarmaActions.Negative.LootContainer)
   else
       -- explicitly check for false, since nil is returned in the event the container doesn't have conflicts
       -- and we don't care to punish anyone for looting something without conflict (like a chest)
       if ( IsMobTaggedBy(container, player) == false ) then
           -- the container is not tagged by the player, therefor the player doesn't have the right to loot.
           ExecuteKarmaAction(player, KarmaActions.Negative.LootUnownedKill)
           return
       end
   end
   -- at this point we just execute a LootContainer action. Karma system should take care of the rest
   ExecuteKarmaAction(player, KarmaActions.Negative.LootContainer, container)
 end

ColorizeMobileName

 -- Colorize a mobile name dependant on their karma
 -- @param mobile(mobileObj) the mobile used to decide the name color
 -- @param newName(string) The name to be colorized
 -- @return colorized newName(string)
 function ColorizeMobileName(mobile, newName)
   if ( mobile == nil ) then
       LuaDebugCallStack("[Karma] Nil mobile supplied to ColorizeMobileName")
       return newName
   end
   local color = nil
   if ( mobile:GetObjVar("ImportantNPC") ~= nil ) then
       color = "F2F5A9"
   else
       color = GetKarmaLevel(GetKarma(mobile)).NameColor
   end
   return "[" .. color .. "]" .. newName .. "[-]"
 end

ColorizePlayerName

 -- Colorize a player's name dependant on their karma
 -- @param player(mobileObj) the playerObj used to decide the name color
 -- @param newName(string) The name to be colorized
 -- @return colorized newName(string)
 function ColorizePlayerName(player, newName)
   if ( player == nil ) then
       LuaDebugCallStack("[Karma] Nil player supplied to ColorizePlayerName")
       return newName
   end
   local color = player:GetObjVar("NameColorOverride")
   if ( color == nil ) then
       if ( IsImmortal(player) and not player:GetObjVar("TestMortal") ) then color = "FFBF00" end
   end
   if ( color == nil ) then
       color = GetKarmaLevel(GetKarma(player)).NameColor
   end
   return "[" .. color .. "]" .. newName .. "[-]"
 end

DailyLogin

 -- Perform the positive karma action Daily Login with checks
 -- @param player(playerObj)
 -- @return none
 function DailyLogin(player)
   local lastDailyLoginReward = player:GetObjVar("KarmaDaily")
   local now = DateTime.UtcNow
   if ( lastDailyLoginReward == nil or lastDailyLoginReward + ServerSettings.Karma.DailyLoginInterval < now ) then
       player:SetObjVar("KarmaDaily", now)
       ExecuteKarmaAction(player, KarmaActions.Positive.DailyLogin)
   end
 end

ExectueKarmaAction

 -- Perform the actual karma handling for a karma action, will adjust mobileA's karma appropriately(if at all) for the given action.
 -- @param mobileA the mobile that is performing the karma action
 -- @param karmaAction(table) The KarmaActions.Positive/KarmaActions.Negative for the action.
 -- @param mobileB(mobileObj)(optional) The mobile that the karma action is being performed on(if any)
 -- @return none
 function ExecuteKarmaAction(mobileA, action, mobileB)
   if ( mobileA == nil ) then
       LuaDebugCallStack("[Karma] Nil mobileA provided, need at least one mobile for a karma action.")
       return
   end
   if ( action == nil ) then
       LuaDebugCallStack("[Karma] Nil action supplied.")
       return
   end
   if ( action.Adjust == nil ) then
       LuaDebugCallStack("[Karma] Supplied action without 'Adjust', this karma action is invalid until resolved.")
       return
   end
   -- if mobileB is passed, ensure it's in the same region.
   if ( mobileB ~= nil and not mobileB:IsValid() ) then return end
   -- karma adjustments can only be applied to players
   if not( IsPlayerCharacter(mobileA) ) then return end
   local karmaA = GetKarma(mobileA)
   -- if they cannot be affected by this karma action any further, stop here.
   if ( action.Adjust < 0 ) then
       if ( action.UpTo and karmaA < action.UpTo ) then return end
   else
       if ( action.UpTo and karmaA > action.UpTo ) then return end
   end
   -- ensure they are both within the karma area.
   if ( not WithinKarmaArea(mobileA) and (mobileB ~= nil and not WithinKarmaArea(mobileB)) ) then
       return
   end
   local isPlayerB = false
   local karmaB = 
   -- handle special karma cases
   if ( mobileB ~= nil ) then
       isPlayerB = IsPlayerCharacter(mobileB)
        -- important npcs are off the charts.
       if ( not isPlayerB and mobileB:HasObjVar("ImportantNPC") ) then karmaB = 1000000 else
           karmaB = GetKarma(mobileB)
       end
   end
   local conflictMod = 1
   local pvpMod = 1
   local negativeAdjustMod = 1
   local npcMod = 1
   -- players in same guilds/opposing factions never effect each other's karma
   if ( isPlayerB ) then
       local guildA = mobileA:GetObjVar("Guild")
       if ( guildA ~= nil ) then
           local guildB = mobileB:GetObjVar("Guild")
           -- they share the same guild, end here
           if ( guildA == guildB ) then return end
           if ( guildB ~= nil and guildA ~= guildB ) then
               -- determined the two involved are both definitely in guilds, and they are not the same guild,
               -- let's compare their factions.
               local guildRecordA = Guild.GetGuildRecord(guildA)
               if ( guildRecordA ~= nil and guildRecordA.PatronGod ~= nil ) then
                   local guildRecordB = Guild.GetGuildRecord(guildB)
                   if ( guildRecordB ~= nil and guildRecordB.PatronGod ~= nil and guildRecordA.PatronGod ~= guildRecordB.PatronGod ) then
                       -- officially determined that playerA and playerB are both in factions, and their factions are not the same.
                       return
                   end
               end
           end
       end
   end
   -- handle negative karma actions
   if ( action.Adjust < 0 ) then
       local karmaALevel = nil
       if ( mobileB ~= nil ) then
           local karmaBLevel = GetKarmaLevel(karmaB)
           -- if guards don't protect them, they are free to perform negative actions on.
           -- the guards are the metaphorical judge/jury/(literal)executioner
           if ( (not isPlayerB and karmaBLevel.GuardProtectNPC ~= true) or (isPlayerB and karmaBLevel.GuardProtectPlayer ~= true) ) then return end
           if ( isPlayerB ) then
               --determine their player vs player modifiers
               karmaALevel = GetKarmaLevel(karmaA)
               karmaBLevel = karmaBLevel or GetKarmaLevel(karmaB)
               pvpMod = karmaALevel.PvPMods[karmaBLevel.Name]
           else
               if ( action.NpcModifier ~= nil ) then
                   -- if mobileB is not a player, modify as such
                   npcMod = action.NpcModifier
               end
           end
           -- get conflict mod
           if ( pvpMod > 0 ) then
               if ( ConflictEquals(GetConflictRelation(mobileB, mobileA), ConflictRelations.Aggressor) ) then
                   local aToBRelation = GetConflictRelation(mobileA, mobileB)
                   if (
                       ConflictEquals(aToBRelation, ConflictRelations.Defender) 
                       or
                       ConflictEquals(aToBRelation, ConflictRelations.Victim)
                   ) then
                       --victim/defender karma action against aggressor, this is free.
                       conflictMod = 0
                   end
               end
           end
       end      
       if ( pvpMod > 0 and conflictMod > 0 ) then
           -- determine the negative karma adjust mod
           karmaALevel = karmaALevel or GetKarmaLevel(karmaA)
           negativeAdjustMod = karmaALevel.NegativeKarmaAdjustMod
       end
   else
       -- handle positive karma actions
   end
   --[[
   DebugMessage("--Execute Karma Action--")
   DebugMessage("Adjust: "..action.Adjust)
   DebugMessage("conMod: "..conflictMod)
   DebugMessage("pvpMod: "..pvpMod)
   DebugMessage("negMod: "..negativeAdjustMod)
   DebugMessage("npcMod: "..npcMod)
   ]]
   local adjust = action.Adjust * conflictMod * pvpMod * negativeAdjustMod * npcMod
   -- prevent positive actions from going negative
   if ( action.Adjust > 0 and adjust < 0 ) then return end
   -- prevent negative actions from going positive
   if ( action.Adjust < 0 and adjust > 0 ) then return end
   -- finally apply all the calculated karma
   AdjustKarma(mobileA, adjust)
 end

GetKarma

 -- Get the Karma for a mobile
 -- @param mobile(mobileObj)
 -- @return Amount of Karma the mobile has(number), 0 if Karma is not set
 function GetKarma(mobile)
   if ( mobile == nil ) then
       LuaDebugCallStack("[Karma] Nil mobile provided to GetKarma()")
       return
   end
   return mobile:GetObjVar("Karma") or 0
 end

GetKarmaLevel

 -- Get the karma level data table for the given amount of karma.
 -- @param karma(number) return value of GetKarma()
 -- @return KarmaLevelData(luaTable) from ServerSettings.Karma.Levels
 function GetKarmaLevel(karma)
   karma = karma or 0
   if ( karma >= 0 ) then
       for i,data in pairs(ServerSettings.Karma.Levels) do
           if ( karma >= data.Amount ) then return data end
       end
   elseif ( karma < 0 ) then
       --negative karma we check the list backwards
       local ii = #ServerSettings.Karma.Levels
       for i,data in pairs(ServerSettings.Karma.Levels) do
           if ( ServerSettings.Karma.Levels[ii].Amount >= karma ) then
               return ServerSettings.Karma.Levels[ii]
           end
           ii = ii - 1
       end
   end
 end

GetKarmaStringAmount

 -- Convert an amount of karma gained/lost to a string representation
 -- @param amount(number)
 -- @return string amount
 function GetKarmaStringAmount(amount)
   if ( amount < 10 ) then
       return " a little "
   elseif ( amount < 50 ) then
       return " some "
   elseif ( amount < 100 ) then
       return " "
   elseif ( amount < 1000 ) then
       return " a lot of "
   else
       return " a substantial amount of "
   end
 end

KarmaPunishAllAggressorsForMurder

 -- Anyone that is an aggressor to the victim will have a Murder Karma action executed on them.
 -- @param victim(mobileObj)
 -- @return none
 function KarmaPunishAllAggressorsForMurder(victim)
   ForeachAggressor(victim, 
       function(aggressor)
           if ( aggressor:IsValid() ) then
               ExecuteKarmaAction(aggressor, KarmaActions.Negative.Murder, victim)
           end
       end)
 end

SetKarma

 -- Set a mobiles karma to an exact amount, does not adjust.
 -- @param mobile(mobileObj)
 -- @param amount(number) The exact number to set their Karma too
 -- @return none
 function SetKarma(mobile, amount)
   if ( mobile == nil ) then
       LuaDebugCallStack("[Karma] Nil mobile provided to SetKarma()")
       return
   end
   mobile:SetObjVar("Karma", amount)
 end

WithinKarmaArea

 -- Determine if a mobile is in a location that karma matters
 -- @param mobileObj
 -- @return true/false
 function WithinKarmaArea(mobileObj)
   for i,j in pairs(ServerSettings.Karma.DisableKarmaZones) do
       if ( mobileObj:IsInRegion(j) ) then return false end
   end
   local regionAddress = GetRegionAddress()
   for i,disabledRegionAddress in pairs(ServerSettings.Karma.DisableKarmaRegionAddresses) do
       if ( regionAddress == disabledRegionAddress ) then return false end
   end
   return true
 end