Signup Now
Page 2 of 5 FirstFirst 1234 ... LastLast
Results 11 to 20 of 44
  1. #11
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    19
    Nope. It does not know how to cast the paladin spells. Also, unlike mages paladins should use a spellcaster that syncs their spells with their distance attack.

  2. #12
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    19
    Updated the script to prefer casting runes with "use on self" hotkeys. This really improves performance when surrounded or close to. Since they are so fast self casts are given a 20% hit count boost when compared to regular casts (they still have to hit the configured min amount though, and the boost also only applies when compared to using the same rune without self cast -- not against other spells).

  3. #13
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Great once again! I was thinking on how to do it but didn't managed to implement it
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  4. #14
    Free User
    Join Date
    Aug 2014
    Location
    Rio de Janeiro - Brazil
    Posts
    356
    Reputation
    66
    Rep Power
    20
    Quote Originally Posted by Dermeister View Post
    Can pallies use this one too?
    I tweaked a bit and it's working for my paladin. The downside is that you won't use potions here, as I'm using my own multi potion user, danger based.


    init start
    -----------------------------------------------------------------------
    -------------------------------- CONFIG -------------------------------
    -----------------------------------------------------------------------
    ShooterConfig = {
    pvpsafe = false,
    buffersize = 2,
    blacklistIds = {469, 1959, 1960},
    manaPotion = "ultimate mana potion",
    useStrongStrikes = true,
    spells = {
    {name = "exevo mas san", monsters = {'Spectre','Banshee','Grim Reaper','Gravedigger','Shadow Pupil','Banshee','Mutated Bat','Werewolf','Nightmare Scion','Nightmare','Hellspawn','Spectre','Vampire Bride','Vampire','Nightstalker',"Medusa","Serpent Spawn","Green Djinn","Efreet","Elder Bonelord","Behemoth","Giant Spider","Massive Earth Elemental","Hydra","Hero","Vicious Squire","Necromancer","Renegade Knight","Vile Grandmaster","Blood Priest","Undead Gladiator","Bonebeast","Lich","Mooh'Tah Warrior","Worm Priestess","Minotaur Hunter","Minotaur Amazon","Execowtioner","Moohtant",'Glooth powered minotaur','Execowtioner','Worm Priestess','Minotaur Invader','Moohtant','Minotaur Amazon','Lost Berserker','Metal Gargoyle','Rustheap Golem','Lost Husher','Lost Thrower','Enslaved Dwarf','Lost Basher',"Medusa","Serpent Spawn","Hydra","Bonebeast","Grim Reaper","Rot Elemental", "Blood Beast","Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout", "Devourer","Glooth Anemone","Glooth Golem","Glooth Brigand","Glooth Bandit","Fury","Spectre","Nightmare","Metal Gargoyle","Rustheap Golem"}, count = 2},
    -- {name = "exevo gran mas vis", monsters = {'Glooth powered minotaur','Execowtioner','Worm Priestess','Minotaur Invader','Moohtant','Minotaur Amazon','Lost Berserker','Metal Gargoyle','Rustheap Golem','Lost Husher','Lost Thrower','Enslaved Dwarf','Lost Basher',"Medusa","Serpent Spawn","Hydra","Bonebeast","Glooth Blob","Grim Reaper","Rot Elemental", "Blood Beast","Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout", "Devourer","Glooth Anemone","Glooth Golem"}, count = 5, minhp = 20},
    -- {name = "thunderstorm rune", count = 5, atLeastOne = {'Glooth powered minotaur','Execowtioner','Worm Priestess','Minotaur Invader','Moohtant','Minotaur Amazon','Lost Berserker','Metal Gargoyle','Rustheap Golem','Lost Husher','Lost Thrower','Enslaved Dwarf','Lost Basher',"Medusa","Serpent Spawn","Hydra","Bonebeast","Glooth Blob","Grim Reaper","Rot Elemental", "Blood Beast","Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout", "Devourer","Glooth Anemone","Glooth Golem"}},
    --{name = "great fireball rune", monsters = 't', count = 5},
    -- {name = "exevo vis hur", monsters = 't', count = 4},
    -- {name = "exevo gran vis lux", monsters = 't', count = 2},
    --{name = "thunderstorm rune", count = 2, atLeastOne = {"Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout"}},-- atLeastOne = {'Glooth powered minotaur','Execowtioner','Worm Priestess','Minotaur Invader','Moohtant','Minotaur Amazon','Lost Berserker','Metal Gargoyle','Rustheap Golem','Lost Husher','Lost Thrower','Enslaved Dwarf','Lost Basher',"Medusa","Serpent Spawn","Hydra","Bonebeast","Glooth Blob","Grim Reaper","Rot Elemental", "Blood Beast","Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout", "Devourer","Glooth Anemone","Glooth Golem"}},
    {name = "thunderstorm rune", monsters = 't', count = 2},
    {name = "great fireball rune", monsters = 'f', count = 2},
    -- {name = "great fireball rune", monsters = 'f', count = 2},
    --{name = "avalanche rune", monsters = 'i', count = 2},
    -- {name = "thunderstorm rune", count = 1},-- atLeastOne = {'Glooth powered minotaur','Execowtioner','Worm Priestess','Minotaur Invader','Moohtant','Minotaur Amazon','Lost Berserker','Metal Gargoyle','Rustheap Golem','Lost Husher','Lost Thrower','Enslaved Dwarf','Lost Basher',"Medusa","Serpent Spawn","Hydra","Bonebeast","Glooth Blob","Grim Reaper","Rot Elemental", "Blood Beast","Quara Constrictor Scout","Quara Hydromancer Scout","Quara Mantassin Scout","Quara Predator Scout", "Quara Pincher Scout", "Devourer","Glooth Anemone","Glooth Golem"}},
    }
    }

    -----------------------------------------------------------------------
    ---------------------------- END OF CONFIG ----------------------------
    -----------------------------------------------------------------------
    local NORTH, EAST, SOUTH, WEST = 'n', 'e', 's', 'w'

    -- All masks must be quadratical. You cannot leave out the zeros.
    -- Also, all masks must have sizes that are odd numbers.
    masks = {

    ball = {
    {0,0,1,1,1,0,0},
    {0,1,1,1,1,1,0},
    {1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1},
    {1,1,1,1,1,1,1},
    {0,1,1,1,1,1,0},
    {0,0,1,1,1,0,0},
    },


    ["exevo mas san"] = {
    {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,1,1,1,0,0,0,0},
    {0,0,0,1,1,1,1,1,0,0,0},
    {0,0,1,1,1,1,1,1,1,0,0},
    {0,0,1,1,1,1,1,1,1,0,0},
    {0,0,1,1,1,1,1,1,1,0,0},
    {0,0,0,1,1,1,1,1,0,0,0},
    {0,0,0,0,1,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},
    },


    -- Waves should be entered as their NORTH direction.
    -- The extra zeros in the other directions are needed for the
    -- rotations to work properly.

    -- Also frigo hur and gran frigo hur
    ["exevo flam hur"] = {
    {0,0,0,1,1,1,0,0,0,},
    {0,0,0,1,1,1,0,0,0,},
    {0,0,0,0,1,0,0,0,0,},
    {0,0,0,0,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,},
    },

    ["exevo vis hur"] = {
    {0,0,0,0,1,1,1,0,0,0,0,},
    {0,0,0,0,1,1,1,0,0,0,0,},
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,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,},
    },
    ["exevo vis lux"] = {
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,1,0,0,0,0,0,},
    {0,0,0,0,0,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,},
    },
    ["exevo gran vis lux"] = {
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,},
    {0,0,0,0,0,0,0,0,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,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,},
    },

    -- Note that the strike mask will be centered over your look position rather than yourself.
    strikes = {
    -- Strikes have no aoe. However, we still need a mask in order to add a safety margin to it.
    {1}
    }
    }

    masks["exevo frigo hur"] = masks["exevo flam hur"]
    masks["exevo gran frigo hur"] = masks["exevo flam hur"]

    -- Positions are stored in one dimensional tables by first converting them to numbers.
    -- Note that for the sake of this script the z-coordinate is irrelevant.
    -- Also, the function is linear. The number for a sum of two positions is the sum of the numbers for each position.
    local function posToNum(x, y)
    return 100000*x+y
    end
    -- Inverse of posToNum.
    local function numToPos(n)
    local y = n % 100000
    local x = (n - y) / 100000
    return x, y
    end

    -- Constants representing what to check for in a converted mask
    local PLAYERS, PLAYERS_AND_MONSTERS = 0, 1

    -- Converts a spell mask to a hashset containing the numerical offsets.
    -- Every position within *margin* squares from a 1 in the original mask is added to the set.
    local function convertMask(mask, margin)
    -- Node that the size of a mask must be an odd number since all spells are symmetrical.
    local xmid, ymid = math.ceil(#mask / 2), math.ceil(#mask[1] / 2)

    local convertedMask = {}

    local function fillMask(margin, value)
    for y = 1, #mask do
    for x = 1, #mask[y] do
    if mask[y][x] == 1 then
    for oy = y - margin, y + margin do
    for ox = x - margin, x + margin do
    -- The masks are stored with indexes starting at 1,
    -- but in-game we have negative offsets for squares above or to the left of the character.
    convertedMask[posToNum(ox - xmid, oy - ymid)] = value
    end
    end
    end
    end
    end
    end

    -- First write the pvp-safe area
    if margin > 0 then
    fillMask(margin, PLAYERS)
    end
    -- Then overwrite the area in which we check for monsters
    fillMask(0, PLAYERS_AND_MONSTERS)

    return convertedMask
    end

    local function getSurroundings(multifloor, blacklist)
    local monsters, players, shootables = {}, {}, {}

    if not multifloor then
    -- First check for blacklisted ids and enable multifloor if one is found
    for x = $posx - 7, $posx + 7 do
    for y = $posy - 5, $posy + 5 do
    local tile = gettile(x, y, $posz)
    for i = 1, tile.itemcount do
    -- Also treat blacklisted ids as players, since players may arrive
    if blacklist[tile.item[i].id] then multifloor = true end
    end
    end
    end
    end
    local maxzdiff = multifloor and 1 or 0

    local xdiff = ShooterConfig.pvpsafe and math.max(2, 4 - ShooterConfig.buffersize) or 4
    local ydiff = ShooterConfig.pvpsafe and math.max(1, 4 - ShooterConfig.buffersize) or 4
    -- First filter out the walkable positions since we cannot shoot on top of walls
    for x = $posx - xdiff, $posx + xdiff do
    for y = $posy - ydiff, $posy + ydiff do
    if tileshootable(x, y, $posz) then
    shootables[posToNum(x, y)] = true
    end
    end
    end

    -- Add our creature tiles
    foreach creature c 'm' do
    if c.posz == $posz then
    monsters[posToNum(c.posx, c.posy)] = c
    end
    end

    -- Add our player tiles
    foreach creature c 'p' do
    -- TODO: Also add party member checks
    if math.abs(c.posz - $posz) <= maxzdiff and
    c.name ~= $name then
    players[posToNum(c.posx, c.posy)] = c
    end
    end

    return monsters, players, shootables
    end

    local function creatures(monsters, targetMonsters, filter)
    local validMonsters = {}
    local cogx, cogy, numMonsters = 0, 0, 0
    for pos, c in pairs(monsters) do
    if (not targetMonsters or targetMonsters[c.name:lower()]) and
    (not filter or filter(c))
    then
    validMonsters[pos] = c
    cogx = cogx + c.posx
    cogy = cogy + c.posy
    numMonsters = numMonsters + 1
    end
    end

    return validMonsters, {x = cogx/numMonsters, y = cogy/numMonsters}
    end

    local function getScore(pos, mask, monsters, players, finalcheck, pvpsafe)
    local score = 0
    local effectedMonsters = {}
    local monsters = monsters or {}

    for offset, TYPE in pairs(mask) do
    if pvpsafe and players[pos + offset] then
    return -1
    elseif TYPE == PLAYERS_AND_MONSTERS and monsters[pos + offset] then
    score = score + 1
    table.insert(effectedMonsters, monsters[pos + offset])
    end
    end
    if not finalcheck or finalcheck(effectedMonsters) then
    return score
    else
    return 0
    end
    end

    local function getBestBallPos(monsters, players, shootables, mask, monsterNames, pvpsafe, filter, finalcheck)
    local monsters, cog = creatures(monsters, monsterNames, filter)
    local cx, cy = cog.x, cog.y

    -- Ties are broken by preference for positions close to the monster's center of gravity
    local function getTieScore(num)
    local x, y = numToPos(num)
    return math.abs(cx-x) + math.abs(cy-y)
    end

    -- Then find the best one among the shootables
    local bestScore, bestTieScore, bestPos
    for pos, _ in pairs(shootables) do
    local score = getScore(pos, mask, monsters, players, finalcheck, pvpsafe)
    local tieScore = getTieScore(pos)
    if not bestScore or score > bestScore or (score == bestScore and tieScore < bestTieScore) then
    bestScore, bestTieScore, bestPos = score, tieScore, pos
    end
    end
    return bestPos, bestScore
    end

    -- For printing purposes
    local SUCCESS, SKIP, RETRY = 0, 1, 2
    statusString = {
    [SUCCESS] = "SUCCESS",
    [SKIP] = "SKIP",
    [RETRY] = "RETRY"
    }
    -- Possible return values for a function that tries to shoot a spell
    local STRIKE, SINGLE_TARGET_RUNE, BALL, UE, WAVE = 0, 1, 2, 3, 4
    local typeString = {
    [STRIKE] = "STRIKE",
    [SINGLE_TARGET_RUNE] = "SINGLE TARGET RUNE",
    [BALL] = "BALL",
    [UE] = "UE",
    [WAVE] = "WAVE",
    }

    local function getType(spell)
    if type(spell) == 'number' then
    spell = Item.GetName(spell)
    end

    spell = spell:lower()

    for _, name in ipairs({"thunderstorm", "avalanche", "great fireball", "stone shower"}) do
    if spell:match(name) then
    return BALL
    end
    end

    if spell:match("exori") then
    return STRIKE
    end

    if spell:match("exevo mas") then
    return UE
    end

    if spell:match("exmageevo") then
    return WAVE
    end

    return SINGLE_TARGET_RUNE
    end

    local function rotateMask(mask, dir)
    local ymax, xmax = #mask, #mask[1]

    local function rotate(x, y)
    if dir == NORTH then
    return x, y
    elseif dir == EAST then
    return (ymax - y) + 1, x
    elseif dir == SOUTH then
    return x, (ymax - y) + 1
    elseif dir == WEST then
    return y, x
    end
    end

    local newMask = {}
    for y = 1, ymax do
    table.insert(newMask, {})
    for x = 1, xmax do
    table.insert(newMask[y], 0)
    end
    end

    for y = 1, ymax do
    for x = 1, xmax do
    if mask[y][x] == 1 then
    local nx, ny = rotate(x, y)
    newMask[ny][nx] = 1
    end
    end
    end

    return newMask
    end

    local function getMask(spell, TYPE, margin)
    if TYPE == WAVE then
    if not masks[spell] then print("Unknown spell: " .. spell) end
    local convertedMasks = {}
    for _, dir in ipairs({NORTH, EAST, SOUTH, WEST}) do
    convertedMasks[dir] = convertMask(rotateMask(masks[spell], dir), margin)
    end
    return convertedMasks
    end

    local base = masks.strikes
    if TYPE == BALL then
    base = masks.ball
    elseif TYPE == UE then
    if not masks[spell] then print("Unknown spell: " .. spell) end
    base = masks[spell]
    end
    return convertMask(base, margin)
    end

    local mana = {
    ["exevo mas san"] = 1800,

    ["exori san"] = 20,
    ["exori con"] = 25,

    ["exori gran con"] = 55,
    }

    function string:titlecase()
    return self:gsub("(%a)([%w_']*)", function(first, rest) return first:upper()..rest:lower() end)
    end

    local function convertCategory(c)
    local monsters = {}
    foreach settingsentry e 'Targeting/Creatures' do
    local name = getsetting(e, 'Name')
    local cat = getsetting(e, 'Category')
    if not name:find('category') and cat == c then
    monsters[name:lower()] = true
    end
    end
    return monsters
    end


    function convertSpellConfig(arg)
    local TYPE = getType(arg.name)
    local spell = arg.name:lower()

    local monsters
    -- Build up the target list, if there is one
    if arg.monsters then
    if type(arg.monsters) == "string" then
    monsters = convertCategory(arg.monsters)
    else
    monsters = {}
    for _, name in ipairs(arg.monsters) do
    monsters[name:lower()] = true
    end
    end
    end

    -- Range and mana settings.
    -- Could be rewritten to use spellinfo, but
    -- I wanted to reuse as much as possible of my old Xeno code.
    local range = spell == "exori gran con" and 7 or 4
    local mana = mana[spell] or 0

    -- Filters determine wheter a given creature is valid or not.
    -- Generally this will inspect the creatures health percentage
    local filter
    if arg.minhp or arg.maxhp then
    local minhp = arg.minhp or 0
    local maxhp = arg.maxhp or 100
    filter = function(c)
    return c.hppc < maxhp and c.hppc > minhp
    end
    end

    -- Final checks determine whether a group of creatures are valid.
    -- For example, check that an ava cast hits at least one Demon Skeleton, otherwise fall through and try a GFB instead.
    local finalcheck
    if arg.atLeastOne then
    local atLeastOneMonsters = {}
    for _, name in ipairs(arg.atLeastOne) do
    atLeastOneMonsters[name] = true
    end

    finalcheck = function(creatures)
    for _, c in ipairs(creatures) do
    if atLeastOneMonsters[c.name] then return true end
    end
    return false
    end
    end

    return {
    spell = spell,
    type = TYPE,
    monsters = monsters,
    range = range,
    mana = mana,
    count = function() return arg.count end,
    enabled = function() return true end,
    filter = filter,
    finalcheck = finalcheck,
    }
    end

    local function tryTurnAway()
    local dirs = {}
    foreach creature c 'ps' do
    if math.max(math.abs(c.posx - $posx), math.abs(c.posy - $posy)) <= 4 then
    if c.posx - $posx > 0 then
    dirs[EAST] = true
    elseif c.posx - $posx < 0 then
    dirs[WEST] = true
    end

    if c.posy - $posy > 0 then
    dirs[SOUTH] = true
    elseif c.posy - $posy < 0 then
    dirs[NORTH] = true
    end
    end
    end

    for _, dir in ipairs({NORTH, EAST, SOUTH, WEST}) do
    if not dirs[dir] then return turn(dir) end
    end
    end

    local function getLookPos()
    if $self.dir == NORTH then
    return $posx, $posy - 1
    elseif $self.dir == EAST then
    return $posx + 1, $posy
    elseif $self.dir == SOUTH then
    return $posx, $posy + 1
    elseif $self.dir == WEST then
    return $posx - 1, $posy
    end
    end

    local lastRuneCast = os.clock()
    function getShooterFunc(config, blacklist)
    if config.type == UE then
    return function(mask, monsters, players)
    if not config.enabled() then return SKIP end

    -- Return instantly if the spell is on cooldown or we dont have mana
    if cooldown(config.spell) > 1000 or $mp < config.mana then
    return SKIP
    elseif cooldown(config.spell) > 0 then
    return RETRY
    end

    local monsters = creatures(monsters, config.monsters, config.filter)
    local score = getScore(posToNum($posx, $posy), mask, monsters, players, config.finalcheck, ShooterConfig.pvpsafe)
    -- If we found enough creatures, try casting, else skip the spell
    if score >= config.count() then
    cast(config.spell)
    return cooldown(config.spell) == 0 and RETRY or SUCCESS
    else
    return SKIP
    end
    end
    elseif config.type == BALL then
    return function(mask, monsters, players, shootables)
    -- Skip if we dont have that rune
    if itemcount(config.spell) == 0 then return SKIP end
    if cooldown("exori con") > 0 then return RETRY end
    -- Find the best position to shoot the rune
    local numpos, score = getBestBallPos(monsters, players, shootables, mask, config.monsters, ShooterConfig.pvpsafe, config.filter, config.finalcheck)
    -- Check the self score since self shooting is faster
    local monsters = creatures(monsters, config.monsters, config.filter)
    local selfScore = getScore(posToNum($posx, $posy), mask, monsters, players, config.finalcheck, ShooterConfig.pvpsafe)
    -- If we found a good position)
    local mincount = config.count()
    if numpos and score >= mincount then
    local x, y = numToPos(numpos)
    pausewalking(1000)
    local selfBonus = math.max(1, 0.2 * score)
    if selfScore + selfBonus >= score and selfScore >= mincount then
    useoncreature(config.spell, $self)
    else
    useitemon(config.spell, 0, ground(x, y, $posz))
    end
    pausewalking(0)
    lastRuneCast = os.clock()
    wait(200)

    if cooldown("exori con") ~= 0 then
    return SUCCESS
    else
    return RETRY
    end
    end
    return SKIP
    end
    elseif config.type == WAVE then
    return function(mask, monsters, players)
    if not config.enabled() then return SKIP end

    -- Skip if the spell is on a long cooldown or we dont have mana
    if cooldown(config.spell) > 2000 or $mp < config.mana then return SKIP end
    local pos = posToNum($posx, $posy)

    local monsters = creatures(monsters, config.monsters, config.filter)
    -- Determine the best direction to wave
    local bestDir, bestScore
    for _, dir in ipairs({NORTH, EAST, SOUTH, WEST}) do
    local score = getScore(pos, mask[dir], monsters, players, config.finalcheck, ShooterConfig.pvpsafe)

    if not bestScore or score > bestScore then
    bestDir, bestScore = dir, score
    end
    end

    -- Skip if we couldn't find any sufficiently good directions
    if not bestDir or bestScore < config.count() then
    return SKIP
    else
    pausewalking(1000)
    -- Note that for waves we prefer to skip the spell rather than retry if something goes wrong
    if $self.dir ~= bestDir then
    turn(bestDir)
    waitping()
    end
    if $self.dir ~= bestDir then return end
    cast(config.spell)
    pausewalking(0)
    return cooldown(config.spell) == 0 and SKIP or SUCCESS
    end
    end
    elseif config.type == SINGLE_TARGET_RUNE then
    return function()
    -- Skip if we dont have a target or we dont have the rune
    if $target.id == 0 or itemcount(config.spell) == 0 then return SKIP end
    -- Retry if the spell is on cooldown
    if cooldown("exori san") > 0 then return RETRY end

    -- Check that the target is valid for out spell before casting
    local c = $target
    if (not config.monsters or config.monsters[c.name]) and
    (not config.filter or filter(c))
    then
    -- Retry if the spell is still on a short cooldown
    useoncreature(config.spell, $target)
    if cooldown("exori san") ~= 0 then
    lastRuneCast = os.clock()
    return SUCCESS
    else
    return RETRY
    end
    else
    return SKIP
    end
    end
    end
    end

    local currentMasks = {}
    local function updateMasks()
    local spells = {}
    for _, data in ipairs(ShooterConfig.spells) do
    local spell = data.name:lower()
    currentMasks[spell] = getMask(spell, getType(spell), ShooterConfig.buffersize)
    end
    currentMasks["exori san"] = getMask("exori san", STRIKE, ShooterConfig.buffersize)
    end
    updateMasks()


    function parseConfig(ShooterConfig)
    local spells = {}

    -- The blacklist is a list of IDs. If an ID from the blacklist is on the screen
    -- the shooter will enable multi-floor pvp safety.
    local blacklist = {}
    for _, id in ipairs(ShooterConfig.blacklistIds) do
    blacklist[id] = true
    end

    -- The set of ball targets is used to allow the potion drinker to drink more frequently.
    local ballTargets = {}

    for _, spell in ipairs(ShooterConfig.spells) do
    local config = convertSpellConfig(spell)

    local shooter = getShooterFunc(config, blacklist)
    table.insert(spells, {name = config.spell, type = config.type, shooter = shooter})

    if config.type == BALL then
    if config.monsters and ballTargets then
    for _, name in ipairs(config.monsters) do
    ballTargets[name] = true
    end
    else
    ballTargets = nil
    end
    end
    end

    return {spells, blacklist}, ballTargets
    end

    local avoidElement = {
    -- Druid
    [16] = "death",
    -- Sorcerer
    [8] = "physical",
    -- Paladin
    [4] = "fire", "energy", "earth", "ice", "death",
    }

    local elementSpells = {
    -- Druid
    [16] = {
    ice = (ShooterConfig.useStrongStrikes and {"exori gran frigo", "exori frigo"} or {"exori frigo"}),
    earth = (ShooterConfig.useStrongStrikes and {"exori gran tera", "exori tear"} or {"exori tera"}),
    fire = {"exori flam"},
    energy = {"exori vis"},
    physical = {"exori moe ico"}
    },
    -- Sorcerer
    [8] = {
    fire = (ShooterConfig.useStrongStrikes and {"exori gran flam", "exori flam"} or {"exori flam"}),
    energy = (ShooterConfig.useStrongStrikes and {"exori gran vis", "exori vis"} or {"exori vis"}),
    earth = {"exori tera"},
    ice = {"exori frigo"},
    death = {"exori mort"}
    },
    -- Paladin
    [4] = {
    physical = (ShooterConfig.useStrongStrikes and {"exori gran con", "exori con"} or {"exori con"}),
    holy = {"exori san"}
    }
    }

    local modmap = {
    holy = "holymod",
    physical = "physicalmod",
    }

    local function getBestStrike(targetName)
    local info = creatureinfo(targetName)
    local elements = {"holy", "physical"}
    table.sort(elements, function(a, b)
    return info[a.."mod"] > info[b.."mod"]
    end)

    for _, element in ipairs(elements) do
    if element ~= avoidElement[$voc] then
    for _, spell in ipairs(elementSpells[$voc][element]) do
    if cooldown(spell) == 0 then
    return spell
    end
    end
    end
    end
    end

    local function tryCastBestStrike(players)
    local c = $target

    -- Don't case without a target!
    if c.id == 0 then return SKIP end

    local spell = getBestStrike(c.name)
    -- getBestSpell only returns nil if all strikes are on cooldown
    if not spell then return SKIP end

    -- Skip if we dont have mana
    if $mp < spellinfo(spell).mp then return SKIP end

    local lx, ly = getLookPos()
    local score = getScore(posToNum(lx, ly), currentMasks["exori san"], nil, players, nil, ShooterConfig.pvpsafe)
    -- If the strike isn't pvpsafe, then try turning away from any other player and then skip the spell.
    -- Don't retry since that may get us stuck in a loop.
    if score == -1 then
    tryTurnAway()
    return SKIP
    end

    -- Check that the target is in range before casting
    if math.max(math.abs(c.posx - $posx), math.abs(c.posy - $posy)) <= 3 then
    cast(spell)
    return cooldown(spell) == 0 and RETRY or SUCCESS
    end
    return SKIP
    end

    local function tryCastSpell(spells, blacklist)
    local monsters, players, shootables = getSurroundings(multifloor, blacklist)
    for _, spell in ipairs(spells) do
    status = spell.shooter(currentMasks[spell.name], monsters, players, shootables)
    if status == SUCCESS or status == RETRY then
    return
    end
    end
    --if $vocshort == 'S' or $vocshort == 'D' then
    tryCastBestStrike(players)
    --end

    end

    local lastDrunk = os.clock()
    local function tryDrinkPotion(potion, runeMonsters)
    local timeToNextCast = cooldown("exori san")
    local timeSinceRuneCast = os.clock() - lastRuneCast
    local precount = itemcount(potion)
    if precount > 0 then
    if (timeSinceRuneCast > 0.9 and os.clock() - lastDrunk > 0.9) and
    ($mppc < 30 or ($mppc < 80 and (maround(runeMonsters and unpack(runeMonsters)) < 2 or timeToNextCast > 800))) then
    useoncreature(potion, $self)
    if precount ~= itemcount(potion) then
    lastDrunk = os.clock()
    end
    end
    end
    end

    local pconfig, runeTargets = parseConfig(ShooterConfig)
    init end

    if ($trapped or not ShooterDisabled) and cooleddown('attack') then
    tryCastSpell(unpack(pconfig))
    end
    if $vocshort == 'S' or $vocshort == 'D' then
    tryDrinkPotion(ShooterConfig.manaPotion, runeTargets)
    end

    auto(100)
    If I`ve helped you somehow and you want to retribute, you can donate me Tibia Coins for char: Musonius

  5. #15
    Free User
    Join Date
    Aug 2014
    Posts
    41
    Reputation
    10
    Rep Power
    0
    Shadow, all work fine, less the rune shooter, he miss a lot of runes, prioritizing shoot in self instead more amount of mobs. How i can fix it ?

    Another thing, how i can change strong strike to cast only if target is about 10%+ hp?


    In generally a excellent work here.
    Thanks

  6. #16
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    19
    Add the strong strike as a spell in the config, with a minhp field, and it will be checked before any other strikes. I've explained self-cast runes above. Windbot is truly horrendously awful at shooting runes without self-cast when you get surrounded, 5 seconds between shots is not rare. If you're not going to block monsters and you don't run a high risk of getting trapped you might be better off without self-cast, but then you'll simply have to wait until I feel like adding an option to disable it.

  7. #17
    Free User
    Join Date
    Aug 2014
    Posts
    41
    Reputation
    10
    Rep Power
    0
    Quote Originally Posted by shAdOwArt View Post
    Add the strong strike as a spell in the config, with a minhp field, and it will be checked before any other strikes. I've explained self-cast runes above. Windbot is truly horrendously awful at shooting runes without self-cast when you get surrounded, 5 seconds between shots is not rare. If you're not going to block monsters and you don't run a high risk of getting trapped you might be better off without self-cast, but then you'll simply have to wait until I feel like adding an option to disable it.
    hm ok, i will wait for you then

  8. #18
    Moderator SuNe's Avatar
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    2,881
    Reputation
    186
    Rep Power
    27
    Nice one,

    You should add a safe list for pvp safety tho, for those who wants to use it in a teamhunt or so
    กิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิ ิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิ ิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิ ิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิ ิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิ ิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิ ิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิ ิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิก ิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิ ิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิ ิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิ ิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิ ิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิ ิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิ ิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิ ิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิ ิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิ ิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิ ิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิ ิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิ ิิิิิิิิ

  9. #19
    Free User shAdOwArt's Avatar
    Join Date
    Apr 2015
    Location
    Kharos
    Posts
    189
    Reputation
    151
    Rep Power
    19
    Quote Originally Posted by SuNe View Post
    Nice one,

    You should add a safe list for pvp safety tho, for those who wants to use it in a teamhunt or so
    I'd rather make it ignore party members (or make it an option to ignore party members) but I couldnt figure out how to do it when I wrote the first draft and since then the need hasn't really come up.

  10. #20
    Moderator SuNe's Avatar
    Join Date
    Dec 2013
    Location
    Sweden
    Posts
    2,881
    Reputation
    186
    Rep Power
    27
    Quote Originally Posted by shAdOwArt View Post
    I'd rather make it ignore party members (or make it an option to ignore party members) but I couldnt figure out how to do it when I wrote the first draft and since then the need hasn't really come up.
    Ye ignoring if in party would work just as good =p
    กิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิ ิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิ ิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิ ิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิ ิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิ ิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิ ิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิ ิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิก ิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิ ิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิ ิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิ ิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิ ิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิ ิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิ ิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิ ิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิ ิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิ ิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิ ิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิ ิิิิิิิิิิิิิิิิกิิิิิิิิิิิิิิิิิิิิกิิิิิิิิิิิิ ิิิิิิิิ

 

 

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •