ConflictSystem

From Legends of Aria Admin and Modding Wiki
Revision as of 08:41, 27 January 2018 by Gizmo (talk | contribs) (IsMobTaggedBy)
Jump to: navigation, search

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

ConflictRelations Table

 ConflictRelations = {
   Warning = {
       Name = "Warning",
       Warning = true,
   },
   BeenWarned = {
       Name = "BeenWarned",
       Warning = true,
   },
   Aggressor = {
       Name = "Aggressor",
   },
   Victim = {
       Name = "Victim"
   },
   Defender = {
       Name = "Defender"
   }
 }

Conflict Functions

AdvanceConflictRelation

 -- Advance mobileA against mobileB in conflict, will also refresh any current 
    conflicts between the two.
 -- @param mobileA(mobileObj)
 -- @param mobileB(mobileObj)
 -- @param noWarning(boolean)(optional) if true, warning will be skipped and they will 
    go directly to aggressor
 -- @param karmaAction(luaTable)(optional) KarmaAction that will be executed on mobileA 
    when they become an aggressor. (default: KarmaActions.Negative.Attack) Setting this 
    to false will not execute any karma actions.
 -- @return none
 function AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction)
   if not( karmaAction == false ) then
       karmaAction = karmaAction or KarmaActions.Negative.Attack
   end
   -- when Warnings are not enabled, we force noWarning to true.
   if ( ServerSettings.Conflict.WarningEnabled ~= true ) then
       noWarning = true
   end
   -- reassign mobileA and mobileB to pet owners, if applicable
   if ( IsPet(mobileA) ) then
       mobileA = mobileA:GetObjectOwner() or mobileA
   end
   if ( mobileB ~= nil and IsPet(mobileB) ) then
       mobileB = mobileB:GetObjectOwner() or mobileB
   end
   -- conflict don't care what you do to yourself or your pets
   if ( mobileA == mobileB ) then return end
   local aToBRelation = GetConflictRelation(mobileA, mobileB)
   local bToARelation = GetConflictRelation(mobileB, mobileA)
   local refreshA = true
   local refreshB = true
   -- when one side has a relation but the other side does not, we count neither side 
      as having a relation.
   -- this is so we can clear a conflict relation on either side and not need to clear 
      both involved parties (the other involved could be in a different region at time 
      of clearing!)
   if ( noWarning ~= true and (aToBRelation == nil or bToARelation == nil) ) then
       UpdateConflictRelation(mobileA, mobileB, ConflictRelations.Warning)
       UpdateConflictRelation(mobileB, mobileA, ConflictRelations.BeenWarned)
       refreshA = false
   elseif ( (noWarning == true and (aToBRelation == nil or bToARelation == nil))
       or ConflictEquals(aToBRelation, ConflictRelations.Warning) ) then
       -- A becomes aggressor, B becomes victim.
       UpdateConflictRelation(mobileA, mobileB, ConflictRelations.Aggressor, true)
       UpdateConflictRelation(mobileB, mobileA, ConflictRelations.Victim)
       refreshA = false
       refreshB = false
       -- Karma action for becoming the aggressor
       if ( karmaAction ) then
           ExecuteKarmaAction(mobileA, karmaAction, mobileB)
       end
       if ( IsInitiate(mobileB) and IsPlayerCharacter(mobileA) ) then 
           EndInitiate(mobileB) 
       end
       elseif ( ConflictEquals(aToBRelation, ConflictRelations.Victim) ) then
       -- A was a victim, now A is a defender.
       UpdateConflictRelation(mobileA, mobileB, ConflictRelations.Defender)
       refreshA = false
   end
   --refresh the conflict expiration.
   -- Possible optimization, only refresh every other one/every third one?
   if ( aToBRelation ~= nil and refreshA ) then
       if ( karmaAction and ConflictEquals(aToBRelation, ConflictRelations.Aggressor) ) 
         then
           ExecuteKarmaAction(mobileA, karmaAction, mobileB)
       end
       UpdateConflictRelation(mobileA, mobileB, ConflictRelations[aToBRelation], true)
   end
   if ( bToARelation ~= nil and refreshB ) then
       UpdateConflictRelation(mobileB, mobileA, ConflictRelations[bToARelation])
   end
 end

ClearConflictTable

 -- Clear the conflict table of a mobile
 -- @param mobile(mobileObj)
 -- @return none
 function ClearConflictTable(mobile, isPlayer)
   if ( mobile == nil or not mobile ) then
       LuaDebugCallStack("[Conflict] Invalid mobile provided.")
       return
   end
   if ( mobile:HasObjVar("Conflicts") ) then
       mobile:DelObjVar("Conflicts")
   end
   if ( isPlayer == true ) then
       InitializeClientConflicts(mobile)
   end
 end

