Difference between revisions of "ConflictSystem"
(→GetConflictTable) |
(→ValidConflictRelationTable) |
||
(51 intermediate revisions by the same user not shown) | |||
Line 1: | Line 1: | ||
− | welcome this will discuss the contents of the globals\helpers\conflict.lua | + | '' |
+ | welcome this will discuss the contents of the globals\helpers\conflict.lua | ||
+ | this documentation is correct as of version 6.0 PRECB1 Release | ||
+ | '' | ||
+ | ''this documentation was added by Gizmo'' | ||
+ | == ConflictRelations Table == | ||
+ | '' | ||
+ | ConflictRelations = { | ||
+ | Warning = { | ||
+ | Name = "Warning", | ||
+ | Warning = true, | ||
+ | }, | ||
+ | BeenWarned = { | ||
+ | Name = "BeenWarned", | ||
+ | Warning = true, | ||
+ | }, | ||
+ | Aggressor = { | ||
+ | Name = "Aggressor", | ||
+ | }, | ||
+ | Victim = { | ||
+ | Name = "Victim" | ||
+ | }, | ||
+ | Defender = { | ||
+ | Name = "Defender" | ||
+ | } | ||
+ | } | ||
+ | '' | ||
− | |||
== Conflict Functions == | == 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | base_mobile || HandleApplyDamage(damager, damageAmount, damageType, isCrit, wasBlocked) | ||
+ | |- | ||
+ | | globals\helpers\karma|| CheckKarmaLoot(player, container) | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | base_mobile || DoResurrect(statPct, resurrector, force) | ||
+ | |- | ||
+ | | base_player_death|| RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict || | ||
+ | UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) | ||
+ | ConflictEquals(strRelation, tableRelation) | ||
+ | AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) | ||
+ | ForeachAggressor(mobile, callback) | ||
+ | IsAggressor(mobile, guardIgnore) | ||
+ | InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | InitializeClientConflicts(mobile) | ||
+ | |- | ||
+ | | globals\helpers\karma|| ExecuteKarmaAction(mobileA, action, mobileB) | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals/helpers/karma|| KarmaPunishAllAggressorsForMurder(victim) | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | base_mobile || DoMobileDeath(damager) | ||
+ | |- | ||
+ | | base_player_death|| RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict || | ||
+ | GetConflictRelation(mobileA, mobileB, mobileAConflictTable) | ||
+ | AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) | ||
+ | ForeachAggressor(mobile, callback) | ||
+ | IsAggressor(mobile, guardIgnore) | ||
+ | InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | InitializeClientConflicts(mobile) | ||
+ | |- | ||
+ | | globals\helpers\karma|| RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) | ||
+ | |} | ||
+ | |||
=== GetConflictTable === | === GetConflictTable === | ||
'' | '' | ||
− | Get the conflict table for a mobile | + | -- Get the conflict table for a mobile |
− | @param mobile(mobileObj) | + | -- @param mobile(mobileObj) |
− | @return luaTable containing all conflicts for this mobile | + | -- @return luaTable containing all conflicts for this mobile |
function GetConflictTable(mobile) | function GetConflictTable(mobile) | ||
Line 17: | Line 271: | ||
end | end | ||
'' | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals/helpers/conflict|| This is used as a tool throughout the conflict file. You will find it used in the FreezContactTable, ValidConflictRelationTable, ForeachAggressor, IsAggressor, InheritAggressivePlayerConflicts and InitializeClientConflicts functions. | ||
+ | |} | ||
+ | |||
+ | === GetNearbyTaggedMobiles === | ||
+ | '' | ||
+ | -- Get all mobiles that are tagged to a mobile and are also nearby | ||
+ | -- @param mobile | ||
+ | -- @return list of nearby mobiles (luaArray) | ||
+ | |||
+ | function GetNearbyTaggedMobiles(mobile) | ||
+ | local validMobiles = {} | ||
+ | if ( mobile ) then | ||
+ | local taggedMobiles = mobile:GetObjVar("Tag") or {} | ||
+ | local loc = mobile:GetLoc() | ||
+ | for id,val in pairs(taggedMobiles) do | ||
+ | local obj = GameObj(id) | ||
+ | if ( obj ~= nil and obj:IsValid() and obj:GetLoc():Distance(loc) <= ServerSettings.Misc.GroupAutolootRange ) then | ||
+ | table.insert(validMobiles, obj) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | return validMobiles | ||
+ | end | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\mobile_extensions_misc|| HandleMobileDeathRewards(victim, karmaLevel) | ||
+ | |} | ||
+ | |||
+ | === InheritAggressivePlayerConflicts === | ||
+ | '' | ||
+ | -- Inherit Aggressor/Defender conflict relations from playerA onto playerB, DOES NOT ENFORCE IsPlayerCharacter() | ||
+ | -- @param playerA(playerObj) The player whos conflicts are being inherited | ||
+ | -- @param playerB(playerObj) The player who is inheriting the conflicts | ||
+ | -- @return none | ||
+ | |||
+ | function InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | if ( playerA == playerB ) then return end | ||
+ | local conflictsA = GetConflictTable(playerA) | ||
+ | -- loop all of playerA's conflicts | ||
+ | for mobileC,aToCRelationTable in pairs(conflictsA) do | ||
+ | if ( mobileC ~= playerB and mobileC:IsValid() ) then | ||
+ | -- using this variable to keep the if nesting from going too deep | ||
+ | local keepgoing = false | ||
+ | -- if A to C is aggressor | ||
+ | if ( | ||
+ | ConflictEquals(aToCRelationTable[1], ConflictRelations.Aggressor) | ||
+ | and | ||
+ | ValidConflictRelationTable(aToCRelationTable) | ||
+ | ) then | ||
+ | local cToARelation = GetConflictRelation(mobileC, playerA) | ||
+ | -- and C to A is Victim/Defender | ||
+ | if ( | ||
+ | cToARelation ~= nil | ||
+ | and | ||
+ | ( | ||
+ | ConflictEquals(cToARelation, ConflictRelations.Victim) | ||
+ | or | ||
+ | ConflictEquals(cToARelation, ConflictRelations.Defender) | ||
+ | ) | ||
+ | ) then | ||
+ | keepgoing = true | ||
+ | end | ||
+ | end | ||
+ | if ( keepgoing ) then | ||
+ | local alreadyAggressive = false | ||
+ | local bToCRelation = GetConflictRelation(playerB, mobileC) | ||
+ | -- if B to C is aggressor | ||
+ | if ( | ||
+ | bToCRelation ~= nil | ||
+ | and | ||
+ | ConflictEquals(bToCRelation, ConflictRelations.Aggressor) | ||
+ | ) then | ||
+ | local cToBRelation = GetConflictRelation(mobileC, playerB) | ||
+ | -- and C to B is Victim/Defender | ||
+ | if ( | ||
+ | cToBRelation ~= nil | ||
+ | and | ||
+ | ( | ||
+ | ConflictEquals(cToBRelation, ConflictRelations.Victim) | ||
+ | or | ||
+ | ConflictEquals(cToBRelation, ConflictRelations.Defender) | ||
+ | ) | ||
+ | ) then | ||
+ | alreadyAggressive = true | ||
+ | end | ||
+ | end | ||
+ | if ( alreadyAggressive ) then | ||
+ | -- refresh it | ||
+ | UpdateConflictRelation(playerB, mobileC, conflictsB[mobileC], true) | ||
+ | UpdateConflictRelation(mobileC, playerB, cRelationTable[mobileB]) | ||
+ | else -- otherwise | ||
+ | -- inherit it. | ||
+ | -- set playerB's relation: aggressor to mobileC. | ||
+ | UpdateConflictRelation(playerB, mobileC, ConflictRelations.Aggressor, true) | ||
+ | -- and set mobileC's relation: victim to playerB | ||
+ | UpdateConflictRelation(mobileC, playerB, ConflictRelations.Victim) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | |- | ||
+ | | globals\helpers\karma|| CheckKarmaBeneficialAction(player, mobileB) | ||
+ | |} | ||
+ | |||
+ | === InitializeClientConflicts === | ||
+ | '' | ||
+ | -- Refresh the client with conflict status on login/region change | ||
+ | -- @param mobile | ||
+ | |||
+ | function InitializeClientConflicts(mobile) | ||
+ | local conflictTable = GetConflictTable(mobile) | ||
+ | for mobileB,relation in pairs(conflictTable) do | ||
+ | if ( mobileB:IsValid() ) then -- if the mobile is in same region currently. | ||
+ | local bToARelation = GetConflictRelation(mobileB, mobile) -- if they have a valid relation with us | ||
+ | if ( bToARelation ~= nil ) then | ||
+ | if (( | ||
+ | ConflictEquals(relation[1], ConflictRelations.Victim) | ||
+ | or | ||
+ | ConflictEquals(relation[1], ConflictRelations.Defender) | ||
+ | ) | ||
+ | and | ||
+ | ConflictEquals(bToARelation, ConflictRelations.Aggressor) | ||
+ | and | ||
+ | ValidConflictRelationTable(relation) | ||
+ | ) then | ||
+ | local timeRemaining = relation[2] - DateTime.UtcNow | ||
+ | mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Aggressed",timeRemaining.TotalSeconds}) | ||
+ | if ( IsPlayerCharacter(mobileB) ) then | ||
+ | mobileB:SendClientMessage("UpdateMobileConflictStatus",{mobile,"Aggressor",timeRemaining.TotalSeconds}) | ||
+ | end | ||
+ | elseif( | ||
+ | ConflictEquals(relation[1], ConflictRelations.Aggressor) | ||
+ | and | ||
+ | ( | ||
+ | ConflictEquals(bToARelation, ConflictRelations.Victim) | ||
+ | or | ||
+ | ConflictEquals(bToARelation, ConflictRelations.Defender) | ||
+ | ) | ||
+ | and | ||
+ | ValidConflictRelationTable(relation) | ||
+ | ) then | ||
+ | local timeRemaining = relation[2] - DateTime.UtcNow | ||
+ | mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Aggressor",timeRemaining.TotalSeconds}) | ||
+ | if ( IsPlayerCharacter(mobileB) ) then | ||
+ | mobileB:SendClientMessage("UpdateMobileConflictStatus",{mobile,"Aggressed",timeRemaining.TotalSeconds}) | ||
+ | end | ||
+ | elseif( | ||
+ | ConflictEquals(relation[1], ConflictRelations.Warning) | ||
+ | and | ||
+ | ConflictEquals(bToARelation, ConflictRelations.BeenWarned) | ||
+ | and | ||
+ | ValidConflictRelationTable(relation) | ||
+ | ) then | ||
+ | local timeRemaining = relation[2] - DateTime.UtcNow | ||
+ | mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Warning",timeRemaining.TotalSeconds}) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| | ||
+ | InitializeClientConflicts(mobile) | ||
+ | ClearConflictTable(mobile, isPlayer) | ||
+ | |- | ||
+ | | player|| CallFunctionDelayed(TimeSpan.FromSeconds(0.5), | ||
+ | |} | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
=== IsAggressor === | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | ai_guard|| IsEnemy(targetObj) | ||
+ | |- | ||
+ | | ai_super_guard|| IsEnemy(targetObj) | ||
+ | |- | ||
+ | | globals\helpers\conflict|| IsAggressor(mobile, guardIgnore) | ||
+ | |- | ||
+ | | player|| UpdateFactions() *note: the code is commented out though | ||
+ | |- | ||
+ | | sp_resurrect_effect|| HandleLoaded() *note: the code is commented out though | ||
+ | |} | ||
+ | |||
=== IsMobTaggedBy === | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| IsMobTaggedBy(mobile, player) | ||
+ | |- | ||
+ | | globals\helpers\karma|| CheckKarmaLoot(player, container) | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| | ||
+ | SetConflictTable(mobile, data) | ||
+ | FreezeConflictTable(mobile, target) | ||
+ | UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) | ||
+ | |- | ||
+ | |} | ||
+ | |||
=== TagMob === | === TagMob === | ||
− | === InheritAggressivePlayerConflicts === | + | '' |
− | === | + | -- Uses the damage list to determine what Single Player/Collective Group did the most damage and sets the ObjVar Tag with the mobiles that fit the bill |
− | === | + | -- @param mobile(mobileObj) the NPC that has died |
+ | |||
+ | function TagMob(mobile) | ||
+ | local damagers = mobile:GetObjVar("Damagers") | ||
+ | if ( damagers ~= nil ) then | ||
+ | -- get a list of all groups/solos involved in all the damage | ||
+ | local groups = {} | ||
+ | local solos = {} | ||
+ | for damager,data in pairs(damagers) do | ||
+ | if ( damager ~= nil and damager:IsValid() ) then | ||
+ | local gid = damager:GetObjVar("Group") | ||
+ | if ( gid ~= nil ) then | ||
+ | if ( groups[gid] == nil ) then groups[gid] = {} end | ||
+ | table.insert(groups[gid], {damager,data.Amount}) | ||
+ | else | ||
+ | -- we have to set it as damager.Id because guilds keep track of members by their Id | ||
+ | table.insert(solos, {damager.Id,data.Amount}) | ||
+ | end | ||
+ | end | ||
+ | end | ||
+ | -- calculate the damage for each group | ||
+ | local groupsDamage = {} | ||
+ | for gid,data in pairs(groups) do | ||
+ | for i,d in pairs(groups[gid]) do | ||
+ | if ( groupsDamage[gid] == nil ) then groupsDamage[gid] = 0 end | ||
+ | groupsDamage[gid] = groupsDamage[gid] + d[2] | ||
+ | end | ||
+ | end | ||
+ | -- finally determine who's the winner | ||
+ | local most = {nil,0} | ||
+ | -- first check solos | ||
+ | for mobileId,data in pairs(solos) do | ||
+ | if ( data[2] > most[2] ) then | ||
+ | most = data | ||
+ | end | ||
+ | end | ||
+ | local isGroup = false | ||
+ | -- then check collective groups | ||
+ | for gid,amount in pairs(groupsDamage) do | ||
+ | if ( amount > most[2] ) then | ||
+ | most = {gid,amount} | ||
+ | -- solos were checked first | ||
+ | isGroup = true | ||
+ | end | ||
+ | end | ||
+ | local tag = {} | ||
+ | if ( isGroup ) then | ||
+ | -- add every mobile in the group that won | ||
+ | local groupRecord = GetGroupRecord(most[1]) or {} | ||
+ | groupRecord.Members = groupRecord.Members or {} | ||
+ | for id,data in pairs(groupRecord.Members) do | ||
+ | tag[id] = true | ||
+ | end | ||
+ | else | ||
+ | -- add the solo mobile that won | ||
+ | tag[most[1]] = true | ||
+ | end | ||
+ | mobile:SetObjVar("Tag", tag) | ||
+ | end | ||
+ | end | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | base_mobile|| DoMobileDeath(damager) | ||
+ | |- | ||
+ | | globals\helpers\conflict|| TagMob(mobile) | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| | ||
+ | UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) | ||
+ | AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) | ||
+ | InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | |- | ||
+ | |} | ||
+ | |||
+ | === 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 | ||
+ | '' | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! Used In File !! Function Used In | ||
+ | |- | ||
+ | | globals\helpers\conflict|| | ||
+ | ValidConflictRelationTable(conflictRelationTable) | ||
+ | ForeachAggressor(mobile, callback) | ||
+ | IsAggressor(mobile, guardIgnore) | ||
+ | InheritAggressivePlayerConflicts(playerA, playerB) | ||
+ | InitializeClientConflicts(mobile) | ||
+ | |||
+ | |- | ||
+ | |} | ||
+ | |||
+ | == Server Settings == | ||
+ | '' | ||
+ | welcome this will discuss the contents of the globals\server_settings\conflict.lua | ||
+ | this documentation is correct as of version 6.0 PRECB1 Release | ||
+ | '' | ||
+ | '' | ||
+ | ServerSettings.Conflict = { | ||
+ | -- Do guards attack non-guard protected karma levels who are aggressors? | ||
+ | GuardsKillAggressors = true, | ||
+ | -- The duration of the Aggressor/Victim/Defender relation. | ||
+ | -- These must be locked in the same timespan so we can do reverse checks. | ||
+ | -- For example: Determine all aggressors by checking who a mobile is a Victim/Defender to. | ||
+ | RelationDuration = TimeSpan.FromSeconds(100), | ||
+ | -- Does the first hit count as a warning? | ||
+ | -- If this is disabled, all hits will immediately jump to aggressor. | ||
+ | WarningEnabled = true, | ||
+ | WarningDuration = TimeSpan.FromSeconds(50), | ||
+ | } | ||
+ | '' | ||
+ | === Conflict Table === | ||
+ | {| class="wikitable" | ||
+ | |- | ||
+ | ! ServerSettings.Conflict Table !! Description | ||
+ | |- | ||
+ | | GuardsKillAggressors || true/false | ||
+ | -- Do guards attack non-guard protected karma levels who are aggressors? | ||
+ | |- | ||
+ | | RelationDuration || Duration of 100 seconds | ||
+ | |- | ||
+ | | WarningEnabled || true/false | ||
+ | -- Does the first hit count as a warning? | ||
+ | -- If this is disabled, all hits will immediately jump to aggressor. | ||
+ | |- | ||
+ | | WarningDuration || Duration of 50 seconds | ||
+ | |} |
Latest revision as of 07:02, 28 January 2018
welcome this will discuss the contents of the globals\helpers\conflict.lua this documentation is correct as of version 6.0 PRECB1 Release
this documentation was added by Gizmo
Contents
- 1 ConflictRelations Table
- 2 Conflict Functions
- 2.1 AdvanceConflictRelation
- 2.2 ClearConflictTable
- 2.3 ConflictEquals
- 2.4 ForeachAggressor
- 2.5 FreezeConflictTable
- 2.6 GetConflictRelation
- 2.7 GetConflictTable
- 2.8 GetNearbyTaggedMobiles
- 2.9 InheritAggressivePlayerConflicts
- 2.10 InitializeClientConflicts
- 2.11 IsAggressor
- 2.12 IsMobTaggedBy
- 2.13 SetConflictTable
- 2.14 TagMob
- 2.15 UpdateConflictRelation
- 2.16 ValidConflictRelationTable
- 3 Server Settings
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
Used In File | Function Used In |
---|---|
base_mobile | HandleApplyDamage(damager, damageAmount, damageType, isCrit, wasBlocked) |
globals\helpers\karma | CheckKarmaLoot(player, container) |
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
Used In File | Function Used In |
---|---|
base_mobile | DoResurrect(statPct, resurrector, force) |
base_player_death | RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) ConflictEquals(strRelation, tableRelation) AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) ForeachAggressor(mobile, callback) IsAggressor(mobile, guardIgnore) InheritAggressivePlayerConflicts(playerA, playerB) InitializeClientConflicts(mobile) |
globals\helpers\karma | ExecuteKarmaAction(mobileA, action, mobileB) |
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
Used In File | Function Used In |
---|---|
globals/helpers/karma | KarmaPunishAllAggressorsForMurder(victim) |
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
Used In File | Function Used In |
---|---|
base_mobile | DoMobileDeath(damager) |
base_player_death | RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
GetConflictRelation(mobileA, mobileB, mobileAConflictTable) AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) ForeachAggressor(mobile, callback) IsAggressor(mobile, guardIgnore) InheritAggressivePlayerConflicts(playerA, playerB) InitializeClientConflicts(mobile) |
globals\helpers\karma | RegisterEventHandler(EventType.CreatedObject, "created_corpse", function (success, objRef) |
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
Used In File | Function Used In |
---|---|
globals/helpers/conflict | This is used as a tool throughout the conflict file. You will find it used in the FreezContactTable, ValidConflictRelationTable, ForeachAggressor, IsAggressor, InheritAggressivePlayerConflicts and InitializeClientConflicts functions. |
GetNearbyTaggedMobiles
-- Get all mobiles that are tagged to a mobile and are also nearby -- @param mobile -- @return list of nearby mobiles (luaArray)
function GetNearbyTaggedMobiles(mobile) local validMobiles = {} if ( mobile ) then local taggedMobiles = mobile:GetObjVar("Tag") or {} local loc = mobile:GetLoc() for id,val in pairs(taggedMobiles) do local obj = GameObj(id) if ( obj ~= nil and obj:IsValid() and obj:GetLoc():Distance(loc) <= ServerSettings.Misc.GroupAutolootRange ) then table.insert(validMobiles, obj) end end end return validMobiles end
Used In File | Function Used In |
---|---|
globals\mobile_extensions_misc | HandleMobileDeathRewards(victim, karmaLevel) |
InheritAggressivePlayerConflicts
-- Inherit Aggressor/Defender conflict relations from playerA onto playerB, DOES NOT ENFORCE IsPlayerCharacter() -- @param playerA(playerObj) The player whos conflicts are being inherited -- @param playerB(playerObj) The player who is inheriting the conflicts -- @return none
function InheritAggressivePlayerConflicts(playerA, playerB) if ( playerA == playerB ) then return end local conflictsA = GetConflictTable(playerA) -- loop all of playerA's conflicts for mobileC,aToCRelationTable in pairs(conflictsA) do if ( mobileC ~= playerB and mobileC:IsValid() ) then -- using this variable to keep the if nesting from going too deep local keepgoing = false -- if A to C is aggressor if ( ConflictEquals(aToCRelationTable[1], ConflictRelations.Aggressor) and ValidConflictRelationTable(aToCRelationTable) ) then local cToARelation = GetConflictRelation(mobileC, playerA) -- and C to A is Victim/Defender if ( cToARelation ~= nil and ( ConflictEquals(cToARelation, ConflictRelations.Victim) or ConflictEquals(cToARelation, ConflictRelations.Defender) ) ) then keepgoing = true end end if ( keepgoing ) then local alreadyAggressive = false local bToCRelation = GetConflictRelation(playerB, mobileC) -- if B to C is aggressor if ( bToCRelation ~= nil and ConflictEquals(bToCRelation, ConflictRelations.Aggressor) ) then local cToBRelation = GetConflictRelation(mobileC, playerB) -- and C to B is Victim/Defender if ( cToBRelation ~= nil and ( ConflictEquals(cToBRelation, ConflictRelations.Victim) or ConflictEquals(cToBRelation, ConflictRelations.Defender) ) ) then alreadyAggressive = true end end if ( alreadyAggressive ) then -- refresh it UpdateConflictRelation(playerB, mobileC, conflictsB[mobileC], true) UpdateConflictRelation(mobileC, playerB, cRelationTable[mobileB]) else -- otherwise -- inherit it. -- set playerB's relation: aggressor to mobileC. UpdateConflictRelation(playerB, mobileC, ConflictRelations.Aggressor, true) -- and set mobileC's relation: victim to playerB UpdateConflictRelation(mobileC, playerB, ConflictRelations.Victim) end end end end end
Used In File | Function Used In |
---|---|
globals\helpers\conflict | InheritAggressivePlayerConflicts(playerA, playerB) |
globals\helpers\karma | CheckKarmaBeneficialAction(player, mobileB) |
InitializeClientConflicts
-- Refresh the client with conflict status on login/region change -- @param mobile
function InitializeClientConflicts(mobile) local conflictTable = GetConflictTable(mobile) for mobileB,relation in pairs(conflictTable) do if ( mobileB:IsValid() ) then -- if the mobile is in same region currently. local bToARelation = GetConflictRelation(mobileB, mobile) -- if they have a valid relation with us if ( bToARelation ~= nil ) then if (( ConflictEquals(relation[1], ConflictRelations.Victim) or ConflictEquals(relation[1], ConflictRelations.Defender) ) and ConflictEquals(bToARelation, ConflictRelations.Aggressor) and ValidConflictRelationTable(relation) ) then local timeRemaining = relation[2] - DateTime.UtcNow mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Aggressed",timeRemaining.TotalSeconds}) if ( IsPlayerCharacter(mobileB) ) then mobileB:SendClientMessage("UpdateMobileConflictStatus",{mobile,"Aggressor",timeRemaining.TotalSeconds}) end elseif( ConflictEquals(relation[1], ConflictRelations.Aggressor) and ( ConflictEquals(bToARelation, ConflictRelations.Victim) or ConflictEquals(bToARelation, ConflictRelations.Defender) ) and ValidConflictRelationTable(relation) ) then local timeRemaining = relation[2] - DateTime.UtcNow mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Aggressor",timeRemaining.TotalSeconds}) if ( IsPlayerCharacter(mobileB) ) then mobileB:SendClientMessage("UpdateMobileConflictStatus",{mobile,"Aggressed",timeRemaining.TotalSeconds}) end elseif( ConflictEquals(relation[1], ConflictRelations.Warning) and ConflictEquals(bToARelation, ConflictRelations.BeenWarned) and ValidConflictRelationTable(relation) ) then local timeRemaining = relation[2] - DateTime.UtcNow mobile:SendClientMessage("UpdateMobileConflictStatus",{mobileB,"Warning",timeRemaining.TotalSeconds}) end end end end end
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
InitializeClientConflicts(mobile) ClearConflictTable(mobile, isPlayer) |
player | CallFunctionDelayed(TimeSpan.FromSeconds(0.5), |
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
Used In File | Function Used In |
---|---|
ai_guard | IsEnemy(targetObj) |
ai_super_guard | IsEnemy(targetObj) |
globals\helpers\conflict | IsAggressor(mobile, guardIgnore) |
player | UpdateFactions() *note: the code is commented out though |
sp_resurrect_effect | HandleLoaded() *note: the code is commented out though |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict | IsMobTaggedBy(mobile, player) |
globals\helpers\karma | CheckKarmaLoot(player, container) |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
SetConflictTable(mobile, data) FreezeConflictTable(mobile, target) UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) |
TagMob
-- Uses the damage list to determine what Single Player/Collective Group did the most damage and sets the ObjVar Tag with the mobiles that fit the bill -- @param mobile(mobileObj) the NPC that has died
function TagMob(mobile) local damagers = mobile:GetObjVar("Damagers") if ( damagers ~= nil ) then -- get a list of all groups/solos involved in all the damage local groups = {} local solos = {} for damager,data in pairs(damagers) do if ( damager ~= nil and damager:IsValid() ) then local gid = damager:GetObjVar("Group") if ( gid ~= nil ) then if ( groups[gid] == nil ) then groups[gid] = {} end table.insert(groups[gid], {damager,data.Amount}) else -- we have to set it as damager.Id because guilds keep track of members by their Id table.insert(solos, {damager.Id,data.Amount}) end end end -- calculate the damage for each group local groupsDamage = {} for gid,data in pairs(groups) do for i,d in pairs(groups[gid]) do if ( groupsDamage[gid] == nil ) then groupsDamage[gid] = 0 end groupsDamage[gid] = groupsDamage[gid] + d[2] end end -- finally determine who's the winner local most = {nil,0} -- first check solos for mobileId,data in pairs(solos) do if ( data[2] > most[2] ) then most = data end end local isGroup = false -- then check collective groups for gid,amount in pairs(groupsDamage) do if ( amount > most[2] ) then most = {gid,amount} -- solos were checked first isGroup = true end end local tag = {} if ( isGroup ) then -- add every mobile in the group that won local groupRecord = GetGroupRecord(most[1]) or {} groupRecord.Members = groupRecord.Members or {} for id,data in pairs(groupRecord.Members) do tag[id] = true end else -- add the solo mobile that won tag[most[1]] = true end mobile:SetObjVar("Tag", tag) end end
Used In File | Function Used In |
---|---|
base_mobile | DoMobileDeath(damager) |
globals\helpers\conflict | TagMob(mobile) |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
UpdateConflictRelation(mobileA, mobileB, newRelation, guardCheck) AdvanceConflictRelation(mobileA, mobileB, noWarning, karmaAction) InheritAggressivePlayerConflicts(playerA, playerB) |
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
Used In File | Function Used In |
---|---|
globals\helpers\conflict |
ValidConflictRelationTable(conflictRelationTable) ForeachAggressor(mobile, callback) IsAggressor(mobile, guardIgnore) InheritAggressivePlayerConflicts(playerA, playerB) InitializeClientConflicts(mobile) |
Server Settings
welcome this will discuss the contents of the globals\server_settings\conflict.lua this documentation is correct as of version 6.0 PRECB1 Release
ServerSettings.Conflict = { -- Do guards attack non-guard protected karma levels who are aggressors? GuardsKillAggressors = true, -- The duration of the Aggressor/Victim/Defender relation. -- These must be locked in the same timespan so we can do reverse checks. -- For example: Determine all aggressors by checking who a mobile is a Victim/Defender to. RelationDuration = TimeSpan.FromSeconds(100), -- Does the first hit count as a warning? -- If this is disabled, all hits will immediately jump to aggressor. WarningEnabled = true, WarningDuration = TimeSpan.FromSeconds(50), }
Conflict Table
ServerSettings.Conflict Table | Description |
---|---|
GuardsKillAggressors | true/false
-- Do guards attack non-guard protected karma levels who are aggressors? |
RelationDuration | Duration of 100 seconds |
WarningEnabled | true/false
-- Does the first hit count as a warning? -- If this is disabled, all hits will immediately jump to aggressor. |
WarningDuration | Duration of 50 seconds |