-- Singleton object for efficiently observing and keeping track of game state,
-- providing convenient functions and events.
local Watcher = {};
Thurallor.Utils.Watcher = Watcher;
local function DoSave()
--Puts("Saving...");
-- Workaround for Turbine localization bug.
local saveData = ExportTable(Watcher.settings);
Turbine.PluginData.Save(Turbine.DataScope.Account, "Thurallor_GameInfo", saveData, function()
--Puts("Save complete.");
end);
end
local startupTime = Turbine.Engine.GetGameTime();
local function SaveSettings()
if (not Watcher.settingsLoaded) then
if (Turbine.Engine.GetGameTime() - startupTime > 5) then
-- Settings file probably doesn't exist if it hasn't loaded in 5 seconds.
Watcher.settingsLoaded = true;
else
return;
end
end
-- Do the save operation at the next Update; thus aggregating multiple consecutive save requests.
if (not Watcher.saveRequested) then
Watcher.saveRequested = true;
Watcher.window:SetWantsUpdates(true);
end
end
local function DoDelayedCallback(_time, event, args)
if (not Watcher.delayedCallbacks) then
Watcher.delayedCallbacks = {};
end
table.insert(Watcher.delayedCallbacks, { time = _time, event = event, args = args } );
Watcher.window:SetWantsUpdates(true);
end
local function AddActorEffect(effectsObject, effect)
local categories, name = effect:GetCategory(), effect:GetName();
if (not effectsObject.cache[name]) then
effectsObject.cache[name] = 0;
end
effectsObject.cache[name] = effectsObject.cache[name] + 1;
if (not Watcher.settings.knownEffects[name]) then
-- This is an effect we have never seen before. Learn some info about it and save for future reference.
categories = effect:GetCategory();
Watcher.settings.knownEffects[name] = {
categories, effect:GetIcon(), effect:IsCurable(), effect:IsDebuff()};
local bits = categories;
if (not Watcher.AddCategories[bits]) then
-- This is a GetCategory value we have never seen before. Create functions for adding/removing.
local AddCategories = "local categories = ...; ";
local RemoveCategories = "local categories = ...; ";
if (bits >= 2048) then
-- Unknown bits; discard them.
bits = bits % 2048;
end
if (bits >= 1226) then
-- Turbine.Gameplay.EffectCategory.Dispellable is the OR of several bits
AddCategories = AddCategories .. "categories[1226] = categories[1226] + 1; ";
RemoveCategories = RemoveCategories .. "categories[1226] = categories[1226] - 1; ";
end
if (bits >= 1024) then
AddCategories = AddCategories .. "categories[1024] = categories[1024] + 1; ";
RemoveCategories = RemoveCategories .. "categories[1024] = categories[1024] - 1; ";
bits = bits - 1024;
end
if (bits >= 512) then
AddCategories = AddCategories .. "categories[512] = categories[512] + 1; ";
RemoveCategories = RemoveCategories .. "categories[512] = categories[512] - 1; ";
bits = bits - 512;
end
if (bits >= 256) then
AddCategories = AddCategories .. "categories[256] = categories[256] + 1; ";
RemoveCategories = RemoveCategories .. "categories[256] = categories[256] - 1; ";
bits = bits - 256;
end
if (bits >= 128) then
AddCategories = AddCategories .. "categories[128] = categories[128] + 1; ";
RemoveCategories = RemoveCategories .. "categories[128] = categories[128] - 1; ";
bits = bits - 128;
end
if (bits >= 64) then
AddCategories = AddCategories .. "categories[64] = categories[64] + 1; ";
RemoveCategories = RemoveCategories .. "categories[64] = categories[64] - 1; ";
bits = bits - 64;
end
if (bits >= 32) then
AddCategories = AddCategories .. "categories[32] = categories[32] + 1; ";
RemoveCategories = RemoveCategories .. "categories[32] = categories[32] - 1; ";
bits = bits - 32;
end
if (bits >= 16) then
AddCategories = AddCategories .. "categories[16] = categories[16] + 1; ";
RemoveCategories = RemoveCategories .. "categories[16] = categories[16] - 1; ";
bits = bits - 16;
end
if (bits >= 8) then
AddCategories = AddCategories .. "categories[8] = categories[8] + 1; ";
RemoveCategories = RemoveCategories .. "categories[8] = categories[8] - 1; ";
bits = bits - 8;
end
if (bits >= 4) then
AddCategories = AddCategories .. "categories[4] = categories[4] + 1; ";
RemoveCategories = RemoveCategories .. "categories[4] = categories[4] - 1; ";
bits = bits - 4;
end
if (bits >= 2) then
AddCategories = AddCategories .. "categories[2] = categories[2] + 1; ";
RemoveCategories = RemoveCategories .. "categories[2] = categories[2] - 1; ";
bits = bits - 2;
end
if (bits >= 1) then
AddCategories = AddCategories .. "categories[1] = categories[1] + 1; ";
RemoveCategories = RemoveCategories .. "categories[1] = categories[1] - 1; ";
end
Watcher.settings.funcs.AddCategories[categories] = AddCategories;
Watcher.AddCategories[categories] = loadstring(AddCategories);
Watcher.settings.funcs.RemoveCategories[categories] = RemoveCategories;
Watcher.RemoveCategories[categories] = loadstring(RemoveCategories);
end
SaveSettings();
else
categories = Watcher.settings.knownEffects[name][1];
end
Watcher.AddCategories[categories](effectsObject.activeCategories);
end
local function RemoveActorEffect(effectsObject, effect)
local categories, name = effect:GetCategory(), effect:GetName();
Watcher.RemoveCategories[categories](effectsObject.activeCategories);
effectsObject.cache[name] = effectsObject.cache[name] - 1;
if (effectsObject.cache[name] == 0) then
effectsObject.cache[name] = nil;
end
end
local function GetActorEffects(effectsObject)
effectsObject.cache = {};
effectsObject.activeCategories = {
[Turbine.Gameplay.EffectCategory.Dispellable] = 0;
[Turbine.Gameplay.EffectCategory.Corruption] = 0;
[Turbine.Gameplay.EffectCategory.Elemental] = 0;
[Turbine.Gameplay.EffectCategory.Tactical] = 0;
[Turbine.Gameplay.EffectCategory.Poison] = 0;
[Turbine.Gameplay.EffectCategory.Fear] = 0;
[Turbine.Gameplay.EffectCategory.Song] = 0;
[Turbine.Gameplay.EffectCategory.Cry] = 0;
[Turbine.Gameplay.EffectCategory.Wound] = 0;
[Turbine.Gameplay.EffectCategory.Physical] = 0;
[Turbine.Gameplay.EffectCategory.Disease] = 0;
[1] = 0;
};
for e = 1, effectsObject:GetCount(), 1 do
local effect = effectsObject:Get(e);
AddActorEffect(effectsObject, effect);
end
end
local function GetEquipment()
local eq = Watcher.playerEquipmentObject;
eq.cache = { byName = {}; bySlot = {}; };
for slot = 1, eq:GetSize() do
local item = eq:GetItem(slot);
if (item) then
local name = item:GetItemInfo():GetName();
eq.cache.byName[name] = { item, slot };
eq.cache.bySlot[slot] = { item, name };
end
end
end
local function GetBackpack()
local bp = Watcher.playerBackpackObject;
bp.cache = { byName = {}; bySlot = {}; };
for slot = 1, bp:GetSize() do
local item = bp:GetItem(slot);
if (item) then
local name = item:GetItemInfo():GetName();
if (not bp.cache.byName[name]) then
bp.cache.byName[name] = { slot };
else
table.insert(bp.cache.byName[name], slot);
end
bp.cache.bySlot[slot] = { item, name };
end
end
end
local function ResetTimeChanged(skill, args)
DoCallbacks(self, "ResetTimeChanged", args);
end
local function GetSkills(skills)
skills.cache = { names = {}; byName = {}; byIcon = {} };
local cache = skills.cache;
for c = 1, skills:GetCount(), 1 do
local skill = skills:GetItem(c);
-- AddCallback(skill, "ResetTimeChanged", ResetTimeChanged);
local skillInfo = skill:GetSkillInfo();
local icon = skillInfo:GetIconImageID();
local name = skillInfo:GetName();
cache.byName[name] = skill;
cache.byIcon[icon] = skill;
table.insert(cache.names, name);
end
table.sort(cache.names);
return skills.cache;
end
local function GetGambits()
local gambits = Watcher.playerTrainedGambits;
gambits.cache = { names = {}; byName = {}; byIcon = {} };
if (Watcher.playerObject:GetClass() == Turbine.Gameplay.Class.Warden) then
for c = 1, gambits:GetCount(), 1 do
local skill = gambits:GetItem(c);
local skillInfo = skill:GetSkillInfo();
local icon = skillInfo:GetIconImageID();
local name = skillInfo:GetName();
gambits.cache.byName[name] = skill;
gambits.cache.byIcon[icon] = skill;
end
end
return gambits.cache;
end
local function GetParty()
local party = Watcher.playerPartyObject;
party.cache = { byName = {}; };
for c = 1, party:GetMemberCount(), 1 do
local member = party:GetMember(c);
local name = member:GetName();
party.cache.byName[name] = member;
end
return party.cache;
end
local function PlayerEffectAdded(effectsObject, args)
local effect = effectsObject:Get(args.Index);
AddActorEffect(effectsObject, effect);
DoCallbacks(Watcher, "PlayerEffectAdded", effect);
end
local function PlayerEffectRemoved(effectsObject, args)
RemoveActorEffect(effectsObject, args.Effect);
DoCallbacks(Watcher, "PlayerEffectRemoved", args.Effect);
end
local function PlayerEffectsCleared(effectsObject, args)
GetActorEffects(effectsObject);
DoCallbacks(Watcher, "PlayerEffectsCleared", args);
end
local function TargetEffectAdded(effectsObject, args)
local effect = effectsObject:Get(args.Index);
AddActorEffect(effectsObject, effect);
DoCallbacks(Watcher, "TargetEffectAdded", effect);
end
function TargetEffectRemoved(effectsObject, args)
RemoveActorEffect(effectsObject, args.Effect);
DoCallbacks(Watcher, "TargetEffectRemoved", args.Effect);
end
local function TargetEffectsCleared(effectsObject, args)
GetActorEffects(effectsObject);
DoCallbacks(Watcher, "TargetEffectsCleared", args);
end
local function PlayerPartyChanged(sender, args)
Watcher.playerPartyObject = Watcher.playerObject:GetParty();
if (Watcher.playerPartyObject) then
AddCallback(Watcher.playerPartyObject, "MemberAdded", PartyMemberAdded);
AddCallback(Watcher.playerPartyObject, "MemberRemoved", PartyMemberRemoved);
GetParty();
end
end
local function UpdatePlayerTarget()
-- Bug workaround:
-- If the target is a member of the player's party, GetEffects() won't work
-- unless we use the Party object to get the Player object.
if (Watcher.targetObject and Watcher.playerPartyObject) then
local name = Watcher.targetObject:GetName();
local member = Watcher.playerPartyObject.cache.byName[name];
if (member) then
Watcher.targetObject = member;
end
end
-- Need to get the effects object now so it can start observing effects.
-- Target effects currently are too buggy to use.
--[[
if (Watcher.targetObject and Watcher.targetObject.GetEffects) then
Watcher.targetEffectsObject = Watcher.targetObject:GetEffects();
end
]]
DoCallbacks(Watcher, "PlayerTargetChanged", Watcher.targetObject);
end
local function PlayerTargetChanged()
Watcher.targetObject = Watcher.playerObject:GetTarget();
Watcher.targetEffectsObject = nil;
-- Client may not immediately have information about the target. When
-- the info arrives, we'll get a BaseMaxPowerChanged event, which seems
-- to occur near the end of the burst of events that will arrive.
if (Watcher.targetObject and Watcher.targetObject.GetBaseMaxPower and (Watcher.targetObject:GetBaseMaxPower() == 0)) then
Watcher.targetObject.BaseMaxPowerChanged = function()
Watcher.targetObject.BaseMaxPowerChanged = nil;
Watcher.targetObject = Watcher.playerObject:GetTarget();
Watcher.targetEffectsObject = nil;
UpdatePlayerTarget();
end
end
UpdatePlayerTarget();
end
local function ItemEquipped(sender, args)
--Puts("ItemEquipped: args = " .. Serialize(args));
local slot, item, name = args.Index, args.Item, args.Item:GetItemInfo():GetName();
Watcher.playerEquipmentObject.cache.byName[name] = { item, slot };
Watcher.playerEquipmentObject.cache.bySlot[slot] = { item, name };
DoCallbacks(Watcher, "ItemEquipped", args);
end
local function ItemUnequipped(sender, args)
--Puts("ItemUnequipped: args = " .. Serialize(args));
local slot, name = args.Index, args.Item:GetItemInfo():GetName();
Watcher.playerEquipmentObject.cache.bySlot[slot] = nil;
Watcher.playerEquipmentObject.cache.byName[name] = nil;
DoCallbacks(Watcher, "ItemUnequipped", args);
end
local function ItemAdded(sender, args)
--Puts("ItemAdded: args = " .. Serialize(args));
-- occurs out-of-combat; efficiency not important
GetBackpack();
end
local function ItemRemoved(sender, args)
--Puts("ItemRemoved: args = " .. Serialize(args));
-- occurs out-of-combat; efficiency not important
GetBackpack();
end
local function ItemMoved(sender, args)
-- occurs out-of-combat; efficiency not important
GetBackpack();
DoCallbacks(Watcher, "ItemMoved", args);
end
local function BackpackSizeChanged(sender, args)
-- rare occurrence, out-of-combat; efficiency not important
GetBackpack();
end
local function ChatReceived(sender, args)
if (args.ChatType == Turbine.ChatType.Advancement) then
if (string.match(args.Message, Watcher.SkillsChangedStr)) then
-- Trait line changed. Reread skills lists.
Watcher.playerTrainedSkills.cache = nil;
Watcher.playerUntrainedSkills.cache = nil;
elseif (string.match(args.Message, Watcher.TraitTreeChangedStr)) then
DoDelayedCallback(Turbine.Engine.GetGameTime() + 1.5, "TraitTreeChanged", args);
end
end
end
local function SkillAdded(sender, args)
-- rare occurrence, out-of-combat; efficiency not important
GetSkills(Watcher.playerTrainedSkills);
GetSkills(Watcher.playerUntrainedSkills);
end
local function SkillRemoved(sender, args)
-- rare occurrence, out-of-combat; efficiency not important
GetSkills(Watcher.playerTrainedSkills);
GetSkills(Watcher.playerUntrainedSkills);
end
local function PartyMemberAdded(sender, args)
-- rare occurrence, out-of-combat; efficiency not important
GetParty();
end
local function PartyMemberRemoved(sender, args)
-- rare occurrence, out-of-combat; efficiency not important
GetParty();
end
local function InCombatChanged()
local inCombat = Watcher.playerObject:IsInCombat();
if (inCombat) then
collectgarbage("stop");
else
collectgarbage("restart");
end
DoCallbacks(Watcher, "InCombatChanged", { InCombat = inCombat });
end
local function StanceChanged()
local stance = Watcher.playerClassAttributes:GetStance();
DoCallbacks(Watcher, "PlayerStanceChanged", { Stance = stance });
end
local function LoadSettings()
--Puts("Loading...");
Turbine.PluginData.Load(Turbine.DataScope.Account, "Thurallor_GameInfo", function(loadData, args)
if (not loadData) then
return;
end
-- Workaround for Turbine localization bug.
local settings = ImportTable(loadData);
if (not settings) then
return;
end
-- Previously-saved settings override the defaults
DeepTableCopy(settings, Watcher.settings);
-- Compile loaded functions
for categories, sourceCode in pairs(settings.funcs.AddCategories) do
Watcher.AddCategories[categories] = loadstring(sourceCode);
end
for categories, sourceCode in pairs(settings.funcs.RemoveCategories) do
Watcher.RemoveCategories[categories] = loadstring(sourceCode);
end
-- Lua API bug workaround: Target effects object will not contain valid
-- data until a few ticks after plugin load.
if (Watcher.targetEffectsObject) then
Watcher.targetEffectsObject.cache = nil;
end
-- Allow save operations to proceed.
Watcher.settingsLoaded = true;
SaveSettings();
--Puts("Load complete.");
end);
end
function Watcher.GetTrainedSkillsInfo()
local cache = Watcher.playerTrainedSkills.cache;
if (not cache) then
cache = GetSkills(Watcher.playerTrainedSkills);
end
return cache;
end
function Watcher.GetUntrainedSkillsInfo()
local cache = Watcher.playerUntrainedSkills.cache;
if (not cache) then
cache = GetSkills(Watcher.playerUntrainedSkills);
end
return cache;
end
function Watcher.GetSkillsInfo(trainedSkillsFirst)
local trainedSkillsCache = Watcher.GetTrainedSkillsInfo();
local untrainedSkillsCache = Watcher.GetUntrainedSkillsInfo();
if (#untrainedSkillsCache.names > 0) then
local cache = { names = {}; byName = {}; byIcon = {} };
for c in values({ trainedSkillsCache, untrainedSkillsCache }) do
for k, v in pairs(c.byIcon) do
cache.byIcon[k] = v;
end
for k, v in pairs(c.byName) do
cache.byName[k] = v;
end
for k, v in pairs(c.names) do
table.insert(cache.names, v);
end
end
if (not trainedSkillsFirst) then
table.sort(cache.names);
end
return cache;
else
return trainedSkillsCache;
end
end
function Watcher.GetGambitsInfo()
local cache = Watcher.playerTrainedGambits.cache;
if (not cache) then
cache = GetGambits();
end
for name in sorted_keys(cache.byName) do
table.insert(cache.names, name);
end
return cache;
end
function Watcher.GetKnownEffectNames()
local names = {};
for name in sorted_keys(Watcher.settings.knownEffects) do
table.insert(names, name);
end
return names;
end
-- If itemName is specified, the equipped item (if any) with the specified name is returned.
-- If itemSlot is specified, the equipped item (if any) at the specified slot is returned.
-- If neither is specified, a random equipped item (if any) is returned.
-- Returns slot, item (or nil).
function Watcher.GetEquippedItem(itemName, itemSlot)
if ((not itemSlot) and (not itemName)) then
for name, byName in pairs(Watcher.playerEquipmentObject.cache.byName) do
local item, slot = unpack(byName);
return slot, item;
end
elseif (not itemSlot) then
local byName = Watcher.playerEquipmentObject.cache.byName[itemName];
if (byName) then
local item, slot = unpack(byName);
return slot, item;
end
elseif (not itemName) then
local bySlot = Watcher.playerEquipmentObject.cache.bySlot[itemSlot];
if (bySlot) then
local item, name = unpack(bySlot);
return itemSlot, item;
end
else -- itemName and itemSlot specified
local bySlot = Watcher.playerEquipmentObject.cache.bySlot[itemSlot];
if (bySlot) then
local item, name = unpack(bySlot);
if (name == itemName) then
return itemSlot, item;
end
end
end
end
function Watcher.GetItemQuantity(itemName)
local quantity = 0;
local slots = Watcher.playerBackpackObject.cache.byName[itemName];
if (slots) then
for i, s in pairs(slots) do
local item = Watcher.playerBackpackObject:GetItem(s);
if (item) then
quantity = quantity + item:GetQuantity();
end
end
end
return quantity;
end
-- Can accept either an iconID (number) or a skill name (string).
function Watcher.SkillReady(arg)
local cache = Watcher.playerTrainedSkills.cache;
if (not cache) then
cache = GetSkills(Watcher.playerTrainedSkills);
end
if (type(arg) == "string") then
skill = cache.byName[arg];
elseif (type(arg) == "number") then
skill = cache.byIcon[arg];
end
-- return (skill and skill:IsUsable() and (skill:GetResetTime() == -1));
return (skill and (skill:GetResetTime() == -1));
end
-- Can accept either an iconID (number) or a skill name (string).
function Watcher.SkillUsable(arg)
local cache = Watcher.playerTrainedSkills.cache;
if (not cache) then
cache = GetSkills(Watcher.playerTrainedSkills);
end
if (type(arg) == "string") then
skill = cache.byName[arg];
elseif (type(arg) == "number") then
skill = cache.byIcon[arg];
end
return (skill and skill:IsUsable());
end
-- Can accept either an iconID (number) or a skill name (string).
function Watcher.SkillTrained(arg)
local cache, skill = Watcher.playerTrainedSkills.cache;
if (not cache) then
cache = GetSkills(Watcher.playerTrainedSkills);
end
if (type(arg) == "string") then
skill = cache.byName[arg];
elseif (type(arg) == "number") then
skill = cache.byIcon[arg];
end
return (skill ~= nil);
end
function PlayerHasEffectCategory(category)
if (category) then
return Watcher.playerEffectsObject.activeCategories[category] > 0;
end
end
function Watcher.PlayerHasEffectCategory(category)
-- Start watching player effects.
Watcher.playerEffectsObject = Watcher.playerObject:GetEffects();
AddCallback(Watcher.playerEffectsObject, "EffectAdded", PlayerEffectAdded);
AddCallback(Watcher.playerEffectsObject, "EffectRemoved", PlayerEffectRemoved);
AddCallback(Watcher.playerEffectsObject, "EffectsCleared", PlayerEffectsCleared);
GetActorEffects(Watcher.playerEffectsObject);
-- Next time this function is called, we don't need to do the above again.
Watcher.PlayerHasEffectCategory = PlayerHasEffectCategory;
Watcher.PlayerHasEffect = PlayerHasEffect;
return PlayerHasEffectCategory(category);
end
function PlayerHasEffect(name)
if (name) then
return Watcher.playerEffectsObject.cache[name];
end
end
function Watcher.PlayerHasEffect(name)
-- Start watching player effects.
Watcher.playerEffectsObject = Watcher.playerObject:GetEffects();
AddCallback(Watcher.playerEffectsObject, "EffectAdded", PlayerEffectAdded);
AddCallback(Watcher.playerEffectsObject, "EffectRemoved", PlayerEffectRemoved);
AddCallback(Watcher.playerEffectsObject, "EffectsCleared", PlayerEffectsCleared);
GetActorEffects(Watcher.playerEffectsObject);
-- Next time this function is called, we don't need to do the above again.
Watcher.PlayerHasEffectCategory = PlayerHasEffectCategory;
Watcher.PlayerHasEffect = PlayerHasEffect;
return PlayerHasEffect(name);
end
function Watcher.TargetHasEffectCategory(category)
-- Target effects tracking is so buggy as to be useless. Hoping Turbine addresses this bug soon.
if (true) then
return false;
end
if (not Watcher.targetEffectsObject) then
-- No target, or target isn't one whose effects can be read.
return false;
end
if (not Watcher.targetEffectsObject.cache) then
-- Bug workaround: Since EffectRemoved event never fires for target
-- effects, we must reread the list every time it is queried.
GetActorEffects(Watcher.targetEffectsObject);
Watcher.window:SetWantsUpdates(true); -- to delete the cache at next Update cycle
end
return Watcher.targetEffectsObject.activeCategories[category] > 0;
end
function Watcher.TargetHasEffect(name)
-- Target effects tracking is so buggy as to be useless. Hoping Turbine addresses this bug soon.
if (true) then
return false;
end
if (not Watcher.targetEffectsObject) then
-- No target, or target isn't one whose effects can be read.
return false;
end
if (not Watcher.targetEffectsObject.cache) then
-- Bug workaround: Since EffectRemoved event never fires for target
-- effects, we must reread the list every time it is queried.
GetActorEffects(Watcher.targetEffectsObject);
Watcher.window:SetWantsUpdates(true); -- to delete the cache at next Update cycle
end
return Watcher.targetEffectsObject.cache[name];
end
local function Constructor()
-- Default settings
Watcher.settings = {};
Watcher.settings.knownEffects = {};
Watcher.settings.funcs = {};
Watcher.settings.funcs.AddCategories = {};
Watcher.settings.funcs.RemoveCategories = {};
-- Optimized functions generated on-the-fly
Watcher.AddCategories = {};
Watcher.RemoveCategories = {};
-- Object for monitoring display updates:
Watcher.window = Turbine.UI.Window();
function Watcher.window:Update()
self:SetWantsUpdates(false);
if (Watcher.saveRequested) then
DoSave();
Watcher.saveRequested = false;
end
if (Watcher.targetEffectsObject) then
Watcher.targetEffectsObject.cache = nil;
end
if (Watcher.delayedCallbacks) then
local notyet = {};
for k, v in pairs(Watcher.delayedCallbacks) do
if (Turbine.Engine.GetGameTime() >= v.time) then
DoCallbacks(Watcher, v.event, v.args);
else
table.insert(notyet, v);
end
end
Watcher.delayedCallbacks = nil;
if (#notyet > 0) then
Watcher.delayedCallbacks = notyet;
self:SetWantsUpdates(true);
end
end
end
-- Object for monitoring player events
Watcher.playerObject = Turbine.Gameplay.LocalPlayer:GetInstance();
AddCallback(Watcher.playerObject, "InCombatChanged", InCombatChanged);
AddCallback(Watcher.playerObject, "PartyChanged", PlayerPartyChanged);
AddCallback(Watcher.playerObject, "TargetChanged", PlayerTargetChanged);
PlayerPartyChanged();
PlayerTargetChanged();
Watcher.playerEquipmentObject = Watcher.playerObject:GetEquipment();
AddCallback(Watcher.playerEquipmentObject, "ItemEquipped", ItemEquipped);
AddCallback(Watcher.playerEquipmentObject, "ItemUnequipped", ItemUnequipped);
GetEquipment();
Watcher.playerBackpackObject = Watcher.playerObject:GetBackpack();
AddCallback(Watcher.playerBackpackObject, "ItemAdded", ItemAdded);
AddCallback(Watcher.playerBackpackObject, "ItemRemoved", ItemRemoved);
AddCallback(Watcher.playerBackpackObject, "ItemMoved", ItemMoved);
AddCallback(Watcher.playerBackpackObject, "SizeChanged", BackpackSizeChanged);
GetBackpack();
Watcher.playerTrainedSkills = Watcher.playerObject:GetTrainedSkills();
AddCallback(Watcher.playerTrainedSkills, "SkillAdded", PlayerSkillAdded);
AddCallback(Watcher.playerTrainedSkills, "SkillRemoved", PlayerSkillRemoved);
GetSkills(Watcher.playerTrainedSkills);
Watcher.playerUntrainedSkills = Watcher.playerObject:GetUntrainedSkills();
GetSkills(Watcher.playerUntrainedSkills);
Watcher.playerClassAttributes = Watcher.playerObject:GetClassAttributes();
if (Watcher.playerObject:GetClass() == Turbine.Gameplay.Class.Warden) then
Watcher.playerTrainedGambits = Watcher.playerClassAttributes:GetTrainedGambits();
else
Watcher.playerTrainedGambits = {};
end
AddCallback(Watcher.playerClassAttributes, "StanceChanged", StanceChanged);
-- For monitoring chat messages
AddCallback(Turbine.Chat, "Received", ChatReceived);
local language = Turbine.Engine:GetLanguage();
if ((language == Turbine.Language.EnglishGB) or (language == Turbine.Language.English)) then
Watcher.SkillsChangedStr = "^You have acquired the .* skill%.";
Watcher.TraitTreeChangedStr = "^You have acquired the Class Specialization Bonus Trait";
elseif (language == Turbine.Language.German) then
Watcher.SkillsChangedStr = "^Ihr habt Euch die Fertigkeit .* angeeignet%.";
Watcher.TraitTreeChangedStr = "^Ihr habt diese Bonus-Eigenschaft für Klassenspezialisierung erlangt";
elseif (language == Turbine.Language.French) then
Watcher.SkillsChangedStr = "^Vous avez acquis la compétence .*%.";
Watcher.TraitTreeChangedStr = "^Vous avez obtenu le trait bonus de spécialisation de classe";
end
end
-- Create single instance and load saved settings (if any).
Constructor();
LoadSettings();
--t = Turbine.UI.Window();
--t:SetVisible(true);
--t:SetBackColor(Turbine.UI.Color.Red);
--t:SetMouseVisible(true);
--function t:MouseClick()
-- Puts("cache = " .. PrettyPrint(Watcher.playerEffectsObject.cache, ""));
--end