ConflictEquals

 -- Convenience function to make checking conflicts more readable
 -- @param strRelation(string) The string Name of the relation to check for
 -- @param tableRelation(luaTable) The ConflictRelations entry to check against
 -- @return true or false
 function ConflictEquals(strRelation, tableRelation)
   return ( strRelation == tableRelation.Name )
 end

ForeachAggressor

 -- Perform a function on each aggressor of a mobile
 -- @param mobile(mobileObj)
 -- @param callback(function(id))
 -- @return none
 function ForeachAggressor(mobile, callback)
   local conflictTable = GetConflictTable(mobile)
   for mobileB,relation in pairs(conflictTable) do
       if ( mobileB:IsValid() ) then
           if (( ConflictEquals(relation[1], ConflictRelations.Victim) or ConflictEquals(relation[1], ConflictRelations.Defender )) and ValidConflictRelationTable(relation)) then
               if ( ConflictEquals(GetConflictRelation(mobileB, mobile), ConflictRelations.Aggressor) ) then
                   callback(mobileB)
               end
           end
       end
   end
 end

FreezeConflictTable

 --- Freeze the conflict table on a mobile, optionally saving the frozen table on a 
 different object. Will clear all conflicts for the mobile if target does not equal 
 mobile.
 -- @param mobile(mobileObj)
 -- @param target(gameObj)(optional) the gameObj the conflict table will be saved to
 -- @return none
 function FreezeConflictTable(mobile, target)
   target = target or mobile
   local conflictTable = GetConflictTable(mobile)
   for mobileId,conflict in pairs(conflictTable) do
       -- set all the expires to true, meaning they never expire
       conflictTable[mobileId][2] = true
   end
   -- save the frozen table on the target
   SetConflictTable(target, conflictTable)
 end

GetConflictRelation

 -- Get the relation of conflict mobileA is to mobileB.
 -- @param mobileA(mobileObj)
 -- @param mobileB(mobileObj)
 -- @param mobileAConflictTable(optional) return value from GetConflictTable()
 -- @return One of ConflictRelations, nil if not-found/expired.
 function GetConflictRelation(mobileA, mobileB, mobileAConflictTable)
   mobileAConflictTable = mobileAConflictTable or GetConflictTable(mobileA) or {}
   -- only valid, frozen/non-expired, will make the cut.
   if ( ValidConflictRelationTable(mobileAConflictTable[mobileB]) ) then
       return mobileAConflictTable[mobileB][1]
   end
   return nil
 end

GetConflictTable

 -- Get the conflict table for a mobile
 -- @param mobile(mobileObj)
 -- @return luaTable containing all conflicts for this mobile
 function GetConflictTable(mobile)
   if ( mobile == nil or not mobile ) then
       LuaDebugCallStack("[Conflict] Invalid mobile provided.")
       return {}
   end
   return mobile:GetObjVar("Conflicts") or {}
 end

IsAggressor

 --- Determine if a mobile is an aggressor
 -- @param mobile(mobileObj)
 -- @param guardIgnore(boolean)(optional) if true, ignore any relation that's tagged guard ignore. (this is so guards ignore when someone is an aggressor against an outcast for example)
 -- @return true if mobile is an aggressor
 function IsAggressor(mobile, guardIgnore)
   for mobileB,conflict in pairs(GetConflictTable(mobile)) do
       if ( 
           guardIgnore ~= true or 
           (guardIgnore == true) and 
           conflict[3] ~= true and 
           ConflictEquals(conflict[1], ConflictRelations.Aggressor) and 
           ValidConflictRelationTable(conflict)
          ) then
           if ( mobileB:IsValid() ) then
               local bToARelation = GetConflictRelation(mobileB, mobile)
               if ( bToARelation ~= nil ) then
                   if ( ConflictEquals(bToARelation, ConflictRelations.Victim) or ConflictEquals(bToARelation, ConflictRelations.Defender) ) then return true end
               end
           end
       end
   end
   return false
 end

SetConflictTable

 -- Set the conflict table for a mobile
 -- @param mobile(mobileObj)
 -- @param data(luaTable)
 -- @return none
 function SetConflictTable(mobile, data)
   mobile:SetObjVar("Conflicts", data)
 end

