diff --git a/Addons/DataToColor/Constants.lua b/Addons/DataToColor/Constants.lua
index 6763c18c4..57958fb53 100644
--- a/Addons/DataToColor/Constants.lua
+++ b/Addons/DataToColor/Constants.lua
@@ -21,7 +21,8 @@ DataToColor.C.Totem = "Totem"
-- Character's name
DataToColor.C.CHARACTER_NAME = UnitName(DataToColor.C.unitPlayer)
DataToColor.C.CHARACTER_GUID = UnitGUID(DataToColor.C.unitPlayer)
-_, DataToColor.C.CHARACTER_CLASS = UnitClass(DataToColor.C.unitPlayer)
+_, DataToColor.C.CHARACTER_CLASS, DataToColor.C.CHARACTER_CLASS_ID = UnitClass(DataToColor.C.unitPlayer)
+_, _, DataToColor.C.CHARACTER_RACE_ID = UnitRace(DataToColor.C.unitPlayer)
-- Actionbar power cost
DataToColor.C.MAX_POWER_TYPE = 1000000
diff --git a/Addons/DataToColor/DataToColor.lua b/Addons/DataToColor/DataToColor.lua
index ac1091572..c2e50c311 100644
--- a/Addons/DataToColor/DataToColor.lua
+++ b/Addons/DataToColor/DataToColor.lua
@@ -48,12 +48,23 @@ DataToColor.lastCombatCreatureDied = 0
DataToColor.lastAutoShot = 0
DataToColor.lastMainHandMeleeSwing = 0
+DataToColor.lastCastEvent = 0
+DataToColor.lastCastSpellId = 0
+
+DataToColor.lastCastStartTime = 0
+DataToColor.CastNum = 0
DataToColor.targetChanged = true
DataToColor.playerGUID = UnitGUID(DataToColor.C.unitPlayer)
DataToColor.petGUID = UnitGUID(DataToColor.C.unitPet)
+-- buff / debuff counters
+local playerDebuffCount = 0
+local playerBuffCount = 0
+local targetDebuffCount = 0
+local targetBuffCount = 0
+
-- Update Queue
stack = {}
DataToColor.stack = stack
@@ -169,6 +180,16 @@ function DataToColor:Reset()
DataToColor.lastAutoShot = 0
DataToColor.lastMainHandMeleeSwing = 0
+ DataToColor.lastCastEvent = 0
+ DataToColor.lastCastSpellId = 0
+
+ DataToColor.lastCastStartTime = 0
+ DataToColor.CastNum = 0
+
+ playerDebuffCount = 0
+ playerBuffCount = 0
+ targetDebuffCount = 0
+ targetBuffCount = 0
end
function DataToColor:Update()
@@ -336,10 +357,14 @@ function DataToColor:CreateFrames(n)
-- 20
bagNum = DataToColor.stack:pop(DataToColor.bagQueue)
if bagNum then
- local freeSlots, bagType = GetContainerNumFreeSlots(bagNum) or 0, 0
+ local freeSlots, bagType = GetContainerNumFreeSlots(bagNum)
+ if not bagType then
+ bagType = 0
+ end
+
-- BagType + Index + FreeSpace + BagSlots
MakePixelSquareArrI(bagType * 1000000 + bagNum * 100000 + freeSlots * 1000 + DataToColor:bagSlots(bagNum), 20)
- --DataToColor:Print("bagQueue "..bagType.." -> "..bagNum.." -> "..freeSlots.." -> "..DataToColor:bagSlots(bagNum))
+ --DataToColor:Print("bagQueue bagType:"..bagType.." | bagNum: "..bagNum.." | freeSlots: "..freeSlots.." | BagSlots: "..DataToColor:bagSlots(bagNum))
end
-- 21 22 23
@@ -425,7 +450,7 @@ function DataToColor:CreateFrames(n)
--MakePixelSquareArrI(DataToColor:GetGossipIcons(), 45) -- Returns which gossip icons are on display in dialogue box
- MakePixelSquareArrI(DataToColor.S.PlayerClass, 46) -- Returns player class as an integer
+ MakePixelSquareArrI(DataToColor.C.CHARACTER_RACE_ID * 100 + DataToColor.C.CHARACTER_CLASS_ID, 46)
MakePixelSquareArrI(DataToColor:isUnskinnable(), 47) -- Returns 1 if creature is unskinnable
MakePixelSquareArrI(DataToColor:shapeshiftForm(), 48) -- Shapeshift id https://wowwiki.fandom.com/wiki/API_GetShapeshiftForm
MakePixelSquareArrI(DataToColor:getRange(), 49) -- 15 Represents if target is within 0-5 5-15 15-20, 20-30, 30-35, or greater than 35 yards
@@ -437,7 +462,25 @@ function DataToColor:CreateFrames(n)
MakePixelSquareArrI(DataToColor:CastingInfoSpellId(DataToColor.C.unitPlayer), 53) -- Spell being cast
MakePixelSquareArrI(DataToColor:ComboPoints(), 54) -- Combo points for rogue / druid
- MakePixelSquareArrI(DataToColor:getAuraCount(UnitDebuff, DataToColor.C.unitPlayer), 55)
+
+ playerDebuffCount = DataToColor:getAuraCount(UnitDebuff, DataToColor.C.unitPlayer)
+ playerBuffCount = DataToColor:getAuraCount(UnitBuff, DataToColor.C.unitPlayer)
+
+ if UnitExists(DataToColor.C.unitTarget) then
+ targetDebuffCount = DataToColor:getAuraCount(UnitDebuff, DataToColor.C.unitTarget)
+ targetBuffCount = DataToColor:getAuraCount(UnitBuff, DataToColor.C.unitTarget)
+ else
+ targetDebuffCount = 0
+ targetBuffCount = 0
+ end
+
+ if playerDebuffCount > 16 then
+ playerDebuffCount = 16
+ end
+
+ -- player/target buff and debuff counts
+ -- formula playerDebuffCount + playerBuffCount + targetDebuffCount + targetBuffCount
+ MakePixelSquareArrI(playerDebuffCount * 1000000 + playerBuffCount * 10000 + targetDebuffCount * 100 + targetBuffCount, 55)
if DataToColor.targetChanged then
MakePixelSquareArrI(DataToColor:targetNpcId(), 56) -- target id
@@ -450,9 +493,8 @@ function DataToColor:CreateFrames(n)
MakePixelSquareArrI(DataToColor.lastAutoShot, 60)
MakePixelSquareArrI(DataToColor.lastMainHandMeleeSwing, 61)
- -- 62 not used
- -- 63 not used
- -- 64 not used
+ MakePixelSquareArrI(DataToColor.lastCastEvent, 62)
+ MakePixelSquareArrI(DataToColor.lastCastSpellId, 63)
MakePixelSquareArrI(DataToColor.lastCombatCreature, 64) -- Combat message creature
MakePixelSquareArrI(DataToColor.lastCombatDamageDoneCreature, 65) -- Last Combat damage done
@@ -461,10 +503,13 @@ function DataToColor:CreateFrames(n)
MakePixelSquareArrI(DataToColor:getGuid(DataToColor.C.unitPet), 68) -- pet guid
MakePixelSquareArrI(DataToColor:getGuid(DataToColor.C.unitPetTarget), 69) -- pet target
+ MakePixelSquareArrI(DataToColor.CastNum, 70)
-- Timers
- MakePixelSquareArrI(DataToColor.globalTime, 70)
- MakePixelSquareArrI(DataToColor.lastLoot, 71)
+ MakePixelSquareArrI(DataToColor.lastLoot, 97)
+ MakePixelSquareArrI(DataToColor.globalTime, 98)
+
+ -- 99 Reserved
DataToColor:ConsumeChanges()
diff --git a/Addons/DataToColor/DataToColor.toc b/Addons/DataToColor/DataToColor.toc
index cf33a38cc..77588a444 100644
--- a/Addons/DataToColor/DataToColor.toc
+++ b/Addons/DataToColor/DataToColor.toc
@@ -2,7 +2,7 @@
## Title: DataToColor
## Author: FreeHongKongMMO
## Notes: An addon that displays player position as color
-## Version: 1.1.26
+## Version: 1.1.35
## RequiredDeps:
## OptionalDeps: Ace3, LibDataBroker-1.1, LibCompress, LibRangeCheck
## SavedVariables:
diff --git a/Addons/DataToColor/EventHandlers.lua b/Addons/DataToColor/EventHandlers.lua
index 7e4fbb241..812679acd 100644
--- a/Addons/DataToColor/EventHandlers.lua
+++ b/Addons/DataToColor/EventHandlers.lua
@@ -1,6 +1,9 @@
local Load = select(2, ...)
local DataToColor = unpack(Load)
+local CAST_START = 999998
+local CAST_SUCCESS = 999999
+
local ignoreErrorList = {
"ERR_ABILITY_COOLDOWN",
"ERR_OUT_OF_RAGE",
@@ -23,6 +26,8 @@ local errorList = {
"ERR_SPELL_COOLDOWN", --7 "Spell is not ready yet."
"ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS", --8 "Another action is in progress"
"ERR_SPELL_FAILED_STUNNED", -- 9 "Can't do that while stunned"
+ "SPELL_FAILED_INTERRUPTED", -- 10 "Interrupted"
+ "SPELL_FAILED_ITEM_NOT_READY" -- 11 "Item is not ready yet"
};
function DataToColor:RegisterEvents()
@@ -39,53 +44,85 @@ function DataToColor:RegisterEvents()
end
function DataToColor:OnUIErrorMessage(event, messageType, message)
- local errorName = GetGameMessageInfo(messageType)
+ local code, ignored, foundMessage, message = DataToColor:GetErrorCode(messageType, message)
+
+ if ignored then
+ UIErrorsFrame:AddMessage(message, 0.7, 0.7, 0.7) -- show as grey messasge
+ elseif foundMessage and code ~= 0 then
+ DataToColor.uiErrorMessage = code;
+ UIErrorsFrame:AddMessage(message, 0, 1, 0) -- show as green messasge
+ else
+ UIErrorsFrame:AddMessage(message, 0, 0, 1) -- show as blue message (unknown message)
+ end
+end
+
+function DataToColor:GetErrorCode(messageType, message)
+
+ local errorName
+ local foundMessage = false
+ local ignored = false
+ local code = 0
+
+ if messageType ~= nil then
+ errorName = GetGameMessageInfo(messageType)
+ end
- local foundMessage=false;
for i = 1, table.getn(ignoreErrorList), 1 do
- if ignoreErrorList[i]==errorName then
- foundMessage=true;
- UIErrorsFrame:AddMessage(message, 0.7, 0.7, 0.7) -- show as grey messasge
+ if ignoreErrorList[i] == errorName then
+ foundMessage = true;
+ ignored = true
end
end
- if not foundMessage then
+ if not ignored and not foundMessage then
for i = 1, table.getn(errorList), 1 do
- if errorList[i]==errorName then
- DataToColor.uiErrorMessage = i;
-
- if errorName==errorList[2] then -- ERR_SPELL_FAILED_S
- if message==SPELL_FAILED_UNIT_NOT_INFRONT then
- DataToColor.uiErrorMessage = 1
- message = message.." ("..ERR_BADATTACKFACING..")"
- elseif message==SPELL_FAILED_MOVING then
- DataToColor.uiErrorMessage = 6
- elseif message==SPELL_FAILED_STUNNED then
- DataToColor.uiErrorMessage = 9
- --message = message.." ("..SPELL_FAILED_STUNNED..")"
- --else
- -- message = message.." (Spell related)"
- end
- end
-
- foundMessage=true;
- UIErrorsFrame:AddMessage(message, 0, 1, 0) -- show as green messasge
+ if errorList[i] == errorName or
+ (_G[errorList[i]] ~= nil and string.find(_G[errorList[i]], message)) then
+ code = i;
+ foundMessage = true;
end
end
end
- if not foundMessage then
- UIErrorsFrame:AddMessage(message, 0, 0, 1) -- show as blue message (unknown message)
+ -- ERR_SPELL_FAILED_S
+ -- find by message ex combatlog
+ if not ignored and (not foundMessage or errorName == errorList[2]) then
+ if string.find(message, SPELL_FAILED_UNIT_NOT_INFRONT) then
+ code = 1
+ foundMessage = true
+ message = message.." ("..ERR_BADATTACKFACING..")"
+ elseif string.find(message, SPELL_FAILED_MOVING) then
+ foundMessage = true
+ code = 6
+ elseif string.find(message, SPELL_FAILED_STUNNED) then
+ foundMessage = true
+ code = 9
+ end
end
+
+ return code, ignored, foundMessage, message
end
local watchedSpells = {
[DataToColor.C.Spell.AutoShotId] = function ()
--DataToColor:Print("Auto Shot detected")
DataToColor.lastAutoShot = DataToColor.globalTime
- end
+ end
}
+local swing_reset_spells = {
+ --[[ Maul ]]
+ [132136]=1,
+ --[[ Raptor Strike ]]
+ [132223]=1,
+ --[[ Cleave ]]
+ [132338]=1,
+ --[[ Heroic Strike ]]
+ [132282]=1,
+ --[[ Slam ]]
+ [132340]=1
+}
+
function DataToColor:OnCombatEvent(...)
local _, eventType, _, sourceGUID, sourceName, _, _, destGUID, destName, _, _, spellId, _, _ = CombatLogGetCurrentEventInfo();
--print(CombatLogGetCurrentEventInfo())
@@ -93,32 +130,58 @@ function DataToColor:OnCombatEvent(...)
DataToColor.lastCombatCreature=0;
elseif string.find(sourceGUID, "Creature") then
DataToColor.lastCombatCreature = DataToColor:getGuidFromUUID(sourceGUID);
- --print(CombatLogGetCurrentEventInfo())
else
DataToColor.lastCombatCreature=0;
- --print("Other "..eventType);
end
if string.find(sourceGUID, "Creature") and (destGUID == DataToColor.playerGUID or destGUID == DataToColor.petGUID) then
DataToColor.lastCombatDamageTakenCreature = DataToColor:getGuidFromUUID(sourceGUID);
- --print(sourceGUID.." "..DataToColor.lastCombatDamageTakenCreature.." "..sourceName);
end
- if eventType=="SPELL_CAST_SUCCESS" and sourceGUID == DataToColor.playerGUID then
- if watchedSpells[spellId] then watchedSpells[spellId]() end
- end
+ if sourceGUID == DataToColor.playerGUID then
+ if eventType=="SPELL_CAST_SUCCESS" then
+ if watchedSpells[spellId] then watchedSpells[spellId]() end
+
+ local _, _, icon = GetSpellInfo(spellId)
+ if swing_reset_spells[icon] then
+ --DataToColor:Print("Special Melee Swing detected")
+ DataToColor.lastMainHandMeleeSwing = DataToColor.globalTime
+ end
+ end
- if string.find(eventType, "_DAMAGE") then
- if sourceGUID == DataToColor.playerGUID or sourceGUID == DataToColor.petGUID then
+ if string.find(eventType, "_CAST_START") then
+ DataToColor.lastCastEvent = CAST_START
+ DataToColor.lastCastSpellId = spellId
+ --print(CombatLogGetCurrentEventInfo())
+ --print("_CAST_START "..spellId)
+ end
+
+ if string.find(eventType, "_CAST_SUCCESS") or string.find(eventType, "_CAST_FAILED") then
+ --print(CombatLogGetCurrentEventInfo())
+ DataToColor.lastCastSpellId = spellId
+
+ if string.find(eventType, "_CAST_FAILED") then
+ --local lastCastEvent = DataToColor.lastCastEvent
+ local failedType = select(15, CombatLogGetCurrentEventInfo())
+ DataToColor.lastCastEvent = DataToColor:GetErrorCode(nil, failedType)
+ --print(lastCastEvent.." -> "..DataToColor.lastCastEvent.." "..failedType.." "..spellId)
+ else
+ DataToColor.lastCastEvent = CAST_SUCCESS
+ end
+ end
+
+ -- matches SWING_ RANGE_ SPELL_ but not SPELL_PERIODIC
+ if not string.find(eventType, "SPELL_PERIODIC") and
+ (string.find(eventType, "_DAMAGE") or string.find(eventType, "_MISSED")) then
DataToColor.lastCombatDamageDoneCreature = DataToColor:getGuidFromUUID(destGUID);
end
- end
- if sourceGUID == DataToColor.playerGUID and string.find(eventType, "SWING_") then
- local _, _, _, _, _, _, _, _, _, isOffHand = select(12, ...)
- if not isOffHand then
- --DataToColor:Print("Melee Swing detected")
- DataToColor.lastMainHandMeleeSwing = DataToColor.globalTime
+ if string.find(eventType, "SWING_") then
+ local _, _, _, _, _, _, _, _, _, isOffHand = select(12, ...)
+ if not isOffHand then
+ --DataToColor:Print("Normal Melee Swing detected")
+ DataToColor.lastMainHandMeleeSwing = DataToColor.globalTime
+ end
end
end
diff --git a/Addons/DataToColor/Query.lua b/Addons/DataToColor/Query.lua
index 95cd84bd7..14b9900f7 100644
--- a/Addons/DataToColor/Query.lua
+++ b/Addons/DataToColor/Query.lua
@@ -98,13 +98,21 @@ function DataToColor:GetTargetName(partition)
return 0
end
-function DataToColor:CastingInfoSpellId(target)
- local _, _, _, _, _, _, _, spellID = UnitCastingInfo(target)
+function DataToColor:CastingInfoSpellId(unitId)
+ local _, _, _, _, startTime, _, _, spellID = UnitCastingInfo(unitId)
if spellID ~= nil then
+ if unitId == DataToColor.C.unitPlayer and startTime ~= DataToColor.lastCastStartTime then
+ DataToColor.lastCastStartTime = startTime
+ DataToColor.CastNum = DataToColor.CastNum + 1
+ end
return spellID
end
- _, _, _, _, _, _, spellID = UnitChannelInfo(target)
+ _, _, _, startTime, _, _, spellID = UnitChannelInfo(unitId)
if spellID ~= nil then
+ if unitId == DataToColor.C.unitPlayer and startTime ~= DataToColor.lastCastStartTime then
+ DataToColor.lastCastStartTime = startTime
+ DataToColor.CastNum = DataToColor.CastNum + 1
+ end
return spellID
end
return 0
diff --git a/Addons/DataToColor/Storage.lua b/Addons/DataToColor/Storage.lua
index 3d73f19cf..9afefc78b 100644
--- a/Addons/DataToColor/Storage.lua
+++ b/Addons/DataToColor/Storage.lua
@@ -1,45 +1,18 @@
local Load = select(2, ...)
local DataToColor = unpack(Load)
-DataToColor.S.PlayerClass = 0
DataToColor.S.spellInRangeList = {}
DataToColor.S.playerBuffs = {}
DataToColor.S.targetDebuffs = {}
function DataToColor:InitStorage()
- CreatePlayerClass()
CreateSpellInRangeList()
CreatePlayerBuffList()
CreateTargetDebuffList()
end
-function CreatePlayerClass()
- -- UnitClass returns class and the class in uppercase e.g. "Mage" and "MAGE"
- if DataToColor.C.CHARACTER_CLASS == "MAGE" then
- DataToColor.S.PlayerClass = 128
- elseif DataToColor.C.CHARACTER_CLASS == "ROGUE" then
- DataToColor.S.PlayerClass = 64
- elseif DataToColor.C.CHARACTER_CLASS == "WARRIOR" then
- DataToColor.S.PlayerClass = 32
- elseif DataToColor.C.CHARACTER_CLASS == "PALADIN" then
- DataToColor.S.PlayerClass = 16
- elseif DataToColor.C.CHARACTER_CLASS == "HUNTER" then
- DataToColor.S.PlayerClass = 8
- elseif DataToColor.C.CHARACTER_CLASS == "PRIEST" then
- DataToColor.S.PlayerClass = 4
- elseif DataToColor.C.CHARACTER_CLASS == "SHAMAN" then
- DataToColor.S.PlayerClass = 2
- elseif DataToColor.C.CHARACTER_CLASS == "WARLOCK" then
- DataToColor.S.PlayerClass = 1
- elseif DataToColor.C.CHARACTER_CLASS == "DRUID" then
- DataToColor.S.PlayerClass = 256
- else
- DataToColor.S.PlayerClass = 0
- end
-end
-
function CreateSpellInRangeList()
if DataToColor.C.CHARACTER_CLASS == "ROGUE" then
DataToColor.S.spellInRangeList = {
@@ -63,9 +36,10 @@ function CreateSpellInRangeList()
elseif DataToColor.C.CHARACTER_CLASS == "PRIEST" then
DataToColor.S.spellInRangeList = {
589, -- "Shadow Word: Pain"
- 8092, -- "Mind Blast"
+ 5019, -- "Shoot"
15407, -- "Mind Flay"
- 5019 -- "Shoot"
+ 8092, -- "Mind Blast"
+ 585 -- "Smite"
}
elseif DataToColor.C.CHARACTER_CLASS == "PALADIN" then
DataToColor.S.spellInRangeList = {
@@ -104,6 +78,7 @@ function CreatePlayerBuffList()
DataToColor.S.playerBuffs[1] = { "Drink", [132794]=1, [132800]=1, [132805]=1, [132802]=1 }
DataToColor.S.playerBuffs[2] = { "Well Fed", [136000]=1 }
DataToColor.S.playerBuffs[3] = { "Mana Regeneration", [2]=1 } -- potion?
+ DataToColor.S.playerBuffs[4] = { "Clearcasting", [136170]=1 } -- Druid / Mage / Shaman
if DataToColor.C.CHARACTER_CLASS == "PRIEST" then
DataToColor.S.playerBuffs[10] = { "Fortitude", [135987]=1, [135941]=1 }
@@ -129,6 +104,8 @@ function CreatePlayerBuffList()
DataToColor.S.playerBuffs[13] = { "Ward", [135806]=1, [135850]=1 }
DataToColor.S.playerBuffs[14] = { "Fire Power", [135817]=1 } -- not sure what is this
DataToColor.S.playerBuffs[15] = { "Mana Shield", [136153]=1 }
+ DataToColor.S.playerBuffs[16] = { "Presence of Mind", [136031]=1 }
+ DataToColor.S.playerBuffs[17] = { "Arcane Power", [136048]=1 }
elseif DataToColor.C.CHARACTER_CLASS == "ROGUE" then
DataToColor.S.playerBuffs[10] = { "Slice and Dice", [132306]=1 }
DataToColor.S.playerBuffs[11] = { "Stealth", [132320]=1 }
@@ -143,6 +120,7 @@ function CreatePlayerBuffList()
DataToColor.S.playerBuffs[10] = { "Lightning Shield", [136051]=1 }
DataToColor.S.playerBuffs[11] = { "Water Shield", [132315]=1 }
DataToColor.S.playerBuffs[12] = { "Focused", [136027]=1 } -- Shamanistic Focus
+ DataToColor.S.playerBuffs[13] = { "Stoneskin", [136098]=1 }
elseif DataToColor.C.CHARACTER_CLASS == "HUNTER" then
DataToColor.S.playerBuffs[10] = { "Aspect of", [136076]=1, [132159]=1, [132252]=1, [132267]=1, [132160]=1, [136074]=1 }
DataToColor.S.playerBuffs[11] = { "Rapid Fire", [132208]=1 }
@@ -160,9 +138,11 @@ function CreateTargetDebuffList()
DataToColor.S.targetDebuffs[2] = { "Rip", [132152]=1 }
DataToColor.S.targetDebuffs[3] = { "Moonfire", [136096]=1 }
DataToColor.S.targetDebuffs[4] = { "Entangling Roots", [136100]=1 }
+ DataToColor.S.targetDebuffs[5] = { "Rake", [132122]=1 }
elseif DataToColor.C.CHARACTER_CLASS == "PALADIN" then
elseif DataToColor.C.CHARACTER_CLASS == "MAGE" then
DataToColor.S.targetDebuffs[0] = { "Frostbite", [135842]=1 }
+ DataToColor.S.targetDebuffs[1] = { "Slow", [136091]=1 }
elseif DataToColor.C.CHARACTER_CLASS == "ROGUE" then
elseif DataToColor.C.CHARACTER_CLASS == "WARRIOR" then
DataToColor.S.targetDebuffs[0] = { "Rend", [132155]=1 }
diff --git a/BlazorServer/Pages/GoapGoalView.razor b/BlazorServer/Pages/GoapGoalView.razor
index 45749cbe8..abe554f98 100644
--- a/BlazorServer/Pages/GoapGoalView.razor
+++ b/BlazorServer/Pages/GoapGoalView.razor
@@ -27,11 +27,10 @@
@if (this.goal.Keys != null)
{
- var lastKeyClicked = Core.KeyAction.LastKeyClicked();
-
+ int lastKeyClicked = Core.KeyAction.LastKeyClicked();
foreach (var key in this.goal.Keys)
{
-
+
@key.Name [@key.ConsoleKey]
|
diff --git a/Core/Actionbar/ActionBarBits.cs b/Core/Actionbar/ActionBarBits.cs
index c88e10c7f..19138335b 100644
--- a/Core/Actionbar/ActionBarBits.cs
+++ b/Core/Actionbar/ActionBarBits.cs
@@ -21,7 +21,7 @@ public bool Is(KeyAction item)
{
if (KeyReader.ActionBarSlotMap.TryGetValue(item.Key, out int slot))
{
- slot += Stance.RuntimeSlotToActionBar(playerReader, slot);
+ slot += Stance.RuntimeSlotToActionBar(item, playerReader, slot);
int array = (int)(slot / 24);
return bits[array].IsBitSet((slot - 1) % 24);
@@ -29,5 +29,16 @@ public bool Is(KeyAction item)
return false;
}
+
+ public int Num(KeyAction item)
+ {
+ if (KeyReader.ActionBarSlotMap.TryGetValue(item.Key, out int slot))
+ {
+ slot += Stance.RuntimeSlotToActionBar(item, playerReader, slot);
+ return slot;
+ }
+
+ return 0;
+ }
}
}
diff --git a/Core/Actionbar/ActionBarCostReader.cs b/Core/Actionbar/ActionBarCostReader.cs
index 08992cd59..19ae107d5 100644
--- a/Core/Actionbar/ActionBarCostReader.cs
+++ b/Core/Actionbar/ActionBarCostReader.cs
@@ -62,13 +62,18 @@ public void Read()
}
}
+ public void Reset()
+ {
+ dict.Clear();
+ }
+
public Tuple GetCostByActionBarSlot(PlayerReader playerReader, KeyAction keyAction)
{
if (KeyReader.ActionBarSlotMap.TryGetValue(keyAction.Key, out int slot))
{
- if (keyAction.FormEnum != Form.None && slot <= 12)
+ if (slot <= 12)
{
- slot += Stance.FormToActionBar(playerReader.PlayerClass, keyAction.FormEnum);
+ slot += Stance.RuntimeSlotToActionBar(keyAction, playerReader, slot);
}
if (dict.TryGetValue(slot, out var tuple))
diff --git a/Core/ActionbarPopulator/ActionBarPopulator.cs b/Core/ActionbarPopulator/ActionBarPopulator.cs
index 36e5be072..8b43aa8e1 100644
--- a/Core/ActionbarPopulator/ActionBarPopulator.cs
+++ b/Core/ActionbarPopulator/ActionBarPopulator.cs
@@ -7,12 +7,12 @@ namespace Core
{
public class ActionBarPopulator
{
- struct ActionBarSource {
+ struct ActionBarSource
+ {
public string Name;
public string Key;
public bool Item;
- public string Requirement;
- public Form Form;
+ public KeyAction KeyAction;
}
private readonly ILogger logger;
@@ -57,15 +57,14 @@ private void CollectKeyActions()
private void AddUnique(KeyAction a)
{
if (!KeyReader.KeyMapping.ContainsKey(a.Key)) return;
- if (sources.Any(i => i.Key == a.Key && i.Form == a.FormEnum)) return;
+ if (sources.Any(i => i.KeyAction.ConsoleKeyFormHash == a.ConsoleKeyFormHash)) return;
var source = new ActionBarSource
{
Name = a.Name,
Key = a.Key,
Item = false,
- Requirement = a.Requirement,
- Form = a.FormEnum
+ KeyAction = a
};
sources.Add(source);
@@ -155,7 +154,7 @@ private string CalculateActionNumber(ActionBarSource a, string key, string prefi
{
if (offset == 0 && hotkey <= 12)
{
- offset += Stance.FormToActionBar(addonReader.PlayerReader.PlayerClass, a.Form);
+ offset += Stance.RuntimeSlotToActionBar(a.KeyAction, addonReader.PlayerReader, hotkey);
}
if (hotkey == 0)
diff --git a/Core/Addon/AddonReader.cs b/Core/Addon/AddonReader.cs
index a6657ca50..d01e481f2 100644
--- a/Core/Addon/AddonReader.cs
+++ b/Core/Addon/AddonReader.cs
@@ -106,6 +106,7 @@ public void Refresh()
public void Reset()
{
PlayerReader.Initialized = false;
+ ActionBarCostReader.Reset();
PlayerReader.Reset();
}
diff --git a/Core/Addon/AuraCount.cs b/Core/Addon/AuraCount.cs
new file mode 100644
index 000000000..f8f657abc
--- /dev/null
+++ b/Core/Addon/AuraCount.cs
@@ -0,0 +1,32 @@
+using System;
+using System.Collections.Generic;
+using System.Text;
+
+namespace Core
+{
+ public class AuraCount
+ {
+ public int Hash { private set; get; }
+ public int PlayerDebuff { private set; get; }
+ public int PlayerBuff { private set; get; }
+ public int TargetDebuff { private set; get; }
+ public int TargetBuff { private set; get; }
+
+ public AuraCount(ISquareReader squareReader, int cell)
+ {
+ Hash = TargetBuff = (int)squareReader.GetLongAtCell(cell);
+
+ // formula
+ // playerDebuffCount * 1000000 + playerBuffCount * 10000 + targetDebuffCount * 100 + targetBuffCount
+
+ PlayerDebuff = (int)(TargetBuff / 1000000f);
+ TargetBuff -= 1000000 * PlayerDebuff;
+
+ PlayerBuff = (int)(TargetBuff / 10000f);
+ TargetBuff -= 10000 * PlayerBuff;
+
+ TargetDebuff = (int)(TargetBuff / 100f);
+ TargetBuff -= 100 * TargetDebuff;
+ }
+ }
+}
diff --git a/Core/Addon/BuffStatus.cs b/Core/Addon/BuffStatus.cs
index 63d88842f..b8c77c976 100644
--- a/Core/Addon/BuffStatus.cs
+++ b/Core/Addon/BuffStatus.cs
@@ -19,6 +19,7 @@ public bool IsBitSet(int pos)
public bool Drinking => IsBitSet(1);
public bool WellFed => IsBitSet(2);
public bool ManaRegeneration => IsBitSet(3);
+ public bool Clearcasting => IsBitSet(4);
// Priest
public bool Fortitude => IsBitSet(10);
@@ -47,6 +48,8 @@ public bool IsBitSet(int pos)
public bool Ward => IsBitSet(13);
public bool FirePower => IsBitSet(14);
public bool ManaShield => IsBitSet(15);
+ public bool PresenceOfMind => IsBitSet(16);
+ public bool ArcanePower => IsBitSet(17);
// Rogue
public bool SliceAndDice => IsBitSet(10);
@@ -65,6 +68,7 @@ public bool IsBitSet(int pos)
public bool LightningShield => IsBitSet(10);
public bool WaterShield => IsBitSet(11);
public bool ShamanisticFocus => IsBitSet(12);
+ public bool Stoneskin => IsBitSet(13);
// Hunter
public bool Aspect => IsBitSet(10); //Any Aspect of
diff --git a/Core/Addon/DeBuffStatus.cs b/Core/Addon/DeBuffStatus.cs
index 55714d55b..3a4b3e11c 100644
--- a/Core/Addon/DeBuffStatus.cs
+++ b/Core/Addon/DeBuffStatus.cs
@@ -31,11 +31,13 @@ public bool IsBitSet(int pos)
public bool Rip => IsBitSet(2);
public bool Moonfire => IsBitSet(3);
public bool EntanglingRoots => IsBitSet(4);
+ public bool Rake => IsBitSet(5);
// Paladin
// Mage
public bool Frostbite => IsBitSet(0);
+ public bool Slow => IsBitSet(1);
// Rogue
diff --git a/Core/Addon/PlayerClassEnum.cs b/Core/Addon/PlayerClassEnum.cs
index 69e700cba..e3d5e707c 100644
--- a/Core/Addon/PlayerClassEnum.cs
+++ b/Core/Addon/PlayerClassEnum.cs
@@ -2,14 +2,18 @@
{
public enum PlayerClassEnum
{
- Druid = 256,
- Mage = 128,
- Rogue = 64,
- Warrior = 32,
- Paladin = 16,
- Hunter = 8,
- Priest = 4,
- Shaman = 2,
- Warlock = 1
+ None,
+ Warrior,
+ Paladin,
+ Hunter,
+ Rogue,
+ Priest,
+ Death_Knight,
+ Shaman,
+ Mage,
+ Warlock,
+ Monk,
+ Druid,
+ Demon_Hunter
}
}
\ No newline at end of file
diff --git a/Core/Addon/PlayerReader.cs b/Core/Addon/PlayerReader.cs
index d55a679ff..dcf326e37 100644
--- a/Core/Addon/PlayerReader.cs
+++ b/Core/Addon/PlayerReader.cs
@@ -113,7 +113,9 @@ public string Target
public long Gold => reader.GetLongAtCell(44) + (reader.GetLongAtCell(45) * 1000000);
- public PlayerClassEnum PlayerClass => (PlayerClassEnum)reader.GetLongAtCell(46);
+ public RaceEnum PlayerRace => (RaceEnum)(int)(reader.GetLongAtCell(46) / 100f);
+
+ public PlayerClassEnum PlayerClass => (PlayerClassEnum)(int)(reader.GetLongAtCell(46) - ((int)PlayerRace * 100f));
public bool Unskinnable => reader.GetLongAtCell(47) != 0; // Returns 1 if creature is unskinnable
@@ -142,7 +144,14 @@ public string Target
public long SpellBeingCast => reader.GetLongAtCell(53);
public long ComboPoints => reader.GetLongAtCell(54);
- public long PlayerDebuffCount => reader.GetLongAtCell(55);
+ public AuraCount AuraCount => new AuraCount(reader, 55);
+
+ public int PlayerDebuffCount => AuraCount.PlayerDebuff;
+ public int PlayerBuffCount => AuraCount.PlayerBuff;
+
+ public int TargetBuffCount => AuraCount.TargetBuff;
+ public int TargetDebuffCount => AuraCount.TargetDebuff;
+
public int TargetId => (int)reader.GetLongAtCell(56);
public long TargetGuid => reader.GetLongAtCell(57);
@@ -156,6 +165,8 @@ public string Target
public RecordInt AutoShot = new RecordInt(60);
public RecordInt MainHandSwing = new RecordInt(61);
+ public RecordInt CastEvent = new RecordInt(62);
+ public RecordInt CastSpellId = new RecordInt(63);
public RecordInt CombatCreatureGuid = new RecordInt(64);
public RecordInt CombatDamageDoneGuid = new RecordInt(65);
@@ -166,8 +177,11 @@ public string Target
public int PetTargetGuid => (int)reader.GetLongAtCell(69);
public bool PetHasTarget => PetTargetGuid != 0;
- public RecordInt GlobalTime = new RecordInt(70);
- public long LastLootTime => reader.GetLongAtCell(71);
+ public int CastCount => (int)reader.GetLongAtCell(70);
+
+ public long LastLootTime => reader.GetLongAtCell(97);
+
+ public RecordInt GlobalTime = new RecordInt(98);
// https://wowpedia.fandom.com/wiki/Mob_experience
public bool TargetYieldXP => PlayerLevel switch
@@ -278,21 +292,25 @@ internal void Updated()
Reset();
}
+ if (UIErrorMessage > 0)
+ {
+ LastUIErrorMessage = (UI_ERROR)UIErrorMessage;
+ }
+
UIMapId.Update(reader);
AutoShot.Update(reader);
MainHandSwing.Update(reader);
+ CastEvent.Update(reader);
+ CastSpellId.Update(reader);
UpdateCreatureLists();
-
- if (UIErrorMessage > 0)
- {
- LastUIErrorMessage = (UI_ERROR)UIErrorMessage;
- }
}
internal void Reset()
{
+ FormCost.Clear();
+
// Reset all CreatureHistory
Creatures.Clear();
DamageTaken.Clear();
@@ -305,6 +323,8 @@ internal void Reset()
AutoShot.Reset();
MainHandSwing.Reset();
+ CastEvent.Reset();
+ CastSpellId.Reset();
CombatCreatureGuid.Reset();
CombatDamageDoneGuid.Reset();
diff --git a/Core/Addon/RaceEnum.cs b/Core/Addon/RaceEnum.cs
new file mode 100644
index 000000000..d05640aea
--- /dev/null
+++ b/Core/Addon/RaceEnum.cs
@@ -0,0 +1,18 @@
+namespace Core
+{
+ public enum RaceEnum
+ {
+ None,
+ Human,
+ Orc,
+ Dwarf,
+ NightElf,
+ Undead,
+ Tauren,
+ Gnome,
+ Troll,
+ Goblin,
+ BloodElf,
+ Draenei
+ }
+}
\ No newline at end of file
diff --git a/Core/Addon/RecordInt.cs b/Core/Addon/RecordInt.cs
index a9dde119f..e5bda91b2 100644
--- a/Core/Addon/RecordInt.cs
+++ b/Core/Addon/RecordInt.cs
@@ -49,5 +49,10 @@ public void Reset()
Value = 0;
temp = 0;
}
+
+ public void ForceUpdate(int value)
+ {
+ Value = value;
+ }
}
}
\ No newline at end of file
diff --git a/Core/Addon/SpellInRange.cs b/Core/Addon/SpellInRange.cs
index fd20486f9..c91a16beb 100644
--- a/Core/Addon/SpellInRange.cs
+++ b/Core/Addon/SpellInRange.cs
@@ -28,10 +28,10 @@ public bool IsBitSet(int pos)
// Priest
public bool Priest_ShadowWordPain => IsBitSet(0);
-
- public bool Priest_MindBlast => IsBitSet(1);
+ public bool Priest_Shoot => IsBitSet(1);
public bool Priest_MindFlay => IsBitSet(2);
- public bool Priest_Shoot => IsBitSet(3);
+ public bool Priest_MindBlast => IsBitSet(3);
+ public bool Priest_Smite => IsBitSet(4);
// Druid
public bool Druid_Wrath => IsBitSet(0);
@@ -66,8 +66,8 @@ public bool IsBitSet(int pos)
{
PlayerClassEnum.Warrior => (playerReader.PlayerLevel >= 4 && Warrior_Charge) || playerReader.IsInMeleeRange,
PlayerClassEnum.Rogue => Rogue_Throw,
- PlayerClassEnum.Priest => Priest_ShadowWordPain,
- PlayerClassEnum.Druid => playerReader.Form == Form.Druid_Bear ? Druid_Maul : playerReader.Form == Form.Druid_Cat ? Druid_Rip : Druid_Wrath,
+ PlayerClassEnum.Priest => Priest_Smite,
+ PlayerClassEnum.Druid => Druid_Wrath,
PlayerClassEnum.Paladin => (playerReader.PlayerLevel >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange,
PlayerClassEnum.Mage => (playerReader.PlayerLevel >= 4 && Mage_Frostbolt) || Mage_Fireball,
PlayerClassEnum.Hunter => (playerReader.PlayerLevel >=4 && Hunter_SerpentSting) || Hunter_AutoShoot,
@@ -80,8 +80,8 @@ public bool IsBitSet(int pos)
{
PlayerClassEnum.Warrior => (playerReader.PlayerLevel >= 4 && Warrior_Rend) || playerReader.IsInMeleeRange,
PlayerClassEnum.Rogue => Rogue_SinisterStrike,
- PlayerClassEnum.Priest => Priest_Shoot,
- PlayerClassEnum.Druid => playerReader.Form == Form.Druid_Bear ? Druid_Maul : playerReader.Form == Form.Druid_Cat ? Druid_Rip : Druid_Wrath,
+ PlayerClassEnum.Priest => Priest_Smite,
+ PlayerClassEnum.Druid => Druid_Wrath || playerReader.IsInMeleeRange,
PlayerClassEnum.Paladin => (playerReader.PlayerLevel >= 4 && Paladin_Judgement) || playerReader.IsInMeleeRange,
PlayerClassEnum.Mage => Mage_Frostbolt || Mage_Fireball,
PlayerClassEnum.Hunter => (playerReader.PlayerLevel >= 4 && Hunter_SerpentSting) || Hunter_AutoShoot || playerReader.IsInMeleeRange,
diff --git a/Core/Addon/Stance.cs b/Core/Addon/Stance.cs
index 3238b234a..e50c2d442 100644
--- a/Core/Addon/Stance.cs
+++ b/Core/Addon/Stance.cs
@@ -68,15 +68,17 @@ public Stance(long value)
_ => Form.None
};
- public static int RuntimeSlotToActionBar(PlayerReader playerReader, int slot)
+ public static int RuntimeSlotToActionBar(KeyAction item, PlayerReader playerReader, int slot)
{
- if (slot > 12 || playerReader.Form == Form.None)
- return 0;
+ if (slot <= 12)
+ {
+ return FormToActionBar(playerReader.PlayerClass, item.HasFormRequirement() ? item.FormEnum : playerReader.Form);
+ }
- return FormToActionBar(playerReader.PlayerClass, playerReader.Form);
+ return 0;
}
- public static int FormToActionBar(PlayerClassEnum playerClass, Form form)
+ private static int FormToActionBar(PlayerClassEnum playerClass, Form form)
{
switch (playerClass)
{
diff --git a/Core/Addon/UI_ERROR.cs b/Core/Addon/UI_ERROR.cs
index 8f0f61a58..b8c0426f2 100644
--- a/Core/Addon/UI_ERROR.cs
+++ b/Core/Addon/UI_ERROR.cs
@@ -11,6 +11,13 @@ public enum UI_ERROR
SPELL_FAILED_MOVING = 6,
ERR_SPELL_COOLDOWN = 7,
ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS = 8,
- ERR_SPELL_FAILED_STUNNED = 9
+ ERR_SPELL_FAILED_STUNNED = 9,
+ ERR_SPELL_FAILED_INTERRUPTED = 10,
+ SPELL_FAILED_ITEM_NOT_READY = 11,
+
+ MAX_ERROR_RANGE = 2000,
+
+ CAST_START = 999998,
+ CAST_SUCCESS = 999999
}
}
\ No newline at end of file
diff --git a/Core/Blacklist/Blacklist.cs b/Core/Blacklist/Blacklist.cs
index b08fd720c..600f6ec60 100644
--- a/Core/Blacklist/Blacklist.cs
+++ b/Core/Blacklist/Blacklist.cs
@@ -43,6 +43,10 @@ public bool IsTargetBlacklisted()
LastWarningTargetGuid = 0;
return false;
}
+ else if (playerReader.DamageTaken.Exists(x => x.LastKnownHealthPercent > 0 && x.CreatureId == playerReader.TargetGuid))
+ {
+ return false;
+ }
if(this.playerReader.PetHasTarget &&
this.playerReader.TargetGuid == playerReader.PetGuid)
diff --git a/Core/BotController.cs b/Core/BotController.cs
index 2f2ed4fba..f0ceecdab 100644
--- a/Core/BotController.cs
+++ b/Core/BotController.cs
@@ -127,7 +127,7 @@ public BotController(ILogger logger, IPPather pather, DataConfig dataConfig, ICo
Thread.Sleep(100);
}
- logger.LogDebug($"Woohoo, I have read the player class. You are a {AddonReader.PlayerReader.PlayerClass}.");
+ logger.LogDebug($"Woohoo, I have read the player class. You are a {AddonReader.PlayerReader.PlayerRace} {AddonReader.PlayerReader.PlayerClass}.");
npcNameFinder = new NpcNameFinder(logger, WowScreen);
npcNameTargeting = new NpcNameTargeting(logger, npcNameFinder, WowProcessInput);
diff --git a/Core/ClassConfig/ClassConfiguration.cs b/Core/ClassConfig/ClassConfiguration.cs
index f5caeec1a..61766fde0 100644
--- a/Core/ClassConfig/ClassConfiguration.cs
+++ b/Core/ClassConfig/ClassConfiguration.cs
@@ -104,12 +104,15 @@ public void Initialise(DataConfig dataConfig, AddonReader addonReader, Requireme
Interact.Name = "Interact";
Interact.WaitForGCD = false;
Interact.DelayAfterCast = 0;
+ Interact.PressDuration = 30;
Interact.Initialise(addonReader, requirementFactory, logger);
Approach.Key = InteractKey;
Approach.Name = "Approach";
Approach.WaitForGCD = false;
Approach.DelayAfterCast = 0;
+ Approach.PressDuration = 10;
+ Approach.Cooldown = 150;
Approach.Initialise(addonReader, requirementFactory, logger);
AutoAttack.Key = InteractKey;
@@ -118,15 +121,19 @@ public void Initialise(DataConfig dataConfig, AddonReader addonReader, Requireme
AutoAttack.DelayAfterCast = 0;
AutoAttack.Initialise(addonReader, requirementFactory, logger);
+ StopAttack.PressDuration = 10;
+
InitializeKeyActions(Pull, Interact, Approach, AutoAttack);
InitializeKeyActions(Combat, Interact, Approach, AutoAttack);
+ logger.LogInformation("[Form] Initialise KeyActions.");
Form.ForEach(i => i.InitialiseForm(addonReader, requirementFactory, logger));
- Pull.Initialise(addonReader, requirementFactory, logger);
- Combat.Initialise(addonReader, requirementFactory, logger);
- Adhoc.Initialise(addonReader, requirementFactory, logger);
- NPC.Initialise(addonReader, requirementFactory, logger);
- Parallel.Initialise(addonReader, requirementFactory, logger);
+
+ Pull.Initialise("Pull", addonReader, requirementFactory, logger);
+ Combat.Initialise("Combat", addonReader, requirementFactory, logger);
+ Adhoc.Initialise("Adhoc", addonReader, requirementFactory, logger);
+ NPC.Initialise("AdhocNpc", addonReader, requirementFactory, logger);
+ Parallel.Initialise("Parallel", addonReader, requirementFactory, logger);
Jump.Key = JumpKey;
Jump.Initialise(addonReader, requirementFactory, logger);
@@ -153,6 +160,7 @@ public void Initialise(DataConfig dataConfig, AddonReader addonReader, Requireme
TargetTargetOfTarget.Initialise(addonReader, requirementFactory, logger);
PetAttack.Key = PetAttackKey;
+ PetAttack.PressDuration = 10;
PetAttack.Initialise(addonReader, requirementFactory, logger);
Mount.Key = MountKey;
@@ -191,6 +199,8 @@ private static void InitializeKeyActions(KeyActions keyActions, params KeyAction
a.Key = l.Key;
a.DelayAfterCast = l.DelayAfterCast;
a.WaitForGCD = l.WaitForGCD;
+ a.PressDuration = l.PressDuration;
+ a.Cooldown = l.Cooldown;
}
});
});
diff --git a/Core/ClassConfig/KeyAction.cs b/Core/ClassConfig/KeyAction.cs
index 075634ebe..926a95bac 100644
--- a/Core/ClassConfig/KeyAction.cs
+++ b/Core/ClassConfig/KeyAction.cs
@@ -60,16 +60,16 @@ public class KeyAction
public List RequirementObjects { get; } = new List();
- protected static ConcurrentDictionary LastClicked { get; } = new ConcurrentDictionary();
+ public int ConsoleKeyFormHash;
- public static ConsoleKey LastKeyClicked()
- {
- if (!LastClicked.Any()) { return ConsoleKey.NoName; }
+ protected static ConcurrentDictionary LastClicked { get; } = new ConcurrentDictionary();
- var last = LastClicked.OrderByDescending(s => s.Value).First();
- if ( (DateTime.Now- last.Value).TotalSeconds>2)
+ public static int LastKeyClicked()
+ {
+ var last = LastClicked.OrderByDescending(s => s.Value).FirstOrDefault();
+ if (last.Key == 0 || (DateTime.Now - last.Value).TotalSeconds > 2)
{
- return ConsoleKey.NoName;
+ return (int)ConsoleKey.NoName;
}
return last.Key;
}
@@ -92,7 +92,7 @@ public void Initialise(AddonReader addonReader, RequirementFactory requirementFa
Requirements.Add(this.Requirement);
}
- if (!string.IsNullOrEmpty(Form))
+ if (HasFormRequirement())
{
if (Enum.TryParse(typeof(Form), Form, out var desiredForm))
{
@@ -101,7 +101,7 @@ public void Initialise(AddonReader addonReader, RequirementFactory requirementFa
if (KeyReader.ActionBarSlotMap.TryGetValue(Key, out int slot))
{
- int offset = Stance.FormToActionBar(playerReader.PlayerClass, FormEnum);
+ int offset = Stance.RuntimeSlotToActionBar(this, playerReader, slot);
this.logger.LogInformation($"[{Name}] Actionbar Form key map: Key:{Key} -> Actionbar:{slot} -> Form Map:{slot + offset}");
}
}
@@ -111,6 +111,8 @@ public void Initialise(AddonReader addonReader, RequirementFactory requirementFa
}
}
+ ConsoleKeyFormHash = ((int)FormEnum * 1000) + (int)ConsoleKey;
+
UpdateMinResourceRequirement(playerReader, addonReader.ActionBarCostReader);
requirementFactory.InitialiseRequirements(this);
@@ -120,7 +122,7 @@ public void InitialiseForm(AddonReader addonReader, RequirementFactory requireme
{
Initialise(addonReader, requirementFactory, logger);
- if (!string.IsNullOrEmpty(Form))
+ if (HasFormRequirement())
{
if (addonReader.PlayerReader.FormCost.ContainsKey(FormEnum))
{
@@ -128,7 +130,7 @@ public void InitialiseForm(AddonReader addonReader, RequirementFactory requireme
}
addonReader.PlayerReader.FormCost.Add(FormEnum, MinMana);
- LogInformation($"Added {FormEnum} to FormCost with {MinMana}");
+ logger.LogInformation($"[{Name}] Added {FormEnum} to FormCost with {MinMana}");
}
}
@@ -139,7 +141,7 @@ public void CreateCooldownRequirement()
this.RequirementObjects.Add(new Requirement
{
HasRequirement = () => GetCooldownRemaining() == 0,
- LogMessage = () => $"Cooldown {GetCooldownRemaining()}",
+ LogMessage = () => $"Cooldown {GetCooldownRemaining() / 1000:F1}",
VisibleIfHasRequirement = false
});
}
@@ -149,12 +151,12 @@ public float GetCooldownRemaining()
{
try
{
- if (!LastClicked.ContainsKey(this.ConsoleKey))
+ if (!LastClicked.ContainsKey(ConsoleKeyFormHash))
{
return 0;
}
- var remaining = this.Cooldown - ((int)(DateTime.Now - LastClicked[this.ConsoleKey]).TotalSeconds);
+ var remaining = Cooldown - (float)(DateTime.Now - LastClicked[ConsoleKeyFormHash]).TotalMilliseconds;
return remaining < 0 ? 0 : remaining;
}
@@ -165,6 +167,12 @@ public float GetCooldownRemaining()
}
}
+ public bool CanDoFormChangeAndHaveMinimumMana()
+ {
+ return playerReader != null &&
+ (playerReader.FormCost.ContainsKey(FormEnum) && playerReader.ManaCurrent >= playerReader.FormCost[FormEnum] + MinMana);
+ }
+
internal void SetClicked()
{
try
@@ -174,13 +182,13 @@ internal void SetClicked()
LastClickPostion = this.playerReader.PlayerLocation;
}
- if (LastClicked.ContainsKey(this.ConsoleKey))
+ if (LastClicked.ContainsKey(ConsoleKeyFormHash))
{
- LastClicked[this.ConsoleKey] = DateTime.Now;
+ LastClicked[ConsoleKeyFormHash] = DateTime.Now;
}
else
{
- LastClicked.TryAdd(this.ConsoleKey, DateTime.Now);
+ LastClicked.TryAdd(ConsoleKeyFormHash, DateTime.Now);
}
}
catch (Exception ex)
@@ -189,14 +197,11 @@ internal void SetClicked()
}
}
- public double MillisecondsSinceLastClick => LastClicked.ContainsKey(this.ConsoleKey) ? (DateTime.Now - LastClicked[this.ConsoleKey]).TotalMilliseconds : double.MaxValue;
+ public double MillisecondsSinceLastClick => LastClicked.ContainsKey(ConsoleKeyFormHash) ? (DateTime.Now - LastClicked[ConsoleKeyFormHash]).TotalMilliseconds : double.MaxValue;
internal void ResetCooldown()
{
- if (LastClicked.ContainsKey(ConsoleKey))
- {
- LastClicked.TryRemove(ConsoleKey, out _);
- }
+ LastClicked.TryRemove(ConsoleKeyFormHash, out _);
}
public void CreateChargeRequirement()
@@ -244,6 +249,11 @@ public bool CanRun()
return !this.RequirementObjects.Any(r => !r.HasRequirement());
}
+ public bool HasFormRequirement()
+ {
+ return !string.IsNullOrEmpty(Form);
+ }
+
private void UpdateMinResourceRequirement(PlayerReader playerReader, ActionBarCostReader actionBarCostReader)
{
var tuple = actionBarCostReader.GetCostByActionBarSlot(playerReader, this);
@@ -266,7 +276,13 @@ private void UpdateMinResourceRequirement(PlayerReader playerReader, ActionBarCo
break;
}
- logger.LogInformation($"[{Name}] Update {tuple.Item1} cost to {tuple.Item2} from {oldValue}");
+ int formCost = 0;
+ if (HasFormRequirement() && FormEnum != Core.Form.None && playerReader.FormCost.ContainsKey(FormEnum))
+ {
+ formCost = playerReader.FormCost[FormEnum];
+ }
+
+ logger.LogInformation($"[{Name}] Update {tuple.Item1} cost to {tuple.Item2} from {oldValue}" + (formCost > 0 ? $" +{formCost} Mana to change {FormEnum} Form" : ""));
}
}
diff --git a/Core/ClassConfig/KeyActions.cs b/Core/ClassConfig/KeyActions.cs
index a5ec37238..29ef1ce6f 100644
--- a/Core/ClassConfig/KeyActions.cs
+++ b/Core/ClassConfig/KeyActions.cs
@@ -7,8 +7,13 @@ public class KeyActions
{
public List Sequence { get; } = new List();
- public void Initialise(AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger)
+ public void Initialise(string prefix, AddonReader addonReader, RequirementFactory requirementFactory, ILogger logger)
{
+ if (Sequence.Count > 0)
+ {
+ logger.LogInformation($"[{prefix}] Initialise KeyActions.");
+ }
+
Sequence.ForEach(i => i.Initialise(addonReader, requirementFactory, logger));
}
}
diff --git a/Core/Goals/ApproachTargetGoal.cs b/Core/Goals/ApproachTargetGoal.cs
index 22785ad53..9f8b75591 100644
--- a/Core/Goals/ApproachTargetGoal.cs
+++ b/Core/Goals/ApproachTargetGoal.cs
@@ -82,6 +82,7 @@ public override async Task OnEnter()
public override async Task PerformAction()
{
lastPlayerLocation = playerReader.PlayerLocation;
+ await wait.Update(1);
if (!playerReader.PlayerBitValues.PlayerInCombat)
{
@@ -110,12 +111,14 @@ public override async Task PerformAction()
playerWasInCombat = true;
}
- await input.TapInteractKey("");
- await wait.Update(1);
+ if (input.ClassConfig.Approach.GetCooldownRemaining() == 0)
+ {
+ await input.TapApproachKey("");
+ }
lastPlayerDistance = WowPoint.DistanceTo(lastPlayerLocation, playerReader.PlayerLocation);
- if (lastPlayerDistance < 0.5 && playerReader.LastUIErrorMessage == UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR)
+ if (lastPlayerDistance < 0.05 && playerReader.LastUIErrorMessage == UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR)
{
playerReader.LastUIErrorMessage = UI_ERROR.NONE;
@@ -123,11 +126,11 @@ public override async Task PerformAction()
await wait.Update(1);
}
- if (SecondsSinceApproachStarted > 1 && lastPlayerDistance < 0.5 && !playerReader.PlayerBitValues.PlayerInCombat)
+ if (SecondsSinceApproachStarted > 1 && lastPlayerDistance < 0.05 && !playerReader.PlayerBitValues.PlayerInCombat)
{
await input.TapClearTarget("");
await wait.Update(1);
- await input.KeyPress(random.Next(2) == 0 ? ConsoleKey.LeftArrow : ConsoleKey.RightArrow, 1000, "Seems stuck! Clear Target. Turn away.");
+ await input.KeyPress(random.Next(2) == 0 ? ConsoleKey.LeftArrow : ConsoleKey.RightArrow, 1000, $"Seems stuck! Clear Target. Turn away. d: {lastPlayerDistance}");
approachStart = DateTime.Now;
}
diff --git a/Core/Goals/CastingHandler.cs b/Core/Goals/CastingHandler.cs
index f97f1f278..a35f80317 100644
--- a/Core/Goals/CastingHandler.cs
+++ b/Core/Goals/CastingHandler.cs
@@ -21,11 +21,14 @@ public class CastingHandler
private readonly StopMoving stopMoving;
private readonly KeyAction defaultKeyAction = new KeyAction();
- private const int MaxWaitCastTimeMs = 500;
- private const int MaxWaitBuffTimeMs = 500;
+
+ private const int GCD = 1500;
+ private const int SpellQueueTimeMs = 325;
+
+ private const int MaxWaitCastTimeMs = GCD;
+ private const int MaxWaitBuffTimeMs = GCD;
private const int MaxCastTimeMs = 15000;
private const int MaxSwingTimeMs = 4000;
- private const int GCD = 1500;
private const int MaxAirTimeMs = 10000;
public CastingHandler(ILogger logger, ConfigurableInput input, Wait wait, PlayerReader playerReader, ClassConfiguration classConfig, IPlayerDirection direction, NpcNameFinder npcNameFinder, StopMoving stopMoving)
@@ -49,9 +52,13 @@ protected bool CanRun(KeyAction item)
var needAdds = bool.Parse(item.CastIfAddsVisible);
if (needAdds != npcNameFinder.PotentialAddsExist)
{
- item.LogInformation($"Only cast if adds exist = {item.CastIfAddsVisible} and it is {npcNameFinder.PotentialAddsExist}");
+ item.LogInformation($"Only cast if adds exist = {item.CastIfAddsVisible} and it is {npcNameFinder.PotentialAddsExist} - Targets:{npcNameFinder.TargetCount} - Adds:{npcNameFinder.AddCount}");
return false;
}
+ else
+ {
+ item.LogInformation($"Only cast if adds exist = {item.CastIfAddsVisible} and it is {npcNameFinder.PotentialAddsExist} - Targets:{npcNameFinder.TargetCount} - Adds:{npcNameFinder.AddCount}");
+ }
}
if(item.School != SchoolMask.None)
@@ -95,22 +102,56 @@ private async Task PressKeyAction(KeyAction item)
item.SetClicked();
}
+ private static bool CastSuccessfull(UI_ERROR uiEvent)
+ {
+ return
+ uiEvent == UI_ERROR.CAST_START ||
+ uiEvent == UI_ERROR.CAST_SUCCESS ||
+ uiEvent == UI_ERROR.NONE;
+ }
+
private async Task CastInstant(KeyAction item)
{
if (item.StopBeforeCast)
{
await stopMoving.Stop();
+ await wait.Update(1);
}
- float minRange = playerReader.MinRange;
+ playerReader.CastEvent.ForceUpdate(0);
+ int beforeCastEventValue = playerReader.CastEvent.Value;
+ int beforeSpellId = playerReader.CastSpellId.Value;
+ bool beforeUsable = playerReader.UsableAction.Is(item);
await PressKeyAction(item);
- (bool input, double inputElapsedMs) = await wait.InterruptTask(item.AfterCastWaitNextSwing ? MaxSwingTimeMs : MaxWaitCastTimeMs,
- () => playerReader.LastUIErrorMessage != UI_ERROR.NONE || !playerReader.CurrentAction.Is(item) || minRange != playerReader.MinRange);
- if (!input)
+ bool inputNotHappened;
+ double inputElapsedMs;
+
+ if (item.AfterCastWaitNextSwing)
+ {
+ (inputNotHappened, inputElapsedMs) = await wait.InterruptTask(MaxSwingTimeMs,
+ interrupt: () => !playerReader.CurrentAction.Is(item),
+ repeat: async () =>
+ {
+ if (classConfig.Approach.GetCooldownRemaining() == 0)
+ {
+ await input.TapApproachKey("");
+ }
+ });
+ }
+ else
+ {
+ (inputNotHappened, inputElapsedMs) = await wait.InterruptTask(MaxWaitCastTimeMs,
+ interrupt: () =>
+ (beforeSpellId != playerReader.CastSpellId.Value && beforeCastEventValue != playerReader.CastEvent.Value) ||
+ beforeUsable != playerReader.UsableAction.Is(item)
+ );
+ }
+
+ if (!inputNotHappened)
{
- item.LogInformation($" ... instant input after {inputElapsedMs}ms");
+ item.LogInformation($" ... instant input {inputElapsedMs}ms");
}
else
{
@@ -118,34 +159,27 @@ private async Task CastInstant(KeyAction item)
return false;
}
- item.LogInformation($" ... usable: {playerReader.UsableAction.Is(item)} -- {playerReader.LastUIErrorMessage}");
+ item.LogInformation($" ... usable: {beforeUsable}->{playerReader.UsableAction.Is(item)} -- ({(UI_ERROR)beforeCastEventValue}->{(UI_ERROR)playerReader.CastEvent.Value})");
- if (playerReader.LastUIErrorMessage != UI_ERROR.NONE)
+ if (!CastSuccessfull((UI_ERROR)playerReader.CastEvent.Value) || !(beforeUsable && !playerReader.UsableAction.Is(item)))
{
- if (playerReader.LastUIErrorMessage == UI_ERROR.ERR_SPELL_COOLDOWN)
- {
- item.LogInformation($" ... instant wait until its ready");
- bool before = playerReader.UsableAction.Is(item);
- await wait.While(() => before != playerReader.UsableAction.Is(item));
- }
- else
- {
- await ReactToLastUIErrorMessage($"{item.Name}-{GetType().Name}: CastInstant");
- }
-
+ await ReactToLastCastingEvent(item, $"{item.Name}-{GetType().Name}: CastInstant");
return false;
}
+ if (item.RequirementObjects.Any())
+ {
+ (bool firstReq, double firstReqElapsedMs) = await wait.InterruptTask(SpellQueueTimeMs,
+ () => !item.CanRun()
+ );
+ item.LogInformation($" ... instant interrupt: {!firstReq} | CanRun:{item.CanRun()} | Delay: {firstReqElapsedMs}ms");
+ }
+
return true;
}
private async Task CastCastbar(KeyAction item)
{
- if (item.StopBeforeCast)
- {
- await stopMoving.Stop();
- }
-
if (playerReader.PlayerBitValues.IsFalling)
{
(bool notfalling, double fallingElapsedMs) = await wait.InterruptTask(MaxAirTimeMs, () => !playerReader.PlayerBitValues.IsFalling);
@@ -155,15 +189,28 @@ private async Task CastCastbar(KeyAction item)
}
}
+ await stopMoving.Stop();
+ await wait.Update(1);
+
bool beforeHasTarget = playerReader.HasTarget;
+ bool beforeUsable = playerReader.UsableAction.Is(item);
+ int beforeCastEventValue = playerReader.CastEvent.Value;
+ int beforeSpellId = playerReader.CastSpellId.Value;
+ int beforeCastCount = playerReader.CastCount;
+
await PressKeyAction(item);
(bool input, double inputElapsedMs) = await wait.InterruptTask(MaxWaitCastTimeMs,
- () => playerReader.IsCasting || playerReader.LastUIErrorMessage != UI_ERROR.NONE);
+ interrupt: () =>
+ beforeCastEventValue != playerReader.CastEvent.Value ||
+ beforeSpellId != playerReader.CastSpellId.Value ||
+ beforeCastCount != playerReader.CastCount
+ );
+
if (!input)
{
- item.LogInformation($" ... castbar input after {inputElapsedMs}ms");
+ item.LogInformation($" ... castbar input {inputElapsedMs}ms");
}
else
{
@@ -171,26 +218,25 @@ private async Task CastCastbar(KeyAction item)
return false;
}
- item.LogInformation($" ... usable: {playerReader.UsableAction.Is(item)} -- {playerReader.LastUIErrorMessage}");
+ item.LogInformation($" ... casting: {playerReader.IsCasting} -- count:{playerReader.CastCount} -- usable: {beforeUsable}->{playerReader.UsableAction.Is(item)} -- {(UI_ERROR)beforeCastEventValue}->{(UI_ERROR)playerReader.CastEvent.Value}");
- if (playerReader.LastUIErrorMessage != UI_ERROR.NONE)
+ if (!CastSuccessfull((UI_ERROR)playerReader.CastEvent.Value))
{
- if (playerReader.LastUIErrorMessage == UI_ERROR.ERR_SPELL_COOLDOWN)
- {
- item.LogInformation($" ... castbar wait until its ready");
- bool before = playerReader.UsableAction.Is(item);
- await wait.While(() => before != playerReader.UsableAction.Is(item));
- }
- else
- {
- await ReactToLastUIErrorMessage($"{item.Name}-{GetType().Name}: CastCastbar");
- }
-
+ await ReactToLastCastingEvent(item, $"{item.Name}-{GetType().Name}: CastCastbar");
return false;
}
- item.LogInformation(" ... waiting for cast bar to end or target loss.");
- await wait.InterruptTask(MaxCastTimeMs, () => !playerReader.IsCasting || beforeHasTarget != playerReader.HasTarget);
+ if (playerReader.IsCasting)
+ {
+ item.LogInformation(" ... waiting for visible cast bar to end or target loss.");
+ await wait.InterruptTask(MaxCastTimeMs, () => !playerReader.IsCasting || beforeHasTarget != playerReader.HasTarget);
+ }
+ else if ((UI_ERROR)playerReader.CastEvent.Value == UI_ERROR.CAST_START)
+ {
+ beforeCastEventValue = playerReader.CastEvent.Value;
+ item.LogInformation(" ... waiting for hidden cast bar to end or target loss.");
+ await wait.InterruptTask(MaxCastTimeMs, () => beforeCastEventValue != playerReader.CastEvent.Value || beforeHasTarget != playerReader.HasTarget);
+ }
return true;
}
@@ -207,14 +253,33 @@ public async Task CastIfReady(KeyAction item, int sleepBeforeCast = 0)
return false;
}
- if (!await SwitchToCorrectStanceForm(item))
+ if (item.Name == classConfig.Approach.Name ||
+ item.Name == classConfig.AutoAttack.Name ||
+ item.Name == classConfig.Interact.Name)
+ {
+ await PressKeyAction(item);
+ return true;
+ }
+
+ bool beforeUsable = playerReader.UsableAction.Is(item);
+ var beforeForm = playerReader.Form;
+
+ if (!await SwitchToCorrectStanceForm(beforeForm, item))
{
return false;
}
+ if (beforeForm != playerReader.Form && !beforeUsable && !playerReader.UsableAction.Is(item))
+ {
+ item.LogInformation(" ... after Form switch still not usable!");
+ return false;
+ }
+
if (playerReader.IsShooting)
{
await input.TapStopAttack("Stop AutoRepeat Shoot");
+ await input.TapStopAttack("Stop AutoRepeat Shoot");
+ await wait.Update(1);
(bool interrupted, double elapsedMs) = await wait.InterruptTask(GCD,
() => playerReader.UsableAction.Is(item));
@@ -227,31 +292,25 @@ public async Task CastIfReady(KeyAction item, int sleepBeforeCast = 0)
if (sleepBeforeCast > 0)
{
+ if (item.StopBeforeCast || item.HasCastBar)
+ {
+ await stopMoving.Stop();
+ await wait.Update(1);
+ await stopMoving.Stop();
+ await wait.Update(1);
+ }
+
item.LogInformation($" Wait {sleepBeforeCast}ms before press.");
await Task.Delay(sleepBeforeCast);
}
- long beforeBuff = playerReader.Buffs.Value;
bool beforeHasTarget = playerReader.HasTarget;
+ int auraHash = playerReader.AuraCount.Hash;
- if (item.WaitForGCD)
- {
- (bool gcd, double gcdElapsedMs) = await wait.InterruptTask(GCD,
- () => playerReader.UsableAction.Is(item) || beforeHasTarget != playerReader.HasTarget);
- if (!gcd)
- {
- item.LogInformation($" ... gcd interrupted {gcdElapsedMs}ms");
- if (beforeHasTarget != playerReader.HasTarget)
- {
- item.LogInformation($" ... lost target!");
- return false;
- }
- }
- else
- {
- item.LogInformation($" ... gcd fully waited {gcdElapsedMs}ms");
- }
+ if (!await WaitForGCD(item, beforeHasTarget))
+ {
+ return false;
}
if (!item.HasCastBar)
@@ -279,11 +338,8 @@ public async Task CastIfReady(KeyAction item, int sleepBeforeCast = 0)
if (item.AfterCastWaitBuff)
{
- (bool notappeared, double elapsedMs) = await wait.InterruptTask(MaxWaitBuffTimeMs, () => beforeBuff != playerReader.Buffs.Value);
- if (!notappeared)
- logger.LogInformation($" ... AfterCastWaitBuff: Buff: {!notappeared} | Delay: {elapsedMs}ms");
- else
- logger.LogInformation($" ... AfterCastWaitBuff: No buff | Delay: {elapsedMs}ms");
+ (bool notappeared, double elapsedMs) = await wait.InterruptTask(MaxWaitBuffTimeMs, () => auraHash != playerReader.AuraCount.Hash);
+ item.LogInformation($" ... AfterCastWaitBuff: Buff: {!notappeared} | pb: {playerReader.AuraCount.PlayerBuff} | pd: {playerReader.AuraCount.PlayerDebuff} | tb: {playerReader.AuraCount.TargetBuff} | td: {playerReader.AuraCount.TargetDebuff} | Delay: {elapsedMs}ms");
}
if (item.DelayAfterCast != defaultKeyAction.DelayAfterCast)
@@ -339,7 +395,32 @@ public async Task CastIfReady(KeyAction item, int sleepBeforeCast = 0)
return true;
}
- protected async Task SwitchToCorrectStanceForm(KeyAction item)
+ private async Task WaitForGCD(KeyAction item, bool beforeHasTarget)
+ {
+ if (item.WaitForGCD)
+ {
+ (bool gcd, double gcdElapsedMs) = await wait.InterruptTask(GCD,
+ () => playerReader.UsableAction.Is(item) || beforeHasTarget != playerReader.HasTarget);
+ if (!gcd)
+ {
+ item.LogInformation($" ... gcd interrupted {gcdElapsedMs}ms");
+
+ if (beforeHasTarget != playerReader.HasTarget)
+ {
+ item.LogInformation($" ... lost target!");
+ return false;
+ }
+ }
+ else
+ {
+ item.LogInformation($" ... gcd fully waited {gcdElapsedMs}ms");
+ }
+ }
+
+ return true;
+ }
+
+ protected async Task SwitchToCorrectStanceForm(Form beforeForm, KeyAction item)
{
if (string.IsNullOrEmpty(item.Form))
return true;
@@ -349,18 +430,25 @@ protected async Task SwitchToCorrectStanceForm(KeyAction item)
return true;
}
- var formKeyKey = classConfig.Form
+ var formKeyAction = classConfig.Form
.Where(s => s.FormEnum == item.FormEnum)
.FirstOrDefault();
- if (formKeyKey == null)
+ if (formKeyAction == null)
{
logger.LogWarning($"Unable to find key in Form to transform into {item.FormEnum}");
return false;
}
- await input.KeyPress(formKeyKey.ConsoleKey, item.PressDuration);
- await wait.Update(1);
+ await input.KeyPress(formKeyAction.ConsoleKey, formKeyAction.PressDuration);
+ (bool notChanged, double elapsedMs) = await wait.InterruptTask(SpellQueueTimeMs, () => beforeForm != playerReader.Form);
+ item.LogInformation($" ... form changed: {!notChanged} | Delay: {elapsedMs}ms");
+
+ if (playerReader.Form == Form.None)
+ {
+ item.LogInformation($" ... wait for GCD after form change {beforeForm}->{playerReader.Form}!");
+ await WaitForGCD(item, playerReader.HasTarget);
+ }
return playerReader.Form == item.FormEnum;
}
@@ -370,13 +458,17 @@ public async Task PressKey(ConsoleKey key, string description = "", int duration
await input.KeyPress(key, duration, description);
}
- public virtual async Task ReactToLastUIErrorMessage(string source)
+ public async Task ReactToLastUIErrorMessage(string source)
{
//var lastError = playerReader.LastUIErrorMessage;
switch (playerReader.LastUIErrorMessage)
{
case UI_ERROR.NONE:
break;
+ case UI_ERROR.CAST_START:
+ break;
+ case UI_ERROR.CAST_SUCCESS:
+ break;
case UI_ERROR.ERR_SPELL_FAILED_STUNNED:
long debuffCount = playerReader.PlayerDebuffCount;
if (debuffCount != 0)
@@ -384,6 +476,7 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_FAILED_STUNNED} -- Wait till losing debuff!");
await wait.While(() => debuffCount == playerReader.PlayerDebuffCount);
+ await wait.Update(1);
playerReader.LastUIErrorMessage = UI_ERROR.NONE;
}
else
@@ -398,9 +491,11 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
return;
}
- logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_OUT_OF_RANGE} -- Start moving forward");
-
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_OUT_OF_RANGE} -- Face enemy and start moving forward");
+ await input.TapInteractKey("");
input.SetKeyState(ConsoleKey.UpArrow, true, false, "");
+
+ await wait.Update(1);
playerReader.LastUIErrorMessage = UI_ERROR.NONE;
break;
case UI_ERROR.ERR_BADATTACKFACING:
@@ -419,6 +514,7 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
await direction.SetDirection(desiredDirection, new WowPoint(0, 0), "");
}
+ await wait.Update(1);
playerReader.LastUIErrorMessage = UI_ERROR.NONE;
break;
case UI_ERROR.SPELL_FAILED_MOVING:
@@ -431,9 +527,15 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
case UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS:
logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS} -- Wait till casting!");
await wait.While(() => playerReader.IsCasting);
+
+ await wait.Update(1);
+ playerReader.LastUIErrorMessage = UI_ERROR.NONE;
break;
case UI_ERROR.ERR_SPELL_COOLDOWN:
logger.LogInformation($"{source} -- Cant react to {UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS}");
+
+ await wait.Update(1);
+ playerReader.LastUIErrorMessage = UI_ERROR.NONE;
break;
case UI_ERROR.ERR_BADATTACKPOS:
if (playerReader.IsAutoAttacking)
@@ -441,6 +543,8 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_BADATTACKPOS} -- Interact!");
await input.TapInteractKey("");
await stopMoving.Stop();
+ await wait.Update(1);
+
playerReader.LastUIErrorMessage = UI_ERROR.NONE;
}
else
@@ -459,5 +563,140 @@ public virtual async Task ReactToLastUIErrorMessage(string source)
// break;
}
}
+
+ private async Task ReactToLastCastingEvent(KeyAction item, string source)
+ {
+ switch ((UI_ERROR)playerReader.CastEvent.Value)
+ {
+ case UI_ERROR.NONE:
+ break;
+ case UI_ERROR.ERR_SPELL_FAILED_INTERRUPTED:
+ item.SetClicked();
+ break;
+ case UI_ERROR.CAST_START:
+ break;
+ case UI_ERROR.CAST_SUCCESS:
+ break;
+ case UI_ERROR.ERR_SPELL_COOLDOWN:
+ logger.LogInformation($"{source} React to {UI_ERROR.ERR_SPELL_COOLDOWN} -- wait until its ready");
+ bool before = playerReader.UsableAction.Is(item);
+ await wait.While(() => before != playerReader.UsableAction.Is(item));
+
+ break;
+ case UI_ERROR.ERR_SPELL_FAILED_STUNNED:
+ long debuffCount = playerReader.PlayerDebuffCount;
+ if (debuffCount != 0)
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_FAILED_STUNNED} -- Wait till losing debuff!");
+ await wait.While(() => debuffCount == playerReader.PlayerDebuffCount);
+ }
+ else
+ {
+ logger.LogInformation($"{source} -- Didn't know how to react {UI_ERROR.ERR_SPELL_FAILED_STUNNED} when PlayerDebuffCount: {debuffCount}");
+ }
+
+ break;
+ case UI_ERROR.ERR_SPELL_OUT_OF_RANGE:
+ if (playerReader.PlayerClass == PlayerClassEnum.Hunter && playerReader.IsInMeleeRange)
+ {
+ logger.LogInformation($"{source} -- As a Hunter didn't know how to react {UI_ERROR.ERR_SPELL_OUT_OF_RANGE}");
+ return;
+ }
+
+ float minRange = playerReader.MinRange;
+ if (playerReader.PlayerBitValues.PlayerInCombat && playerReader.HasTarget && !playerReader.IsTargetCasting)
+ {
+ await wait.Update(2);
+ if (playerReader.TargetTarget == TargetTargetEnum.TargetIsTargettingMe)
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_OUT_OF_RANGE} -- Just wait for the target to get in range.");
+
+ (bool inputNotHappened, double inputElapsedMs) = await wait.InterruptTask(MaxWaitCastTimeMs,
+ () => minRange != playerReader.MinRange || playerReader.IsTargetCasting
+ );
+ }
+ }
+ else
+ {
+ double beforeDirection = playerReader.Direction;
+ await input.TapInteractKey("");
+ await input.TapStopAttack();
+ await stopMoving.Stop();
+ await wait.Update(1);
+
+ if (beforeDirection != playerReader.Direction)
+ {
+ await input.TapInteractKey("");
+
+ (bool inputNotHappened, double inputElapsedMs) = await wait.InterruptTask(MaxWaitCastTimeMs,
+ () => minRange != playerReader.MinRange);
+
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_OUT_OF_RANGE} -- Approached target {minRange}->{playerReader.MinRange}");
+ }
+ else
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_OUT_OF_RANGE} -- Start moving forward");
+ input.SetKeyState(ConsoleKey.UpArrow, true, false, "");
+ }
+
+
+ }
+
+ break;
+ case UI_ERROR.ERR_BADATTACKFACING:
+ if (playerReader.IsInMeleeRange)
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_BADATTACKFACING} -- Interact!");
+ await input.TapInteractKey("");
+ }
+ else
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_BADATTACKFACING} -- Turning 180!");
+
+ double desiredDirection = playerReader.Direction + Math.PI;
+ desiredDirection = desiredDirection > Math.PI * 2 ? desiredDirection - (Math.PI * 2) : desiredDirection;
+ await direction.SetDirection(desiredDirection, new WowPoint(0, 0), "");
+
+ await wait.Update(1);
+ }
+
+ break;
+ case UI_ERROR.SPELL_FAILED_MOVING:
+ logger.LogInformation($"{source} -- React to {UI_ERROR.SPELL_FAILED_MOVING} -- Stop moving!");
+ await stopMoving.Stop();
+ await wait.Update(1);
+
+ break;
+ case UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS:
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_SPELL_FAILED_ANOTHER_IN_PROGRESS} -- Wait till casting!");
+ await wait.While(() => playerReader.IsCasting);
+
+ break;
+ case UI_ERROR.ERR_BADATTACKPOS:
+ if (playerReader.IsAutoAttacking)
+ {
+ logger.LogInformation($"{source} -- React to {UI_ERROR.ERR_BADATTACKPOS} -- Interact!");
+ await input.TapInteractKey("");
+ await stopMoving.Stop();
+ await wait.Update(1);
+ }
+ else
+ {
+ logger.LogInformation($"{source} -- Didn't know how to React to {(UI_ERROR)playerReader.CastEvent.Value}");
+ }
+
+ break;
+ default:
+ logger.LogInformation($"{source} -- Didn't know how to React to {(UI_ERROR)playerReader.CastEvent.Value}");
+
+ break;
+ //case UI_ERROR.ERR_SPELL_FAILED_S:
+ //case UI_ERROR.ERR_BADATTACKPOS:
+ //case UI_ERROR.ERR_SPELL_OUT_OF_RANGE:
+ //case UI_ERROR.ERR_AUTOFOLLOW_TOO_FAR:
+ // this.playerReader.LastUIErrorMessage = UI_ERROR.NONE;
+ // break;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/Core/Goals/CombatGoal.cs b/Core/Goals/CombatGoal.cs
index 2495ddb9e..e5c6bb8ca 100644
--- a/Core/Goals/CombatGoal.cs
+++ b/Core/Goals/CombatGoal.cs
@@ -76,13 +76,14 @@ protected async Task Fight()
lastKnownMaxDistance = playerReader.MaxRange;
}
- if (playerReader.IsAutoAttacking)
- {
- await castingHandler.ReactToLastUIErrorMessage($"{GetType().Name}: Fight AutoAttacking");
- }
-
if (await castingHandler.CastIfReady(item, item.DelayBeforeCast))
{
+ if (item.Name == classConfiguration.Approach.Name ||
+ item.Name == classConfiguration.AutoAttack.Name)
+ {
+ await castingHandler.ReactToLastUIErrorMessage($"{GetType().Name}: Fight {item.Name}");
+ }
+
break;
}
}
@@ -132,14 +133,9 @@ public override async Task OnEnter()
await input.TapDismount();
}
- // this one is important for melees
- // if not waiting a bit, the player will constantly moving forward
- await stopMoving.Stop();
- await wait.Update(1);
lastDirectionForTurnAround = playerReader.Direction;
- logger.LogInformation($"{GetType().Name}: OnEnter");
SendActionEvent(new ActionEventArgs(GoapKey.fighting, true));
}
@@ -166,8 +162,9 @@ public override async Task PerformAction()
lastDirectionForTurnAround = playerReader.Direction;
}
- if (await StopDrowning())
+ if (playerReader.PlayerBitValues.IsDrowning)
{
+ await StopDrowning();
return;
}
@@ -218,31 +215,35 @@ private async Task CreatureTargetMeOrMyPet()
return playerReader.HasTarget;
}
- await input.TapNearestTarget($"{GetType().Name}: Checking target in front of me");
- await wait.Update(1);
- if (playerReader.HasTarget)
+ if (playerReader.CombatCreatureCount > 1)
{
- if (playerReader.PlayerBitValues.TargetInCombat && playerReader.PlayerBitValues.TargetOfTargetIsPlayer)
+ await input.TapNearestTarget($"{GetType().Name}: Checking target in front of me");
+ await wait.Update(1);
+ if (playerReader.HasTarget)
{
- ResetCooldowns();
-
- logger.LogWarning("---- Somebody is attacking me!");
- await input.TapInteractKey("Found new target to attack");
+ if (playerReader.PlayerBitValues.TargetInCombat && playerReader.PlayerBitValues.TargetOfTargetIsPlayer)
+ {
+ ResetCooldowns();
+
+ logger.LogWarning("---- Somebody is attacking me!");
+ await input.TapInteractKey("Found new target to attack");
+ await stopMoving.Stop();
+ await wait.Update(1);
+ return true;
+ }
+
+ await input.TapClearTarget();
await wait.Update(1);
- return true;
}
-
- await input.TapClearTarget();
- await wait.Update(1);
- }
- else
- {
- // threat must be behind me
- var anyDamageTakens = playerReader.DamageTaken.Where(x => (DateTime.Now - x.LastEvent).TotalSeconds < 10 && x.LastKnownHealthPercent > 0);
- if (anyDamageTakens.Any())
+ else
{
- logger.LogWarning($"---- Possible threats found behind {anyDamageTakens.Count()}. Waiting for my target to change!");
- await wait.Interrupt(2000, () => playerReader.HasTarget);
+ // threat must be behind me
+ var anyDamageTakens = playerReader.DamageTaken.Where(x => (DateTime.Now - x.LastEvent).TotalSeconds < 10 && x.LastKnownHealthPercent > 0);
+ if (anyDamageTakens.Any())
+ {
+ logger.LogWarning($"---- Possible threats found behind {anyDamageTakens.Count()}. Waiting for my target to change!");
+ await wait.Interrupt(2000, () => playerReader.HasTarget);
+ }
}
}
@@ -251,16 +252,10 @@ private async Task CreatureTargetMeOrMyPet()
return false;
}
- private async Task StopDrowning()
+ private async Task StopDrowning()
{
- if (playerReader.PlayerBitValues.IsDrowning)
- {
- await input.TapJump("Drowning! Swim up");
- await wait.Update(1);
- return true;
- }
-
- return false;
+ await input.TapJump("Drowning! Swim up");
+ await wait.Update(1);
}
private WowPoint GetCorpseLocation(double distance)
diff --git a/Core/Goals/FollowRouteGoal.cs b/Core/Goals/FollowRouteGoal.cs
index c5558eb1b..398de4a76 100644
--- a/Core/Goals/FollowRouteGoal.cs
+++ b/Core/Goals/FollowRouteGoal.cs
@@ -156,6 +156,11 @@ public override async Task PerformAction()
return;
}
+ if (playerReader.PlayerBitValues.IsDrowning)
+ {
+ await StopDrowning();
+ }
+
await SwitchGatherType();
if (this.playerReader.PlayerBitValues.PlayerInCombat && classConfiguration.Mode != Mode.AttendedGather) { return; }
@@ -191,7 +196,6 @@ public override async Task PerformAction()
}
await RandomJump();
- await StopDrowning();
var location = new WowPoint(playerReader.XCoord, playerReader.YCoord, playerReader.ZCoord);
var distance = WowPoint.DistanceTo(location, routeToWaypoint.Peek());
@@ -259,7 +263,7 @@ public override async Task PerformAction()
LastActive = DateTime.Now;
- await Task.Delay(10);
+ await wait.Update(1);
}
private void StartLookingForTarget()
@@ -274,6 +278,7 @@ private void StartLookingForTarget()
while (!found && !targetFinderCts.IsCancellationRequested)
{
found = await targetFinder.Search(GetType().Name, targetFinderCts.Token);
+ await wait.Update(1);
}
if (found)
@@ -342,6 +347,7 @@ private async Task MountIfRequired()
if (!npcNameFinder.MobsVisible)
{
await mountHandler.MountUp();
+ stuckDetector.ResetStuckParameters();
}
else
{
@@ -509,11 +515,8 @@ public static Vector2 GetClosestPointOnLineSegment(Vector2 A, Vector2 B, Vector2
private async Task StopDrowning()
{
- if (playerReader.PlayerBitValues.IsDrowning)
- {
- await input.TapJump("Drowning! Swim up");
- await wait.Update(1);
- }
+ await input.TapJump("Drowning! Swim up");
+ await wait.Update(1);
}
private void Log(string text)
diff --git a/Core/Goals/GoalThread.cs b/Core/Goals/GoalThread.cs
index 75542d335..15b93987b 100644
--- a/Core/Goals/GoalThread.cs
+++ b/Core/Goals/GoalThread.cs
@@ -47,7 +47,9 @@ public void OnActionEvent(object sender, ActionEventArgs e)
if (routeInfo != null && routeInfo.PoiList.Any())
{
var closest = routeInfo.PoiList.Where(p => p.Name == "Corpse").
- Min(i => (WowPoint.DistanceTo(goapAgent.PlayerReader.PlayerLocation, i.Location), i));
+ Select(i => new { i, d = WowPoint.DistanceTo(goapAgent.PlayerReader.PlayerLocation, i.Location) }).
+ Aggregate((a, b) => a.d <= b.d ? a : b);
+
if (closest.i != null)
{
routeInfo.PoiList.Remove(closest.i);
diff --git a/Core/Goals/LootGoal.cs b/Core/Goals/LootGoal.cs
index e73aa7aa8..7d85230ac 100644
--- a/Core/Goals/LootGoal.cs
+++ b/Core/Goals/LootGoal.cs
@@ -70,7 +70,7 @@ public override async Task PerformAction()
await stopMoving.Stop();
combatUtil.Update();
- await npcNameTargeting.WaitForNUpdate(1);
+ await npcNameTargeting.WaitForNUpdate(2);
bool foundCursor = await npcNameTargeting.FindByCursorType(Cursor.CursorClassification.Loot);
if (foundCursor)
{
diff --git a/Core/Goals/MountHandler.cs b/Core/Goals/MountHandler.cs
index 5ed4ed8f9..44e42bd9f 100644
--- a/Core/Goals/MountHandler.cs
+++ b/Core/Goals/MountHandler.cs
@@ -41,21 +41,25 @@ public async Task MountUp()
}
else
{
- await stopMoving.Stop();
- await wait.Update(1);
-
if (playerReader.PlayerBitValues.IsFalling)
{
(bool notfalling, double fallingElapsedMs) = await wait.InterruptTask(10000, () => !playerReader.PlayerBitValues.IsFalling);
- if (!notfalling)
- {
- logger.LogInformation($"{GetType().Name}: waited for landing {fallingElapsedMs}ms");
- }
+ logger.LogInformation($"{GetType().Name}: waited for landing interrupted: {!notfalling} - {fallingElapsedMs}ms");
}
- playerReader.LastUIErrorMessage = UI_ERROR.NONE;
+ await stopMoving.Stop();
+ await wait.Update(1);
+
await input.TapMount();
- await wait.Interrupt(mountCastTimeMs, () => playerReader.PlayerBitValues.IsMounted || playerReader.LastUIErrorMessage != UI_ERROR.NONE);
+
+ (bool notStartedCasted, double castStartElapsedMs) = await wait.InterruptTask(400, () => playerReader.PlayerBitValues.IsMounted || playerReader.IsCasting);
+ logger.LogInformation($"{GetType().Name}: casting: {!notStartedCasted} | Mounted: {playerReader.PlayerBitValues.IsMounted} | Delay: {castStartElapsedMs}ms");
+
+ if (!playerReader.PlayerBitValues.IsMounted)
+ {
+ (bool notmounted, double elapsedMs) = await wait.InterruptTask(mountCastTimeMs, () => playerReader.PlayerBitValues.IsMounted || !playerReader.IsCasting);
+ logger.LogInformation($"{GetType().Name}: interrupted: {!notmounted} | Mounted: {playerReader.PlayerBitValues.IsMounted} | Delay: {elapsedMs}ms");
+ }
}
}
}
diff --git a/Core/Goals/PullTargetGoal.cs b/Core/Goals/PullTargetGoal.cs
index a3294c8a5..6cd8a01e2 100644
--- a/Core/Goals/PullTargetGoal.cs
+++ b/Core/Goals/PullTargetGoal.cs
@@ -60,6 +60,10 @@ public override async Task OnEnter()
await input.TapDismount();
}
+ await input.TapApproachKey($"{GetType().Name}: OnEnter - Face the target and stop");
+ await stopMoving.Stop();
+ await wait.Update(1);
+
pullStart = DateTime.Now;
}
@@ -116,25 +120,15 @@ public override async Task PerformAction()
await stuckDetector.Unstick();
}
- await Interact("No pulled!");
- await wait.Update(1);
+ if (classConfiguration.Approach.GetCooldownRemaining() == 0)
+ {
+ await input.TapApproachKey($"{GetType().Name}");
+ await wait.Update(1);
+ }
}
else
{
SendActionEvent(new ActionEventArgs(GoapKey.pulled, true));
- playerReader.LastUIErrorMessage = UI_ERROR.NONE;
- }
- }
-
- private async Task Interact(string source)
- {
- if (classConfiguration.Interact.GetCooldownRemaining() == 0)
- {
- playerReader.LastUIErrorMessage = UI_ERROR.NONE;
- await input.TapInteractKey($"{GetType().Name} {source}");
- await wait.Update(1);
-
- await castingHandler.ReactToLastUIErrorMessage($"{GetType().Name}-Interact: ");
}
}
@@ -146,19 +140,20 @@ protected bool HasPickedUpAnAdd
}
}
- protected async Task WaitForWithinMeleeRange(KeyAction item)
+ protected async Task WaitForWithinMeleeRange(KeyAction item, bool lastCastSuccess)
{
await stopMoving.Stop();
+ await wait.Update(1);
var start = DateTime.Now;
- var playerHealth = playerReader.HealthCurrent;
+ var lastKnownHealth = playerReader.HealthCurrent;
int maxWaitTime = 10;
Log($"Waiting for the target to reach melee range - max {maxWaitTime}s");
while (playerReader.HasTarget && !playerReader.IsInMeleeRange && (DateTime.Now - start).TotalSeconds < maxWaitTime)
{
- if (playerHealth < playerReader.HealthCurrent)
+ if (playerReader.HealthCurrent < lastKnownHealth)
{
Log("Got damage. Stop waiting for melee range.");
break;
@@ -170,6 +165,13 @@ protected async Task WaitForWithinMeleeRange(KeyAction item)
break;
}
+ if (lastCastSuccess && playerReader.UsableAction.Is(item))
+ {
+ Log($"While waiting, repeat current action: {item.Name}");
+ lastCastSuccess = await castingHandler.CastIfReady(item, item.DelayBeforeCast);
+ Log($"Repeat current action: {lastCastSuccess}");
+ }
+
await wait.Update(1);
}
}
@@ -180,8 +182,6 @@ public async Task Pull()
{
await input.TapStopAttack();
await wait.Update(1);
-
- playerReader.LastUIErrorMessage = UI_ERROR.NONE;
}
if (playerReader.PlayerBitValues.HasPet && !playerReader.PetHasTarget)
@@ -189,32 +189,35 @@ public async Task Pull()
await input.TapPetAttack();
}
+ bool castAny = false;
foreach (var item in Keys)
{
- if (item.StopBeforeCast)
- {
- await stopMoving.Stop();
- await wait.Update(1);
- }
-
var success = await castingHandler.CastIfReady(item, item.DelayBeforeCast);
-
- if (!playerReader.HasTarget)
+ if (success)
{
- return false;
- }
+ if (!playerReader.HasTarget)
+ {
+ return false;
+ }
- if (success && item.WaitForWithinMeleeRange)
- {
- await WaitForWithinMeleeRange(item);
+ castAny = true;
+
+ if (item.WaitForWithinMeleeRange)
+ {
+ await WaitForWithinMeleeRange(item, success);
+ }
}
}
- // Wait for combat
- (bool interrupted, double elapsedMs) = await wait.InterruptTask(1000, () => playerReader.PlayerBitValues.PlayerInCombat);
- if (!interrupted)
+ if (castAny)
{
- Log($"Entered combat after {elapsedMs}ms");
+ (bool interrupted, double elapsedMs) = await wait.InterruptTask(1000,
+ () => playerReader.TargetTarget == TargetTargetEnum.TargetIsTargettingMe ||
+ playerReader.TargetTarget == TargetTargetEnum.TargetIsTargettingPet);
+ if (!interrupted)
+ {
+ Log($"Entered combat after {elapsedMs}ms");
+ }
}
return playerReader.PlayerBitValues.PlayerInCombat;
diff --git a/Core/Goals/StopMoving.cs b/Core/Goals/StopMoving.cs
index da2151403..fea842c9a 100644
--- a/Core/Goals/StopMoving.cs
+++ b/Core/Goals/StopMoving.cs
@@ -41,7 +41,7 @@ public async Task StopForward()
input.SetKeyState(ConsoleKey.UpArrow, false, false, "");
input.SetKeyState(ConsoleKey.DownArrow, false, false, "StopForward");
- await Task.Delay(1);
+ await Task.Delay(10);
}
this.XCoord = playerReader.XCoord;
diff --git a/Core/Goals/TargetFinder.cs b/Core/Goals/TargetFinder.cs
index c4be85078..ecf0a3b62 100644
--- a/Core/Goals/TargetFinder.cs
+++ b/Core/Goals/TargetFinder.cs
@@ -68,7 +68,10 @@ private async Task LookForTarget(string source, CancellationToken cancella
else
{
if (!cancellationToken.IsCancellationRequested)
+ {
+ npcNameTargeting.ChangeNpcType(NpcNameToFind);
await input.TapNearestTarget(source);
+ }
if (!playerReader.HasTarget && !cancellationToken.IsCancellationRequested)
{
diff --git a/Core/Goals/Wait.cs b/Core/Goals/Wait.cs
index 6cdfbcd81..926b44ff3 100644
--- a/Core/Goals/Wait.cs
+++ b/Core/Goals/Wait.cs
@@ -49,6 +49,21 @@ public async Task> InterruptTask(int durationMs, Func
return Tuple.Create(true, elapsedMs);
}
+ public async Task> InterruptTask(int durationMs, Func interrupt, Action repeat)
+ {
+ DateTime start = DateTime.Now;
+ double elapsedMs;
+ while ((elapsedMs = (DateTime.Now - start).TotalMilliseconds) < durationMs)
+ {
+ repeat();
+ await Update(1);
+ if (interrupt())
+ return Tuple.Create(false, elapsedMs);
+ }
+
+ return Tuple.Create(true, elapsedMs);
+ }
+
public async Task Interrupt(int durationMs, Task exit)
{
DateTime start = DateTime.Now;
diff --git a/Core/Input/ConfigurableInput.cs b/Core/Input/ConfigurableInput.cs
index 090439f7d..442ee8cce 100644
--- a/Core/Input/ConfigurableInput.cs
+++ b/Core/Input/ConfigurableInput.cs
@@ -27,6 +27,12 @@ public async Task TapInteractKey(string source)
this.ClassConfig.Interact.SetClicked();
}
+ public async Task TapApproachKey(string source)
+ {
+ await KeyPress(ClassConfig.Approach.ConsoleKey, ClassConfig.Approach.PressDuration, string.IsNullOrEmpty(source) ? "" : $"TapApproachKey ({source})");
+ this.ClassConfig.Approach.SetClicked();
+ }
+
public async Task TapLastTargetKey(string source)
{
await KeyPress(ClassConfig.TargetLastTarget.ConsoleKey, defaultKeyPress, $"TapLastTarget ({source})");
@@ -47,7 +53,7 @@ public async Task TapClearTarget(string desc = "")
public async Task TapStopAttack(string desc = "")
{
- await KeyPress(ClassConfig.StopAttack.ConsoleKey, defaultKeyPress, string.IsNullOrEmpty(desc) ? "" : $"TapStopAttack: {desc}");
+ await KeyPress(ClassConfig.StopAttack.ConsoleKey, ClassConfig.StopAttack.PressDuration, string.IsNullOrEmpty(desc) ? "" : $"TapStopAttack: {desc}");
this.ClassConfig.StopAttack.SetClicked();
}
@@ -77,7 +83,7 @@ public async Task TapJump(string desc = "")
public async Task TapPetAttack(string source = "")
{
- await KeyPress(ClassConfig.PetAttack.ConsoleKey, defaultKeyPress, $"TapPetAttack ({source})");
+ await KeyPress(ClassConfig.PetAttack.ConsoleKey, ClassConfig.PetAttack.PressDuration, $"TapPetAttack ({source})");
this.ClassConfig.PetAttack.SetClicked();
}
diff --git a/Core/Requirement/RequirementFactory.cs b/Core/Requirement/RequirementFactory.cs
index 71f078a45..4b3622e3c 100644
--- a/Core/Requirement/RequirementFactory.cs
+++ b/Core/Requirement/RequirementFactory.cs
@@ -34,7 +34,7 @@ public void InitialiseRequirements(KeyAction item)
};
foreach (string part in requirement.Split("||"))
{
- var sub = GetRequirement(item.Name, part, item.FormEnum);
+ var sub = GetRequirement(item.Name, part);
orCombinedRequirement = orCombinedRequirement.Or(sub);
}
@@ -48,7 +48,7 @@ public void InitialiseRequirements(KeyAction item)
};
foreach (string part in requirement.Split("&&"))
{
- var sub = GetRequirement(item.Name, part, item.FormEnum);
+ var sub = GetRequirement(item.Name, part);
andCombinedRequirement = andCombinedRequirement.And(sub);
}
@@ -56,7 +56,7 @@ public void InitialiseRequirements(KeyAction item)
}
else
{
- item.RequirementObjects.Add(GetRequirement(item.Name, requirement, item.FormEnum));
+ item.RequirementObjects.Add(GetRequirement(item.Name, requirement));
}
}
@@ -64,13 +64,13 @@ public void InitialiseRequirements(KeyAction item)
CreateMinRequirement(item.RequirementObjects, "Rage", item.MinRage);
CreateMinRequirement(item.RequirementObjects, "Energy", item.MinEnergy);
+ CreateConsumableRequirement("Water", item);
+ CreateConsumableRequirement("Food", item);
+
CreateMinComboPointsRequirement(item.RequirementObjects, item);
CreateTargetIsCastingRequirement(item.RequirementObjects, item.UseWhenTargetIsCasting);
CreateActionUsableRequirement(item.RequirementObjects, item);
- CreateWaterRequirement(item);
- CreateFoodRequirement(item);
-
item.CreateCooldownRequirement();
item.CreateChargeRequirement();
}
@@ -91,11 +91,11 @@ private void CreateMinRequirement(List RequirementObjects, string t
{
if (value > 0)
{
- if( type == "Mana")
+ if (type == "Mana")
{
RequirementObjects.Add(new Requirement
{
- HasRequirement = () => playerReader.ManaCurrent >= value,
+ HasRequirement = () => playerReader.ManaCurrent >= value || playerReader.Buffs.Clearcasting,
LogMessage = () => $"{type} {playerReader.ManaCurrent} >= {value}"
});
}
@@ -103,7 +103,7 @@ private void CreateMinRequirement(List RequirementObjects, string t
{
RequirementObjects.Add(new Requirement
{
- HasRequirement = () => playerReader.PTCurrent >= value,
+ HasRequirement = () => playerReader.PTCurrent >= value || playerReader.Buffs.Clearcasting,
LogMessage = () => $"{type} {playerReader.PTCurrent} >= {value}"
});
}
@@ -121,51 +121,33 @@ private void CreateMinComboPointsRequirement(List RequirementObject
});
}
}
+
private void CreateActionUsableRequirement(List RequirementObjects, KeyAction item)
{
if (item.WhenUsable && !string.IsNullOrEmpty(item.Key))
{
RequirementObjects.Add(new Requirement
{
- HasRequirement = () => playerReader.UsableAction.Is(item),
- LogMessage = () => $"Usable"
- });
- }
- }
-
- private void CreateWaterRequirement(KeyAction item)
- {
- if (item.Name == "Water")
- {
- item.RequirementObjects.Add(new Requirement
- {
- HasRequirement = () => bagReader.ItemCount(bagReader.HighestQuantityOfWaterId()) > 0,
- LogMessage = () => $"Has Water"
- });
-
- item.RequirementObjects.Add(new Requirement
- {
- HasRequirement = () => !playerReader.PlayerBitValues.IsSwimming,
- LogMessage = () => $"Not swim"
- });
-
- item.RequirementObjects.Add(new Requirement
- {
- HasRequirement = () => !playerReader.PlayerBitValues.IsFalling,
- LogMessage = () => $"Not falling"
+ HasRequirement = () =>
+ !item.HasFormRequirement() ? playerReader.UsableAction.Is(item) :
+ (playerReader.Form == item.FormEnum && playerReader.UsableAction.Is(item)) ||
+ (playerReader.Form != item.FormEnum && item.CanDoFormChangeAndHaveMinimumMana()),
+
+ LogMessage = () =>
+ !item.HasFormRequirement() ? $"Usable" : // {playerReader.UsableAction.Num(item)}
+ (playerReader.Form != item.FormEnum && item.CanDoFormChangeAndHaveMinimumMana()) ? $"Usable after Form change" : // {playerReader.UsableAction.Num(item)}
+ (playerReader.Form == item.FormEnum && playerReader.UsableAction.Is(item)) ? $"Usable current Form" : $"not Usable current Form" // {playerReader.UsableAction.Num(item)}
});
}
}
- private void CreateFoodRequirement(KeyAction item)
+ private void CreateConsumableRequirement(string name, KeyAction item)
{
- if (item.Name == "Food")
+ if (item.Name == name)
{
- item.RequirementObjects.Add(new Requirement
- {
- HasRequirement = () => bagReader.ItemCount(bagReader.HighestQuantityOfFoodId()) > 0,
- LogMessage = () => $"Has Food"
- });
+ item.StopBeforeCast = true;
+ item.WhenUsable = true;
+ item.AfterCastWaitBuff = true;
item.RequirementObjects.Add(new Requirement
{
@@ -181,7 +163,7 @@ private void CreateFoodRequirement(KeyAction item)
}
}
- public Requirement GetRequirement(string name, string requirement, Form form)
+ public Requirement GetRequirement(string name, string requirement)
{
this.logger.LogInformation($"[{name}] Processing requirement: {requirement}");
@@ -189,7 +171,7 @@ public Requirement GetRequirement(string name, string requirement, Form form)
if (requirement.Contains(">") || requirement.Contains("<"))
{
- return GetValueBasedRequirement(name, requirement, form);
+ return GetValueBasedRequirement(name, requirement);
}
if (requirement.Contains("npcID:"))
@@ -212,6 +194,16 @@ public Requirement GetRequirement(string name, string requirement, Form form)
return CreateTargetCastingSpellRequirement(requirement);
}
+ if (requirement.Contains("Form"))
+ {
+ return CreateFormRequirement(requirement);
+ }
+
+ if (requirement.Contains("Race"))
+ {
+ return CreateRaceRequirement(requirement);
+ }
+
if (BuffDictionary.Count == 0)
{
BuffDictionary = new Dictionary>
@@ -244,6 +236,7 @@ public Requirement GetRequirement(string name, string requirement, Form form)
{ "Drinking", ()=> playerReader.Buffs.Drinking },
{ "Mana Regeneration", ()=> playerReader.Buffs.ManaRegeneration },
{ "Well Fed", ()=> playerReader.Buffs.WellFed },
+ { "Clearcasting", ()=> playerReader.Buffs.Clearcasting },
//Priest
{ "Fortitude", ()=> playerReader.Buffs.Fortitude },
{ "InnerFire", ()=> playerReader.Buffs.InnerFire },
@@ -271,6 +264,8 @@ public Requirement GetRequirement(string name, string requirement, Form form)
{ "Ward", ()=>playerReader.Buffs.Ward },
{ "Fire Power", ()=>playerReader.Buffs.FirePower },
{ "Mana Shield", ()=>playerReader.Buffs.ManaShield },
+ { "Presence of Mind", ()=>playerReader.Buffs.PresenceOfMind },
+ { "Arcane Power", ()=>playerReader.Buffs.ArcanePower },
// Rogue
{ "Slice and Dice", ()=> playerReader.Buffs.SliceAndDice },
{ "Stealth", ()=> playerReader.Buffs.Stealth },
@@ -286,6 +281,7 @@ public Requirement GetRequirement(string name, string requirement, Form form)
{ "Lightning Shield", ()=> playerReader.Buffs.LightningShield },
{ "Water Shield", ()=> playerReader.Buffs.WaterShield },
{ "Shamanistic Focus", ()=> playerReader.Buffs.ShamanisticFocus },
+ { "Stoneskin", ()=> playerReader.Buffs.Stoneskin },
//Hunter
{ "Aspect of the Cheetah", ()=> playerReader.Buffs.Aspect },
{ "Aspect of the Pack", ()=> playerReader.Buffs.Aspect },
@@ -303,12 +299,14 @@ public Requirement GetRequirement(string name, string requirement, Form form)
{ "Rip", ()=> playerReader.Debuffs.Rip },
{ "Moonfire", ()=> playerReader.Debuffs.Moonfire },
{ "Entangling Roots", ()=> playerReader.Debuffs.EntanglingRoots },
+ { "Rake", ()=> playerReader.Debuffs.Rake },
// Warrior Debuff
{ "Rend", ()=> playerReader.Debuffs.Rend },
// Priest Debuff
{ "Shadow Word: Pain", ()=> playerReader.Debuffs.ShadowWordPain },
// Mage Debuff
{ "Frostbite", ()=> playerReader.Debuffs.Frostbite },
+ { "Slow", ()=> playerReader.Debuffs.Slow },
// Warlock Debuff
{ "Curse of Weakness", ()=> playerReader.Debuffs.Curseof },
{ "Curse of Elements", ()=> playerReader.Debuffs.Curseof },
@@ -397,6 +395,47 @@ private Requirement CreateTargetCastingSpellRequirement(string requirement)
}
}
+ private Requirement CreateFormRequirement(string requirement)
+ {
+ var parts = requirement.Split(":");
+ var form = Enum.Parse