Code:
-- Attack magic including area runes.
-- By Tururu. V1.00
-- CONFIGURATION
-- Monsters for each attack
local SpellMonsters={"Lizard Dragon Priest","Lizard High Guard","Lizard Legionnaire","Lizard Zaogun","Killer Caiman","Eternal Guardians","Death Blob","Mutated Bat","Mutated Tiger"}
local AreaRuneMonsters={"Lizard Dragon Priest","Lizard High Guard","Lizard Legionnaire","Lizard Zaogun","Killer Caiman","Eternal Guardians","Death Blob","Mutated Bat","Mutated Tiger"}
local UltimateMonsters={"Demon Skeleton"}
-- Number of monsters for area rune and ultimate attack
local AreaRuneMin=3
local UltimateMin=5
-- Spells and runes to use
local SpellAttack = "exori Frigo"
local SpellRange = 3
local SpellMana = 20
local AreaRuneID = 3161 -- 3202 Thunderstorm
local UltimateAttack = "exori vis"
local UltimateMana = 20
local MaxRadius=3 --For search of suitable tiles to shoot runes, a radius around the center of gravity
-- END OF CONFIGURATION
local InsideAreaRune={
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
local ScoreAreaRune={
{2,2,2,1,0,0,0,0,0,0,0,0,0,0,0},
{2,2,2,1,0,0,0,0,0,0,0,0,0,0,0},
{2,2,1,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}
local AreaRuneN
local UltimateN
local RuneExhaust=0
local tx=0
local ty=0
local tz=0
local selfx
local selfy
function TrySpell() --A function to use the default spell attack
if(Self.CanCastSpell(SpellAttack))then
--Get current target creature. Targeting handled by xenobot
local target = Creature.GetByID(Self.TargetID())
if(target:isValid() and target:isMonster())then
if(target:isOnScreen(false) and target:isVisible() and target:isAlive())then
for n in pairs(SpellMonsters) do
if(SpellMonsters[n]==target:Name()) then
if(target:DistanceFromSelf() <= SpellRange) then
Self.Cast(SpellAttack,SpellMana)
end
break
end
end
end
end
end
end
while(true) do
--Get all creatures in my floor, alive, which are monsters
local tbl = {}
for i = CREATURES_LOW, CREATURES_HIGH do
local creature = Creature.GetFromIndex(i)
if(creature:isValid() and creature:isMonster())then
if(creature:isOnScreen(false) and creature:isVisible() and creature:isAlive())then
table.insert(tbl, creature)
end
end
end
--Calculate number of creatures to use area rune/ultimate spell
local MonsterX = {}
local MonsterY = {}
local AverageX=0
local AverageY=0
AreaRuneN=0
UltimateN=0
if(table.getn(tbl)>=AreaRuneMin) then
for _, cid in ipairs(tbl) do
for n in pairs(AreaRuneMonsters) do
if(AreaRuneMonsters[n]==cid:Name()) then
AreaRuneN=AreaRuneN+1
MonsterX[AreaRuneN]= cid:Position().x
MonsterY[AreaRuneN]= cid:Position().y
AverageX=AverageX+MonsterX[AreaRuneN]
AverageY=AverageY+MonsterY[AreaRuneN]
break
end
end
for n in pairs(UltimateMonsters) do
if(UltimateMonsters[n]==cid:Name()) then
UltimateN=UltimateN+1
break
end
end
end
end
if(UltimateN>=UltimateMin and Self.CanCastSpell(UltimateAttack))then --Many creatures on screen, cast ultimate spell
Self.Cast(UltimateAttack,UltimateMana)
wait(600)
elseif(AreaRuneN>=AreaRuneMin and RuneExhaust<=0 and Self.ItemCount(AreaRuneID)>0) then --Enough creatures to shoot runes, search suitable tile to shoot area rune
AverageX=math.floor(AverageX/AreaRuneN) -- Center of gravity of the creatures included in the area rune list. A good starting point to search
AverageY=math.floor(AverageY/AreaRuneN)
local SuitableTileFound=false
local SuitableX
local SuitableY
local OnTileN=0
local OnTileScore=0
local MaxScore=0
local MaxOnTile=0
tz=Self.Position().z
selfx=Self.Position().x
selfy=Self.Position().y
for tx=AverageX-MaxRadius,AverageX+MaxRadius do
if(math.abs(tx-selfx)<=7) then --Dont want to check tiles outside of the screen
for ty=AverageY-MaxRadius,AverageY+MaxRadius do
if(math.abs(ty-selfy)<=5) then --Dont want to check tiles outside of the screen
if( Map.IsTileWalkable(tx,ty,tz))then --Can shoot only if walkable
OnTileN=0
OnTileScore=0
for i= 1, AreaRuneN do
--Checking whether the monster is inside the rune by checking against the array with the drawing of the area. Adding 1 to the indexes because lua starts in i=1
--Same for scores. Give more points to the inside of the area, to try to catch moving monsters.
if(InsideAreaRune[math.abs(ty-MonsterY[i])+1][math.abs(tx-MonsterX[i])+1]==1)then
OnTileN=OnTileN+1
OnTileScore=OnTileScore+ScoreAreaRune[math.abs(ty-MonsterY[i])+1][math.abs(tx-MonsterX[i])+1]
end
end
if(OnTileN>=AreaRuneMin) then --Check that the position we just checked covers enough monsters to be worth a rune shot
SuitableTileFound=true
if(OnTileScore>MaxScore)then --Keep in memory the position with the best score
SuitableX=tx
SuitableY=ty
MaxScore=OnTileScore
MaxOnTile=OnTileN
end
end
end
end
end
end
end
if(SuitableTileFound)then --We found a place to shoot
local AreaRunesLeft=Self.ItemCount(AreaRuneID)
print("Found X:"..SuitableX.." Y:"..SuitableY.." to hit "..MaxOnTile.." monsters with score "..MaxScore..". Runes: "..AreaRunesLeft)
delayWalker(400)
Self.UseItemWithGround(AreaRuneID,SuitableX,SuitableY,Self.Position().z)
wait(600)
if(Self.ItemCount(AreaRuneID)<AreaRunesLeft)then --The exhaust for runes is 2 seconds. We already waited 600 ms, wait 1400 more if we are positive in this check that we shot a rune.
print("Exhausted")
RuneExhaust=1400
end
else
TrySpell()
end
else -- Default situation, use normal spells
TrySpell()
end
if(RuneExhaust>0)then
RuneExhaust=RuneExhaust-200
end
wait(200)
end