UpdateConflictRelation

 -- Update the conflict relation between two mobiles, also cleans mobileA's conflict 
    table of any expired.
 -- @param mobileA(mobileObj)
 -- @param mobileB(mobileObj)
 -- @param newRelation(luaTable) the entry from ConflictRelations
 -- @param guardCheck(boolean) If true, this update will check for guard protection, 
     this way you can refresh a conflict on both sides but only one will cares about 
     guards.
 -- @return none
 function UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck)
   if ( newRelation == nil ) then
       LuaDebugCallStack("[Conflict] Nil conflict relation table provided")
       return
   end
   if ( newRelation.Name == nil ) then
       LuaDebugCallStack("[Conflict] Invalid conflict relation table, missing Name")
       return
   end
   if ( mobileA == nil ) then
       LuaDebugCallStack("[Conflict] Nil mobileA provided.")
       return
   end
   if ( mobileB == nil ) then
       LuaDebugCallStack("[Conflict] Nil mobileB provided.")
       return
   end
   local tableA = GetConflictTable(mobileA)
   local wasGuardIgnored = false
   if ( not guardCheck and tableA[mobileB] ~= nil and tableA[mobileB][3] ) then
       wasGuardIgnored = true
   end
   -- set/update the conflict
   local now = DateTime.UtcNow
   tableA[mobileB] = {newRelation.Name}
   -- when the conflict should end
   if ( newRelation.Warning == true ) then
       tableA[mobileB][2] = now + ServerSettings.Conflict.WarningDuration
   else
       tableA[mobileB][2] = now + ServerSettings.Conflict.RelationDuration
   end
   -- cleanse table of any expired.
   for k,v in pairs(tableA) do
       if ( now >= v[2] ) then
           -- it's expired
           tableA[k] = nil
       end
   end
   if ( ConflictEquals(newRelation.Name, ConflictRelations.Aggressor) and 
        IsPlayerCharacter(mobileA) and IsPlayerCharacter(mobileB)) then
       -- mobileB has zero karma concequences against mobileA
       -- Turn mobileA AGGRESSIVE on mobileB's client (so B is looking at an aggressive 
       A)
       mobileA:SendClientMessage("UpdateMobileConflictStatus",
       {mobileB,"Aggressor",ServerSettings.Conflict.RelationDuration.TotalSeconds})
       mobileB:SendClientMessage("UpdateMobileConflictStatus",
       {mobileA,"Aggressed",ServerSettings.Conflict.RelationDuration.TotalSeconds})
       -- 100
   elseif ( ConflictEquals(newRelation.Name, ConflictRelations.Warning) and 
            IsPlayerCharacter(mobileA) and IsPlayerCharacter(mobileB) ) then
       -- mobileA is nearly an aggressor to mobileB
       -- Turn mobileB YELLOW on mobileA's client (so A is looking at a mobile they 
           know if they hit again they are an aggressor)
       mobileA:SendClientMessage("UpdateMobileConflictStatus",
       {mobileB,"Warning",ServerSettings.Conflict.WarningDuration.TotalSeconds})
   end
   --[[
       Handle Guard protection triggers for aggressive actions.
   ]]
   -- if mobileA's relation is aggressor
   if ( guardCheck and ConflictEquals(newRelation.Name, ConflictRelations.Aggressor) ) 
     then
       local karmaBLevel = GetKarmaLevel(GetKarma(mobileB))
       local guardIgnore = true
       -- if mobileB's karma level is guard protected
       if ( karmaBLevel.GuardProtectPlayer or karmaBLevel.GuardProtectNPC ) then
           local isPlayerB = IsPlayerCharacter(mobileB)
           if ( (isPlayerB and karmaBLevel.GuardProtectPlayer)
           or (not isPlayerB and karmaBLevel.GuardProtectNPC) ) then
               -- make the guards protect B from A
               GuardProtect(mobileB, mobileA)
               guardIgnore = false
           end
       end
       -- if guards don't protect mobileB's karma level, add an ignore guard entry
       -- (This is so, for example, players can be aggressors against an outcast, but 
          guards won't attack them for being aggressors against outcasts)
       if ( guardIgnore == true ) then
           tableA[mobileB][3] = true
       end
   end
   -- when skipping guard protect this value would be ignored and overwritten if not 
      cached from previous data.
   if ( wasGuardIgnored ) then
       tableA[mobileB][3] = true
   end
   -- save the updated conflict table for the mobile
   SetConflictTable(mobileA, tableA)
 end

ValidConflictRelationTable

 --- Validate a conflict relation table (make sure it's not expired)
 -- @param conflictRelationTable(luaTable) A single entry from return value 
    GetConflictTable()
 -- @return true if valid, false if not
 function ValidConflictRelationTable(conflictRelationTable)
   return (
       conflictRelationTable ~= nil
       and
       (
           -- frozen
           conflictRelationTable[2] == true
           or
           -- or non-expired
           DateTime.UtcNow < conflictRelationTable[2]
       )
   )
 end


IsMobTaggedBy

 -- Check to see if a mobile(npc) is tagged by a player
 -- @param mobile(mobileObj) DOES NOT force this to be an NPC ( but it should be )
 -- @param player(playerObj) DOES NOT force this to be an Player ( but it should be )
 -- @return true or false or nil (nil if the mobile had no conflicts)
 function IsMobTaggedBy(mobile, player)
   local tag = mobile:GetObjVar("Tag") or {}
   return ( player ~= nil and tag[player.Id] == true )
 end

TagMob

InheritAggressivePlayerConflicts

GetNearbyTaggedMobiles

InitializeClientConflicts