KarmaSystem
Contents
- 1 Karma Actions
- 2 Karma Functions
- 2.1 AdjustKarma
- 2.2 AlterKarmaAction
- 2.3 CheckKarmaBeneficialAction
- 2.4 CheckKarmaLoot
- 2.5 ColorizeMobileName
- 2.6 ColorizePlayerName
- 2.7 DailyLogin
- 2.8 ExectueKarmaAction
- 2.9 GetKarma
- 2.10 GetKarmaLevel
- 2.11 GetKarmaStringAmount
- 2.12 KarmaPunishAllAggressorsForMurder
- 2.13 SetKarma
- 2.14 WithinKarmaArea
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
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