Post by ~Byakko~ on Mar 22, 2009 12:37:31 GMT 8
Hello guyz, long time no post. Nywayz, here is a way to make your bot KS.
This method would work on the following assumptions:
1. The Game has not been modified/updated meaning the bots can still log in.
2. The bot in usage is suitable for the code below. Note: works for 2.0.6.1
Otherwise, there is no guarantee that this will work.
Now, how to make ur bot KS.
Step 1. Open ur bot folder.
Step 2. Open the 'src' folder.
Step 3. Open the 'AI' folder.
Step 4. (Optional but recommended) Make a back up copy of 'Attack.pm'.
Step 5. Open 'Attack.pm' using notepad and erase everything inside.
Step 6. Paste this code inside it.
Step 7. In your config, set teleportAuto_dropTargetKS to 0.
Step 8. Run your bot and pray it works.
Note: Even though the console will still display the message "Dropping target - you will not kill steal others", it does not follow it anymore. Assuming of course, the code above still works.
That's it. Enjoy ur modified bot /no1
This method would work on the following assumptions:
1. The Game has not been modified/updated meaning the bots can still log in.
2. The bot in usage is suitable for the code below. Note: works for 2.0.6.1
Otherwise, there is no guarantee that this will work.
Now, how to make ur bot KS.
Step 1. Open ur bot folder.
Step 2. Open the 'src' folder.
Step 3. Open the 'AI' folder.
Step 4. (Optional but recommended) Make a back up copy of 'Attack.pm'.
Step 5. Open 'Attack.pm' using notepad and erase everything inside.
Step 6. Paste this code inside it.
#########################################################################
# OpenKore - Attack AI
# Copyright (c) 2006 OpenKore Team
#
# This software is open source, licensed under the GNU General Public
# License, version 2.
# Basically, this means that you're allowed to modify and distribute
# this software. However, if you distribute modified versions, you MUST
# also distribute the source code.
# See http://www.gnu.org/licenses/gpl.html for the full license.
#
# $Revision: 4286 $
# $Id: Commands.pm 4286 2006-04-17 14:02:27Z illusion_kore $
#
#########################################################################
#
# This module contains the attack AI's code.
package AI::Attack;
use strict;
use Carp::Assert;
use Time::HiRes qw(time);
use Globals;
use AI;
use Actor;
use Field;
use Log qw(message debug warning);
use Translation qw(T TF);
use Network::Send ();
use Skill;
use Misc;
use Utils;
use Utils::Benchmark;
use Utils::PathFinding;
sub process {
Benchmark::begin("ai_attack") if DEBUG;
my $args = AI::args;
if (AI::action eq "attack" && AI::args->{suspended}) {
$args->{ai_attack_giveup}{time} += time - $args->{suspended};
delete $args->{suspended};
}
if (AI::action eq "attack" && $args->{move_start}) {
# We've just finished moving to the monster.
# Don't count the time we spent on moving
$args->{ai_attack_giveup}{time} += time - $args->{move_start};
undef $args->{unstuck}{time};
undef $args->{move_start};
} elsif (AI::action eq "attack" && $args->{avoiding} && $args->{attackID}) {
my $target = Actor::get($args->{attackID});
$args->{ai_attack_giveup}{time} = time + $target->{time_move_calc} + 3;
undef $args->{avoiding};
} elsif (((AI::action eq "route" && AI::action(1) eq "attack") || (AI::action eq "move" && AI::action(2) eq "attack"))
&& $args->{attackID} && timeOut($AI::Temp::attack_route_adjust, 1)) {
# We're on route to the monster; check whether the monster has moved
my $ID = $args->{attackID};
my $attackSeq = (AI::action eq "route") ? AI::args(1) : AI::args(2);
my $target = Actor::get($ID);
if ($target->{type} ne 'Unknown' && $attackSeq->{monsterPos} && %{$attackSeq->{monsterPos}}
&& round(distance(calcPosition($target), $attackSeq->{monsterPos})) > $attackSeq->{attackMethod}{maxDistance}) {
# Monster has moved; stop moving and let the attack AI readjust route
AI::dequeue;
AI::dequeue if (AI::action eq "route");
$attackSeq->{ai_attack_giveup}{time} = time;
debug "Target has moved more than $attackSeq->{attackMethod}{maxDistance} blocks; readjusting route\n", "ai_attack";
} elsif ($target->{type} ne 'Unknown' && $attackSeq->{monsterPos} && %{$attackSeq->{monsterPos}}
&& round(distance(calcPosition($target), calcPosition($char))) <= $attackSeq->{attackMethod}{maxDistance}) {
# Monster is within attack range; stop moving
AI::dequeue;
AI::dequeue if (AI::action eq "route");
$attackSeq->{ai_attack_giveup}{time} = time;
debug "Target at ($attackSeq->{monsterPos}{x},$attackSeq->{monsterPos}{y}) is now within " .
"$attackSeq->{attackMethod}{maxDistance} blocks; stop moving\n", "ai_attack";
}
$AI::Temp::attack_route_adjust = time;
}
if (AI::action eq "attack") {
my $ID = $args->{ID};
if (targetGone()) {
finishAttacking();
} elsif (shouldGiveUp()) {
giveUp();
} else {
if (timeOut($args->{attackMainTimeout}, 0.1)) {
$args->{attackMainTimeout} = time;
main();
}
}
}
# Check for kill steal and mob-training while moving
if ((AI::is("move", "route") && $args->{attackID} && AI::inQueue("attack")
&& timeOut($args->{movingWhileAttackingTimeout}, 0.2))) {
my $ID = AI::args->{attackID};
my $monster = $monsters{$ID};
# Check for kill steal while moving
#if ($monster && !Misc::checkMonsterCleanness($ID)) {
# message T("Dropping target - you will not kill steal others\n");
# stopAttack();
# $monster->{ignore} = 1;
# Right now, the queue is either
# move, route, attack
# -or-
# route, attack
# AI::dequeue;
# AI::dequeue;
# AI::dequeue if (AI::action eq "attack");
if ($config{teleportAuto_dropTargetKS}) {
message T("Teleport due to dropping attack target\n");
useTeleport(1);
}
#}
# Mob-training, stop attacking the monster if it is already aggressive
if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
if ($control->{attack_auto} == 3
&& ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) {
message TF("Dropping target - %s (%s) has been provoked\n", $monster->{name}, $monster->{binID});
stopAttack();
$monster->{ignore} = 1;
# Right now, the queue is either
# move, route, attack
# -or-
# route, attack
AI::dequeue;
AI::dequeue;
AI::dequeue if (AI::action eq "attack");
}
}
$args->{movingWhileAttackingTimeout} = time;
}
Benchmark::end("ai_attack") if DEBUG;
}
sub shouldGiveUp {
my $args = AI::args;
return !$config{attackNoGiveup} && (timeOut($args->{ai_attack_giveup}) || $args->{unstuck}{count} > 5);
}
sub giveUp {
my $ID = AI::args->{ID};
my $target = Actor::get($ID);
$target->{attack_failed} = time if ($monsters{$ID});
AI::dequeue;
message T("Can't reach or damage target, dropping target\n"), "ai_attack";
if ($config{'teleportAuto_dropTarget'}) {
message T("Teleport due to dropping attack target\n");
useTeleport(1);
}
}
sub targetGone {
my $args = AI::args;
return !$monsters{$args->{ID}} && (!$players{$args->{ID}} || $players{$args->{ID}}{dead});
}
sub finishAttacking {
my $args = AI::args;
$timeout{'ai_attack'}{'time'} -= $timeout{'ai_attack'}{'timeout'};
my $ID = $args->{ID};
AI::dequeue;
if ($monsters_old{$ID} && $monsters_old{$ID}{dead}) {
message T("Target died\n"), "ai_attack";
Plugins::callHook("target_died");
monKilled();
# Pickup loot when monster's dead
if ($AI == 2 && $config{'itemsTakeAuto'} && $monsters_old{$ID}{dmgFromYou} > 0 && !$monsters_old{$ID}{ignore}) {
AI::clear("items_take");
ai_items_take($monsters_old{$ID}{pos}{x}, $monsters_old{$ID}{pos}{y},
$monsters_old{$ID}{pos_to}{x}, $monsters_old{$ID}{pos_to}{y});
} else {
# Cheap way to suspend all movement to make it look real
ai_clientSuspend(0, $timeout{'ai_attack_waitAfterKill'}{'timeout'});
}
## kokal start
## mosters counting
my $i = 0;
my $found = 0;
while ($monsters_Killed[$i]) {
if ($monsters_Killed[$i]{'nameID'} eq $monsters_old{$ID}{'nameID'}) {
$monsters_Killed[$i]{'count'}++;
monsterLog($monsters_Killed[$i]{'name'});
$found = 1;
last;
}
$i++;
}
if (!$found) {
$monsters_Killed[$i]{'nameID'} = $monsters_old{$ID}{'nameID'};
$monsters_Killed[$i]{'name'} = $monsters_old{$ID}{'name'};
$monsters_Killed[$i]{'count'} = 1;
monsterLog($monsters_Killed[$i]{'name'})
}
## kokal end
} elsif ($config{teleportAuto_lostTarget}) {
message T("Target lost, teleporting.\n"), "ai_attack";
useTeleport(1);
} else {
message T("Target lost\n"), "ai_attack";
}
Plugins::callHook('attack_end', {ID => $ID})
}
sub dropTargetWhileMoving {
my $ID = AI::args->{attackID};
#message T("Dropping target - you will not kill steal others\n");
#stopAttack();
#$monsters{$ID}{ignore} = 1;
# Right now, the queue is either
# move, route, attack
# -or-
# route, attack
#AI::dequeue;
#AI::dequeue;
#AI::dequeue if (AI::action eq "attack");
if ($config{teleportAuto_dropTargetKS}) {
message T("Teleport due to dropping attack target\n");
useTeleport(1);
}
}
sub main {
my $args = AI::args;
Benchmark::begin("ai_attack (part 1)") if DEBUG;
Benchmark::begin("ai_attack (part 1.1)") if DEBUG;
# The attack sequence hasn't timed out and the monster is on screen
# Update information about the monster and the current situation
my $args = AI::args;
my $followIndex = AI::findAction("follow");
my $following;
my $followID;
if (defined $followIndex) {
$following = AI::args($followIndex)->{following};
$followID = AI::args($followIndex)->{ID};
}
my $ID = $args->{ID};
my $target = Actor::get($ID);
my $myPos = $char->{pos_to};
my $monsterPos = $target->{pos_to};
my $monsterDist = round(distance($myPos, $monsterPos));
my ($realMyPos, $realMonsterPos, $realMonsterDist, $hitYou);
my $realMyPos = calcPosition($char);
my $realMonsterPos = calcPosition($target);
my $realMonsterDist = round(distance($realMyPos, $realMonsterPos));
if (!$config{'runFromTarget'}) {
$myPos = $realMyPos;
$monsterPos = $realMonsterPos;
}
my $cleanMonster = checkMonsterCleanness($ID);
# If the damage numbers have changed, update the giveup time so we don't timeout
if ($args->{dmgToYou_last} != $target->{dmgToYou}
|| $args->{missedYou_last} != $target->{missedYou}
|| $args->{dmgFromYou_last} != $target->{dmgFromYou}
|| $args->{lastSkillTime} != $char->{last_skill_time}) {
$args->{ai_attack_giveup}{time} = time;
debug "Update attack giveup time\n", "ai_attack", 2;
}
$hitYou = ($args->{dmgToYou_last} != $target->{dmgToYou}
|| $args->{missedYou_last} != $target->{missedYou});
$args->{dmgToYou_last} = $target->{dmgToYou};
$args->{missedYou_last} = $target->{missedYou};
$args->{dmgFromYou_last} = $target->{dmgFromYou};
$args->{missedFromYou_last} = $target->{missedFromYou};
$args->{lastSkillTime} = $char->{last_skill_time};
Benchmark::end("ai_attack (part 1.1)") if DEBUG;
Benchmark::begin("ai_attack (part 1.2)") if DEBUG;
# Determine what combo skill to use
delete $args->{attackMethod};
my $lastSkill;
if ($char->{last_skill_used}) {
$lastSkill = Skill->new(idn => $char->{last_skill_used})->getName();
}
my $i = 0;
while (exists $config{"attackComboSlot_$i"}) {
if (!$config{"attackComboSlot_$i"}) {
$i++;
next;
}
if ($config{"attackComboSlot_${i}_afterSkill"} eq $lastSkill
&& ( !$config{"attackComboSlot_${i}_maxUses"} || $args->{attackComboSlot_uses}{$i} < $config{"attackComboSlot_${i}_maxUses"} )
&& ( !$config{"attackComboSlot_${i}_autoCombo"} || ($char->{combo_packet} && $config{"attackComboSlot_${i}_autoCombo"}) )
&& ( !defined($args->{ID}) || $args->{ID} eq $char->{last_skill_target} || !$config{"attackComboSlot_${i}_isSelfSkill"})
&& checkSelfCondition("attackComboSlot_$i")
&& (!$config{"attackComboSlot_${i}_monsters"} || existsInList($config{"attackComboSlot_${i}_monsters"}, $target->{name}))
&& (!$config{"attackComboSlot_${i}_notMonsters"} || !existsInList($config{"attackComboSlot_${i}_notMonsters"}, $target->{name}))
&& checkMonsterCondition("attackComboSlot_${i}_target", $target)) {
$args->{attackComboSlot_uses}{$i}++;
delete $char->{last_skill_used};
if ($config{"attackComboSlot_${i}_autoCombo"}) {
$char->{combo_packet} = 1500 if ($char->{combo_packet} > 1500);
# eAthena seems to have a bug where the combo_packet overflows and gives an
# abnormally high number. This causes kore to get stuck in a waitBeforeUse timeout.
$config{"attackComboSlot_${i}_waitBeforeUse"} = ($char->{combo_packet} / 1000);
}
delete $char->{combo_packet};
$args->{attackMethod}{type} = "combo";
$args->{attackMethod}{comboSlot} = $i;
$args->{attackMethod}{distance} = $config{"attackComboSlot_${i}_dist"};
$args->{attackMethod}{maxDistance} = $config{"attackComboSlot_${i}_dist"};
$args->{attackMethod}{isSelfSkill} = $config{"attackComboSlot_${i}_isSelfSkill"};
last;
}
$i++;
}
# Determine what skill to use to attack
if (!$args->{attackMethod}{type}) {
if ($config{'attackUseWeapon'}) {
$args->{attackMethod}{distance} = $config{'attackDistance'};
$args->{attackMethod}{maxDistance} = $config{'attackMaxDistance'};
$args->{attackMethod}{type} = "weapon";
} else {
$args->{attackMethod}{distance} = 30;
$args->{attackMethod}{maxDistance} = 30;
undef $args->{attackMethod}{type};
}
$i = 0;
while (exists $config{"attackSkillSlot_$i"}) {
if (!$config{"attackSkillSlot_$i"}) {
$i++;
next;
}
my $skill = new Skill(name => $config{"attackSkillSlot_$i"});
if (checkSelfCondition("attackSkillSlot_$i")
&& (!$config{"attackSkillSlot_$i"."_maxUses"} ||
$target->{skillUses}{$skill->getHandle()} < $config{"attackSkillSlot_$i"."_maxUses"})
&& (!$config{"attackSkillSlot_$i"."_maxAttempts"} || $args->{attackSkillSlot_attempts}{$i} < $config{"attackSkillSlot_$i"."_maxAttempts"})
&& (!$config{"attackSkillSlot_$i"."_monsters"} || existsInList($config{"attackSkillSlot_$i"."_monsters"}, $target->{'name'}))
&& (!$config{"attackSkillSlot_$i"."_notMonsters"} || !existsInList($config{"attackSkillSlot_$i"."_notMonsters"}, $target->{'name'}))
&& (!$config{"attackSkillSlot_$i"."_previousDamage"} || inRange($target->{dmgTo}, $config{"attackSkillSlot_$i"."_previousDamage"}))
&& checkMonsterCondition("attackSkillSlot_${i}_target", $target)
) {
$args->{attackSkillSlot_attempts}{$i}++;
$args->{attackMethod}{distance} = $config{"attackSkillSlot_$i"."_dist"};
$args->{attackMethod}{maxDistance} = $config{"attackSkillSlot_$i"."_dist"};
$args->{attackMethod}{type} = "skill";
$args->{attackMethod}{skillSlot} = $i;
last;
}
$i++;
}
if ($config{'runFromTarget'} && $config{'runFromTarget_dist'} > $args->{attackMethod}{distance}) {
$args->{attackMethod}{distance} = $config{'runFromTarget_dist'};
}
}
$args->{attackMethod}{maxDistance} ||= $config{attackMaxDistance};
$args->{attackMethod}{distance} ||= $config{attackDistance};
if ($args->{attackMethod}{maxDistance} < $args->{attackMethod}{distance}) {
$args->{attackMethod}{maxDistance} = $args->{attackMethod}{distance};
}
Benchmark::end("ai_attack (part 1.2)") if DEBUG;
Benchmark::end("ai_attack (part 1)") if DEBUG;
if ($char->{sitting}) {
ai_setSuspend(0);
stand();
} elsif (!$cleanMonster) {
# Drop target if it's already attacked by someone else
message T("Dropping target - you will not kill steal others\n"), "ai_attack";
$messageSender->sendMove($realMyPos->{x}, $realMyPos->{y});
AI::dequeue;
if ($config{teleportAuto_dropTargetKS}) {
message T("Teleporting due to dropping attack target\n"), "teleport";
useTeleport(1);
}
} elsif (
$config{attackCheckLOS} && $args->{attackMethod}{distance} > 2
&& (($config{attackCanSnipe} && !checkLineSnipable($realMyPos, $realMonsterPos))
|| (!$config{attackCanSnipe} && $realMonsterDist < $args->{attackMethod}{maxDistance} && !checkLineWalkable($realMyPos, $realMonsterPos)))
) {
# We are a ranged attacker without LOS
# Calculate squares around monster within shooting range, but not
# closer than runFromTarget_dist
my @stand = calcRectArea2($realMonsterPos->{x}, $realMonsterPos->{y},
$args->{attackMethod}{distance},
$config{runFromTarget} ? $config{runFromTarget_dist} : 0);
my ($master, $masterPos);
if ($config{follow}) {
foreach (keys %players) {
if ($players{$_}{name} eq $config{followTarget}) {
$master = $players{$_};
last;
}
}
$masterPos = calcPosition($master) if $master;
}
# Determine which of these spots are snipable
my $best_spot;
my $best_dist;
for my $spot (@stand) {
# Is this spot acceptable?
# 1. It must have LOS to the target ($realMonsterPos).
# 2. It must be within $config{followDistanceMax} of
# $masterPos, if we have a master.
if (
(($config{attackCanSnipe} && checkLineSnipable($spot, $realMonsterPos))
|| checkLineWalkable($spot, $realMonsterPos))
&& $field->isWalkable($spot->{x}, $spot->{y})
&& (!$master || round(distance($spot, $masterPos)) <= $config{followDistanceMax})
) {
my $dist = distance($realMyPos, $spot);
if (!defined($best_dist) || $dist < $best_dist) {
$best_dist = $dist;
$best_spot = $spot;
}
}
}
# Move to the closest spot
my $msg = "No LOS from ($realMyPos->{x}, $realMyPos->{y}) to target ($realMonsterPos->{x}, $realMonsterPos->{y})";
if ($best_spot) {
message TF("%s; moving to (%s, %s)\n", $msg, $best_spot->{x}, $best_spot->{y});
if ($config{attackChangeTarget} == 2) {
# Restart attack from processAutoAttack
AI::dequeue;
ai_route($field{name}, $best_spot->{x}, $best_spot->{y}, LOSSubRoute => 1);
} else {
ai_route($field{name}, $best_spot->{x}, $best_spot->{y});
}
} else {
warning TF("%s; no acceptable place to stand\n", $msg);
AI::dequeue;
}
} elsif ($config{'runFromTarget'} && ($realMonsterDist < $config{'runFromTarget_dist'} || $hitYou)) {
#my $begin = time;
# Get a list of blocks that we can run to
my @blocks = calcRectArea($myPos->{x}, $myPos->{y},
# If the monster hit you while you're running, then your recorded
# location may be out of date. So we use a smaller distance so we can still move.
($hitYou) ? $config{'runFromTarget_dist'} / 2 : $config{'runFromTarget_dist'});
# Find the distance value of the block that's farthest away from a wall
my $highest;
foreach (@blocks) {
my $dist = ord(substr($field{dstMap}, $_->{y} * $field{width} + $_->{x}));
if (!defined $highest || $dist > $highest) {
$highest = $dist;
}
}
# Get rid of rediculously large route distances (such as spots that are on a hill)
# Get rid of blocks that are near a wall
my $pathfinding = new PathFinding;
use constant AVOID_WALLS => 4;
for (my $i = 0; $i < @blocks; $i++) {
# We want to avoid walls (so we don't get cornered), if possible
my $dist = ord(substr($field{dstMap}, $blocks[$i]{y} * $field{width} + $blocks[$i]{x}));
if ($highest >= AVOID_WALLS && $dist < AVOID_WALLS) {
delete $blocks[$i];
next;
}
$pathfinding->reset(
field => \%field,
start => $myPos,
dest => $blocks[$i]);
my $ret = $pathfinding->runcount;
if ($ret <= 0 || $ret > $config{'runFromTarget_dist'} * 2) {
delete $blocks[$i];
next;
}
}
# Find the block that's farthest to us
my $largestDist;
my $bestBlock;
foreach (@blocks) {
next unless defined $_;
my $dist = distance($monsterPos, $_);
if (!defined $largestDist || $dist > $largestDist) {
$largestDist = $dist;
$bestBlock = $_;
}
}
#message "Time spent: " . (time - $begin) . "\n";
#debug_showSpots('runFromTarget', \@blocks, $bestBlock);
$args->{avoiding} = 1;
move($bestBlock->{x}, $bestBlock->{y}, $ID);
} elsif ($realMonsterDist > $args->{attackMethod}{maxDistance}
&& timeOut($args->{ai_attack_giveup}, 0.5)) {
# The target monster moved; move to target
$args->{move_start} = time;
$args->{monsterPos} = {%{$monsterPos}};
my $pos = meetingPosition($target, $args->{attackMethod}{maxDistance});
my $dist = sprintf("%.1f", $monsterDist);
debug "Target distance $dist is >$args->{attackMethod}{maxDistance}; moving to target: " .
"from ($myPos->{x},$myPos->{y}) to ($pos->{x},$pos->{y})\n", "ai_attack";
my $result = ai_route($field{'name'}, $pos->{x}, $pos->{y},
maxRouteTime => $config{'attackMaxRouteTime'},
attackID => $ID,
noMapRoute => 1,
noAvoidWalls => 1);
if (!$result) {
# Unable to calculate a route to target
$target->{attack_failed} = time;
AI::dequeue;
message T("Unable to calculate a route to target, dropping target\n"), "ai_attack";
if ($config{'teleportAuto_dropTarget'}) {
message T("Teleport due to dropping attack target\n");
useTeleport(1);
}
}
} elsif ((!$config{'runFromTarget'} || $realMonsterDist >= $config{'runFromTarget_dist'})
&& (!$config{'tankMode'} || !$target->{dmgFromYou})) {
# Attack the target. In case of tanking, only attack if it hasn't been hit once.
if (!$args->{firstAttack}) {
$args->{firstAttack} = 1;
my $pos = "$myPos->{x},$myPos->{y}";
debug "Ready to attack target (which is $realMonsterDist blocks away); we're at ($pos)\n", "ai_attack";
}
$args->{unstuck}{time} = time if (!$args->{unstuck}{time});
if (!$target->{dmgFromYou} && timeOut($args->{unstuck})) {
# We are close enough to the target, and we're trying to attack it,
# but some time has passed and we still haven't dealed any damage.
# Our recorded position might be out of sync, so try to unstuck
$args->{unstuck}{time} = time;
debug("Attack - trying to unstuck\n", "ai_attack");
move($myPos->{x}, $myPos->{y});
$args->{unstuck}{count}++;
}
if ($args->{attackMethod}{type} eq "weapon" && timeOut($timeout{ai_attack})) {
if (Actor::Item::scanConfigAndCheck("attackEquip")) {
#check if item needs to be equipped
Actor::Item::scanConfigAndEquip("attackEquip");
} else {
$messageSender->sendAttack($ID,
($config{'tankMode'}) ? 0 : 7);
$timeout{ai_attack}{time} = time;
delete $args->{attackMethod};
}
} elsif ($args->{attackMethod}{type} eq "skill") {
my $slot = $args->{attackMethod}{skillSlot};
delete $args->{attackMethod};
ai_setSuspend(0);
my $skill = new Skill(name => lc($config{"attackSkillSlot_$slot"}));
if (!ai_getSkillUseType($skill->getHandle())) {
ai_skillUse(
$skill->getHandle(),
$config{"attackSkillSlot_${slot}_lvl"},
$config{"attackSkillSlot_${slot}_maxCastTime"},
$config{"attackSkillSlot_${slot}_minCastTime"},
$config{"attackSkillSlot_${slot}_isSelfSkill"} ? $accountID : $ID,
undef,
"attackSkill",
undef,
undef,
"attackSkillSlot_${slot}");
} else {
my $pos = calcPosition($config{"attackSkillSlot_${slot}_isSelfSkill"} ? $char : $target);
ai_skillUse(
$skill->getHandle(),
$config{"attackSkillSlot_${slot}_lvl"},
$config{"attackSkillSlot_${slot}_maxCastTime"},
$config{"attackSkillSlot_${slot}_minCastTime"},
$pos->{x},
$pos->{y},
"attackSkill",
undef,
undef,
"attackSkillSlot_${slot}");
}
$args->{monsterID} = $ID;
debug "Auto-skill on monster ".getActorName($ID).": ".qq~$config{"attackSkillSlot_$slot"} (lvl $config{"attackSkillSlot_${slot}_lvl"})\n~, "ai_attack";
} elsif ($args->{attackMethod}{type} eq "combo") {
my $slot = $args->{attackMethod}{comboSlot};
my $isSelfSkill = $args->{attackMethod}{isSelfSkill};
my $skill = Skill->new(name => $config{"attackComboSlot_$slot"})->getHandle();
delete $args->{attackMethod};
if (!ai_getSkillUseType($skill)) {
my $targetID = ($isSelfSkill) ? $accountID : $ID;
ai_skillUse(
$skill,
$config{"attackComboSlot_${slot}_lvl"},
$config{"attackComboSlot_${slot}_maxCastTime"},
$config{"attackComboSlot_${slot}_minCastTime"},
$targetID,
undef,
undef,
undef,
$config{"attackComboSlot_${slot}_waitBeforeUse"});
} else {
my $pos = ($isSelfSkill) ? $char->{pos_to} : $target->{pos_to};
ai_skillUse(
$skill,
$config{"attackComboSlot_${slot}_lvl"},
$config{"attackComboSlot_${slot}_maxCastTime"},
$config{"attackComboSlot_${slot}_minCastTime"},
$pos->{x},
$pos->{y},
undef,
undef,
$config{"attackComboSlot_${slot}_waitBeforeUse"});
}
$args->{monsterID} = $ID;
}
} elsif ($config{tankMode}) {
if ($args->{dmgTo_last} != $target->{dmgTo}) {
$args->{ai_attack_giveup}{time} = time;
}
$args->{dmgTo_last} = $target->{dmgTo};
}
}
1;
Step 7. In your config, set teleportAuto_dropTargetKS to 0.
Step 8. Run your bot and pray it works.
Note: Even though the console will still display the message "Dropping target - you will not kill steal others", it does not follow it anymore. Assuming of course, the code above still works.
That's it. Enjoy ur modified bot /no1