Bahamut failed to share his updated code. luckily i managed to make it work too
Code: Select all
# autowarper by Hexo55
# Edited from autowarpn by Joseph and Kissa2k
package autowarper;
use strict;
use Globals;
use Log qw(message error);
use Utils;
use Network::Send;
use Misc;
use AI;
use Translation;
Plugins::register('autowarper', 'Autowarp for all your warping needs', \&unload);
my $hooks = Plugins::addHooks(
['AI_pre', \&AI_hook],
['packet_skilluse', \&warpopen_hook],
['packet/warp_portal_list', \&warplist_hook],
['Network::Receive::map_changed', \&mapchange_hook],
['player_disappeared', \&playerdisappeared_hook],
['packet_pre/area_spell_disappears', \&portaldisappeared_hook],
['is_casting', \&casting_hook]
);
my $commandsID = Commands::register(
["autowarp", "Autowarp for all your warping needs", \&cmdAutoWarp]
);
#global variables
my $map;
my $move_flag;
my @payload;
my $castTries;
my $isCasting;
my $timeoutBeforeCasting;
my $warpOpened;
my $warpNowOpen;
my $warpCoordinates;
sub unload {
Plugins::delHooks($hooks);
Commands::unregister($commandsID);
}
sub cmdAutoWarp {
if (!$net || $net->getState() != Network::IN_GAME) {
error TF("You must be logged in the game to use this command (%s).\n", shift);
return;
}
elsif (AI::action eq "autowarp") {
error T("We have already initiated autowarp, wait until it finishes.\n");
return;
}
my (undef, $args_string) = @_;
my @args = parseArgs($args_string);
#initialize variables
$castTries = 0;
$warpOpened = 0;
$warpNowOpen = 0;
$move_flag = 0;
if ($args[0] eq 'move') {
$move_flag = 1;
shift(@args);
}
$map = shift(@args);
# Remove payload from args if it
# the payload is currently in sight
@payload = ();
if (@args){
foreach my $player (@{$playersList->getItems()}){
last if (!@args);
for (my $i = 0; $i < @args; $i++) {
if ($args[$i] eq $player->{name}) {
push(@payload, $args[$i]);
splice(@args, $i, 1);
last;
}
}
}
}
if (@args) {
error TF("Payload(s) %s is not in sight.\n", join(', ', @args));
}
if ($map =~ /^([a-zA-Z]+).*(\d*)$/) {
# checks if you have the warp skill
if (!($char->{skills}{AL_WARP}) || !($char->{skills}{AL_WARP}{lv} > 0)) {
error T("Skill Warp Portal cannot be used because your character has no such skill.\n");
return;
}
# checks if the destination you specify exists
elsif (!defined $maps_lut{$map.'.rsw'}) {
error TF("Map '%s' does not exist.\n", $map);
return;
}
# add to AI::queue so that the bot will finish first
# what it is doing before proceeding with autowarp
# we also need to save AI::args->timeout into a temporary
# variable since it is cleared whenever we call AI:queue
# $temp variable is used when we are casting a skill so that
# we will cast the warp portal after the skill has been cast.
my $temp = AI::args->{timeout};
AI::queue("autowarp");
AI::args->{timeout} = ($isCasting ? $temp+1 : 1);
AI::args->{time} = time;
$timeoutBeforeCasting = 0;
# We need to stand to be able to cast the skill
Commands::cmdStand();
if(@payload) {
message (sprintf("Now warping payload(s) %s.\n", join(', ', @payload)), "list");
}
message(sprintf("Preparing to cast a warp portal to '%s'.\n", $map), "list");
}
else {
error T("Syntax Error in function 'autowarp'\n" .
"Usage: autowarp [move] <field_name> [<player1> <player2> ...]\n");
}
}
sub mapchange_hook {
my $ai_string;
if (@ai_seq) {
$ai_string = join(' ', @ai_seq);
}
# AI::Dequeue is not enough
# since after moving into warp, the first
# element in ai_seq is 'route' not 'autowarp'
# because we invoke main::ai_route
if ($ai_string =~ /autowarp/) {
AI::clear;
return;
}
}
sub playerdisappeared_hook {
my ($hookmname, $args) = @_;
# This will run only if we are
# autowarping, we have payloads, and
# the warp is still open.
if(AI::action eq "autowarp" && @payload && $warpNowOpen) {
my $player = $args->{player};
# Remove player from the payload list if
# he disappeared.
for (my $i = 0; $i < @payload; $i++) {
if ($payload[$i] eq $player->{name}) {
message (sprintf("Payload %s disappeared.\n", $payload[$i]), "list");
splice(@payload, $i, 1);
last;
}
}
if (!@payload && !$move_flag) {
message ("No more Payload.\n", "list");
AI::dequeue;
return;
}
elsif (!@payload && $move_flag && $warpNowOpen) {
message (sprintf("Moving into warp portal at (%d, %d).\n",$warpCoordinates->{x},$warpCoordinates->{y}), "list" );
main::ai_route($field->name(), $warpCoordinates->{x}, $warpCoordinates->{y},
noSitAuto => 1,
attackOnRoute => 0);
$move_flag = 0;
}
}
}
sub portaldisappeared_hook {
my ($hookname, $args) = @_;
# This will only run if we are autowarping
# and we have casted a warp portal
if(AI::action eq "autowarp" && $warpNowOpen){
$warpNowOpen = 0;
my $ID = $args->{ID};
my $spell = $spells{$ID};
if (($spell->{type} == 0x81) && ($spell->{sourceID} eq $accountID)) {
# We need to cast another warp portal immediately
AI::args->{timeout} = 1;
AI::args->{time} = time;
}
}
}
sub AI_hook {
my $hookName = shift;
if (timeOut(AI::args)) {
# If we are casting wait until
# finishes
if ($isCasting) {
# Restore timeout
AI::args->{timeout} = $timeoutBeforeCasting;
AI::args->{time} = time;
$isCasting = 0;
}
elsif (AI::action eq "autowarp") {
# Check if we have reached $maxCastTries
my $maxCastTries = defined($config{'autoWarp_maxCastTries'}) ? $config{'autoWarp_maxCastTries'} : 3;
if ($castTries >= $maxCastTries) {
error TF("Unable to cast skill Warp Portal in %d tries.\n", $castTries);
AI::dequeue;
return;
}
elsif ($warpOpened >= $maxCastTries) {
error TF("I have already opened %d warp portals for the payloads, I will not open another.\n", $warpOpened);
AI::dequeue;
return;
}
my $pos = getEmptyBlock($char, 8);
if ($pos) {
my $warplvl = $char->{skills}{AL_WARP}{lv};
# This adds a skill task so as not to
# interfere with current auto skills
Commands::cmdUseSkill('sl', "27 $pos->{x} $pos->{y}");
#stopAttack();
message (sprintf("Attempting to open warp portal at (%d, %d).\n",$pos->{x}, $pos->{y}), "list");
# If we have failed to cast a warp, wait
# timeout before casting again
AI::args->{timeout} = defined($config{'autoWarp_timeout'}) ? $config{'autoWarp_timeout'} : 15;
AI::args->{time} = time;
$castTries++;
}
else {
error T("Cannot cast warp portal on current location, move to another location and try again.\n");
AI::dequeue;
return;
}
}
}
}
sub warplist_hook {
if (AI::action eq "autowarp") {
# checks if you have memo to your target destination
for (my $i = 0; $i < @{$char->{warp}{memo}}; $i++) {
last if ($char->{warp}{memo}[$i] eq $map);
if (($i == @{$char->{warp}{memo}} - 1) && ($char->{warp}{memo}[$i] ne $map)) {
error TF("You do not have a warp to '%s'.\n", $map);
# You still need to do this to close the warp list
$messageSender->sendWarpTele(27, $map. ".gat");
AI::dequeue;
return;
}
}
$messageSender->sendWarpTele(27, $map.".gat");
}
}
sub warpopen_hook {
my ($hookname, $args) = @_;
if (AI::action eq "autowarp") {
# it's our warp portal!
if ($args->{sourceID} eq $accountID && $args->{skillID} == 27) {
$warpCoordinates->{x} = $args->{x};
$warpCoordinates->{y} = $args->{y};
$warpOpened++;
$warpNowOpen = 1;
$castTries = 0;
if (@payload) {
message("Waiting for the payload(s) to enter the warp portal.\n", "list");
#extend timeout
AI::args->{timeout} = 30;
AI::args->{time} = time;
}
elsif (!$move_flag) {
AI::dequeue;
return;
}
elsif (!@payload && $move_flag && $warpNowOpen) {
message (sprintf("Moving into warp portal at (%d, %d).\n",$warpCoordinates->{x},$warpCoordinates->{y}), "list" );
main::ai_route($field->name(), $warpCoordinates->{x}, $warpCoordinates->{y},
noSitAuto => 1,
attackOnRoute => 0);
$move_flag = 0;
}
}
}
}
# This sub adds timeout in case we are
# casting a spell.
sub casting_hook {
my ($hookname, $args) = @_;
if ($args->{sourceID} eq $accountID) {
# First we need to store the timeout before casting
# so we can continue counting after we cast
$timeoutBeforeCasting = AI::args->{timeout};
AI::args->{timeout} = int($args->{castTime} / 1000) + 1;
AI::args->{time} = time;
$isCasting = 1;
}
}
# My version of spiral scanning cells around the caster.
# This scans for an empty 3x3 cell around the caster
# up to $maxDist-1. This is slower than the previous
# algorithm since it has to scan more cells.
sub getEmptyBlock {
my ($obj) = shift;
my ($maxDist) = shift;
my $cellPriority = $castTries;
my $pos;
#Get the position of all players
foreach (@{$playersList->getItems()}) {
$pos->{$_->{pos_to}{x}}->{$_->{pos_to}{y}} = 1;
}
my $vectorx = {
1 => -1,
-1 => 1
};
my $vectory = {
-1 => -1,
1 => 1
};
my $target_pos;
my $cell;
for (my $i = 2; $i < $maxDist; $i++) {
$cell->{x} = $i;
$cell->{y} = $i;
my $traversal_flag = 1;
for (my $k = 0; $k < ($i*8); $k++) {
$target_pos->{x} = $obj->{pos_to}{x} + $cell->{x};
$target_pos->{y} = $obj->{pos_to}{y} + $cell->{y};
if(is_subBlockEmpty($target_pos,$pos) && checkLineWalkable($obj->{pos_to}, $target_pos)) {
# This ensures that if the warp skill failed, the next cast will
# probably on a new spot.
if ($cellPriority == 0 && int(rand(2)) == 1) {
return $target_pos;
}
elsif($cellPriority > 0) {
$cellPriority--;
}
}
$cell->{$traversal_flag ? 'y':'x'} += $traversal_flag ? $vectorx->{$cell->{x}/$i}:$vectory->{$cell->{y}/$i};
if ($traversal_flag && $vectory->{$cell->{y}/$i}) {
$traversal_flag = 0;
}
elsif (!$traversal_flag && $vectorx->{$cell->{x}/$i}) {
$traversal_flag = 1;
}
}
}
return 0;
}
#Scans if the (3x3) subBlock is empty
sub is_subBlockEmpty {
my $center_pos = shift;
my $pos = shift;
my $vectors = [
[0,0],
[1,1],
[1,0],
[1,-1],
[0,-1],
[-1,-1],
[-1,0],
[-1,1],
[0,1]
];
my $posx = 0;
my $posy = 0;
foreach my $coord (@{$vectors}) {
$posx = ${$coord}[0] + $center_pos->{x};
$posy = ${$coord}[1] + $center_pos->{y};
unless ($field->isWalkable($posx, $posy) && !$pos->{$posx}->{$posy}) {
return 0;
}
}
return 1;
}
1;