Misc.pm
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:138k
- my $changed = 0;
- foreach (keys %skillsState) {
- if ($param1 == $_) {
- if (!$actor->{statuses}{$skillsState{$_}}) {
- $actor->{statuses}{$skillsState{$_}} = 1;
- message "$actor $are in $skillsState{$_} staten", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- } elsif ($actor->{statuses}{$skillsState{$_}}) {
- delete $actor->{statuses}{$skillsState{$_}};
- message "$actor $are out of $skillsState{$_} staten", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- }
- foreach (keys %skillsAilments) {
- if (($param2 & $_) == $_) {
- if (!$actor->{statuses}{$skillsAilments{$_}}) {
- $actor->{statuses}{$skillsAilments{$_}} = 1;
- message "$actor $have ailments: $skillsAilments{$_}n", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- } elsif ($actor->{statuses}{$skillsAilments{$_}}) {
- delete $actor->{statuses}{$skillsAilments{$_}};
- message "$actor $are out of ailments: $skillsAilments{$_}n", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- }
- foreach (keys %skillsLooks) {
- if (($param3 & $_) == $_) {
- if (!$actor->{statuses}{$skillsLooks{$_}}) {
- $actor->{statuses}{$skillsLooks{$_}} = 1;
- debug "$actor $have look: $skillsLooks{$_}n", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- } elsif ($actor->{statuses}{$skillsLooks{$_}}) {
- delete $actor->{statuses}{$skillsLooks{$_}};
- debug "$actor $are out of look: $skillsLooks{$_}n", "parseMsg_statuslook", $verbosity;
- $changed = 1;
- }
- }
- Plugins::callHook('changed_status',{actor => $actor, changed => $changed});
- # Remove perfectly hidden objects
- if ($actor->{statuses}{'GM Perfect Hide'}) {
- if (UNIVERSAL::isa($actor, "Actor::Player")) {
- message TF("Remove perfectly hidden %sn", $actor);
- $playersList->remove($actor);
- # Call the hook when a perfectly hidden player is detected
- Plugins::callHook('perfect_hidden_player',undef);
-
- } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) {
- message TF("Remove perfectly hidden %sn", $actor);
- $monstersList->remove($actor);
- # NPCs do this on purpose (who knows why)
- } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) {
- message "Remove perfectly hidden $actorn";
- $npcsList->remove($actor);
- }
- return 1;
- } else {
- return 0;
- }
- }
- # Increment counter for monster being casted on
- sub countCastOn {
- my ($sourceID, $targetID, $skillID, $x, $y) = @_;
- return unless defined $targetID;
- my $source = Actor::get($sourceID);
- my $target = Actor::get($targetID);
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
- if ($targetID eq $accountID) {
- $source->{castOnToYou}++;
- } elsif ($target->isa('Actor::Player')) {
- $source->{castOnToPlayer}{$targetID}++;
- } elsif ($target->isa('Actor::Monster')) {
- $source->{castOnToMonster}{$targetID}++;
- }
- if ($sourceID eq $accountID) {
- $target->{castOnByYou}++;
- } elsif ($source->isa('Actor::Player')) {
- $target->{castOnByPlayer}{$sourceID}++;
- } elsif ($source->isa('Actor::Monster')) {
- $target->{castOnByMonster}{$sourceID}++;
- }
- }
- sub stopAttack {
- my $pos = calcPosition($char);
- $messageSender->sendMove($pos->{x}, $pos->{y});
- }
- ##
- # boolean stripLanguageCode(String* msg)
- # msg: a chat message, as sent by the RO server.
- # Returns: whether the language code was stripped.
- #
- # Strip the language code character from a chat message.
- sub stripLanguageCode {
- my $r_msg = shift;
- if ($config{chatLangCode} && $config{chatLangCode} ne "none") {
- if ($$r_msg =~ /^|..(.*)/) {
- $$r_msg = $1;
- return 1;
- }
- return 0;
- } else {
- return 0;
- }
- }
- ##
- # void switchConf(String filename)
- # filename: a configuration file.
- # Returns: 1 on success, 0 if $filename does not exist.
- #
- # Switch to another configuration file.
- sub switchConfigFile {
- my $filename = shift;
- if (! -f $filename) {
- error TF("%s does not exist.n", $filename);
- return 0;
- }
- Settings::setConfigFilename($filename);
- parseConfigFile($filename, %config);
- return 1;
- }
- sub updateDamageTables {
- my ($ID1, $ID2, $damage) = @_;
- # Track deltaHp
- #
- # A player's "deltaHp" initially starts at 0.
- # When he takes damage, the damage is subtracted from his deltaHp.
- # When he is healed, this amount is added to the deltaHp.
- # If the deltaHp becomes positive, it is reset to 0.
- #
- # Someone with a lot of negative deltaHp is probably in need of healing.
- # This allows us to intelligently heal non-party members.
- if (my $target = Actor::get($ID2)) {
- $target->{deltaHp} -= $damage;
- $target->{deltaHp} = 0 if $target->{deltaHp} > 0;
- }
- if ($ID1 eq $accountID) {
- if ((my $monster = $monstersList->getByID($ID2))) {
- # You attack monster
- $monster->{dmgTo} += $damage;
- $monster->{dmgFromYou} += $damage;
- $monster->{numAtkFromYou}++;
- if ($damage <= ($config{missDamage} || 0)) {
- $monster->{missedFromYou}++;
- debug "Incremented missedFromYou count to $monster->{missedFromYou}n", "attackMonMiss";
- $monster->{atkMiss}++;
- } else {
- $monster->{atkMiss} = 0;
- }
- if ($config{teleportAuto_atkMiss} && $monster->{atkMiss} >= $config{teleportAuto_atkMiss}) {
- message T("Teleporting because of attack missn"), "teleport";
- useTeleport(1);
- }
- if ($config{teleportAuto_atkCount} && $monster->{numAtkFromYou} >= $config{teleportAuto_atkCount}) {
- message TF("Teleporting after attacking a monster %d timesn", $config{teleportAuto_atkCount}), "teleport";
- useTeleport(1);
- }
- if (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3 && $damage) {
- # Mob-training, you only need to attack the monster once to provoke it
- message TF("%s (%s) has been provoked, searching another monstern", $monster->{name}, $monster->{binID});
- stopAttack();
- AI::dequeue();
- }
- }
- } elsif ($ID2 eq $accountID) {
- if ((my $monster = $monstersList->getByID($ID1))) {
- # Monster attacks you
- $monster->{dmgFrom} += $damage;
- $monster->{dmgToYou} += $damage;
- if ($damage == 0) {
- $monster->{missedYou}++;
- }
- $monster->{attackedYou}++ unless (
- scalar(keys %{$monster->{dmgFromPlayer}}) ||
- scalar(keys %{$monster->{dmgToPlayer}}) ||
- $monster->{missedFromPlayer} ||
- $monster->{missedToPlayer}
- );
- $monster->{target} = $ID2;
- if ($AI == 2) {
- my $teleport = 0;
- if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){
- message TF("Teleporting due to attack from %sn",
- $monster->{name}), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_deadly} && $damage >= $char->{hp}
- && !whenStatusActive("Hallucination")) {
- message TF("Next %d dmg could kill you. Teleporting...n",
- $damage), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_maxDmg} && $damage >= $config{teleportAuto_maxDmg}
- && !whenStatusActive("Hallucination")
- && !($config{teleportAuto_maxDmgInLock} && $field{name} eq $config{lockMap})) {
- message TF("%s hit you for more than %d dmg. Teleporting...n",
- $monster->{name}, $config{teleportAuto_maxDmg}), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_maxDmgInLock} && $field{name} eq $config{lockMap}
- && $damage >= $config{teleportAuto_maxDmgInLock}
- && !whenStatusActive("Hallucination")) {
- message TF("%s hit you for more than %d dmg in lockMap. Teleporting...n",
- $monster->{name}, $config{teleportAuto_maxDmgInLock}), "teleport";
- $teleport = 1;
- } elsif (AI::inQueue("sitAuto") && $config{teleportAuto_attackedWhenSitting}
- && $damage > 0) {
- message TF("%s attacks you while you are sitting. Teleporting...n",
- $monster->{name}), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_totalDmg}
- && $monster->{dmgToYou} >= $config{teleportAuto_totalDmg}
- && !whenStatusActive("Hallucination")
- && !($config{teleportAuto_totalDmgInLock} && $field{name} eq $config{lockMap})) {
- message TF("%s hit you for a total of more than %d dmg. Teleporting...n",
- $monster->{name}, $config{teleportAuto_totalDmg}), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_totalDmgInLock} && $field{name} eq $config{lockMap}
- && $monster->{dmgToYou} >= $config{teleportAuto_totalDmgInLock}
- && !whenStatusActive("Hallucination")) {
- message TF("%s hit you for a total of more than %d dmg in lockMap. Teleporting...n",
- $monster->{name}, $config{teleportAuto_totalDmgInLock}), "teleport";
- $teleport = 1;
- } elsif ($config{teleportAuto_hp} && percent_hp($char) <= $config{teleportAuto_hp}) {
- message TF("%s hit you when your HP is too low. Teleporting...n",
- $monster->{name}), "teleport";
- $teleport = 1;
- } elsif ($config{attackChangeTarget} && ((AI::action eq "route" && AI::action(1) eq "attack") || (AI::action eq "move" && AI::action(2) eq "attack"))
- && AI::args->{attackID} && AI::args()->{attackID} ne $ID1) {
- my $attackTarget = Actor::get(AI::args->{attackID});
- my $attackSeq = (AI::action eq "route") ? AI::args(1) : AI::args(2);
- if (!$attackTarget->{dmgToYou} && !$attackTarget->{dmgFromYou} && distance($monster->{pos_to}, calcPosition($char)) <= $attackSeq->{attackMethod}{distance}) {
- my $ignore = 0;
- # Don't attack ignored monsters
- if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
- $ignore = 1 if ( ($control->{attack_auto} == -1)
- || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
- || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
- || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp})
- || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp})
- || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou}))
- );
- }
- if (!$ignore) {
- # Change target to closer aggressive monster
- message TF("Change target to aggressive : %s (%s)n", $monster->name, $monster->{binID});
- stopAttack();
- AI::dequeue;
- AI::dequeue if (AI::action eq "route");
- AI::dequeue;
- attack($ID1);
- }
- }
- } elsif (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3
- && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) {
- # Mob-training, stop attacking the monster if it has been attacking you
- message TF("%s (%s) has been provoked, searching another monstern", $monster->{name}, $monster->{binID});
- stopAttack();
- AI::dequeue();
- }
- useTeleport(1, undef, 1) if ($teleport);
- }
- }
- } elsif ((my $monster = $monstersList->getByID($ID1))) {
- if ((my $player = $playersList->getByID($ID2) || $slavesList->getByID($ID2))) {
- # Monster attacks player
- $monster->{dmgFrom} += $damage;
- $monster->{dmgToPlayer}{$ID2} += $damage;
- $player->{dmgFromMonster}{$ID1} += $damage;
- if ($damage == 0) {
- $monster->{missedToPlayer}{$ID2}++;
- $player->{missedFromMonster}{$ID1}++;
- }
- if (existsInList($config{tankersList}, $player->{name}) ||
- ($char->{slaves} && %{$char->{slaves}} && $char->{slaves}{$ID2} && %{$char->{slaves}{$ID2}}) ||
- ($char->{party} && %{$char->{party}} && $char->{party}{users}{$ID2} && %{$char->{party}{users}{$ID2}})) {
- # Monster attacks party member
- $monster->{dmgToParty} += $damage;
- $monster->{missedToParty}++ if ($damage == 0);
- }
- $monster->{target} = $ID2;
- OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables);
- if ($AI == 2 && ($char->{slaves} && $char->{slaves}{$ID2})) {
- my $teleport = 0;
- if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){
- message TF("Slave teleporting due to attack from %sn",
- $monster->{name}), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_deadly'} && $damage >= $char->{slaves}{$ID2}{hp}
- && !whenStatusActive("Hallucination")) {
- message TF("Next %d dmg could kill your slave. Teleporting...n",
- $damage), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'} && $damage >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'}
- && !whenStatusActive("Hallucination")
- && !($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'} && $field{name} eq $config{lockMap})) {
- message TF("%s hit your slave for more than %d dmg. Teleporting...n",
- $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'}), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'} && $field{name} eq $config{lockMap}
- && $damage >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'}
- && !whenStatusActive("Hallucination")) {
- message TF("%s hit your slave for more than %d dmg in lockMap. Teleporting...n",
- $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'}), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}
- && $monster->{dmgToPlayer}{$ID2} >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}
- && !whenStatusActive("Hallucination")
- && !($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'} && $field{name} eq $config{lockMap})) {
- message TF("%s hit your slave for a total of more than %d dmg. Teleporting...n",
- $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'} && $field{name} eq $config{lockMap}
- && $monster->{dmgToPlayer}{$ID2} >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'}
- && !whenStatusActive("Hallucination")) {
- message TF("%s hit your slave for a total of more than %d dmg in lockMap. Teleporting...n",
- $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'}), "teleport";
- $teleport = 1;
- } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_hp'} && $char->{slaves}{$ID2}{hpPercent} <= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_hp'}) {
- message TF("%s hit your slave when your homunculus' HP is too low. Teleporting...n",
- $monster->{name}), "teleport";
- $teleport = 1;
- } elsif (
- $config{$char->{slaves}{$ID2}{slave_configPrefix}.'attackChangeTarget'}
- && (
- $char->{slaves}{$ID2}->action eq 'route' && $char->{slaves}{$ID2}->action(1) eq 'attack'
- or $char->{slaves}{$ID2}->action eq 'move' && $char->{slaves}{$ID2}->action(2) eq 'attack'
- )
- && $char->{slaves}{$ID2}->args->{attackID} && $char->{slaves}{$ID2}->args->{attackID} ne $ID1
- ) {
- my $attackTarget = Actor::get($char->{slaves}{$ID2}->args->{attackID});
- my $attackSeq = ($char->{slaves}{$ID2}->action eq 'route') ? $char->{slaves}{$ID2}->args(1) : $char->{slaves}{$ID2}->args(2);
- if (!$attackTarget->{dmgToPlayer}{$ID2} && !$attackTarget->{dmgFromPlayer}{$ID2} && distance($monster->{pos_to}, calcPosition($char->{slaves}{$ID2})) <= $attackSeq->{attackMethod}{distance}) {
- my $ignore = 0;
- # Don't attack ignored monsters
- if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
- $ignore = 1 if ( ($control->{attack_auto} == -1)
- || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
- || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
- || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp})
- || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp})
- );
- }
- if (!$ignore) {
- # Change target to closer aggressive monster
- message TF("Slave change target to aggressive : %s (%s)n", $monster->name, $monster->{binID});
- $char->{slaves}{$ID2}->slave_stopAttack();
- $char->{slaves}{$ID2}->dequeue;
- $char->{slaves}{$ID2}->dequeue if $char->{slaves}{$ID2}->action eq 'route';
- $char->{slaves}{$ID2}->dequeue;
- $char->{slaves}{$ID2}->slave_attack($ID1);
- }
- }
- }
- useTeleport(1, undef, 1) if ($teleport);
- }
- }
- } elsif ((my $player = $playersList->getByID($ID1) || $slavesList->getByID($ID1))) {
- if ((my $monster = $monstersList->getByID($ID2))) {
- # Player attacks monster
- $monster->{dmgTo} += $damage;
- $monster->{dmgFromPlayer}{$ID1} += $damage;
- $monster->{lastAttackFrom} = $ID1;
- $player->{dmgToMonster}{$ID2} += $damage;
- if ($damage == 0) {
- $monster->{missedFromPlayer}{$ID1}++;
- $player->{missedToMonster}{$ID2}++;
- }
- if (existsInList($config{tankersList}, $player->{name}) || ($char->{slaves} && $char->{slaves}{$ID1}) ||
- ($char->{party} && %{$char->{party}} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}})) {
- $monster->{dmgFromParty} += $damage;
- }
- OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables);
- }
- }
- }
- ##
- # updatePlayerNameCache(player)
- # player: a player actor object.
- sub updatePlayerNameCache {
- my ($player) = @_;
- return if (!$config{cachePlayerNames});
- # First, cleanup the cache. Remove entries that are too old.
- # Default life time: 15 minutes
- my $changed = 1;
- for (my $i = 0; $i < @playerNameCacheIDs; $i++) {
- my $ID = $playerNameCacheIDs[$i];
- if (timeOut($playerNameCache{$ID}{time}, $config{cachePlayerNames_duration})) {
- delete $playerNameCacheIDs[$i];
- delete $playerNameCache{$ID};
- $changed = 1;
- }
- }
- compactArray(@playerNameCacheIDs) if ($changed);
- # Resize the cache if it's still too large.
- # Default cache size: 100
- while (@playerNameCacheIDs > $config{cachePlayerNames_maxSize}) {
- my $ID = shift @playerNameCacheIDs;
- delete $playerNameCache{$ID};
- }
- # Add this player name to the cache.
- my $ID = $player->{ID};
- if (!$playerNameCache{$ID}) {
- push @playerNameCacheIDs, $ID;
- my %entry = (
- name => $player->{name},
- guild => $player->{guild},
- time => time,
- lv => $player->{lv},
- jobID => $player->{jobID}
- );
- $playerNameCache{$ID} = %entry;
- }
- }
- ##
- # useTeleport(level)
- # level: 1 to teleport to a random spot, 2 to respawn.
- sub useTeleport {
- my ($use_lvl, $internal, $emergency) = @_;
-
- my %args = (
- level => $use_lvl, # 1 = Teleport, 2 = respawn
- emergency => $emergency, # Needs a fast tele
- internal => $internal # Did we call useTeleport from inside useTeleport?
- );
-
- if ($use_lvl == 2 && $config{saveMap_warpChatCommand}) {
- Plugins::callHook('teleport_sent', %args);
- sendMessage($messageSender, "c", $config{saveMap_warpChatCommand});
- return 1;
- }
- if ($use_lvl == 1 && $config{teleportAuto_useChatCommand}) {
- Plugins::callHook('teleport_sent', %args);
- sendMessage($messageSender, "c", $config{teleportAuto_useChatCommand});
- return 1;
- }
- # for possible recursive calls
- if (!defined $internal) {
- $internal = $config{teleportAuto_useSkill};
- }
- # look if the character has the skill
- my $sk_lvl = 0;
- if ($char->{skills}{AL_TELEPORT}) {
- $sk_lvl = $char->{skills}{AL_TELEPORT}{lv};
- }
- # only if we want to use skill ?
- return if ($char->{muted});
- if ($sk_lvl > 0 && $internal > 0 && ($use_lvl == 1 || !$config{'teleportAuto_useItemForRespawn'})) {
- # We have the teleport skill, and should use it
- my $skill = new Skill(handle => 'AL_TELEPORT');
- if ($use_lvl == 2 || $internal == 1 || ($internal == 2 && !isSafe())) {
- # Send skill use packet to appear legitimate
- # (Always send skill use packet for level 2 so that saveMap
- # autodetection works)
- if ($char->{sitting}) {
- Plugins::callHook('teleport_sent', %args);
- main::ai_skillUse($skill->getHandle(), $use_lvl, 0, 0, $accountID);
- return 1;
- } else {
- $messageSender->sendSkillUse($skill->getIDN(), $sk_lvl, $accountID);
- undef $char->{permitSkill};
- }
- if (!$emergency && $use_lvl == 1) {
- Plugins::callHook('teleport_sent', %args);
- $timeout{ai_teleport_retry}{time} = time;
- AI::queue('teleport');
- return 1;
- }
- }
- delete $ai_v{temp}{teleport};
- debug "Sending Teleport using Level $use_lvln", "useTeleport";
- if ($use_lvl == 1) {
- Plugins::callHook('teleport_sent', %args);
- $messageSender->sendTeleport("Random");
- return 1;
- } elsif ($use_lvl == 2) {
- # check for possible skill level abuse
- message T("Using Teleport Skill Level 2 though we not have it!n"), "useTeleport" if ($sk_lvl == 1);
- # If saveMap is not set simply use a wrong .gat.
- # eAthena servers ignore it, but this trick doesn't work
- # on official servers.
- my $telemap = "prontera.gat";
- $telemap = "$config{saveMap}.gat" if ($config{saveMap} ne "");
- Plugins::callHook('teleport_sent', %args);
- $messageSender->sendTeleport($telemap);
- return 1;
- }
- }
- # No skill try to equip a Tele clip or something,
- # if teleportAuto_equip_* is set
- if (Actor::Item::scanConfigAndCheck('teleportAuto_equip') && ($use_lvl == 1 || !$config{'teleportAuto_useItemForRespawn'})) {
- return if AI::inQueue('teleport');
- debug "Equipping Accessory to teleportn", "useTeleport";
- AI::queue('teleport', {lv => $use_lvl});
- if ($emergency ||
- !$config{teleportAuto_useSkill} ||
- $config{teleportAuto_useSkill} == 3 ||
- $config{teleportAuto_useSkill} == 2 && isSafe()) {
- $timeout{ai_teleport_delay}{time} = 1;
- }
- Actor::Item::scanConfigAndEquip('teleportAuto_equip');
- #Commands::run('aiv');
- return 1;
- }
- # else if $internal == 0 or $sk_lvl == 0
- # try to use item
- # could lead to problems if the ItemID would be different on some servers
- # 1 Jan 2006 - instead of nameID, search for *wing in the inventory
- my $item;
- if ($use_lvl == 1) {
- $item = $char->inventory->getByName("Fly Wing");
- } elsif ($use_lvl == 2) {
- $item = $char->inventory->getByName("Butterfly Wing");
- }
- if ($item) {
- # We have Fly Wing/Butterfly Wing.
- # Don't spam the "use fly wing" packet, or we'll end up using too many wings.
- if (timeOut($timeout{ai_teleport})) {
- Plugins::callHook('teleport_sent', %args);
- $messageSender->sendItemUse($item->{index}, $accountID);
- $timeout{ai_teleport}{time} = time;
- }
- return 1;
- }
- # no item, but skill is still available
- if ( $sk_lvl > 0 ) {
- message T("No Fly Wing or Butterfly Wing, fallback to Teleport Skilln"), "useTeleport";
- return useTeleport($use_lvl, 1, $emergency);
- }
- if ($use_lvl == 1) {
- message T("You don't have the Teleport skill or a Fly Wingn"), "teleport";
- } else {
- message T("You don't have the Teleport skill or a Butterfly Wingn"), "teleport";
- }
-
- return 0;
- }
- ##
- # top10Listing(args)
- # args: a 282 bytes packet representing 10 names followed by 10 ranks
- #
- # Returns a formatted list of [# ], Name and points
- sub top10Listing {
- my ($args) = @_;
-
- my $msg = $args->{RAW_MSG};
- my @list;
- my @points;
- my $i;
- my $textList = "";
- for ($i = 0; $i < 10; $i++) {
- $list[$i] = unpack("Z24", substr($msg, 2 + (24*$i), 24));
- }
- for ($i = 0; $i < 10; $i++) {
- $points[$i] = unpack("V1", substr($msg, 242 + ($i*4), 4));
- }
- for ($i = 0; $i < 10; $i++) {
- $textList .= swrite("[@<] @<<<<<<<<<<<<<<<<<<<<<<<< @>>>>>>",
- [$i+1, $list[$i], $points[$i]]);
- }
-
- return $textList;
- }
- ##
- # whenGroundStatus(target, statuses, mine)
- # target: coordinates hash
- # statuses: a comma-separated list of ground effects e.g. Safety Wall,Pneuma
- # mine: if true, only consider ground effects that originated from me
- #
- # Returns 1 if $target has one of the ground effects specified by $statuses.
- sub whenGroundStatus {
- my ($pos, $statuses, $mine) = @_;
- my ($x, $y) = ($pos->{x}, $pos->{y});
- for my $ID (@spellsID) {
- my $spell;
- next unless $spell = $spells{$ID};
- next if $mine && $spell->{sourceID} ne $accountID;
- if ($x == $spell->{pos}{x} &&
- $y == $spell->{pos}{y}) {
- return 1 if existsInList($statuses, getSpellName($spell->{type}));
- }
- }
- return 0;
- }
- sub whenStatusActive {
- my $statuses = shift;
- my @arr = split /s*,s*/, $statuses;
- foreach (@arr) {
- return 1 if exists($char->{statuses}{$_});
- }
- return 0;
- }
- sub whenStatusActiveMon {
- my ($monster, $statuses) = @_;
- my @arr = split /s*,s*/, $statuses;
- foreach (@arr) {
- return 1 if $monster->{statuses}{$_};
- }
- return 0;
- }
- sub whenStatusActivePL {
- my ($ID, $statuses) = @_;
-
- # Incase this method was called with empty values, send TRUE back... since the user doesnt have any statusses they want to check
- return 1 if (!$ID || !$statuses);
- return whenStatusActive($statuses) if ($ID eq $accountID);
- if (my $player = $playersList->getByID($ID)) {
- my @arr = split /s*,s*/, $statuses;
- foreach (@arr) {
- return 1 if $player->{statuses}{$_};
- }
- }
- if ($char->{party}{users}{$ID}{online}) {
- my @arr = split /s*,s*/, $statuses;
- foreach (@arr) {
- return 1 if $char->{party}{users}{$ID}{statuses}{$_};
- }
- }
- if (my $slave = $slavesList->getByID($ID)) {
- my @arr = split /s*,s*/, $statuses;
- foreach (@arr) {
- return 1 if $slave->{statuses}{$_};
- }
- }
- return 0;
- }
- sub writeStorageLog {
- my ($show_error_on_fail) = @_;
- my $f;
- if (open($f, ">:utf8", $Settings::storage_log_file)) {
- print $f TF("---------- Storage %s -----------n", getFormattedDate(int(time)));
- for (my $i = 0; $i < @storageID; $i++) {
- next if (!$storageID[$i]);
- my $item = $storage{$storageID[$i]};
- my $display = sprintf "%2d %s x %s", $i, $item->{name}, $item->{amount};
- # Translation Comment: Mark to show not identified items
- $display .= " -- " . T("Not Identified") if !$item->{identified};
- # Translation Comment: Mark to show broken items
- $display .= " -- " . T("Broken") if $item->{broken};
- print $f "$displayn";
- }
- # Translation Comment: Storage Capacity
- print $f TF("nCapacity: %d/%dn", $storage{items}, $storage{items_max});
- print $f "-------------------------------n";
- close $f;
- message T("Storage loggedn"), "success";
- } elsif ($show_error_on_fail) {
- error TF("Unable to write to %sn", $Settings::storage_log_file);
- }
- }
- ##
- # getBestTarget(possibleTargets, nonLOSNotAllowed)
- # possibleTargets: reference to an array of monsters' IDs
- # nonLOSNotAllowed: if set, non-LOS monsters (and monsters that aren't in attackMaxDistance) aren't checked up
- #
- # Returns ID of the best target
- sub getBestTarget {
- my ($possibleTargets, $nonLOSNotAllowed) = @_;
- if (!$possibleTargets) {
- return;
- }
- my $portalDist = $config{'attackMinPortalDistance'} || 4;
- my $playerDist = $config{'attackMinPlayerDistance'} || 1;
- my @noLOSMonsters;
- my $myPos = calcPosition($char);
- my ($highestPri, $smallestDist, $bestTarget);
- # First of all we check monsters in LOS, then the rest of monsters
- foreach (@{$possibleTargets}) {
- my $monster = $monsters{$_};
- my $pos = calcPosition($monster);
- next if (positionNearPlayer($pos, $playerDist)
- || positionNearPortal($pos, $portalDist)
- );
- if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
- next if ( ($control->{attack_auto} == -1)
- || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
- || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
- || ($control->{attack_hp} ne "" && $control->{attack_hp} > $char->{hp})
- || ($control->{attack_sp} ne "" && $control->{attack_sp} > $char->{sp})
- || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou}))
- || ($control->{attack_auto} == 0 && !($monster->{dmgToYou} || $monster->{missedYou}))
- );
- }
- if ($config{'attackCanSnipe'}) {
- if (!checkLineSnipable($myPos, $pos)) {
- push(@noLOSMonsters, $_);
- next;
- }
- } else {
- if (!checkLineWalkable($myPos, $pos)) {
- push(@noLOSMonsters, $_);
- next;
- }
- }
- my $name = lc $monster->{name};
- my $dist = round(distance($myPos, $pos));
-
- # COMMENTED (FIX THIS): attackMaxDistance should never be used as indication of LOS
- # The objective of attackMaxDistance is to determine the range of normal attack,
- # and not the range of character's ability to engage monsters
- ## Monsters that aren't in attackMaxDistance are not checked up
- ##if ($nonLOSNotAllowed && ($config{'attackMaxDistance'} < $dist)) {
- ## next;
- ##}
- if (!defined($highestPri) || ($priority{$name} > $highestPri)) {
- $highestPri = $priority{$name};
- $smallestDist = $dist;
- $bestTarget = $_;
- }
- if ((!defined($highestPri) || $priority{$name} == $highestPri)
- && (!defined($smallestDist) || $dist < $smallestDist)) {
- $highestPri = $priority{$name};
- $smallestDist = $dist;
- $bestTarget = $_;
- }
- }
- if (!$nonLOSNotAllowed && !$bestTarget && scalar(@noLOSMonsters) > 0) {
- foreach (@noLOSMonsters) {
- # The most optimal solution is to include the path lenghts' comparison, however it will take
- # more time and CPU resources, so, we use rough solution with priority and distance comparison
- my $monster = $monsters{$_};
- my $pos = calcPosition($monster);
- my $name = lc $monster->{name};
- my $dist = round(distance($myPos, $pos));
- if (!defined($highestPri) || ($priority{$name} > $highestPri)) {
- $highestPri = $priority{$name};
- $smallestDist = $dist;
- $bestTarget = $_;
- }
- if ((!defined($highestPri) || $priority{$name} == $highestPri)
- && (!defined($smallestDist) || $dist < $smallestDist)) {
- $highestPri = $priority{$name};
- $smallestDist = $dist;
- $bestTarget = $_;
- }
- }
- }
- return $bestTarget;
- }
- ##
- # Returns 1 if there is a player nearby (except party and homunculus) or 0 if not
- sub isSafe {
- foreach (@playersID) {
- if (!$char->{party}{users}{$_} && (!$char->{homunculus} || $_ ne $char->{homunculus}{ID})) {
- return 0;
- }
- }
- return 1;
- }
- #######################################
- #######################################
- ###CATEGORY: Actor's Actions Text
- #######################################
- #######################################
- ##
- # String attack_string(Actor source, Actor target, int damage, int delay)
- #
- # Generates a proper message string for when actor $source attacks actor $target.
- sub attack_string {
- my ($source, $target, $damage, $delay) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
- return TF("%s %s %s - Dmg: %s (delay %s)n",
- $source->nameString,
- $source->verb('attack', 'attacks'),
- $target->nameString($source),
- $damage, $delay);
- }
- sub skillCast_string {
- my ($source, $target, $x, $y, $skillName, $delay) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
- # Location
- if ($x != 0 || $y != 0) {
- if ($source->isa('Actor::You')) {
- return TF("You are casting %s on location (%d, %d) - (time %sms)n", $skillName, $x, $y, $delay);
- } elsif ($source->isa('Actor::Player')) {
- return TF("Player %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name,
- $source->{binID}, $skillName, $x, $y, $delay);
- } elsif ($source->isa('Actor::Monster')) {
- return TF("Monster %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name,
- $source->{binID}, $skillName, $x, $y, $delay);
- } elsif ($source->isa('Actor::Slave')) {
- return TF("Slave %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name,
- $source->{binID}, $skillName, $x, $y, $delay);
- } elsif ($source->isa('Actor::Unknown')) {
- return TF("Unknown #%s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $x, $y, $delay);
- }
- # You
- } elsif ($source->isa('Actor::You')) {
- if ($target->isa('Actor::You')) {
- return TF("You are casting %s on yourself (time %sms)n", $skillName, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("You are casting %s on player %s (%d) (time %sms)n", $skillName,
- $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("You are casting %s on monster %s (%d) (time %sms)n", $skillName,
- $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("You are casting %s on slave %s (%d) (time %sms)n", $skillName,
- $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("You are casting %s on Unknown #%s (%d) (time %sms)n", $skillName,
- $target->{nameID}, $target->{binID}, $delay);
- }
- # Player
- } elsif ($source->isa('Actor::Player')) {
- if ($target->isa('Actor::You')) {
- return TF("Player %s (%d) is casting %s on you (time %sms)n", $source->name, $source->{binID},
- $skillName, $delay);
- } elsif ($target->isa('Actor::Player')) {
- # consider gender
- if ($source ne $target) {
- return TF("Player %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($source->{sex}) {
- return TF("Player %s (%d) is casting %s on himself (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- } else {
- return TF("Player %s (%d) is casting %s on herself (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- }
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Player %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Player %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Player %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
- }
- # Monster
- } elsif ($source->isa('Actor::Monster')) {
- if ($target->isa('Actor::You')) {
- return TF("Monster %s (%d) is casting %s on you (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Monster %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- if ($source ne $target) {
- return TF("Monster %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } else {
- return TF("Monster %s (%d) is casting %s on itself (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- }
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Monster %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Monster %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
- }
- # Slave
- } elsif ($source->isa('Actor::Slave')) {
- if ($target->isa('Actor::You')) {
- return TF("Slave %s (%d) is casting %s on you (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Slave %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Slave %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- if ($source ne $target) {
- return TF("Slave %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } else {
- return TF("Slave %s (%d) is casting %s on itself (time %sms)n", $source->name,
- $source->{binID}, $skillName, $delay);
- }
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Slave %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
- }
- # Unknown
- } elsif ($source->isa('Actor::Unknown')) {
- if ($target->isa('Actor::You')) {
- return TF("Unknown #%s (%d) is casting %s on you (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Unknown #%s (%d) is casting %s on player %s (%d) (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Unknown #%s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Unknown #%s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- if ($source ne $target) {
- return TF("Unknown #%s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
- } else {
- return TF("Unknown #%s (%d) is casting %s on himself (time %sms)n", $source->{nameID},
- $source->{binID}, $skillName, $delay);
- }
- }
- }
- }
- sub skillUse_string {
- my ($source, $target, $skillName, $damage, $level, $delay) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
- $damage ||= "Miss!";
- if ($damage != -30000) {
- # Translation Comment: Ammount of Damage on Skill
- $damage = "- " . TF("Dmg: %s", $damage) . " ";
- } else {
- $damage = '';
- }
- # Translation Comment: Skill name + level
- $skillName = TF("%s (lvl %s)", $skillName, $level) unless $level == 65535;
- # You
- if ($source->isa('Actor::You')) {
- if ($target->isa('Actor::You')) {
- return TF("You use %s on yourself %s(delay %s)n", $skillName, $damage, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("You use %s on player %s (%d) %s(delay %s)n", $skillName,
- $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("You use %s on monster %s (%d) %s(delay %s)n", $skillName,
- $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("You use %s on slave %s (%d) %s(delay %s)n", $skillName,
- $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("You use %s on Unknown #%s (%d) %s(delay %s)n", $skillName,
- $target->{nameID}, $target->{binID}, $damage, $delay);
- }
- # Player
- } elsif ($source->isa('Actor::Player')) {
- if ($target->isa('Actor::You')) {
- return TF("Player %s (%d) uses %s on you %s(delay %s)n", $source->name, $source->{binID},
- $skillName, $damage, $delay);
- } elsif ($target->isa('Actor::Player')) {
- # consider gender
- if ($source ne $target) {
- return TF("Player %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($source->{sex}) {
- return TF("Player %s (%d) uses %s on himself %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- } else {
- return TF("Player %s (%d) uses %s on herself %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- }
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Player %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Player %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Player %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
- }
- # Monster
- } elsif ($source->isa('Actor::Monster')) {
- if ($target->isa('Actor::You')) {
- return TF("Monster %s (%d) uses %s on you %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Monster %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- if ($source ne $target) {
- return TF("Monster %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } else {
- return TF("Monster %s (%d) uses %s on itself %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- }
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Monster %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Monster %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
- }
- # Slave
- } elsif ($source->isa('Actor::Slave')) {
- if ($target->isa('Actor::You')) {
- return TF("Slave %s (%d) uses %s on you %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Slave %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Slave %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- if ($source ne $target) {
- return TF("Slave %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } else {
- return TF("Slave %s (%d) uses %s on itself %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $damage, $delay);
- }
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Slave %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
- }
- # Unknown
- } elsif ($source->isa('Actor::Unknown')) {
- message "test2n";
- if ($target->isa('Actor::You')) {
- return TF("Unknown #%s (%d) uses %s on you %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $damage, $delay);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Unknown #%s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Unknown #%s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Unknown #%s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
- } elsif ($target->isa('Actor::Unknown')) {
- if ($source ne $target) {
- return TF("Unknown #%s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
- } else {
- return TF("Unknown #%s (%d) uses %s on himself %s(delay %s)n", $source->{nameID},
- $source->{binID}, $skillName, $damage, $delay);
- }
- }
- }
- }
- sub skillUseLocation_string {
- my ($source, $skillName, $args) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- # Translation Comment: Skill name + level
- $skillName = TF("%s (lvl %s)", $skillName, $args->{lv}) unless $args->{lv} == 65535;
- if ($source->isa('Actor::You')) {
- return TF("You use %s on location (%d, %d)n", $skillName, $args->{x}, $args->{y});
- } elsif ($source->isa('Actor::Player')) {
- return TF("Player %s (%d) uses %s on location (%d, %d)n", $source->name,
- $source->{binID}, $skillName, $args->{x}, $args->{y});
- } elsif ($source->isa('Actor::Monster')) {
- return TF("Monster %s (%d) uses %s on location (%d, %d)n", $source->name,
- $source->{binID}, $skillName, $args->{x}, $args->{y});
- } elsif ($source->isa('Actor::Slave')) {
- return TF("Slave %s (%d) uses %s on location (%d, %d)n", $source->name,
- $source->{binID}, $skillName, $args->{x}, $args->{y});
- } elsif ($source->isa('Actor::Unknown')) {
- return TF("Unknown #%s (%d) uses %s on location (%d, %d)n", $source->{nameID},
- $source->{binID}, $skillName, $args->{x}, $args->{y});
- }
- }
- sub skillUseNoDamage_string {
- my ($source, $target, $skillID, $skillName, $amount) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
- if ($skillID == 28) {
- # Translation Comment: used Healing skill
- $amount = ': ' . TF("%s hp gained", $amount);
- } else {
- if ($amount) {
- # Translation Comment: used non-Healing skill - displays skill level
- $amount = ': ' . TF("Lv %s", $amount);
- } else {
- $amount = '';
- }
- }
- if ($source->isa('Actor::You')) {
- if ($target->isa('Actor::You')) {
- return TF("You use %s on yourself %sn", $skillName, $amount);
- } else {
- # Translation Comment: You use a certain skill on an actor.
- return TF("You use %s on %s %sn", $skillName, $target->nameString, $amount);
- }
- # Player
- } elsif ($source->isa('Actor::Player')) {
- if ($target->isa('Actor::You')) {
- return TF("Player %s uses %s on you %sn", $source->name,
- $skillName, $amount);
- } elsif ($target->isa('Actor::Player')) {
- # consider gender
- if ($source ne $target) {
- return TF("Player %s (%d) uses %s on player %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($source->{sex}) {
- return TF("Player %s (%d) uses %s on himself %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- } else {
- return TF("Player %s (%d) uses %s on herself %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- }
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Player %s (%d) uses %s on monster %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Player %s (%d) uses %s on slave %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Player %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
- }
- # Monster
- } elsif ($source->isa('Actor::Monster')) {
- if ($target->isa('Actor::You')) {
- return TF("Monster %s (%d) uses %s on you %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Monster %s (%d) uses %s on player %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Monster')) {
- if ($source ne $target) {
- return TF("Monster %s (%d) uses %s on monster %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } else {
- return TF("Monster %s (%d) uses %s on itself %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- }
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Monster %s (%d) uses %s on slave %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Monster %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
- }
- # Slave
- } elsif ($source->isa('Actor::Slave')) {
- if ($target->isa('Actor::You')) {
- return TF("Slave %s (%d) uses %s on you %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Slave %s (%d) uses %s on player %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Slave %s (%d) uses %s on monster %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Slave')) {
- if ($source ne $target) {
- return TF("Slave %s (%d) uses %s on slave %s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } else {
- return TF("Slave %s (%d) uses %s on itself %sn", $source->name,
- $source->{binID}, $skillName, $amount);
- }
- } elsif ($target->isa('Actor::Unknown')) {
- return TF("Slave %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
- }
- # Unknown
- } elsif ($source->isa('Actor::Unknown')) {
- if ($target->isa('Actor::You')) {
- return TF("Unknown #%s (%d) uses %s on you %sn", $source->{nameID},
- $source->{binID}, $skillName, $amount);
- } elsif ($target->isa('Actor::Player')) {
- return TF("Unknown #%s (%d) uses %s on player %s (%d) %sn", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Monster')) {
- return TF("Unknown #%s (%d) uses %s on monster %s (%d) %sn", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Slave')) {
- return TF("Unknown #%s (%d) uses %s on slave %s (%d) %sn", $source->{nameID},
- $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
- } elsif ($target->isa('Actor::Unknown')) {
- if ($source ne $target) {
- return TF("Unknown #%s (%d) uses %s on Unknown #%s (%d) %sn", $source->{nameID},
- $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
- } else {
- return TF("Unknown #%s (%d) uses %s on himself %sn", $source->{nameID},
- $source->{binID}, $skillName, $amount);
- }
- }
- }
- }
- sub status_string {
- my ($source, $statusName, $mode, $seconds) = @_;
- assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
- if ($mode eq 'now') {
- if ($source->isa('Actor::You')) {
- if (($seconds)&&($seconds > 0)) {
- return TF("You are now: %s for: %s seconds.n", $statusName, $seconds || "N/A");
- } else {
- return TF("You are now: %sn", $statusName);
- }
- } elsif ($source->isa('Actor::Player')) {
- return TF("Player %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Monster')) {
- return TF("Monster %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Slave')) {
- return TF("Slave %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Unknown')) {
- return TF("Unknown #%s (%d) is now: %sn", $source->{nameID}, $source->{binID}, $statusName);
- }
- } elsif ($mode eq 'again') {
- if ($source->isa('Actor::You')) {
- if (($seconds)&&($seconds > 0)) {
- return TF("You are again: %s for: %s seconds.n", $statusName, $seconds || "N/A");
- } else {
- return TF("You are again: %sn", $statusName);
- }
- } elsif ($source->isa('Actor::Player')) {
- return TF("Player %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Monster')) {
- return TF("Monster %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Slave')) {
- return TF("Slave %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Unknown')) {
- return TF("Unknown #%s (%d) is again: %sn", $source->{nameID}, $source->{binID}, $statusName);
- }
- } elsif ($mode eq 'no longer') {
- if ($source->isa('Actor::You')) {
- return TF("You are no longer: %sn", $statusName);
- } elsif ($source->isa('Actor::Player')) {
- return TF("Player %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Monster')) {
- return TF("Monster %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Slave')) {
- return TF("Slave %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
- } elsif ($source->isa('Actor::Unknown')) {
- return TF("Unknown #%s (%d) is no longer: %sn", $source->{nameID}, $source->{binID}, $statusName);
- }
- }
- }
- #######################################
- #######################################
- ###CATEGORY: AI Math
- #######################################
- #######################################
- sub lineIntersection {
- my $r_pos1 = shift;
- my $r_pos2 = shift;
- my $r_pos3 = shift;
- my $r_pos4 = shift;
- my ($x1, $x2, $x3, $x4, $y1, $y2, $y3, $y4, $result, $result1, $result2);
- $x1 = $$r_pos1{'x'};
- $y1 = $$r_pos1{'y'};
- $x2 = $$r_pos2{'x'};
- $y2 = $$r_pos2{'y'};
- $x3 = $$r_pos3{'x'};
- $y3 = $$r_pos3{'y'};
- $x4 = $$r_pos4{'x'};
- $y4 = $$r_pos4{'y'};
- $result1 = ($x4 - $x3)*($y1 - $y3) - ($y4 - $y3)*($x1 - $x3);
- $result2 = ($y4 - $y3)*($x2 - $x1) - ($x4 - $x3)*($y2 - $y1);
- if ($result2 != 0) {
- $result = $result1 / $result2;
- }
- return $result;
- }
- sub percent_hp {
- my $r_hash = shift;
- if (!$$r_hash{'hp_max'}) {
- return 0;
- } else {
- return ($$r_hash{'hp'} / $$r_hash{'hp_max'} * 100);
- }
- }
- sub percent_sp {
- my $r_hash = shift;
- if (!$$r_hash{'sp_max'}) {
- return 0;
- } else {
- return ($$r_hash{'sp'} / $$r_hash{'sp_max'} * 100);
- }
- }
- sub percent_weight {
- my $r_hash = shift;
- if (!$$r_hash{'weight_max'}) {
- return 0;
- } else {
- return ($$r_hash{'weight'} / $$r_hash{'weight_max'} * 100);
- }
- }
- #######################################
- #######################################
- ###CATEGORY: Misc Functions
- #######################################
- #######################################
- sub avoidGM_near {
- my $players = $playersList->getItems();
- foreach my $player (@{$players}) {
- # skip this person if we dont know the name
- next if (!defined $player->{name});
- # Check whether this "GM" is on the ignore list
- # in order to prevent false matches
- last if (existsInList($config{avoidGM_ignoreList}, $player->{name}));
- # check if this name matches the GM filter
- last unless ($config{avoidGM_namePattern} ? $player->{name} =~ /$config{avoidGM_namePattern}/ : $player->{name} =~ /^([a-z]?ro)?-?(Sub)?-?[?GM]?/i);
- my %args = (
- name => $player->{name},
- ID => $player->{ID}
- );
- Plugins::callHook('avoidGM_near', %args);
- return 1 if ($args{return});
- my $msg;
- if ($config{avoidGM_near} == 1) {
- # Mode 1: teleport & disconnect
- useTeleport(1);
- $msg = TF("GM %s is nearby, teleport & disconnect for %d seconds", $player->{name}, $config{avoidGM_reconnect});
- relog($config{avoidGM_reconnect}, 1);
- } elsif ($config{avoidGM_near} == 2) {
- # Mode 2: disconnect
- $msg = TF("GM %s is nearby, disconnect for %s seconds", $player->{name}, $config{avoidGM_reconnect});
- relog($config{avoidGM_reconnect}, 1);
- } elsif ($config{avoidGM_near} == 3) {
- # Mode 3: teleport
- useTeleport(1);
- $msg = TF("GM %s is nearby, teleporting", $player->{name});
- } elsif ($config{avoidGM_near} >= 4) {
- # Mode 4: respawn
- useTeleport(2);
- $msg = TF("GM %s is nearby, respawning", $player->{name});
- }
- warning "$msgn";
- chatLog("k", "*** $msg ***n");
- return 1;
- }
- return 0;
- }
- ##
- # avoidList_near()
- # Returns: 1 if someone was detected, 0 if no one was detected.
- #
- # Checks if any of the surrounding players are on the avoid.txt avoid list.
- # Disconnects / teleports if a player is detected.
- sub avoidList_near {
- return if ($config{avoidList_inLockOnly} && $field{name} ne $config{lockMap});
- my $players = $playersList->getItems();
- foreach my $player (@{$players}) {
- my $avoidPlayer = $avoid{Players}{lc($player->{name})};
- my $avoidID = $avoid{ID}{$player->{nameID}};
- if (!$net->clientAlive() && ( ($avoidPlayer && $avoidPlayer->{disconnect_on_sight}) || ($avoidID && $avoidID->{disconnect_on_sight}) )) {
- warning TF("%s (%s) is nearby, disconnecting...n", $player->{name}, $player->{nameID});
- chatLog("k", TF("*** Found %s (%s) nearby and disconnected ***n", $player->{name}, $player->{nameID}));
- warning TF("Disconnect for %s seconds...n", $config{avoidList_reconnect});
- relog($config{avoidList_reconnect}, 1);
- return 1;
- } elsif (($avoidPlayer && $avoidPlayer->{teleport_on_sight}) || ($avoidID && $avoidID->{teleport_on_sight})) {
- message TF("Teleporting to avoid player %s (%s)n", $player->{name}, $player->{nameID}), "teleport";
- chatLog("k", TF("*** Found %s (%s) nearby and teleported ***n", $player->{name}, $player->{nameID}));
- useTeleport(1);
- return 1;
- }
- }
- return 0;
- }
- sub avoidList_ID {
- return if (!($config{avoidList}) || ($config{avoidList_inLockOnly} && $field{name} ne $config{lockMap}));
- my $avoidID = unpack("V", shift);
- if ($avoid{ID}{$avoidID} && $avoid{ID}{$avoidID}{disconnect_on_sight}) {
- warning TF("%s is nearby, disconnecting...n", $avoidID);
- chatLog("k", TF("*** Found %s nearby and disconnected ***n", $avoidID));
- warning TF("Disconnect for %s seconds...n", $config{avoidList_reconnect});
- relog($config{avoidList_reconnect}, 1);
- return 1;
- }
- return 0;
- }
- sub compilePortals {
- my $checkOnly = shift;
- my %mapPortals;
- my %mapSpawns;
- my %missingMap;
- my $pathfinding;
- my @solution;
- my $field;
- # Collect portal source and destination coordinates per map
- foreach my $portal (keys %portals_lut) {
- $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{x} = $portals_lut{$portal}{source}{x};
- $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{y} = $portals_lut{$portal}{source}{y};
- foreach my $dest (keys %{$portals_lut{$portal}{dest}}) {
- next if $portals_lut{$portal}{dest}{$dest}{map} eq '';
- $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_lut{$portal}{dest}{$dest}{x};
- $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_lut{$portal}{dest}{$dest}{y};
- }
- }
- $pathfinding = new PathFinding if (!$checkOnly);
- # Calculate LOS values from each spawn point per map to other portals on same map
- foreach my $map (sort keys %mapSpawns) {
- message TF("Processing map %s...n", $map), "system" unless $checkOnly;
- foreach my $spawn (keys %{$mapSpawns{$map}}) {
- foreach my $portal (keys %{$mapPortals{$map}}) {
- next if $spawn eq $portal;
- next if $portals_los{$spawn}{$portal} ne '';
- return 1 if $checkOnly;
- if ((!$field || $field->{name} ne $map) && !$missingMap{$map}) {
- eval {
- $field = new Field(name => $map);
- };
- if ($@) {
- $missingMap{$map} = 1;
- }
- }
- my %start = %{$mapSpawns{$map}{$spawn}};
- my %dest = %{$mapPortals{$map}{$portal}};
- closestWalkableSpot($field, %start);
- closestWalkableSpot($field, %dest);
- $pathfinding->reset(
- start => %start,
- dest => %dest,
- field => $field
- );
- my $count = $pathfinding->runcount;
- $portals_los{$spawn}{$portal} = ($count >= 0) ? $count : 0;
- debug "LOS in $map from $start{x},$start{y} to $dest{x},$dest{y}: $portals_los{$spawn}{$portal}n";
- }
- }
- }
- return 0 if $checkOnly;
- # Write new portalsLOS.txt
- writePortalsLOS(Settings::getTableFilename("portalsLOS.txt"), %portals_los);
- message TF("Wrote portals Line of Sight table to '%s'n", Settings::getTableFilename("portalsLOS.txt")), "system";
- # Print warning for missing fields
- if (%missingMap) {
- warning TF("----------------------------Error Summary----------------------------n");
- warning TF("Missing: %s.fldn", $_) foreach (sort keys %missingMap);
- warning TF("Note: LOS information for the above listed map(s) will be inaccurate;n" .
- " however it is safe to ignore if those map(s) are not usedn");
- warning TF("----------------------------Error Summary----------------------------n");
- }
- }
- sub compilePortals_check {
- return compilePortals(1);
- }
- sub portalExists {
- my ($map, $r_pos) = @_;
- foreach (keys %portals_lut) {
- if ($portals_lut{$_}{source}{map} eq $map
- && $portals_lut{$_}{source}{x} == $r_pos->{x}
- && $portals_lut{$_}{source}{y} == $r_pos->{y}) {
- return $_;
- }
- }
- return;
- }
- sub portalExists2 {
- my ($src, $src_pos, $dest, $dest_pos) = @_;
- my $srcx = $src_pos->{x};
- my $srcy = $src_pos->{y};
- my $destx = $dest_pos->{x};
- my $desty = $dest_pos->{y};
- my $destID = "$dest $destx $desty";
- foreach (keys %portals_lut) {
- my $entry = $portals_lut{$_};
- if ($entry->{source}{map} eq $src
- && $entry->{source}{pos}{x} == $srcx
- && $entry->{source}{pos}{y} == $srcy
- && $entry->{dest}{$destID}) {
- return $_;
- }
- }
- return;
- }
- sub redirectXKoreMessages {
- my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_;
- return if ($config{'XKore_silent'} || $type eq "debug" || $level > 0 || $net->getState() != Network::IN_GAME || $XKore_dontRedirect);
- return if ($domain =~ /^(connection|startup|pm|publicchat|guildchat|guildnotice|selfchat|emotion|drop|inventory|deal|storage|input)$/);
- return if ($domain =~ /^(attack|skill|list|info|partychat|npc|route)/);
- $message =~ s/n*$//s;
- $message =~ s/n/\n/g;
- sendMessage($messageSender, "k", $message);
- }
- sub monKilled {
- $monkilltime = time();
- # if someone kills it
- if (($monstarttime == 0) || ($monkilltime < $monstarttime)) {
- $monstarttime = 0;
- $monkilltime = 0;
- }
- $elasped = $monkilltime - $monstarttime;
- $totalelasped = $totalelasped + $elasped;
- if ($totalelasped == 0) {
- $dmgpsec = 0
- } else {
- $dmgpsec = $totaldmg / $totalelasped;
- }
- }
- # Resolves a player or monster ID into a name
- # Obsoleted by Actor module, don't use this!
- sub getActorName {
- my $id = shift;
- if (!$id) {
- return 'Nothing';
- } else {
- my $hash = Actor::get($id);
- return $hash->nameString;
- }
- }
- # Resolves a pair of player/monster IDs into names
- sub getActorNames {
- my ($sourceID, $targetID, $verb1, $verb2) = @_;
- my $source = getActorName($sourceID);
- my $verb = $source eq 'You' ? $verb1 : $verb2;
- my $target;
- if ($targetID eq $sourceID) {
- if ($targetID eq $accountID) {
- $target = 'yourself';
- } else {
- $target = 'self';
- }
- } else {
- $target = getActorName($targetID);
- }
- return ($source, $verb, $target);
- }
- # return ID based on name if party member is online
- sub findPartyUserID {
- if ($char->{party} && %{$char->{party}}) {
- my $partyUserName = shift;
- for (my $j = 0; $j < @partyUsersID; $j++) {
- next if ($partyUsersID[$j] eq "");
- if ($partyUserName eq $char->{party}{users}{$partyUsersID[$j]}{name}
- && $char->{party}{users}{$partyUsersID[$j]}{online}) {
- return $partyUsersID[$j];
- }
- }
- }
- return undef;
- }
- # fill in a hash of NPC information either based on location ("map x y")
- sub getNPCInfo {
- my $id = shift;
- my $return_hash = shift;
- undef %{$return_hash};
- my ($map, $x, $y) = split(/ +/, $id, 3);
- $$return_hash{map} = $map;
- $$return_hash{pos}{x} = $x;
- $$return_hash{pos}{y} = $y;
- if (($$return_hash{map} ne "") && ($$return_hash{pos}{x} ne "") && ($$return_hash{pos}{y} ne "")) {
- $$return_hash{ok} = 1;
- } else {
- error TF("Invalid NPC information for autoBuy, autoSell or autoStorage! (%s)n", $id);
- }
- }
- sub checkSelfCondition {
- my $prefix = shift;
- return 0 if (!$prefix);
- return 0 if ($config{$prefix . "_disabled"});
- return 0 if $config{$prefix."_whenIdle"} && !AI::isIdle();
- # *_manualAI 0 = auto only
- # *_manualAI 1 = manual only
- # *_manualAI 2 = auto or manual
- if ($config{$prefix . "_manualAI"} == 0 || !(defined $config{$prefix . "_manualAI"})) {
- return 0 if ($AI != 2);
- }elsif ($config{$prefix . "_manualAI"} == 1){
- return 0 if ($AI != 1);
- }else {
- return 0 if ($AI == 0);
- }
- if ($config{$prefix . "_hp"}) {
- if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->hp_percent, $1));
- } else {
- return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"}));
- }
- }
- if ($config{$prefix."_sp"}) {
- if ($config{$prefix."_sp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->sp_percent, $1));
- } else {
- return 0 if (!inRange($char->{sp}, $config{$prefix."_sp"}));
- }
- }
- if ($config{$prefix."_homunculus"} =~ /S/) {
- return 0 if (!!$config{$prefix."_homunculus"}) ^ ($char->{homunculus} && !$char->{homunculus}{state});
- }
- if ($char->{homunculus}) {
- if ($config{$prefix . "_homunculus_hp"}) {
- if ($config{$prefix."_homunculus_hp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->{homunculus}{hpPercent}, $1));
- } else {
- return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix."_homunculus_hp"}));
- }
- }
- if ($config{$prefix."_homunculus_sp"}) {
- if ($config{$prefix."_homunculus_sp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->{homunculus}{spPercent}, $1));
- } else {
- return 0 if (!inRange($char->{homunculus}{sp}, $config{$prefix."_homunculus_sp"}));
- }
- }
- if ($config{$prefix."_homunculus_dead"}) {
- return 0 unless ($char->{homunculus}{state} & 4);
- }
- }
- if ($config{$prefix."_mercenary"} =~ /S/) {
- return 0 if (!!$config{$prefix."_mercenary"}) ^ (!!$char->{mercenary});
- }
- if ($char->{mercenary}) {
- if ($config{$prefix . "_mercenary_hp"}) {
- if ($config{$prefix."_mercenary_hp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->{mercenary}{hpPercent}, $1));
- } else {
- return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix."_mercenary_hp"}));
- }
- }
- if ($config{$prefix."_mercenary_sp"}) {
- if ($config{$prefix."_mercenary_sp"} =~ /^(.*)%$/) {
- return 0 if (!inRange($char->{mercenary}{spPercent}, $1));
- } else {
- return 0 if (!inRange($char->{mercenary}{sp}, $config{$prefix."_mercenary_sp"}));
- }
- }
-
- if ($config{$prefix . "_mercenary_whenStatusActive"}) { return 0 unless (whenStatusActivePL($char->{mercenary}, $config{$prefix . "_mercenary_whenStatusActive"})); }
- if ($config{$prefix . "_mercenary_whenStatusInactive"}) { return 0 if (whenStatusActivePL($char->{mercenary}, $config{$prefix . "_mercenary_whenStatusInactive"})); }
- }
- # check skill use SP if this is a 'use skill' condition
- if ($prefix =~ /skill/i) {
- my $skill_handle = Skill->new(name => lc($config{$prefix}))->getHandle();
- return 0 unless (($char->{skills}{$skill_handle} && $char->{skills}{$skill_handle}{lv} >= 1)
- || ($char->{permitSkill} && $char->{permitSkill}->getName() eq $config{$prefix})
- || $config{$prefix."_equip_leftAccessory"}
- || $config{$prefix."_equip_rightAccessory"}
- || $config{$prefix."_equip_leftHand"}
- || $config{$prefix."_equip_rightHand"}
- || $config{$prefix."_equip_robe"}
- );
- return 0 unless ($char->{sp} >= $skillsSP_lut{$skill_handle}{$config{$prefix . "_lvl"}});
- }
- if (defined $config{$prefix . "_aggressives"}) {
- return 0 unless (inRange(scalar ai_getAggressives(), $config{$prefix . "_aggressives"}));
- }
- if (defined $config{$prefix . "_partyAggressives"}) {
- return 0 unless (inRange(scalar ai_getAggressives(undef, 1), $config{$prefix . "_partyAggressives"}));
- }
- if ($config{$prefix . "_stopWhenHit"} > 0) { return 0 if (scalar ai_getMonstersAttacking($accountID)); }
- if ($config{$prefix . "_whenFollowing"} && $config{follow}) {
- return 0 if (!checkFollowMode());
- }
- if ($config{$prefix . "_whenStatusActive"}) { return 0 unless (whenStatusActive($config{$prefix . "_whenStatusActive"})); }
- if ($config{$prefix . "_whenStatusInactive"}) { return 0 if (whenStatusActive($config{$prefix . "_whenStatusInactive"})); }
- if ($config{$prefix . "_onAction"}) { return 0 unless (existsInList($config{$prefix . "_onAction"}, AI::action())); }
- if ($config{$prefix . "_notOnAction"}) { return 0 if (existsInList($config{$prefix . "_notOnAction"}, AI::action())); }
- if ($config{$prefix . "_spirit"}) {return 0 unless (inRange($char->{spirits}, $config{$prefix . "_spirit"})); }
- if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}, $config{$prefix . "_timeout"}) }
- if ($config{$prefix . "_inLockOnly"} > 0) { return 0 unless ($field{name} eq $config{lockMap}); }
- if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($char->{sitting}); }
- if ($config{$prefix . "_notInTown"} > 0) { return 0 if ($cities_lut{$field{name}.'.rsw'}); }
- if ($config{$prefix . "_monsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) {
- my $exists;
- foreach (ai_getAggressives()) {
- if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}->name)) {
- $exists = 1;
- last;
- }
- }
- return 0 unless $exists;
- }
- if ($config{$prefix . "_defendMonsters"}) {
- my $exists;
- foreach (ai_getMonstersAttacking($accountID)) {
- if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->name)) {
- $exists = 1;
- last;
- }
- }
- return 0 unless $exists;
- }
- if ($config{$prefix . "_notMonsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) {
- my $exists;
- foreach (ai_getAggressives()) {
- if (existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->name)) {
- return 0;
- }
- }
- }
- if ($config{$prefix."_inInventory"}) {
- foreach my $input (split / *, */, $config{$prefix."_inInventory"}) {
- my ($itemName, $count) = $input =~ /(.*?)(?:s+([><]=? *d+))?$/;
- $count = '>0' if $count eq '';
- my $item = $char->inventory->getByName($itemName);
- return 0 if !inRange(!$item ? 0 : $item->{amount}, $count);
- }
- }
- if ($config{$prefix."_inCart"}) {
- foreach my $input (split / *, */, $config{$prefix."_inCart"}) {
- my ($item,$count) = $input =~ /(.*?)(?:s+([><]=? *d+))?$/;
- $count = '>0' if $count eq '';
- my $iX = findIndexString_lc($cart{inventory}, "name", $item);
- my $item = $cart{inventory}[$iX];
- return 0 if !inRange(!defined $iX ? 0 : $item->{amount}, $count);
- }
- }
- if ($config{$prefix."_whenGround"}) {
- return 0 unless whenGroundStatus(calcPosition($char), $config{$prefix."_whenGround"});
- }
- if ($config{$prefix."_whenNotGround"}) {
- return 0 if whenGroundStatus(calcPosition($char), $config{$prefix."_whenNotGround"});
- }
- if ($config{$prefix."_whenPermitSkill"}) {
- return 0 unless $char->{permitSkill} &&
- $char->{permitSkill}->getName() eq $config{$prefix."_whenPermitSkill"};
- }
- if ($config{$prefix."_whenNotPermitSkill"}) {
- return 0 if $char->{permitSkill} &&
- $char->{permitSkill}->getName() eq $config{$prefix."_whenNotPermitSkill"};
- }
- if ($config{$prefix."_whenFlag"}) {
- return 0 unless $flags{$config{$prefix."_whenFlag"}};
- }
- if ($config{$prefix."_whenNotFlag"}) {
- return 0 unless !$flags{$config{$prefix."_whenNotFlag"}};
- }
- if ($config{$prefix."_onlyWhenSafe"}) {
- return 0 if !isSafe();
- }
- if ($config{$prefix."_inMap"}) {
- return 0 unless (existsInList($config{$prefix . "_inMap"}, $field{name}));
- }
- if ($config{$prefix."_notInMap"}) {
- return 0 if (existsInList($config{$prefix . "_notInMap"}, $field{name}));
- }
- if ($config{$prefix."_whenEquipped"}) {
- my $item = Actor::Item::get($config{$prefix."_whenEquipped"});
- return 0 unless $item && $item->{equipped};
- }
- if ($config{$prefix."_whenNotEquipped"}) {
- my $item = Actor::Item::get($config{$prefix."_whenNotEquipped"});
- return 0 if $item && $item->{equipped};
- }
- if ($config{$prefix."_zeny"}) {
- return 0 if (!inRange($char->{zenny}, $config{$prefix."_zeny"}));
- }
- # not working yet
- if ($config{$prefix."_whenWater"}) {
- my $pos = calcPosition($char);
- return 0 if ($field->getBlock($pos->{x}, $pos->{y}) != Field::WALKABLE_WATER);
- }
- my %hookArgs;
- $hookArgs{prefix} = $prefix;
- $hookArgs{return} = 1;
- Plugins::callHook("checkSelfCondition", %hookArgs);
- return 0 if (!$hookArgs{return});
- return 1;
- }
- sub checkPlayerCondition {
- my ($prefix, $id) = @_;
- return 0 if (!$id);
-
- my $player = $playersList->getByID($id) || $slavesList->getByID($id);
- if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$id}, $config{$prefix . "_timeout"}) }
- if ($config{$prefix . "_whenStatusActive"}) { return 0 unless (whenStatusActivePL($id, $config{$prefix . "_whenStatusActive"})); }
- if ($config{$prefix . "_whenStatusInactive"}) { return 0 if (whenStatusActivePL($id, $config{$prefix . "_whenStatusInactive"})); }
- if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($player->{sitting}); }
- # we will have player HP info (only) if we are in the same party
- if ($char->{party} && $char->{party}{users}{$id}) {
- if ($config{$prefix . "_hp"}) {
- if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
- return 0 if (!inRange(percent_hp($char->{party}{users}{$id}), $1));
- } else {
- return 0 if (!inRange($char->{party}{users}{$id}{hp}, $config{$prefix . "_hp"}));
- }
- }
- } elsif ($char->{homunculus} && $char->{homunculus}{ID} eq $id) {
- if ($config{$prefix . "_hp"}) {
- if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
- return 0 if (!inRange(percent_hp($char->{homunculus}), $1));
- } else {
- return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix . "_hp"}));
- }
- }
- }
- if ($config{$prefix."_deltaHp"}){
- return 0 unless inRange($player->{deltaHp}, $config{$prefix."_deltaHp"});
- }
- # check player job class
- if ($config{$prefix . "_isJob"}) { return 0 unless (existsInList($config{$prefix . "_isJob"}, $jobs_lut{$player->{jobID}})); }
- if ($config{$prefix . "_isNotJob"}) { return 0 if (existsInList($config{$prefix . "_isNotJob"}, $jobs_lut{$player->{jobID}})); }
- if ($config{$prefix . "_aggressives"}) {
- return 0 unless (inRange(scalar ai_getPlayerAggressives($id), $config{$prefix . "_aggressives"}));
- }
- if ($config{$prefix . "_defendMonsters"}) {
- my $exists;
- foreach (ai_getMonstersAttacking($id)) {
- if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{name})) {
- $exists = 1;
- last;
- }
- }
- return 0 unless $exists;
- }
- if ($config{$prefix . "_monsters"}) {
- my $exists;
- foreach (ai_getPlayerAggressives($id)) {
- if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}{name})) {
- $exists = 1;
- last;
- }
- }
- return 0 unless $exists;
- }
- if ($config{$prefix."_whenGround"}) {
- return 0 unless whenGroundStatus(calcPosition($player), $config{$prefix."_whenGround"});
- }
- if ($config{$prefix."_whenNotGround"}) {
- return 0 if whenGroundStatus(calcPosition($player), $config{$prefix."_whenNotGround"});
- }
- if ($config{$prefix."_dead"}) {
- return 0 if !$player->{dead};
- } else {
- return 0 if $player->{dead};
- }
- if ($config{$prefix."_whenWeaponEquipped"}) {
- return 0 unless $player->{weapon};
- }
- if ($config{$prefix."_whenShieldEquipped"}) {
- return 0 unless $player->{shield};
- }
- if ($config{$prefix."_isGuild"}) {
- return 0 unless ($player->{guild} && existsInList($config{$prefix . "_isGuild"}, $player->{guild}{name}));
- }
- if ($config{$prefix."_dist"}) {
- return 0 unless inRange(distance(calcPosition($char), calcPosition($player)), $config{$prefix."_dist"});
- }
- my %args = (
- player => $player,
- prefix => $prefix,
- return => 1
- );
- Plugins::callHook('checkPlayerCondition', %args);
- return $args{return};
- }
- sub checkMonsterCondition {
- my ($prefix, $monster) = @_;
- if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$monster->{ID}}, $config{$prefix . "_timeout"}) }
- if (my $misses = $config{$prefix . "_misses"}) {
- return 0 unless inRange($monster->{atkMiss}, $misses);
- }
- if (my $misses = $config{$prefix . "_totalMisses"}) {
- return 0 unless inRange($monster->{missedFromYou}, $misses);
- }
- if ($config{$prefix . "_whenStatusActive"}) {
- return 0 unless (whenStatusActiveMon($monster, $config{$prefix . "_whenStatusActive"}));
- }
- if ($config{$prefix . "_whenStatusInactive"}) {
- return 0 if (whenStatusActiveMon($monster, $config{$prefix . "_whenStatusInactive"}));
- }
- if ($config{$prefix."_whenGround"}) {
- return 0 unless whenGroundStatus(calcPosition($monster), $config{$prefix."_whenGround"});
- }
- if ($config{$prefix."_whenNotGround"}) {
- return 0 if whenGroundStatus(calcPosition($monster), $config{$prefix."_whenNotGround"});
- }
- if ($config{$prefix."_dist"}) {
- return 0 unless inRange(distance(calcPosition($char), calcPosition($monster)), $config{$prefix."_dist"});
- }
- if ($config{$prefix."_deltaHp"}){
- return 0 unless inRange($monster->{deltaHp}, $config{$prefix."_deltaHp"});
- }
- # This is only supposed to make sense for players,
- # but it has to be here for attackSkillSlot PVP to work
- if ($config{$prefix."_whenWeaponEquipped"}) {
- return 0 unless $monster->{weapon};
- }
- if ($config{$prefix."_whenShieldEquipped"}) {
- return 0 unless $monster->{shield};
- }
- my %args = (
- monster => $monster,
- prefix => $prefix,
- return => 1
- );
- Plugins::callHook('checkMonsterCondition', %args);
- return $args{return};
- }
- ##
- # findCartItemInit()
- #
- # Resets all "found" flags in the cart to 0.
- sub findCartItemInit {
- for (@{$cart{inventory}}) {
- next unless $_ && %{$_};
- undef $_->{found};
- }
- }
- ##
- # findCartItem($name [, $found [, $nounid]])
- #
- # Returns the integer index into $cart{inventory} for the cart item matching
- # the given name, or undef.
- #
- # If an item is found, the "found" value for that item is set to 1. Items
- # cannot be found again until you reset the "found" flags using
- # findCartItemInit(), if $found is true.
- #
- # Unidentified items will not be returned if $nounid is true.
- sub findCartItem {
- my ($name, $found, $nounid) = @_;
- $name = lc($name);
- my $index = 0;
- for (@{$cart{inventory}}) {
- if (lc($_->{name}) eq $name &&
- !($found && $_->{found}) &&
- !($nounid && !$_->{identified})) {
- $_->{found} = 1;
- return $index;
- }
- $index++;
- }
- return undef;
- }
- ##
- # makeShop()
- #
- # Returns an array of items to sell. The array can be no larger than the
- # maximum number of items that the character can vend. Each item is a hash
- # reference containing the keys "index", "amount" and "price".
- #
- # If there is a problem with opening a shop, an error message will be printed
- # and nothing will be returned.
- sub makeShop {
- if ($shopstarted) {
- error T("A shop has already been opened.n");
- return;
- }
- return unless $char;
- if (!$char->{skills}{MC_VENDING}{lv}) {
- error T("You don't have the Vending skill.n");
- return;
- }
- if (!$shop{title_line}) {
- error T("Your shop does not have a title.n");
- return;
- }
- my @items = ();
- my $max_items = $char->{skills}{MC_VENDING}{lv} + 2;
- # Iterate through items to be sold
- findCartItemInit();
- shuffleArray(@{$shop{items}}) if ($config{'shop_random'} eq "2");
- for my $sale (@{$shop{items}}) {
- my $index = findCartItem($sale->{name}, 1, 1);
- next unless defined($index);
- # Found item to vend
- my $cart_item = $cart{inventory}[$index];
- my $amount = $cart_item->{amount};
- my %item;
- $item{name} = $cart_item->{name};
- $item{index} = $index;
- $item{price} = $sale->{price};
- $item{amount} =
- $sale->{amount} && $sale->{amount} < $amount ?
- $sale->{amount} : $amount;
- push(@items, %item);
- # We can't vend anymore items
- last if @items >= $max_items;
- }
- if (!@items) {
- error T("There are no items to sell.n");
- return;
- }
- shuffleArray(@items) if ($config{'shop_random'} eq "1");
- return @items;
- }
- sub openShop {
- my @items = makeShop();
- my @shopnames;
- return unless @items;
- @shopnames = split(/;;/, $shop{title_line});
- $shop{title} = $shopnames[int rand($#shopnames + 1)];
- $shop{title} = ($config{shopTitleOversize}) ? $shop{title} : substr($shop{title},0,36);
- $messageSender->sendOpenShop($shop{title}, @items);
- message TF("Shop opened (%s) with %d selling items.n", $shop{title}, @items.""), "success";
- $shopstarted = 1;
- $shopEarned = 0;
- }
- sub closeShop {
- if (!$shopstarted) {
- error T("A shop has not been opened.n");
- return;
- }
- $messageSender->sendCloseShop();
- $shopstarted = 0;
- $timeout{'ai_shop'}{'time'} = time;
- message T("Shop closed.n");
- }
- ##
- # inLockMap()
- #
- # Returns 1 (true) if character is located in its lockmap.
- # Returns 0 (false) if character is not located in lockmap.
- sub inLockMap {
- if ($field{'name'} eq $config{'lockMap'}) {
- return 1;
- } else {
- return 0;
- }
- }
- sub parseReload {
- my ($args) = @_;
- eval {
- my $progressHandler = sub {
- my ($filename) = @_;
- message TF("Loading %s...n", $filename);
- };
- if ($args eq 'all') {
- Settings::loadAll($progressHandler);
- } else {
- Settings::loadByRegexp(qr/$args/, $progressHandler);
- }
- Log::initLogFiles();
- };
- if (my $e = caught('UTF8MalformedException')) {
- error TF(
- "The file %s must be valid UTF-8 encoded, which it is n" .
- "currently not. To solve this prolem, please use Notepadn" .
- "to save that file as valid UTF-8.",
- $e->textfile);
- } elsif ($@) {
- die $@;
- }
- }
- sub MODINIT {
- OpenKoreMod::initMisc() if (defined(&OpenKoreMod::initMisc));
- }
- return 1;