Misc.pm.svn-base
上传用户:market2
上传日期:2018-11-18
资源大小:18786k
文件大小:138k
源码类别:

外挂编程

开发平台:

Windows_Unix

  1. my $changed = 0;
  2. foreach (keys %skillsState) {
  3. if ($param1 == $_) {
  4. if (!$actor->{statuses}{$skillsState{$_}}) {
  5. $actor->{statuses}{$skillsState{$_}} = 1;
  6. message "$actor $are in $skillsState{$_} staten", "parseMsg_statuslook", $verbosity;
  7. $changed = 1;
  8. }
  9. } elsif ($actor->{statuses}{$skillsState{$_}}) {
  10. delete $actor->{statuses}{$skillsState{$_}};
  11. message "$actor $are out of $skillsState{$_} staten", "parseMsg_statuslook", $verbosity;
  12. $changed = 1;
  13. }
  14. }
  15. foreach (keys %skillsAilments) {
  16. if (($param2 & $_) == $_) {
  17. if (!$actor->{statuses}{$skillsAilments{$_}}) {
  18. $actor->{statuses}{$skillsAilments{$_}} = 1;
  19. message "$actor $have ailments: $skillsAilments{$_}n", "parseMsg_statuslook", $verbosity;
  20. $changed = 1;
  21. }
  22. } elsif ($actor->{statuses}{$skillsAilments{$_}}) {
  23. delete $actor->{statuses}{$skillsAilments{$_}};
  24. message "$actor $are out of ailments: $skillsAilments{$_}n", "parseMsg_statuslook", $verbosity;
  25. $changed = 1;
  26. }
  27. }
  28. foreach (keys %skillsLooks) {
  29. if (($param3 & $_) == $_) {
  30. if (!$actor->{statuses}{$skillsLooks{$_}}) {
  31. $actor->{statuses}{$skillsLooks{$_}} = 1;
  32. debug "$actor $have look: $skillsLooks{$_}n", "parseMsg_statuslook", $verbosity;
  33. $changed = 1;
  34. }
  35. } elsif ($actor->{statuses}{$skillsLooks{$_}}) {
  36. delete $actor->{statuses}{$skillsLooks{$_}};
  37. debug "$actor $are out of look: $skillsLooks{$_}n", "parseMsg_statuslook", $verbosity;
  38. $changed = 1;
  39. }
  40. }
  41. Plugins::callHook('changed_status',{actor => $actor, changed => $changed});
  42. # Remove perfectly hidden objects
  43. if ($actor->{statuses}{'GM Perfect Hide'}) {
  44. if (UNIVERSAL::isa($actor, "Actor::Player")) {
  45. message TF("Remove perfectly hidden %sn", $actor);
  46. $playersList->remove($actor);
  47. # Call the hook when a perfectly hidden player is detected
  48. Plugins::callHook('perfect_hidden_player',undef);
  49. } elsif (UNIVERSAL::isa($actor, "Actor::Monster")) {
  50. message TF("Remove perfectly hidden %sn", $actor);
  51. $monstersList->remove($actor);
  52. # NPCs do this on purpose (who knows why)
  53. } elsif (UNIVERSAL::isa($actor, "Actor::NPC")) {
  54. message "Remove perfectly hidden $actorn";
  55. $npcsList->remove($actor);
  56. }
  57. return 1;
  58. } else {
  59. return 0;
  60. }
  61. }
  62. # Increment counter for monster being casted on
  63. sub countCastOn {
  64. my ($sourceID, $targetID, $skillID, $x, $y) = @_;
  65. return unless defined $targetID;
  66. my $source = Actor::get($sourceID);
  67. my $target = Actor::get($targetID);
  68. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  69. assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
  70. if ($targetID eq $accountID) {
  71. $source->{castOnToYou}++;
  72. } elsif ($target->isa('Actor::Player')) {
  73. $source->{castOnToPlayer}{$targetID}++;
  74. } elsif ($target->isa('Actor::Monster')) {
  75. $source->{castOnToMonster}{$targetID}++;
  76. }
  77. if ($sourceID eq $accountID) {
  78. $target->{castOnByYou}++;
  79. } elsif ($source->isa('Actor::Player')) {
  80. $target->{castOnByPlayer}{$sourceID}++;
  81. } elsif ($source->isa('Actor::Monster')) {
  82. $target->{castOnByMonster}{$sourceID}++;
  83. }
  84. }
  85. sub stopAttack {
  86. my $pos = calcPosition($char);
  87. $messageSender->sendMove($pos->{x}, $pos->{y});
  88. }
  89. ##
  90. # boolean stripLanguageCode(String* msg)
  91. # msg: a chat message, as sent by the RO server.
  92. # Returns: whether the language code was stripped.
  93. #
  94. # Strip the language code character from a chat message.
  95. sub stripLanguageCode {
  96. my $r_msg = shift;
  97. if ($config{chatLangCode} && $config{chatLangCode} ne "none") {
  98. if ($$r_msg =~ /^|..(.*)/) {
  99. $$r_msg = $1;
  100. return 1;
  101. }
  102. return 0;
  103. } else {
  104. return 0;
  105. }
  106. }
  107. ##
  108. # void switchConf(String filename)
  109. # filename: a configuration file.
  110. # Returns: 1 on success, 0 if $filename does not exist.
  111. #
  112. # Switch to another configuration file.
  113. sub switchConfigFile {
  114. my $filename = shift;
  115. if (! -f $filename) {
  116. error TF("%s does not exist.n", $filename);
  117. return 0;
  118. }
  119. Settings::setConfigFilename($filename);
  120. parseConfigFile($filename, %config);
  121. return 1;
  122. }
  123. sub updateDamageTables {
  124. my ($ID1, $ID2, $damage) = @_;
  125. # Track deltaHp
  126. #
  127. # A player's "deltaHp" initially starts at 0.
  128. # When he takes damage, the damage is subtracted from his deltaHp.
  129. # When he is healed, this amount is added to the deltaHp.
  130. # If the deltaHp becomes positive, it is reset to 0.
  131. #
  132. # Someone with a lot of negative deltaHp is probably in need of healing.
  133. # This allows us to intelligently heal non-party members.
  134. if (my $target = Actor::get($ID2)) {
  135. $target->{deltaHp} -= $damage;
  136. $target->{deltaHp} = 0 if $target->{deltaHp} > 0;
  137. }
  138. if ($ID1 eq $accountID) {
  139. if ((my $monster = $monstersList->getByID($ID2))) {
  140. # You attack monster
  141. $monster->{dmgTo} += $damage;
  142. $monster->{dmgFromYou} += $damage;
  143. $monster->{numAtkFromYou}++;
  144. if ($damage <= ($config{missDamage} || 0)) {
  145. $monster->{missedFromYou}++;
  146. debug "Incremented missedFromYou count to $monster->{missedFromYou}n", "attackMonMiss";
  147. $monster->{atkMiss}++;
  148. } else {
  149. $monster->{atkMiss} = 0;
  150. }
  151. if ($config{teleportAuto_atkMiss} && $monster->{atkMiss} >= $config{teleportAuto_atkMiss}) {
  152. message T("Teleporting because of attack missn"), "teleport";
  153. useTeleport(1);
  154. }
  155. if ($config{teleportAuto_atkCount} && $monster->{numAtkFromYou} >= $config{teleportAuto_atkCount}) {
  156. message TF("Teleporting after attacking a monster %d timesn", $config{teleportAuto_atkCount}), "teleport";
  157. useTeleport(1);
  158. }
  159. if (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3 && $damage) {
  160. # Mob-training, you only need to attack the monster once to provoke it
  161. message TF("%s (%s) has been provoked, searching another monstern", $monster->{name}, $monster->{binID});
  162. stopAttack();
  163. AI::dequeue();
  164. }
  165. }
  166. } elsif ($ID2 eq $accountID) {
  167. if ((my $monster = $monstersList->getByID($ID1))) {
  168. # Monster attacks you
  169. $monster->{dmgFrom} += $damage;
  170. $monster->{dmgToYou} += $damage;
  171. if ($damage == 0) {
  172. $monster->{missedYou}++;
  173. }
  174. $monster->{attackedYou}++ unless (
  175. scalar(keys %{$monster->{dmgFromPlayer}}) ||
  176. scalar(keys %{$monster->{dmgToPlayer}}) ||
  177. $monster->{missedFromPlayer} ||
  178. $monster->{missedToPlayer}
  179. );
  180. $monster->{target} = $ID2;
  181. if ($AI == 2) {
  182. my $teleport = 0;
  183. if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){
  184. message TF("Teleporting due to attack from %sn",
  185. $monster->{name}), "teleport";
  186. $teleport = 1;
  187. } elsif ($config{teleportAuto_deadly} && $damage >= $char->{hp}
  188.       && !whenStatusActive("Hallucination")) {
  189. message TF("Next %d dmg could kill you. Teleporting...n",
  190. $damage), "teleport";
  191. $teleport = 1;
  192. } elsif ($config{teleportAuto_maxDmg} && $damage >= $config{teleportAuto_maxDmg}
  193.       && !whenStatusActive("Hallucination")
  194.       && !($config{teleportAuto_maxDmgInLock} && $field{name} eq $config{lockMap})) {
  195. message TF("%s hit you for more than %d dmg. Teleporting...n",
  196. $monster->{name}, $config{teleportAuto_maxDmg}), "teleport";
  197. $teleport = 1;
  198. } elsif ($config{teleportAuto_maxDmgInLock} && $field{name} eq $config{lockMap}
  199.       && $damage >= $config{teleportAuto_maxDmgInLock}
  200.       && !whenStatusActive("Hallucination")) {
  201. message TF("%s hit you for more than %d dmg in lockMap. Teleporting...n",
  202. $monster->{name}, $config{teleportAuto_maxDmgInLock}), "teleport";
  203. $teleport = 1;
  204. } elsif (AI::inQueue("sitAuto") && $config{teleportAuto_attackedWhenSitting}
  205.       && $damage > 0) {
  206. message TF("%s attacks you while you are sitting. Teleporting...n",
  207. $monster->{name}), "teleport";
  208. $teleport = 1;
  209. } elsif ($config{teleportAuto_totalDmg}
  210.       && $monster->{dmgToYou} >= $config{teleportAuto_totalDmg}
  211.       && !whenStatusActive("Hallucination")
  212.       && !($config{teleportAuto_totalDmgInLock} && $field{name} eq $config{lockMap})) {
  213. message TF("%s hit you for a total of more than %d dmg. Teleporting...n",
  214. $monster->{name}, $config{teleportAuto_totalDmg}), "teleport";
  215. $teleport = 1;
  216. } elsif ($config{teleportAuto_totalDmgInLock} && $field{name} eq $config{lockMap}
  217.       && $monster->{dmgToYou} >= $config{teleportAuto_totalDmgInLock}
  218.       && !whenStatusActive("Hallucination")) {
  219. message TF("%s hit you for a total of more than %d dmg in lockMap. Teleporting...n",
  220. $monster->{name}, $config{teleportAuto_totalDmgInLock}), "teleport";
  221. $teleport = 1;
  222. } elsif ($config{teleportAuto_hp} && percent_hp($char) <= $config{teleportAuto_hp}) {
  223. message TF("%s hit you when your HP is too low. Teleporting...n",
  224. $monster->{name}), "teleport";
  225. $teleport = 1;
  226. } elsif ($config{attackChangeTarget} && ((AI::action eq "route" && AI::action(1) eq "attack") || (AI::action eq "move" && AI::action(2) eq "attack"))
  227.    && AI::args->{attackID} && AI::args()->{attackID} ne $ID1) {
  228. my $attackTarget = Actor::get(AI::args->{attackID});
  229. my $attackSeq = (AI::action eq "route") ? AI::args(1) : AI::args(2);
  230. if (!$attackTarget->{dmgToYou} && !$attackTarget->{dmgFromYou} && distance($monster->{pos_to}, calcPosition($char)) <= $attackSeq->{attackMethod}{distance}) {
  231. my $ignore = 0;
  232. # Don't attack ignored monsters
  233. if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
  234. $ignore = 1 if ( ($control->{attack_auto} == -1)
  235. || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
  236. || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
  237. || ($control->{attack_hp}  ne "" && $control->{attack_hp} > $char->{hp})
  238. || ($control->{attack_sp}  ne "" && $control->{attack_sp} > $char->{sp})
  239. || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou}))
  240. );
  241. }
  242. if (!$ignore) {
  243. # Change target to closer aggressive monster
  244. message TF("Change target to aggressive : %s (%s)n", $monster->name, $monster->{binID});
  245. stopAttack();
  246. AI::dequeue;
  247. AI::dequeue if (AI::action eq "route");
  248. AI::dequeue;
  249. attack($ID1);
  250. }
  251. }
  252. } elsif (AI::action eq "attack" && mon_control($monster->{name},$monster->{nameID})->{attack_auto} == 3
  253. && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou})) {
  254. # Mob-training, stop attacking the monster if it has been attacking you
  255. message TF("%s (%s) has been provoked, searching another monstern", $monster->{name}, $monster->{binID});
  256. stopAttack();
  257. AI::dequeue();
  258. }
  259. useTeleport(1, undef, 1) if ($teleport);
  260. }
  261. }
  262. } elsif ((my $monster = $monstersList->getByID($ID1))) {
  263. if ((my $player = $playersList->getByID($ID2) || $slavesList->getByID($ID2))) {
  264. # Monster attacks player
  265. $monster->{dmgFrom} += $damage;
  266. $monster->{dmgToPlayer}{$ID2} += $damage;
  267. $player->{dmgFromMonster}{$ID1} += $damage;
  268. if ($damage == 0) {
  269. $monster->{missedToPlayer}{$ID2}++;
  270. $player->{missedFromMonster}{$ID1}++;
  271. }
  272. if (existsInList($config{tankersList}, $player->{name}) ||
  273.     ($char->{slaves} && %{$char->{slaves}} && $char->{slaves}{$ID2} && %{$char->{slaves}{$ID2}}) ||
  274.     ($char->{party} && %{$char->{party}} && $char->{party}{users}{$ID2} && %{$char->{party}{users}{$ID2}})) {
  275. # Monster attacks party member
  276. $monster->{dmgToParty} += $damage;
  277. $monster->{missedToParty}++ if ($damage == 0);
  278. }
  279. $monster->{target} = $ID2;
  280. OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables);
  281. if ($AI == 2 && ($char->{slaves} && $char->{slaves}{$ID2})) {
  282. my $teleport = 0;
  283. if (mon_control($monster->{name},$monster->{nameID})->{teleport_auto} == 2 && $damage){
  284. message TF("Slave teleporting due to attack from %sn",
  285. $monster->{name}), "teleport";
  286. $teleport = 1;
  287. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_deadly'} && $damage >= $char->{slaves}{$ID2}{hp}
  288.       && !whenStatusActive("Hallucination")) {
  289. message TF("Next %d dmg could kill your slave. Teleporting...n",
  290. $damage), "teleport";
  291. $teleport = 1;
  292. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'} && $damage >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'}
  293.       && !whenStatusActive("Hallucination")
  294.       && !($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'} && $field{name} eq $config{lockMap})) {
  295. message TF("%s hit your slave for more than %d dmg. Teleporting...n",
  296. $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmg'}), "teleport";
  297. $teleport = 1;
  298. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'} && $field{name} eq $config{lockMap}
  299.       && $damage >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'}
  300.       && !whenStatusActive("Hallucination")) {
  301. message TF("%s hit your slave for more than %d dmg in lockMap. Teleporting...n",
  302. $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_maxDmgInLock'}), "teleport";
  303. $teleport = 1;
  304. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}
  305.       && $monster->{dmgToPlayer}{$ID2} >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}
  306.       && !whenStatusActive("Hallucination")
  307.       && !($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'} && $field{name} eq $config{lockMap})) {
  308. message TF("%s hit your slave for a total of more than %d dmg. Teleporting...n",
  309. $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmg'}), "teleport";
  310. $teleport = 1;
  311. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'} && $field{name} eq $config{lockMap}
  312.       && $monster->{dmgToPlayer}{$ID2} >= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'}
  313.       && !whenStatusActive("Hallucination")) {
  314. message TF("%s hit your slave for a total of more than %d dmg in lockMap. Teleporting...n",
  315. $monster->{name}, $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_totalDmgInLock'}), "teleport";
  316. $teleport = 1;
  317. } elsif ($config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_hp'} && $char->{slaves}{$ID2}{hpPercent} <= $config{$char->{slaves}{$ID2}{slave_configPrefix}.'teleportAuto_hp'}) {
  318. message TF("%s hit your slave when your homunculus' HP is too low. Teleporting...n",
  319. $monster->{name}), "teleport";
  320. $teleport = 1;
  321. } elsif (
  322. $config{$char->{slaves}{$ID2}{slave_configPrefix}.'attackChangeTarget'}
  323. && (
  324. $char->{slaves}{$ID2}->action eq 'route' && $char->{slaves}{$ID2}->action(1) eq 'attack'
  325. or $char->{slaves}{$ID2}->action eq 'move' && $char->{slaves}{$ID2}->action(2) eq 'attack'
  326. )
  327. && $char->{slaves}{$ID2}->args->{attackID} && $char->{slaves}{$ID2}->args->{attackID} ne $ID1
  328. ) {
  329. my $attackTarget = Actor::get($char->{slaves}{$ID2}->args->{attackID});
  330. my $attackSeq = ($char->{slaves}{$ID2}->action eq 'route') ? $char->{slaves}{$ID2}->args(1) : $char->{slaves}{$ID2}->args(2);
  331. if (!$attackTarget->{dmgToPlayer}{$ID2} && !$attackTarget->{dmgFromPlayer}{$ID2} && distance($monster->{pos_to}, calcPosition($char->{slaves}{$ID2})) <= $attackSeq->{attackMethod}{distance}) {
  332. my $ignore = 0;
  333. # Don't attack ignored monsters
  334. if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
  335. $ignore = 1 if ( ($control->{attack_auto} == -1)
  336. || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
  337. || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
  338. || ($control->{attack_hp}  ne "" && $control->{attack_hp} > $char->{hp})
  339. || ($control->{attack_sp}  ne "" && $control->{attack_sp} > $char->{sp})
  340. );
  341. }
  342. if (!$ignore) {
  343. # Change target to closer aggressive monster
  344. message TF("Slave change target to aggressive : %s (%s)n", $monster->name, $monster->{binID});
  345. $char->{slaves}{$ID2}->slave_stopAttack();
  346. $char->{slaves}{$ID2}->dequeue;
  347. $char->{slaves}{$ID2}->dequeue if $char->{slaves}{$ID2}->action eq 'route';
  348. $char->{slaves}{$ID2}->dequeue;
  349. $char->{slaves}{$ID2}->slave_attack($ID1);
  350. }
  351. }
  352. }
  353. useTeleport(1, undef, 1) if ($teleport);
  354. }
  355. }
  356. } elsif ((my $player = $playersList->getByID($ID1) || $slavesList->getByID($ID1))) {
  357. if ((my $monster = $monstersList->getByID($ID2))) {
  358. # Player attacks monster
  359. $monster->{dmgTo} += $damage;
  360. $monster->{dmgFromPlayer}{$ID1} += $damage;
  361. $monster->{lastAttackFrom} = $ID1;
  362. $player->{dmgToMonster}{$ID2} += $damage;
  363. if ($damage == 0) {
  364. $monster->{missedFromPlayer}{$ID1}++;
  365. $player->{missedToMonster}{$ID2}++;
  366. }
  367. if (existsInList($config{tankersList}, $player->{name}) || ($char->{slaves} && $char->{slaves}{$ID1}) ||
  368.     ($char->{party} && %{$char->{party}} && $char->{party}{users}{$ID1} && %{$char->{party}{users}{$ID1}})) {
  369. $monster->{dmgFromParty} += $damage;
  370. }
  371. OpenKoreMod::updateDamageTables($monster) if (defined &OpenKoreMod::updateDamageTables);
  372. }
  373. }
  374. }
  375. ##
  376. # updatePlayerNameCache(player)
  377. # player: a player actor object.
  378. sub updatePlayerNameCache {
  379. my ($player) = @_;
  380. return if (!$config{cachePlayerNames});
  381. # First, cleanup the cache. Remove entries that are too old.
  382. # Default life time: 15 minutes
  383. my $changed = 1;
  384. for (my $i = 0; $i < @playerNameCacheIDs; $i++) {
  385. my $ID = $playerNameCacheIDs[$i];
  386. if (timeOut($playerNameCache{$ID}{time}, $config{cachePlayerNames_duration})) {
  387. delete $playerNameCacheIDs[$i];
  388. delete $playerNameCache{$ID};
  389. $changed = 1;
  390. }
  391. }
  392. compactArray(@playerNameCacheIDs) if ($changed);
  393. # Resize the cache if it's still too large.
  394. # Default cache size: 100
  395. while (@playerNameCacheIDs > $config{cachePlayerNames_maxSize}) {
  396. my $ID = shift @playerNameCacheIDs;
  397. delete $playerNameCache{$ID};
  398. }
  399. # Add this player name to the cache.
  400. my $ID = $player->{ID};
  401. if (!$playerNameCache{$ID}) {
  402. push @playerNameCacheIDs, $ID;
  403. my %entry = (
  404. name => $player->{name},
  405. guild => $player->{guild},
  406. time => time,
  407. lv => $player->{lv},
  408. jobID => $player->{jobID}
  409. );
  410. $playerNameCache{$ID} = %entry;
  411. }
  412. }
  413. ##
  414. # useTeleport(level)
  415. # level: 1 to teleport to a random spot, 2 to respawn.
  416. sub useTeleport {
  417. my ($use_lvl, $internal, $emergency) = @_;
  418. my %args = (
  419. level => $use_lvl, # 1 = Teleport, 2 = respawn
  420. emergency => $emergency, # Needs a fast tele
  421. internal => $internal # Did we call useTeleport from inside useTeleport?
  422. );
  423. if ($use_lvl == 2 && $config{saveMap_warpChatCommand}) {
  424. Plugins::callHook('teleport_sent', %args);
  425. sendMessage($messageSender, "c", $config{saveMap_warpChatCommand});
  426. return 1;
  427. }
  428. if ($use_lvl == 1 && $config{teleportAuto_useChatCommand}) {
  429. Plugins::callHook('teleport_sent', %args);
  430. sendMessage($messageSender, "c", $config{teleportAuto_useChatCommand});
  431. return 1;
  432. }
  433. # for possible recursive calls
  434. if (!defined $internal) {
  435. $internal = $config{teleportAuto_useSkill};
  436. }
  437. # look if the character has the skill
  438. my $sk_lvl = 0;
  439. if ($char->{skills}{AL_TELEPORT}) {
  440. $sk_lvl = $char->{skills}{AL_TELEPORT}{lv};
  441. }
  442. # only if we want to use skill ?
  443. return if ($char->{muted});
  444. if ($sk_lvl > 0 && $internal > 0 && ($use_lvl == 1 || !$config{'teleportAuto_useItemForRespawn'})) {
  445. # We have the teleport skill, and should use it
  446. my $skill = new Skill(handle => 'AL_TELEPORT');
  447. if ($use_lvl == 2 || $internal == 1 || ($internal == 2 && !isSafe())) {
  448. # Send skill use packet to appear legitimate
  449. # (Always send skill use packet for level 2 so that saveMap
  450. # autodetection works)
  451. if ($char->{sitting}) {
  452. Plugins::callHook('teleport_sent', %args);
  453. main::ai_skillUse($skill->getHandle(), $use_lvl, 0, 0, $accountID);
  454. return 1;
  455. } else {
  456. $messageSender->sendSkillUse($skill->getIDN(), $sk_lvl, $accountID);
  457. undef $char->{permitSkill};
  458. }
  459. if (!$emergency && $use_lvl == 1) {
  460. Plugins::callHook('teleport_sent', %args);
  461. $timeout{ai_teleport_retry}{time} = time;
  462. AI::queue('teleport');
  463. return 1;
  464. }
  465. }
  466. delete $ai_v{temp}{teleport};
  467. debug "Sending Teleport using Level $use_lvln", "useTeleport";
  468. if ($use_lvl == 1) {
  469. Plugins::callHook('teleport_sent', %args);
  470. $messageSender->sendTeleport("Random");
  471. return 1;
  472. } elsif ($use_lvl == 2) {
  473. # check for possible skill level abuse
  474. message T("Using Teleport Skill Level 2 though we not have it!n"), "useTeleport" if ($sk_lvl == 1);
  475. # If saveMap is not set simply use a wrong .gat.
  476. # eAthena servers ignore it, but this trick doesn't work
  477. # on official servers.
  478. my $telemap = "prontera.gat";
  479. $telemap = "$config{saveMap}.gat" if ($config{saveMap} ne "");
  480. Plugins::callHook('teleport_sent', %args);
  481. $messageSender->sendTeleport($telemap);
  482. return 1;
  483. }
  484. }
  485. # No skill try to equip a Tele clip or something,
  486. # if teleportAuto_equip_* is set
  487. if (Actor::Item::scanConfigAndCheck('teleportAuto_equip') && ($use_lvl == 1 || !$config{'teleportAuto_useItemForRespawn'})) {
  488. return if AI::inQueue('teleport');
  489. debug "Equipping Accessory to teleportn", "useTeleport";
  490. AI::queue('teleport', {lv => $use_lvl});
  491. if ($emergency ||
  492.     !$config{teleportAuto_useSkill} ||
  493.     $config{teleportAuto_useSkill} == 3 ||
  494.     $config{teleportAuto_useSkill} == 2 && isSafe()) {
  495. $timeout{ai_teleport_delay}{time} = 1;
  496. }
  497. Actor::Item::scanConfigAndEquip('teleportAuto_equip');
  498. #Commands::run('aiv');
  499. return 1;
  500. }
  501. # else if $internal == 0 or $sk_lvl == 0
  502. # try to use item
  503. # could lead to problems if the ItemID would be different on some servers
  504. # 1 Jan 2006 - instead of nameID, search for *wing in the inventory
  505. my $item;
  506. if ($use_lvl == 1) {
  507. $item = $char->inventory->getByName("Fly Wing");
  508. } elsif ($use_lvl == 2) {
  509. $item = $char->inventory->getByName("Butterfly Wing");
  510. }
  511. if ($item) {
  512. # We have Fly Wing/Butterfly Wing.
  513. # Don't spam the "use fly wing" packet, or we'll end up using too many wings.
  514. if (timeOut($timeout{ai_teleport})) {
  515. Plugins::callHook('teleport_sent', %args);
  516. $messageSender->sendItemUse($item->{index}, $accountID);
  517. $timeout{ai_teleport}{time} = time;
  518. }
  519. return 1;
  520. }
  521. # no item, but skill is still available
  522. if ( $sk_lvl > 0 ) {
  523. message T("No Fly Wing or Butterfly Wing, fallback to Teleport Skilln"), "useTeleport";
  524. return useTeleport($use_lvl, 1, $emergency);
  525. }
  526. if ($use_lvl == 1) {
  527. message T("You don't have the Teleport skill or a Fly Wingn"), "teleport";
  528. } else {
  529. message T("You don't have the Teleport skill or a Butterfly Wingn"), "teleport";
  530. }
  531. return 0;
  532. }
  533. ##
  534. # top10Listing(args)
  535. # args: a 282 bytes packet representing 10 names followed by 10 ranks
  536. #
  537. # Returns a formatted list of [# ], Name and points
  538. sub top10Listing {
  539. my ($args) = @_;
  540. my $msg = $args->{RAW_MSG};
  541. my @list;
  542. my @points;
  543. my $i;
  544. my $textList = "";
  545. for ($i = 0; $i < 10; $i++) {
  546. $list[$i] = unpack("Z24", substr($msg, 2 + (24*$i), 24));
  547. }
  548. for ($i = 0; $i < 10; $i++) {
  549. $points[$i] = unpack("V1", substr($msg, 242 + ($i*4), 4));
  550. }
  551. for ($i = 0; $i < 10; $i++) {
  552. $textList .= swrite("[@<] @<<<<<<<<<<<<<<<<<<<<<<<<        @>>>>>>",
  553. [$i+1, $list[$i], $points[$i]]);
  554. }
  555. return $textList;
  556. }
  557. ##
  558. # whenGroundStatus(target, statuses, mine)
  559. # target: coordinates hash
  560. # statuses: a comma-separated list of ground effects e.g. Safety Wall,Pneuma
  561. # mine: if true, only consider ground effects that originated from me
  562. #
  563. # Returns 1 if $target has one of the ground effects specified by $statuses.
  564. sub whenGroundStatus {
  565. my ($pos, $statuses, $mine) = @_;
  566. my ($x, $y) = ($pos->{x}, $pos->{y});
  567. for my $ID (@spellsID) {
  568. my $spell;
  569. next unless $spell = $spells{$ID};
  570. next if $mine && $spell->{sourceID} ne $accountID;
  571. if ($x == $spell->{pos}{x} &&
  572.     $y == $spell->{pos}{y}) {
  573. return 1 if existsInList($statuses, getSpellName($spell->{type}));
  574. }
  575. }
  576. return 0;
  577. }
  578. sub whenStatusActive {
  579. my $statuses = shift;
  580. my @arr = split /s*,s*/, $statuses;
  581. foreach (@arr) {
  582. return 1 if exists($char->{statuses}{$_});
  583. }
  584. return 0;
  585. }
  586. sub whenStatusActiveMon {
  587. my ($monster, $statuses) = @_;
  588. my @arr = split /s*,s*/, $statuses;
  589. foreach (@arr) {
  590. return 1 if $monster->{statuses}{$_};
  591. }
  592. return 0;
  593. }
  594. sub whenStatusActivePL {
  595. my ($ID, $statuses) = @_;
  596. # Incase this method was called with empty values, send TRUE back... since the user doesnt have any statusses they want to check
  597. return 1 if (!$ID || !$statuses);
  598. return whenStatusActive($statuses) if ($ID eq $accountID);
  599. if (my $player = $playersList->getByID($ID)) {
  600. my @arr = split /s*,s*/, $statuses;
  601. foreach (@arr) {
  602. return 1 if $player->{statuses}{$_};
  603. }
  604. }
  605. if ($char->{party}{users}{$ID}{online}) {
  606. my @arr = split /s*,s*/, $statuses;
  607. foreach (@arr) {
  608. return 1 if $char->{party}{users}{$ID}{statuses}{$_};
  609. }
  610. }
  611. if (my $slave = $slavesList->getByID($ID)) {
  612. my @arr = split /s*,s*/, $statuses;
  613. foreach (@arr) {
  614. return 1 if $slave->{statuses}{$_};
  615. }
  616. }
  617. return 0;
  618. }
  619. sub writeStorageLog {
  620. my ($show_error_on_fail) = @_;
  621. my $f;
  622. if (open($f, ">:utf8", $Settings::storage_log_file)) {
  623. print $f TF("---------- Storage %s -----------n", getFormattedDate(int(time)));
  624. for (my $i = 0; $i < @storageID; $i++) {
  625. next if (!$storageID[$i]);
  626. my $item = $storage{$storageID[$i]};
  627. my $display = sprintf "%2d %s x %s", $i, $item->{name}, $item->{amount};
  628. # Translation Comment: Mark to show not identified items
  629. $display .= " -- " . T("Not Identified") if !$item->{identified};
  630. # Translation Comment: Mark to show broken items
  631. $display .= " -- " . T("Broken") if $item->{broken};
  632. print $f "$displayn";
  633. }
  634. # Translation Comment: Storage Capacity
  635. print $f TF("nCapacity: %d/%dn", $storage{items}, $storage{items_max});
  636. print $f "-------------------------------n";
  637. close $f;
  638. message T("Storage loggedn"), "success";
  639. } elsif ($show_error_on_fail) {
  640. error TF("Unable to write to %sn", $Settings::storage_log_file);
  641. }
  642. }
  643. ##
  644. # getBestTarget(possibleTargets, nonLOSNotAllowed)
  645. # possibleTargets: reference to an array of monsters' IDs
  646. # nonLOSNotAllowed: if set, non-LOS monsters (and monsters that aren't in attackMaxDistance) aren't checked up
  647. #
  648. # Returns ID of the best target
  649. sub getBestTarget {
  650. my ($possibleTargets, $nonLOSNotAllowed) = @_;
  651. if (!$possibleTargets) {
  652. return;
  653. }
  654. my $portalDist = $config{'attackMinPortalDistance'} || 4;
  655. my $playerDist = $config{'attackMinPlayerDistance'} || 1;
  656. my @noLOSMonsters;
  657. my $myPos = calcPosition($char);
  658. my ($highestPri, $smallestDist, $bestTarget);
  659. # First of all we check monsters in LOS, then the rest of monsters
  660. foreach (@{$possibleTargets}) {
  661. my $monster = $monsters{$_};
  662. my $pos = calcPosition($monster);
  663. next if (positionNearPlayer($pos, $playerDist)
  664. || positionNearPortal($pos, $portalDist)
  665. );
  666. if ((my $control = mon_control($monster->{name},$monster->{nameID}))) {
  667. next if ( ($control->{attack_auto} == -1)
  668. || ($control->{attack_lvl} ne "" && $control->{attack_lvl} > $char->{lv})
  669. || ($control->{attack_jlvl} ne "" && $control->{attack_jlvl} > $char->{lv_job})
  670. || ($control->{attack_hp}  ne "" && $control->{attack_hp} > $char->{hp})
  671. || ($control->{attack_sp}  ne "" && $control->{attack_sp} > $char->{sp})
  672. || ($control->{attack_auto} == 3 && ($monster->{dmgToYou} || $monster->{missedYou} || $monster->{dmgFromYou}))
  673. || ($control->{attack_auto} == 0 && !($monster->{dmgToYou} || $monster->{missedYou}))
  674. );
  675. }
  676. if ($config{'attackCanSnipe'}) {
  677. if (!checkLineSnipable($myPos, $pos)) {
  678. push(@noLOSMonsters, $_);
  679. next;
  680. }
  681. } else {
  682. if (!checkLineWalkable($myPos, $pos)) {
  683. push(@noLOSMonsters, $_);
  684. next;
  685. }
  686. }
  687. my $name = lc $monster->{name};
  688. my $dist = round(distance($myPos, $pos));
  689. # COMMENTED (FIX THIS): attackMaxDistance should never be used as indication of LOS
  690. #     The objective of attackMaxDistance is to determine the range of normal attack,
  691. #     and not the range of character's ability to engage monsters
  692. ## Monsters that aren't in attackMaxDistance are not checked up
  693. ##if ($nonLOSNotAllowed && ($config{'attackMaxDistance'} < $dist)) {
  694. ## next;
  695. ##}
  696. if (!defined($highestPri) || ($priority{$name} > $highestPri)) {
  697. $highestPri = $priority{$name};
  698. $smallestDist = $dist;
  699. $bestTarget = $_;
  700. }
  701. if ((!defined($highestPri) || $priority{$name} == $highestPri)
  702.   && (!defined($smallestDist) || $dist < $smallestDist)) {
  703. $highestPri = $priority{$name};
  704. $smallestDist = $dist;
  705. $bestTarget = $_;
  706. }
  707. }
  708. if (!$nonLOSNotAllowed && !$bestTarget && scalar(@noLOSMonsters) > 0) {
  709. foreach (@noLOSMonsters) {
  710. # The most optimal solution is to include the path lenghts' comparison, however it will take
  711. # more time and CPU resources, so, we use rough solution with priority and distance comparison
  712. my $monster = $monsters{$_};
  713. my $pos = calcPosition($monster);
  714. my $name = lc $monster->{name};
  715. my $dist = round(distance($myPos, $pos));
  716. if (!defined($highestPri) || ($priority{$name} > $highestPri)) {
  717. $highestPri = $priority{$name};
  718. $smallestDist = $dist;
  719. $bestTarget = $_;
  720. }
  721. if ((!defined($highestPri) || $priority{$name} == $highestPri)
  722.   && (!defined($smallestDist) || $dist < $smallestDist)) {
  723. $highestPri = $priority{$name};
  724. $smallestDist = $dist;
  725. $bestTarget = $_;
  726. }
  727. }
  728. }
  729. return $bestTarget;
  730. }
  731. ##
  732. # Returns 1 if there is a player nearby (except party and homunculus) or 0 if not
  733. sub isSafe {
  734. foreach (@playersID) {
  735. if (!$char->{party}{users}{$_} && (!$char->{homunculus} || $_ ne $char->{homunculus}{ID})) {
  736. return 0;
  737. }
  738. }
  739. return 1;
  740. }
  741. #######################################
  742. #######################################
  743. ###CATEGORY: Actor's Actions Text
  744. #######################################
  745. #######################################
  746. ##
  747. # String attack_string(Actor source, Actor target, int damage, int delay)
  748. #
  749. # Generates a proper message string for when actor $source attacks actor $target.
  750. sub attack_string {
  751. my ($source, $target, $damage, $delay) = @_;
  752. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  753. assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
  754. return TF("%s %s %s - Dmg: %s (delay %s)n",
  755. $source->nameString,
  756. $source->verb('attack', 'attacks'),
  757. $target->nameString($source),
  758. $damage, $delay);
  759. }
  760. sub skillCast_string {
  761. my ($source, $target, $x, $y, $skillName, $delay) = @_;
  762. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  763. assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
  764. # Location
  765. if ($x != 0 || $y != 0) {
  766. if ($source->isa('Actor::You')) {
  767. return TF("You are casting %s on location (%d, %d) - (time %sms)n", $skillName, $x, $y, $delay);
  768. } elsif ($source->isa('Actor::Player')) {
  769. return TF("Player %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name, 
  770. $source->{binID}, $skillName, $x, $y, $delay);
  771. } elsif ($source->isa('Actor::Monster')) {
  772. return TF("Monster %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name, 
  773. $source->{binID}, $skillName, $x, $y, $delay);
  774. } elsif ($source->isa('Actor::Slave')) {
  775. return TF("Slave %s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->name, 
  776. $source->{binID}, $skillName, $x, $y, $delay);
  777. } elsif ($source->isa('Actor::Unknown')) {
  778. return TF("Unknown #%s (%d) is casting %s on location (%d, %d) - (time %sms)n", $source->{nameID},
  779. $source->{binID}, $skillName, $x, $y, $delay);
  780. }
  781. # You
  782. } elsif ($source->isa('Actor::You')) {
  783. if ($target->isa('Actor::You')) {
  784. return TF("You are casting %s on yourself (time %sms)n", $skillName, $delay);
  785. } elsif ($target->isa('Actor::Player')) {
  786. return TF("You are casting %s on player %s (%d) (time %sms)n", $skillName,
  787. $target->name, $target->{binID}, $delay);
  788. } elsif ($target->isa('Actor::Monster')) {
  789. return TF("You are casting %s on monster %s (%d) (time %sms)n", $skillName,
  790. $target->name, $target->{binID}, $delay);
  791. } elsif ($target->isa('Actor::Slave')) {
  792. return TF("You are casting %s on slave %s (%d) (time %sms)n", $skillName,
  793. $target->name, $target->{binID}, $delay);
  794. } elsif ($target->isa('Actor::Unknown')) {
  795. return TF("You are casting %s on Unknown #%s (%d) (time %sms)n", $skillName,
  796. $target->{nameID}, $target->{binID}, $delay);
  797. }
  798. # Player
  799. } elsif ($source->isa('Actor::Player')) {
  800. if ($target->isa('Actor::You')) {
  801. return TF("Player %s (%d) is casting %s on you (time %sms)n", $source->name, $source->{binID},
  802. $skillName, $delay);
  803. } elsif ($target->isa('Actor::Player')) {
  804. # consider gender
  805. if ($source ne $target) {
  806. return TF("Player %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
  807. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  808. } elsif ($source->{sex}) {
  809. return TF("Player %s (%d) is casting %s on himself (time %sms)n", $source->name,
  810. $source->{binID}, $skillName, $delay);
  811. } else {
  812. return TF("Player %s (%d) is casting %s on herself (time %sms)n", $source->name,
  813. $source->{binID}, $skillName, $delay);
  814. }
  815. } elsif ($target->isa('Actor::Monster')) {
  816. return TF("Player %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
  817. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  818. } elsif ($target->isa('Actor::Slave')) {
  819. return TF("Player %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
  820. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  821. } elsif ($target->isa('Actor::Unknown')) {
  822. return TF("Player %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
  823. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
  824. }
  825. # Monster
  826. } elsif ($source->isa('Actor::Monster')) {
  827. if ($target->isa('Actor::You')) {
  828. return TF("Monster %s (%d) is casting %s on you (time %sms)n", $source->name,
  829. $source->{binID}, $skillName, $delay);
  830. } elsif ($target->isa('Actor::Player')) {
  831. return TF("Monster %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
  832. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  833. } elsif ($target->isa('Actor::Monster')) {
  834. if ($source ne $target) {
  835. return TF("Monster %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
  836. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  837. } else {
  838. return TF("Monster %s (%d) is casting %s on itself (time %sms)n", $source->name,
  839. $source->{binID}, $skillName, $delay);
  840. }
  841. } elsif ($target->isa('Actor::Slave')) {
  842. return TF("Monster %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
  843. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  844. } elsif ($target->isa('Actor::Unknown')) {
  845. return TF("Monster %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
  846. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
  847. }
  848. # Slave
  849. } elsif ($source->isa('Actor::Slave')) {
  850. if ($target->isa('Actor::You')) {
  851. return TF("Slave %s (%d) is casting %s on you (time %sms)n", $source->name,
  852. $source->{binID}, $skillName, $delay);
  853. } elsif ($target->isa('Actor::Player')) {
  854. return TF("Slave %s (%d) is casting %s on player %s (%d) (time %sms)n", $source->name,
  855. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  856. } elsif ($target->isa('Actor::Monster')) {
  857. return TF("Slave %s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->name,
  858. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  859. } elsif ($target->isa('Actor::Slave')) {
  860. if ($source ne $target) {
  861. return TF("Slave %s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->name,
  862. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  863. } else {
  864. return TF("Slave %s (%d) is casting %s on itself (time %sms)n", $source->name,
  865. $source->{binID}, $skillName, $delay);
  866. }
  867. } elsif ($target->isa('Actor::Unknown')) {
  868. return TF("Slave %s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->name,
  869. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
  870. }
  871. # Unknown
  872. } elsif ($source->isa('Actor::Unknown')) {
  873. if ($target->isa('Actor::You')) {
  874. return TF("Unknown #%s (%d) is casting %s on you (time %sms)n", $source->{nameID},
  875. $source->{binID}, $skillName, $delay);
  876. } elsif ($target->isa('Actor::Player')) {
  877. return TF("Unknown #%s (%d) is casting %s on player %s (%d) (time %sms)n", $source->{nameID},
  878. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  879. } elsif ($target->isa('Actor::Monster')) {
  880. return TF("Unknown #%s (%d) is casting %s on monster %s (%d) (time %sms)n", $source->{nameID},
  881. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  882. } elsif ($target->isa('Actor::Slave')) {
  883. return TF("Unknown #%s (%d) is casting %s on slave %s (%d) (time %sms)n", $source->{nameID},
  884. $source->{binID}, $skillName, $target->name, $target->{binID}, $delay);
  885. } elsif ($target->isa('Actor::Unknown')) {
  886. if ($source ne $target) {
  887. return TF("Unknown #%s (%d) is casting %s on Unknown #%s (%d) (time %sms)n", $source->{nameID},
  888. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $delay);
  889. } else {
  890. return TF("Unknown #%s (%d) is casting %s on himself (time %sms)n", $source->{nameID},
  891. $source->{binID}, $skillName, $delay);
  892. }
  893. }
  894. }
  895. }
  896. sub skillUse_string {
  897. my ($source, $target, $skillName, $damage, $level, $delay) = @_;
  898. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  899. assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
  900. $damage ||= "Miss!";
  901. if ($damage != -30000) {
  902. # Translation Comment: Ammount of Damage on Skill
  903. $damage = "- " . TF("Dmg: %s", $damage) . " ";
  904. } else {
  905. $damage = '';
  906. }
  907. # Translation Comment: Skill name + level
  908. $skillName = TF("%s (lvl %s)", $skillName, $level) unless $level == 65535;
  909. # You
  910. if ($source->isa('Actor::You')) {
  911. if ($target->isa('Actor::You')) {
  912. return TF("You use %s on yourself %s(delay %s)n", $skillName, $damage, $delay);
  913. } elsif ($target->isa('Actor::Player')) {
  914. return TF("You use %s on player %s (%d) %s(delay %s)n", $skillName,
  915. $target->name, $target->{binID}, $damage, $delay);
  916. } elsif ($target->isa('Actor::Monster')) {
  917. return TF("You use %s on monster %s (%d) %s(delay %s)n", $skillName,
  918. $target->name, $target->{binID}, $damage, $delay);
  919. } elsif ($target->isa('Actor::Slave')) {
  920. return TF("You use %s on slave %s (%d) %s(delay %s)n", $skillName,
  921. $target->name, $target->{binID}, $damage, $delay);
  922. } elsif ($target->isa('Actor::Unknown')) {
  923. return TF("You use %s on Unknown #%s (%d) %s(delay %s)n", $skillName,
  924. $target->{nameID}, $target->{binID}, $damage, $delay);
  925. }
  926. # Player
  927. } elsif ($source->isa('Actor::Player')) {
  928. if ($target->isa('Actor::You')) {
  929. return TF("Player %s (%d) uses %s on you %s(delay %s)n", $source->name, $source->{binID},
  930. $skillName, $damage, $delay);
  931. } elsif ($target->isa('Actor::Player')) {
  932. # consider gender
  933. if ($source ne $target) {
  934. return TF("Player %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
  935. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  936. } elsif ($source->{sex}) {
  937. return TF("Player %s (%d) uses %s on himself %s(delay %s)n", $source->name,
  938. $source->{binID}, $skillName, $damage, $delay);
  939. } else {
  940. return TF("Player %s (%d) uses %s on herself %s(delay %s)n", $source->name,
  941. $source->{binID}, $skillName, $damage, $delay);
  942. }
  943. } elsif ($target->isa('Actor::Monster')) {
  944. return TF("Player %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
  945. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  946. } elsif ($target->isa('Actor::Slave')) {
  947. return TF("Player %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
  948. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  949. } elsif ($target->isa('Actor::Unknown')) {
  950. return TF("Player %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
  951. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
  952. }
  953. # Monster
  954. } elsif ($source->isa('Actor::Monster')) {
  955. if ($target->isa('Actor::You')) {
  956. return TF("Monster %s (%d) uses %s on you %s(delay %s)n", $source->name,
  957. $source->{binID}, $skillName, $damage, $delay);
  958. } elsif ($target->isa('Actor::Player')) {
  959. return TF("Monster %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
  960. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  961. } elsif ($target->isa('Actor::Monster')) {
  962. if ($source ne $target) {
  963. return TF("Monster %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
  964. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  965. } else {
  966. return TF("Monster %s (%d) uses %s on itself %s(delay %s)n", $source->name,
  967. $source->{binID}, $skillName, $damage, $delay);
  968. }
  969. } elsif ($target->isa('Actor::Slave')) {
  970. return TF("Monster %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
  971. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  972. } elsif ($target->isa('Actor::Unknown')) {
  973. return TF("Monster %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
  974. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
  975. }
  976. # Slave
  977. } elsif ($source->isa('Actor::Slave')) {
  978. if ($target->isa('Actor::You')) {
  979. return TF("Slave %s (%d) uses %s on you %s(delay %s)n", $source->name,
  980. $source->{binID}, $skillName, $damage, $delay);
  981. } elsif ($target->isa('Actor::Player')) {
  982. return TF("Slave %s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->name,
  983. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  984. } elsif ($target->isa('Actor::Monster')) {
  985. return TF("Slave %s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->name,
  986. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  987. } elsif ($target->isa('Actor::Slave')) {
  988. if ($source ne $target) {
  989. return TF("Slave %s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->name,
  990. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  991. } else {
  992. return TF("Slave %s (%d) uses %s on itself %s(delay %s)n", $source->name,
  993. $source->{binID}, $skillName, $damage, $delay);
  994. }
  995. } elsif ($target->isa('Actor::Unknown')) {
  996. return TF("Slave %s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->name,
  997. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
  998. }
  999. # Unknown
  1000. } elsif ($source->isa('Actor::Unknown')) {
  1001. message "test2n";
  1002. if ($target->isa('Actor::You')) {
  1003. return TF("Unknown #%s (%d) uses %s on you %s(delay %s)n", $source->{nameID},
  1004. $source->{binID}, $skillName, $damage, $delay);
  1005. } elsif ($target->isa('Actor::Player')) {
  1006. return TF("Unknown #%s (%d) uses %s on player %s (%d) %s(delay %s)n", $source->{nameID},
  1007. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  1008. } elsif ($target->isa('Actor::Monster')) {
  1009. return TF("Unknown #%s (%d) uses %s on monster %s (%d) %s(delay %s)n", $source->{nameID},
  1010. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  1011. } elsif ($target->isa('Actor::Slave')) {
  1012. return TF("Unknown #%s (%d) uses %s on slave %s (%d) %s(delay %s)n", $source->{nameID},
  1013. $source->{binID}, $skillName, $target->name, $target->{binID}, $damage, $delay);
  1014. } elsif ($target->isa('Actor::Unknown')) {
  1015. if ($source ne $target) {
  1016. return TF("Unknown #%s (%d) uses %s on Unknown #%s (%d) %s(delay %s)n", $source->{nameID},
  1017. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $damage, $delay);
  1018. } else {
  1019. return TF("Unknown #%s (%d) uses %s on himself %s(delay %s)n", $source->{nameID},
  1020. $source->{binID}, $skillName, $damage, $delay);
  1021. }
  1022. }
  1023. }
  1024. }
  1025. sub skillUseLocation_string {
  1026. my ($source, $skillName, $args) = @_;
  1027. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  1028. # Translation Comment: Skill name + level
  1029. $skillName = TF("%s (lvl %s)", $skillName, $args->{lv}) unless $args->{lv} == 65535;
  1030. if ($source->isa('Actor::You')) {
  1031. return TF("You use %s on location (%d, %d)n", $skillName, $args->{x}, $args->{y});
  1032. } elsif ($source->isa('Actor::Player')) {
  1033. return TF("Player %s (%d) uses %s on location (%d, %d)n", $source->name,
  1034. $source->{binID}, $skillName, $args->{x}, $args->{y});
  1035. } elsif ($source->isa('Actor::Monster')) {
  1036. return TF("Monster %s (%d) uses %s on location (%d, %d)n", $source->name,
  1037. $source->{binID}, $skillName, $args->{x}, $args->{y});
  1038. } elsif ($source->isa('Actor::Slave')) {
  1039. return TF("Slave %s (%d) uses %s on location (%d, %d)n", $source->name,
  1040. $source->{binID}, $skillName, $args->{x}, $args->{y});
  1041. } elsif ($source->isa('Actor::Unknown')) {
  1042. return TF("Unknown #%s (%d) uses %s on location (%d, %d)n", $source->{nameID},
  1043. $source->{binID}, $skillName, $args->{x}, $args->{y});
  1044. }
  1045. }
  1046. sub skillUseNoDamage_string {
  1047. my ($source, $target, $skillID, $skillName, $amount) = @_;
  1048. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  1049. assert(UNIVERSAL::isa($target, 'Actor')) if DEBUG;
  1050. if ($skillID == 28) {
  1051. # Translation Comment: used Healing skill
  1052. $amount = ': ' . TF("%s hp gained", $amount);
  1053. } else {
  1054. if ($amount) {
  1055. # Translation Comment: used non-Healing skill - displays skill level
  1056. $amount = ': ' . TF("Lv %s", $amount);
  1057. } else {
  1058. $amount = '';
  1059. }
  1060. }
  1061. if ($source->isa('Actor::You')) {
  1062. if ($target->isa('Actor::You')) {
  1063. return TF("You use %s on yourself %sn", $skillName, $amount);
  1064. } else {
  1065. # Translation Comment: You use a certain skill on an actor.
  1066. return TF("You use %s on %s %sn", $skillName, $target->nameString, $amount);
  1067. }
  1068. # Player
  1069. } elsif ($source->isa('Actor::Player')) {
  1070. if ($target->isa('Actor::You')) {
  1071. return TF("Player %s uses %s on you %sn", $source->name,
  1072. $skillName, $amount);
  1073. } elsif ($target->isa('Actor::Player')) {
  1074. # consider gender
  1075. if ($source ne $target) {
  1076. return TF("Player %s (%d) uses %s on player %s (%d) %sn", $source->name,
  1077. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1078. } elsif ($source->{sex}) {
  1079. return TF("Player %s (%d) uses %s on himself %sn", $source->name,
  1080. $source->{binID}, $skillName, $amount);
  1081. } else {
  1082. return TF("Player %s (%d) uses %s on herself %sn", $source->name,
  1083. $source->{binID}, $skillName, $amount);
  1084. }
  1085. } elsif ($target->isa('Actor::Monster')) {
  1086. return TF("Player %s (%d) uses %s on monster %s (%d) %sn", $source->name,
  1087. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1088. } elsif ($target->isa('Actor::Slave')) {
  1089. return TF("Player %s (%d) uses %s on slave %s (%d) %sn", $source->name,
  1090. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1091. } elsif ($target->isa('Actor::Unknown')) {
  1092. return TF("Player %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
  1093. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
  1094. }
  1095. # Monster
  1096. } elsif ($source->isa('Actor::Monster')) {
  1097. if ($target->isa('Actor::You')) {
  1098. return TF("Monster %s (%d) uses %s on you %sn", $source->name,
  1099. $source->{binID}, $skillName, $amount);
  1100. } elsif ($target->isa('Actor::Player')) {
  1101. return TF("Monster %s (%d) uses %s on player %s (%d) %sn", $source->name,
  1102. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1103. } elsif ($target->isa('Actor::Monster')) {
  1104. if ($source ne $target) {
  1105. return TF("Monster %s (%d) uses %s on monster %s (%d) %sn", $source->name,
  1106. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1107. } else {
  1108. return TF("Monster %s (%d) uses %s on itself %sn", $source->name,
  1109. $source->{binID}, $skillName, $amount);
  1110. }
  1111. } elsif ($target->isa('Actor::Slave')) {
  1112. return TF("Monster %s (%d) uses %s on slave %s (%d) %sn", $source->name,
  1113. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1114. } elsif ($target->isa('Actor::Unknown')) {
  1115. return TF("Monster %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
  1116. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
  1117. }
  1118. # Slave
  1119. } elsif ($source->isa('Actor::Slave')) {
  1120. if ($target->isa('Actor::You')) {
  1121. return TF("Slave %s (%d) uses %s on you %sn", $source->name,
  1122. $source->{binID}, $skillName, $amount);
  1123. } elsif ($target->isa('Actor::Player')) {
  1124. return TF("Slave %s (%d) uses %s on player %s (%d) %sn", $source->name,
  1125. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1126. } elsif ($target->isa('Actor::Monster')) {
  1127. return TF("Slave %s (%d) uses %s on monster %s (%d) %sn", $source->name,
  1128. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1129. } elsif ($target->isa('Actor::Slave')) {
  1130. if ($source ne $target) {
  1131. return TF("Slave %s (%d) uses %s on slave %s (%d) %sn", $source->name,
  1132. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1133. } else {
  1134. return TF("Slave %s (%d) uses %s on itself %sn", $source->name,
  1135. $source->{binID}, $skillName, $amount);
  1136. }
  1137. } elsif ($target->isa('Actor::Unknown')) {
  1138. return TF("Slave %s (%d) uses %s on Unknown #%s (%d) %sn", $source->name,
  1139. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
  1140. }
  1141. # Unknown
  1142. } elsif ($source->isa('Actor::Unknown')) {
  1143. if ($target->isa('Actor::You')) {
  1144. return TF("Unknown #%s (%d) uses %s on you %sn", $source->{nameID},
  1145. $source->{binID}, $skillName, $amount);
  1146. } elsif ($target->isa('Actor::Player')) {
  1147. return TF("Unknown #%s (%d) uses %s on player %s (%d) %sn", $source->{nameID},
  1148. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1149. } elsif ($target->isa('Actor::Monster')) {
  1150. return TF("Unknown #%s (%d) uses %s on monster %s (%d) %sn", $source->{nameID},
  1151. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1152. } elsif ($target->isa('Actor::Slave')) {
  1153. return TF("Unknown #%s (%d) uses %s on slave %s (%d) %sn", $source->{nameID},
  1154. $source->{binID}, $skillName, $target->name, $target->{binID}, $amount);
  1155. } elsif ($target->isa('Actor::Unknown')) {
  1156. if ($source ne $target) {
  1157. return TF("Unknown #%s (%d) uses %s on Unknown #%s (%d) %sn", $source->{nameID},
  1158. $source->{binID}, $skillName, $target->{nameID}, $target->{binID}, $amount);
  1159. } else {
  1160. return TF("Unknown #%s (%d) uses %s on himself %sn", $source->{nameID},
  1161. $source->{binID}, $skillName, $amount);
  1162. }
  1163. }
  1164. }
  1165. }
  1166. sub status_string {
  1167. my ($source, $statusName, $mode, $seconds) = @_;
  1168. assert(UNIVERSAL::isa($source, 'Actor')) if DEBUG;
  1169. if ($mode eq 'now') {
  1170. if ($source->isa('Actor::You')) {
  1171. if (($seconds)&&($seconds > 0)) {
  1172. return TF("You are now: %s for: %s seconds.n", $statusName, $seconds || "N/A");
  1173. } else {
  1174. return TF("You are now: %sn", $statusName);
  1175. }
  1176. } elsif ($source->isa('Actor::Player')) {
  1177. return TF("Player %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
  1178. } elsif ($source->isa('Actor::Monster')) {
  1179. return TF("Monster %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
  1180. } elsif ($source->isa('Actor::Slave')) {
  1181. return TF("Slave %s (%d) is now: %sn", $source->name, $source->{binID}, $statusName);
  1182. } elsif ($source->isa('Actor::Unknown')) {
  1183. return TF("Unknown #%s (%d) is now: %sn", $source->{nameID}, $source->{binID}, $statusName);
  1184. }
  1185. } elsif ($mode eq 'again') {
  1186. if ($source->isa('Actor::You')) {
  1187. if (($seconds)&&($seconds > 0)) {
  1188. return TF("You are again: %s for: %s seconds.n", $statusName, $seconds || "N/A");
  1189. } else {
  1190. return TF("You are again: %sn", $statusName);
  1191. }
  1192. } elsif ($source->isa('Actor::Player')) {
  1193. return TF("Player %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
  1194. } elsif ($source->isa('Actor::Monster')) {
  1195. return TF("Monster %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
  1196. } elsif ($source->isa('Actor::Slave')) {
  1197. return TF("Slave %s (%d) is again: %sn", $source->name, $source->{binID}, $statusName);
  1198. } elsif ($source->isa('Actor::Unknown')) {
  1199. return TF("Unknown #%s (%d) is again: %sn", $source->{nameID}, $source->{binID}, $statusName);
  1200. }
  1201. } elsif ($mode eq 'no longer') {
  1202. if ($source->isa('Actor::You')) {
  1203. return TF("You are no longer: %sn", $statusName);
  1204. } elsif ($source->isa('Actor::Player')) {
  1205. return TF("Player %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
  1206. } elsif ($source->isa('Actor::Monster')) {
  1207. return TF("Monster %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
  1208. } elsif ($source->isa('Actor::Slave')) {
  1209. return TF("Slave %s (%d) is no longer: %sn", $source->name, $source->{binID}, $statusName);
  1210. } elsif ($source->isa('Actor::Unknown')) {
  1211. return TF("Unknown #%s (%d) is no longer: %sn", $source->{nameID}, $source->{binID}, $statusName);
  1212. }
  1213. }
  1214. }
  1215. #######################################
  1216. #######################################
  1217. ###CATEGORY: AI Math
  1218. #######################################
  1219. #######################################
  1220. sub lineIntersection {
  1221. my $r_pos1 = shift;
  1222. my $r_pos2 = shift;
  1223. my $r_pos3 = shift;
  1224. my $r_pos4 = shift;
  1225. my ($x1, $x2, $x3, $x4, $y1, $y2, $y3, $y4, $result, $result1, $result2);
  1226. $x1 = $$r_pos1{'x'};
  1227. $y1 = $$r_pos1{'y'};
  1228. $x2 = $$r_pos2{'x'};
  1229. $y2 = $$r_pos2{'y'};
  1230. $x3 = $$r_pos3{'x'};
  1231. $y3 = $$r_pos3{'y'};
  1232. $x4 = $$r_pos4{'x'};
  1233. $y4 = $$r_pos4{'y'};
  1234. $result1 = ($x4 - $x3)*($y1 - $y3) - ($y4 - $y3)*($x1 - $x3);
  1235. $result2 = ($y4 - $y3)*($x2 - $x1) - ($x4 - $x3)*($y2 - $y1);
  1236. if ($result2 != 0) {
  1237. $result = $result1 / $result2;
  1238. }
  1239. return $result;
  1240. }
  1241. sub percent_hp {
  1242. my $r_hash = shift;
  1243. if (!$$r_hash{'hp_max'}) {
  1244. return 0;
  1245. } else {
  1246. return ($$r_hash{'hp'} / $$r_hash{'hp_max'} * 100);
  1247. }
  1248. }
  1249. sub percent_sp {
  1250. my $r_hash = shift;
  1251. if (!$$r_hash{'sp_max'}) {
  1252. return 0;
  1253. } else {
  1254. return ($$r_hash{'sp'} / $$r_hash{'sp_max'} * 100);
  1255. }
  1256. }
  1257. sub percent_weight {
  1258. my $r_hash = shift;
  1259. if (!$$r_hash{'weight_max'}) {
  1260. return 0;
  1261. } else {
  1262. return ($$r_hash{'weight'} / $$r_hash{'weight_max'} * 100);
  1263. }
  1264. }
  1265. #######################################
  1266. #######################################
  1267. ###CATEGORY: Misc Functions
  1268. #######################################
  1269. #######################################
  1270. sub avoidGM_near {
  1271. my $players = $playersList->getItems();
  1272. foreach my $player (@{$players}) {
  1273. # skip this person if we dont know the name
  1274. next if (!defined $player->{name});
  1275. # Check whether this "GM" is on the ignore list
  1276. # in order to prevent false matches
  1277. last if (existsInList($config{avoidGM_ignoreList}, $player->{name}));
  1278. # check if this name matches the GM filter
  1279. last unless ($config{avoidGM_namePattern} ? $player->{name} =~ /$config{avoidGM_namePattern}/ : $player->{name} =~ /^([a-z]?ro)?-?(Sub)?-?[?GM]?/i);
  1280. my %args = (
  1281. name => $player->{name},
  1282. ID => $player->{ID}
  1283. );
  1284. Plugins::callHook('avoidGM_near', %args);
  1285. return 1 if ($args{return});
  1286. my $msg;
  1287. if ($config{avoidGM_near} == 1) {
  1288. # Mode 1: teleport & disconnect
  1289. useTeleport(1);
  1290. $msg = TF("GM %s is nearby, teleport & disconnect for %d seconds", $player->{name}, $config{avoidGM_reconnect});
  1291. relog($config{avoidGM_reconnect}, 1);
  1292. } elsif ($config{avoidGM_near} == 2) {
  1293. # Mode 2: disconnect
  1294. $msg = TF("GM %s is nearby, disconnect for %s seconds", $player->{name}, $config{avoidGM_reconnect});
  1295. relog($config{avoidGM_reconnect}, 1);
  1296. } elsif ($config{avoidGM_near} == 3) {
  1297. # Mode 3: teleport
  1298. useTeleport(1);
  1299. $msg = TF("GM %s is nearby, teleporting", $player->{name});
  1300. } elsif ($config{avoidGM_near} >= 4) {
  1301. # Mode 4: respawn
  1302. useTeleport(2);
  1303. $msg = TF("GM %s is nearby, respawning", $player->{name});
  1304. }
  1305. warning "$msgn";
  1306. chatLog("k", "*** $msg ***n");
  1307. return 1;
  1308. }
  1309. return 0;
  1310. }
  1311. ##
  1312. # avoidList_near()
  1313. # Returns: 1 if someone was detected, 0 if no one was detected.
  1314. #
  1315. # Checks if any of the surrounding players are on the avoid.txt avoid list.
  1316. # Disconnects / teleports if a player is detected.
  1317. sub avoidList_near {
  1318. return if ($config{avoidList_inLockOnly} && $field{name} ne $config{lockMap});
  1319. my $players = $playersList->getItems();
  1320. foreach my $player (@{$players}) {
  1321. my $avoidPlayer = $avoid{Players}{lc($player->{name})};
  1322. my $avoidID = $avoid{ID}{$player->{nameID}};
  1323. if (!$net->clientAlive() && ( ($avoidPlayer && $avoidPlayer->{disconnect_on_sight}) || ($avoidID && $avoidID->{disconnect_on_sight}) )) {
  1324. warning TF("%s (%s) is nearby, disconnecting...n", $player->{name}, $player->{nameID});
  1325. chatLog("k", TF("*** Found %s (%s) nearby and disconnected ***n", $player->{name}, $player->{nameID}));
  1326. warning TF("Disconnect for %s seconds...n", $config{avoidList_reconnect});
  1327. relog($config{avoidList_reconnect}, 1);
  1328. return 1;
  1329. } elsif (($avoidPlayer && $avoidPlayer->{teleport_on_sight}) || ($avoidID && $avoidID->{teleport_on_sight})) {
  1330. message TF("Teleporting to avoid player %s (%s)n", $player->{name}, $player->{nameID}), "teleport";
  1331. chatLog("k", TF("*** Found %s (%s) nearby and teleported ***n", $player->{name}, $player->{nameID}));
  1332. useTeleport(1);
  1333. return 1;
  1334. }
  1335. }
  1336. return 0;
  1337. }
  1338. sub avoidList_ID {
  1339. return if (!($config{avoidList}) || ($config{avoidList_inLockOnly} && $field{name} ne $config{lockMap}));
  1340. my $avoidID = unpack("V", shift);
  1341. if ($avoid{ID}{$avoidID} && $avoid{ID}{$avoidID}{disconnect_on_sight}) {
  1342. warning TF("%s is nearby, disconnecting...n", $avoidID);
  1343. chatLog("k", TF("*** Found %s nearby and disconnected ***n", $avoidID));
  1344. warning TF("Disconnect for %s seconds...n", $config{avoidList_reconnect});
  1345. relog($config{avoidList_reconnect}, 1);
  1346. return 1;
  1347. }
  1348. return 0;
  1349. }
  1350. sub compilePortals {
  1351. my $checkOnly = shift;
  1352. my %mapPortals;
  1353. my %mapSpawns;
  1354. my %missingMap;
  1355. my $pathfinding;
  1356. my @solution;
  1357. my $field;
  1358. # Collect portal source and destination coordinates per map
  1359. foreach my $portal (keys %portals_lut) {
  1360. $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{x} = $portals_lut{$portal}{source}{x};
  1361. $mapPortals{$portals_lut{$portal}{source}{map}}{$portal}{y} = $portals_lut{$portal}{source}{y};
  1362. foreach my $dest (keys %{$portals_lut{$portal}{dest}}) {
  1363. next if $portals_lut{$portal}{dest}{$dest}{map} eq '';
  1364. $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{x} = $portals_lut{$portal}{dest}{$dest}{x};
  1365. $mapSpawns{$portals_lut{$portal}{dest}{$dest}{map}}{$dest}{y} = $portals_lut{$portal}{dest}{$dest}{y};
  1366. }
  1367. }
  1368. $pathfinding = new PathFinding if (!$checkOnly);
  1369. # Calculate LOS values from each spawn point per map to other portals on same map
  1370. foreach my $map (sort keys %mapSpawns) {
  1371. message TF("Processing map %s...n", $map), "system" unless $checkOnly;
  1372. foreach my $spawn (keys %{$mapSpawns{$map}}) {
  1373. foreach my $portal (keys %{$mapPortals{$map}}) {
  1374. next if $spawn eq $portal;
  1375. next if $portals_los{$spawn}{$portal} ne '';
  1376. return 1 if $checkOnly;
  1377. if ((!$field || $field->{name} ne $map) && !$missingMap{$map}) {
  1378. eval {
  1379. $field = new Field(name => $map);
  1380. };
  1381. if ($@) {
  1382. $missingMap{$map} = 1;
  1383. }
  1384. }
  1385. my %start = %{$mapSpawns{$map}{$spawn}};
  1386. my %dest = %{$mapPortals{$map}{$portal}};
  1387. closestWalkableSpot($field, %start);
  1388. closestWalkableSpot($field, %dest);
  1389. $pathfinding->reset(
  1390. start => %start,
  1391. dest  => %dest,
  1392. field => $field
  1393. );
  1394. my $count = $pathfinding->runcount;
  1395. $portals_los{$spawn}{$portal} = ($count >= 0) ? $count : 0;
  1396. debug "LOS in $map from $start{x},$start{y} to $dest{x},$dest{y}: $portals_los{$spawn}{$portal}n";
  1397. }
  1398. }
  1399. }
  1400. return 0 if $checkOnly;
  1401. # Write new portalsLOS.txt
  1402. writePortalsLOS(Settings::getTableFilename("portalsLOS.txt"), %portals_los);
  1403. message TF("Wrote portals Line of Sight table to '%s'n", Settings::getTableFilename("portalsLOS.txt")), "system";
  1404. # Print warning for missing fields
  1405. if (%missingMap) {
  1406. warning TF("----------------------------Error Summary----------------------------n");
  1407. warning TF("Missing: %s.fldn", $_) foreach (sort keys %missingMap);
  1408. warning TF("Note: LOS information for the above listed map(s) will be inaccurate;n" .
  1409. "      however it is safe to ignore if those map(s) are not usedn");
  1410. warning TF("----------------------------Error Summary----------------------------n");
  1411. }
  1412. }
  1413. sub compilePortals_check {
  1414. return compilePortals(1);
  1415. }
  1416. sub portalExists {
  1417. my ($map, $r_pos) = @_;
  1418. foreach (keys %portals_lut) {
  1419. if ($portals_lut{$_}{source}{map} eq $map
  1420.     && $portals_lut{$_}{source}{x} == $r_pos->{x}
  1421.     && $portals_lut{$_}{source}{y} == $r_pos->{y}) {
  1422. return $_;
  1423. }
  1424. }
  1425. return;
  1426. }
  1427. sub portalExists2 {
  1428. my ($src, $src_pos, $dest, $dest_pos) = @_;
  1429. my $srcx = $src_pos->{x};
  1430. my $srcy = $src_pos->{y};
  1431. my $destx = $dest_pos->{x};
  1432. my $desty = $dest_pos->{y};
  1433. my $destID = "$dest $destx $desty";
  1434. foreach (keys %portals_lut) {
  1435. my $entry = $portals_lut{$_};
  1436. if ($entry->{source}{map} eq $src
  1437.  && $entry->{source}{pos}{x} == $srcx
  1438.  && $entry->{source}{pos}{y} == $srcy
  1439.  && $entry->{dest}{$destID}) {
  1440. return $_;
  1441. }
  1442. }
  1443. return;
  1444. }
  1445. sub redirectXKoreMessages {
  1446. my ($type, $domain, $level, $globalVerbosity, $message, $user_data) = @_;
  1447. return if ($config{'XKore_silent'} || $type eq "debug" || $level > 0 || $net->getState() != Network::IN_GAME || $XKore_dontRedirect);
  1448. return if ($domain =~ /^(connection|startup|pm|publicchat|guildchat|guildnotice|selfchat|emotion|drop|inventory|deal|storage|input)$/);
  1449. return if ($domain =~ /^(attack|skill|list|info|partychat|npc|route)/);
  1450. $message =~ s/n*$//s;
  1451. $message =~ s/n/\n/g;
  1452. sendMessage($messageSender, "k", $message);
  1453. }
  1454. sub monKilled {
  1455. $monkilltime = time();
  1456. # if someone kills it
  1457. if (($monstarttime == 0) || ($monkilltime < $monstarttime)) {
  1458. $monstarttime = 0;
  1459. $monkilltime = 0;
  1460. }
  1461. $elasped = $monkilltime - $monstarttime;
  1462. $totalelasped = $totalelasped + $elasped;
  1463. if ($totalelasped == 0) {
  1464. $dmgpsec = 0
  1465. } else {
  1466. $dmgpsec = $totaldmg / $totalelasped;
  1467. }
  1468. }
  1469. # Resolves a player or monster ID into a name
  1470. # Obsoleted by Actor module, don't use this!
  1471. sub getActorName {
  1472. my $id = shift;
  1473. if (!$id) {
  1474. return 'Nothing';
  1475. } else {
  1476. my $hash = Actor::get($id);
  1477. return $hash->nameString;
  1478. }
  1479. }
  1480. # Resolves a pair of player/monster IDs into names
  1481. sub getActorNames {
  1482. my ($sourceID, $targetID, $verb1, $verb2) = @_;
  1483. my $source = getActorName($sourceID);
  1484. my $verb = $source eq 'You' ? $verb1 : $verb2;
  1485. my $target;
  1486. if ($targetID eq $sourceID) {
  1487. if ($targetID eq $accountID) {
  1488. $target = 'yourself';
  1489. } else {
  1490. $target = 'self';
  1491. }
  1492. } else {
  1493. $target = getActorName($targetID);
  1494. }
  1495. return ($source, $verb, $target);
  1496. }
  1497. # return ID based on name if party member is online
  1498. sub findPartyUserID {
  1499. if ($char->{party} && %{$char->{party}}) {
  1500. my $partyUserName = shift;
  1501. for (my $j = 0; $j < @partyUsersID; $j++) {
  1502.          next if ($partyUsersID[$j] eq "");
  1503. if ($partyUserName eq $char->{party}{users}{$partyUsersID[$j]}{name}
  1504. && $char->{party}{users}{$partyUsersID[$j]}{online}) {
  1505. return $partyUsersID[$j];
  1506. }
  1507. }
  1508. }
  1509. return undef;
  1510. }
  1511. # fill in a hash of NPC information either based on location ("map x y")
  1512. sub getNPCInfo {
  1513. my $id = shift;
  1514. my $return_hash = shift;
  1515. undef %{$return_hash};
  1516. my ($map, $x, $y) = split(/ +/, $id, 3);
  1517. $$return_hash{map} = $map;
  1518. $$return_hash{pos}{x} = $x;
  1519. $$return_hash{pos}{y} = $y;
  1520. if (($$return_hash{map} ne "") && ($$return_hash{pos}{x} ne "") && ($$return_hash{pos}{y} ne "")) {
  1521. $$return_hash{ok} = 1;
  1522. } else {
  1523. error TF("Invalid NPC information for autoBuy, autoSell or autoStorage! (%s)n", $id);
  1524. }
  1525. }
  1526. sub checkSelfCondition {
  1527. my $prefix = shift;
  1528. return 0 if (!$prefix);
  1529. return 0 if ($config{$prefix . "_disabled"});
  1530. return 0 if $config{$prefix."_whenIdle"} && !AI::isIdle();
  1531. # *_manualAI 0 = auto only
  1532. # *_manualAI 1 = manual only
  1533. # *_manualAI 2 = auto or manual
  1534.      if ($config{$prefix . "_manualAI"} == 0 || !(defined $config{$prefix . "_manualAI"})) {
  1535. return 0 if ($AI != 2);
  1536. }elsif ($config{$prefix . "_manualAI"} == 1){
  1537.           return 0 if ($AI != 1);
  1538.   }else {
  1539.           return 0 if ($AI == 0);
  1540. }
  1541. if ($config{$prefix . "_hp"}) {
  1542. if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
  1543. return 0 if (!inRange($char->hp_percent, $1));
  1544. } else {
  1545. return 0 if (!inRange($char->{hp}, $config{$prefix."_hp"}));
  1546. }
  1547. }
  1548. if ($config{$prefix."_sp"}) {
  1549. if ($config{$prefix."_sp"} =~ /^(.*)%$/) {
  1550. return 0 if (!inRange($char->sp_percent, $1));
  1551. } else {
  1552. return 0 if (!inRange($char->{sp}, $config{$prefix."_sp"}));
  1553. }
  1554. }
  1555. if ($config{$prefix."_homunculus"} =~ /S/) {
  1556. return 0 if (!!$config{$prefix."_homunculus"}) ^ ($char->{homunculus} && !$char->{homunculus}{state});
  1557. }
  1558. if ($char->{homunculus}) {
  1559. if ($config{$prefix . "_homunculus_hp"}) {
  1560. if ($config{$prefix."_homunculus_hp"} =~ /^(.*)%$/) {
  1561. return 0 if (!inRange($char->{homunculus}{hpPercent}, $1));
  1562. } else {
  1563. return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix."_homunculus_hp"}));
  1564. }
  1565. }
  1566. if ($config{$prefix."_homunculus_sp"}) {
  1567. if ($config{$prefix."_homunculus_sp"} =~ /^(.*)%$/) {
  1568. return 0 if (!inRange($char->{homunculus}{spPercent}, $1));
  1569. } else {
  1570. return 0 if (!inRange($char->{homunculus}{sp}, $config{$prefix."_homunculus_sp"}));
  1571. }
  1572. }
  1573. if ($config{$prefix."_homunculus_dead"}) {
  1574. return 0 unless ($char->{homunculus}{state} & 4);
  1575. }
  1576. }
  1577. if ($config{$prefix."_mercenary"} =~ /S/) {
  1578. return 0 if (!!$config{$prefix."_mercenary"}) ^ (!!$char->{mercenary});
  1579. }
  1580. if ($char->{mercenary}) {
  1581. if ($config{$prefix . "_mercenary_hp"}) {
  1582. if ($config{$prefix."_mercenary_hp"} =~ /^(.*)%$/) {
  1583. return 0 if (!inRange($char->{mercenary}{hpPercent}, $1));
  1584. } else {
  1585. return 0 if (!inRange($char->{mercenary}{hp}, $config{$prefix."_mercenary_hp"}));
  1586. }
  1587. }
  1588. if ($config{$prefix."_mercenary_sp"}) {
  1589. if ($config{$prefix."_mercenary_sp"} =~ /^(.*)%$/) {
  1590. return 0 if (!inRange($char->{mercenary}{spPercent}, $1));
  1591. } else {
  1592. return 0 if (!inRange($char->{mercenary}{sp}, $config{$prefix."_mercenary_sp"}));
  1593. }
  1594. }
  1595. if ($config{$prefix . "_mercenary_whenStatusActive"}) { return 0 unless (whenStatusActivePL($char->{mercenary}, $config{$prefix . "_mercenary_whenStatusActive"})); }
  1596. if ($config{$prefix . "_mercenary_whenStatusInactive"}) { return 0 if (whenStatusActivePL($char->{mercenary}, $config{$prefix . "_mercenary_whenStatusInactive"})); }
  1597. }
  1598. # check skill use SP if this is a 'use skill' condition
  1599. if ($prefix =~ /skill/i) {
  1600. my $skill_handle = Skill->new(name => lc($config{$prefix}))->getHandle();
  1601. return 0 unless (($char->{skills}{$skill_handle} && $char->{skills}{$skill_handle}{lv} >= 1)
  1602. || ($char->{permitSkill} && $char->{permitSkill}->getName() eq $config{$prefix})
  1603. || $config{$prefix."_equip_leftAccessory"}
  1604. || $config{$prefix."_equip_rightAccessory"}
  1605. || $config{$prefix."_equip_leftHand"}
  1606. || $config{$prefix."_equip_rightHand"}
  1607. || $config{$prefix."_equip_robe"}
  1608. );
  1609. return 0 unless ($char->{sp} >= $skillsSP_lut{$skill_handle}{$config{$prefix . "_lvl"}});
  1610. }
  1611. if (defined $config{$prefix . "_aggressives"}) {
  1612. return 0 unless (inRange(scalar ai_getAggressives(), $config{$prefix . "_aggressives"}));
  1613. }
  1614. if (defined $config{$prefix . "_partyAggressives"}) {
  1615. return 0 unless (inRange(scalar ai_getAggressives(undef, 1), $config{$prefix . "_partyAggressives"}));
  1616. }
  1617. if ($config{$prefix . "_stopWhenHit"} > 0) { return 0 if (scalar ai_getMonstersAttacking($accountID)); }
  1618. if ($config{$prefix . "_whenFollowing"} && $config{follow}) {
  1619. return 0 if (!checkFollowMode());
  1620. }
  1621. if ($config{$prefix . "_whenStatusActive"}) { return 0 unless (whenStatusActive($config{$prefix . "_whenStatusActive"})); }
  1622. if ($config{$prefix . "_whenStatusInactive"}) { return 0 if (whenStatusActive($config{$prefix . "_whenStatusInactive"})); }
  1623. if ($config{$prefix . "_onAction"}) { return 0 unless (existsInList($config{$prefix . "_onAction"}, AI::action())); }
  1624. if ($config{$prefix . "_notOnAction"}) { return 0 if (existsInList($config{$prefix . "_notOnAction"}, AI::action())); }
  1625. if ($config{$prefix . "_spirit"}) {return 0 unless (inRange($char->{spirits}, $config{$prefix . "_spirit"})); }
  1626. if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}, $config{$prefix . "_timeout"}) }
  1627. if ($config{$prefix . "_inLockOnly"} > 0) { return 0 unless ($field{name} eq $config{lockMap}); }
  1628. if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($char->{sitting}); }
  1629. if ($config{$prefix . "_notInTown"} > 0) { return 0 if ($cities_lut{$field{name}.'.rsw'}); }
  1630. if ($config{$prefix . "_monsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) {
  1631. my $exists;
  1632. foreach (ai_getAggressives()) {
  1633. if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}->name)) {
  1634. $exists = 1;
  1635. last;
  1636. }
  1637. }
  1638. return 0 unless $exists;
  1639. }
  1640. if ($config{$prefix . "_defendMonsters"}) {
  1641. my $exists;
  1642. foreach (ai_getMonstersAttacking($accountID)) {
  1643. if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}->name)) {
  1644. $exists = 1;
  1645. last;
  1646. }
  1647. }
  1648. return 0 unless $exists;
  1649. }
  1650. if ($config{$prefix . "_notMonsters"} && !($prefix =~ /skillSlot/i) && !($prefix =~ /ComboSlot/i)) {
  1651. my $exists;
  1652. foreach (ai_getAggressives()) {
  1653. if (existsInList($config{$prefix . "_notMonsters"}, $monsters{$_}->name)) {
  1654. return 0;
  1655. }
  1656. }
  1657. }
  1658. if ($config{$prefix."_inInventory"}) {
  1659. foreach my $input (split / *, */, $config{$prefix."_inInventory"}) {
  1660. my ($itemName, $count) = $input =~ /(.*?)(?:s+([><]=? *d+))?$/;
  1661. $count = '>0' if $count eq '';
  1662. my $item = $char->inventory->getByName($itemName);
  1663.   return 0 if !inRange(!$item ? 0 : $item->{amount}, $count);
  1664. }
  1665. }
  1666. if ($config{$prefix."_inCart"}) {
  1667. foreach my $input (split / *, */, $config{$prefix."_inCart"}) {
  1668. my ($item,$count) = $input =~ /(.*?)(?:s+([><]=? *d+))?$/;
  1669. $count = '>0' if $count eq '';
  1670. my $iX = findIndexString_lc($cart{inventory}, "name", $item);
  1671.   my $item = $cart{inventory}[$iX];
  1672. return 0 if !inRange(!defined $iX ? 0 : $item->{amount}, $count);
  1673. }
  1674. }
  1675. if ($config{$prefix."_whenGround"}) {
  1676. return 0 unless whenGroundStatus(calcPosition($char), $config{$prefix."_whenGround"});
  1677. }
  1678. if ($config{$prefix."_whenNotGround"}) {
  1679. return 0 if whenGroundStatus(calcPosition($char), $config{$prefix."_whenNotGround"});
  1680. }
  1681. if ($config{$prefix."_whenPermitSkill"}) {
  1682. return 0 unless $char->{permitSkill} &&
  1683. $char->{permitSkill}->getName() eq $config{$prefix."_whenPermitSkill"};
  1684. }
  1685. if ($config{$prefix."_whenNotPermitSkill"}) {
  1686. return 0 if $char->{permitSkill} &&
  1687. $char->{permitSkill}->getName() eq $config{$prefix."_whenNotPermitSkill"};
  1688. }
  1689. if ($config{$prefix."_whenFlag"}) {
  1690. return 0 unless $flags{$config{$prefix."_whenFlag"}};
  1691. }
  1692. if ($config{$prefix."_whenNotFlag"}) {
  1693. return 0 unless !$flags{$config{$prefix."_whenNotFlag"}};
  1694. }
  1695. if ($config{$prefix."_onlyWhenSafe"}) {
  1696. return 0 if !isSafe();
  1697. }
  1698. if ($config{$prefix."_inMap"}) {
  1699. return 0 unless (existsInList($config{$prefix . "_inMap"}, $field{name}));
  1700. }
  1701. if ($config{$prefix."_notInMap"}) {
  1702. return 0 if (existsInList($config{$prefix . "_notInMap"}, $field{name}));
  1703. }
  1704. if ($config{$prefix."_whenEquipped"}) {
  1705. my $item = Actor::Item::get($config{$prefix."_whenEquipped"});
  1706. return 0 unless $item && $item->{equipped};
  1707. }
  1708. if ($config{$prefix."_whenNotEquipped"}) {
  1709. my $item = Actor::Item::get($config{$prefix."_whenNotEquipped"});
  1710. return 0 if $item && $item->{equipped};
  1711. }
  1712. if ($config{$prefix."_zeny"}) {
  1713. return 0 if (!inRange($char->{zenny}, $config{$prefix."_zeny"}));
  1714. }
  1715. # not working yet
  1716. if ($config{$prefix."_whenWater"}) {
  1717. my $pos = calcPosition($char);
  1718. return 0 if ($field->getBlock($pos->{x}, $pos->{y}) != Field::WALKABLE_WATER);
  1719. }
  1720. my %hookArgs;
  1721. $hookArgs{prefix} = $prefix;
  1722. $hookArgs{return} = 1;
  1723. Plugins::callHook("checkSelfCondition", %hookArgs);
  1724. return 0 if (!$hookArgs{return});
  1725. return 1;
  1726. }
  1727. sub checkPlayerCondition {
  1728. my ($prefix, $id) = @_;
  1729. return 0 if (!$id);
  1730. my $player = $playersList->getByID($id) || $slavesList->getByID($id);
  1731. if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$id}, $config{$prefix . "_timeout"}) }
  1732. if ($config{$prefix . "_whenStatusActive"}) { return 0 unless (whenStatusActivePL($id, $config{$prefix . "_whenStatusActive"})); }
  1733. if ($config{$prefix . "_whenStatusInactive"}) { return 0 if (whenStatusActivePL($id, $config{$prefix . "_whenStatusInactive"})); }
  1734. if ($config{$prefix . "_notWhileSitting"} > 0) { return 0 if ($player->{sitting}); }
  1735. # we will have player HP info (only) if we are in the same party
  1736. if ($char->{party} && $char->{party}{users}{$id}) {
  1737. if ($config{$prefix . "_hp"}) {
  1738. if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
  1739. return 0 if (!inRange(percent_hp($char->{party}{users}{$id}), $1));
  1740. } else {
  1741. return 0 if (!inRange($char->{party}{users}{$id}{hp}, $config{$prefix . "_hp"}));
  1742. }
  1743. }
  1744. } elsif ($char->{homunculus} && $char->{homunculus}{ID} eq $id) {
  1745. if ($config{$prefix . "_hp"}) {
  1746. if ($config{$prefix."_hp"} =~ /^(.*)%$/) {
  1747. return 0 if (!inRange(percent_hp($char->{homunculus}), $1));
  1748. } else {
  1749. return 0 if (!inRange($char->{homunculus}{hp}, $config{$prefix . "_hp"}));
  1750. }
  1751. }
  1752. }
  1753. if ($config{$prefix."_deltaHp"}){
  1754. return 0 unless inRange($player->{deltaHp}, $config{$prefix."_deltaHp"});
  1755. }
  1756. # check player job class
  1757. if ($config{$prefix . "_isJob"}) { return 0 unless (existsInList($config{$prefix . "_isJob"}, $jobs_lut{$player->{jobID}})); }
  1758. if ($config{$prefix . "_isNotJob"}) { return 0 if (existsInList($config{$prefix . "_isNotJob"}, $jobs_lut{$player->{jobID}})); }
  1759. if ($config{$prefix . "_aggressives"}) {
  1760. return 0 unless (inRange(scalar ai_getPlayerAggressives($id), $config{$prefix . "_aggressives"}));
  1761. }
  1762. if ($config{$prefix . "_defendMonsters"}) {
  1763. my $exists;
  1764. foreach (ai_getMonstersAttacking($id)) {
  1765. if (existsInList($config{$prefix . "_defendMonsters"}, $monsters{$_}{name})) {
  1766. $exists = 1;
  1767. last;
  1768. }
  1769. }
  1770. return 0 unless $exists;
  1771. }
  1772. if ($config{$prefix . "_monsters"}) {
  1773. my $exists;
  1774. foreach (ai_getPlayerAggressives($id)) {
  1775. if (existsInList($config{$prefix . "_monsters"}, $monsters{$_}{name})) {
  1776. $exists = 1;
  1777. last;
  1778. }
  1779. }
  1780. return 0 unless $exists;
  1781. }
  1782. if ($config{$prefix."_whenGround"}) {
  1783. return 0 unless whenGroundStatus(calcPosition($player), $config{$prefix."_whenGround"});
  1784. }
  1785. if ($config{$prefix."_whenNotGround"}) {
  1786. return 0 if whenGroundStatus(calcPosition($player), $config{$prefix."_whenNotGround"});
  1787. }
  1788. if ($config{$prefix."_dead"}) {
  1789. return 0 if !$player->{dead};
  1790. } else {
  1791. return 0 if $player->{dead};
  1792. }
  1793. if ($config{$prefix."_whenWeaponEquipped"}) {
  1794. return 0 unless $player->{weapon};
  1795. }
  1796. if ($config{$prefix."_whenShieldEquipped"}) {
  1797. return 0 unless $player->{shield};
  1798. }
  1799. if ($config{$prefix."_isGuild"}) {
  1800. return 0 unless ($player->{guild} && existsInList($config{$prefix . "_isGuild"}, $player->{guild}{name}));
  1801. }
  1802. if ($config{$prefix."_dist"}) {
  1803. return 0 unless inRange(distance(calcPosition($char), calcPosition($player)), $config{$prefix."_dist"});
  1804. }
  1805. my %args = (
  1806. player => $player,
  1807. prefix => $prefix,
  1808. return => 1
  1809. );
  1810. Plugins::callHook('checkPlayerCondition', %args);
  1811. return $args{return};
  1812. }
  1813. sub checkMonsterCondition {
  1814. my ($prefix, $monster) = @_;
  1815. if ($config{$prefix . "_timeout"}) { return 0 unless timeOut($ai_v{$prefix . "_time"}{$monster->{ID}}, $config{$prefix . "_timeout"}) }
  1816. if (my $misses = $config{$prefix . "_misses"}) {
  1817. return 0 unless inRange($monster->{atkMiss}, $misses);
  1818. }
  1819. if (my $misses = $config{$prefix . "_totalMisses"}) {
  1820. return 0 unless inRange($monster->{missedFromYou}, $misses);
  1821. }
  1822. if ($config{$prefix . "_whenStatusActive"}) {
  1823. return 0 unless (whenStatusActiveMon($monster, $config{$prefix . "_whenStatusActive"}));
  1824. }
  1825. if ($config{$prefix . "_whenStatusInactive"}) {
  1826. return 0 if (whenStatusActiveMon($monster, $config{$prefix . "_whenStatusInactive"}));
  1827. }
  1828. if ($config{$prefix."_whenGround"}) {
  1829. return 0 unless whenGroundStatus(calcPosition($monster), $config{$prefix."_whenGround"});
  1830. }
  1831. if ($config{$prefix."_whenNotGround"}) {
  1832. return 0 if whenGroundStatus(calcPosition($monster), $config{$prefix."_whenNotGround"});
  1833. }
  1834. if ($config{$prefix."_dist"}) {
  1835. return 0 unless inRange(distance(calcPosition($char), calcPosition($monster)), $config{$prefix."_dist"});
  1836. }
  1837. if ($config{$prefix."_deltaHp"}){
  1838. return 0 unless inRange($monster->{deltaHp}, $config{$prefix."_deltaHp"});
  1839. }
  1840. # This is only supposed to make sense for players,
  1841. # but it has to be here for attackSkillSlot PVP to work
  1842. if ($config{$prefix."_whenWeaponEquipped"}) {
  1843. return 0 unless $monster->{weapon};
  1844. }
  1845. if ($config{$prefix."_whenShieldEquipped"}) {
  1846. return 0 unless $monster->{shield};
  1847. }
  1848. my %args = (
  1849. monster => $monster,
  1850. prefix => $prefix,
  1851. return => 1
  1852. );
  1853. Plugins::callHook('checkMonsterCondition', %args);
  1854. return $args{return};
  1855. }
  1856. ##
  1857. # findCartItemInit()
  1858. #
  1859. # Resets all "found" flags in the cart to 0.
  1860. sub findCartItemInit {
  1861. for (@{$cart{inventory}}) {
  1862. next unless $_ && %{$_};
  1863. undef $_->{found};
  1864. }
  1865. }
  1866. ##
  1867. # findCartItem($name [, $found [, $nounid]])
  1868. #
  1869. # Returns the integer index into $cart{inventory} for the cart item matching
  1870. # the given name, or undef.
  1871. #
  1872. # If an item is found, the "found" value for that item is set to 1. Items
  1873. # cannot be found again until you reset the "found" flags using
  1874. # findCartItemInit(), if $found is true.
  1875. #
  1876. # Unidentified items will not be returned if $nounid is true.
  1877. sub findCartItem {
  1878. my ($name, $found, $nounid) = @_;
  1879. $name = lc($name);
  1880. my $index = 0;
  1881. for (@{$cart{inventory}}) {
  1882. if (lc($_->{name}) eq $name &&
  1883.     !($found && $_->{found}) &&
  1884. !($nounid && !$_->{identified})) {
  1885. $_->{found} = 1;
  1886. return $index;
  1887. }
  1888. $index++;
  1889. }
  1890. return undef;
  1891. }
  1892. ##
  1893. # makeShop()
  1894. #
  1895. # Returns an array of items to sell. The array can be no larger than the
  1896. # maximum number of items that the character can vend. Each item is a hash
  1897. # reference containing the keys "index", "amount" and "price".
  1898. #
  1899. # If there is a problem with opening a shop, an error message will be printed
  1900. # and nothing will be returned.
  1901. sub makeShop {
  1902. if ($shopstarted) {
  1903. error T("A shop has already been opened.n");
  1904. return;
  1905. }
  1906. return unless $char;
  1907. if (!$char->{skills}{MC_VENDING}{lv}) {
  1908. error T("You don't have the Vending skill.n");
  1909. return;
  1910. }
  1911. if (!$shop{title_line}) {
  1912. error T("Your shop does not have a title.n");
  1913. return;
  1914. }
  1915. my @items = ();
  1916. my $max_items = $char->{skills}{MC_VENDING}{lv} + 2;
  1917. # Iterate through items to be sold
  1918. findCartItemInit();
  1919. shuffleArray(@{$shop{items}}) if ($config{'shop_random'} eq "2");
  1920. for my $sale (@{$shop{items}}) {
  1921. my $index = findCartItem($sale->{name}, 1, 1);
  1922. next unless defined($index);
  1923. # Found item to vend
  1924. my $cart_item = $cart{inventory}[$index];
  1925. my $amount = $cart_item->{amount};
  1926. my %item;
  1927. $item{name} = $cart_item->{name};
  1928. $item{index} = $index;
  1929. $item{price} = $sale->{price};
  1930. $item{amount} =
  1931. $sale->{amount} && $sale->{amount} < $amount ?
  1932. $sale->{amount} : $amount;
  1933. push(@items, %item);
  1934. # We can't vend anymore items
  1935. last if @items >= $max_items;
  1936. }
  1937. if (!@items) {
  1938. error T("There are no items to sell.n");
  1939. return;
  1940. }
  1941. shuffleArray(@items) if ($config{'shop_random'} eq "1");
  1942. return @items;
  1943. }
  1944. sub openShop {
  1945. my @items = makeShop();
  1946. my @shopnames;
  1947. return unless @items;
  1948. @shopnames = split(/;;/, $shop{title_line});
  1949. $shop{title} = $shopnames[int rand($#shopnames + 1)];
  1950. $shop{title} = ($config{shopTitleOversize}) ? $shop{title} : substr($shop{title},0,36);
  1951. $messageSender->sendOpenShop($shop{title}, @items);
  1952. message TF("Shop opened (%s) with %d selling items.n", $shop{title}, @items.""), "success";
  1953. $shopstarted = 1;
  1954. $shopEarned = 0;
  1955. }
  1956. sub closeShop {
  1957. if (!$shopstarted) {
  1958. error T("A shop has not been opened.n");
  1959. return;
  1960. }
  1961. $messageSender->sendCloseShop();
  1962. $shopstarted = 0;
  1963. $timeout{'ai_shop'}{'time'} = time;
  1964. message T("Shop closed.n");
  1965. }
  1966. ##
  1967. # inLockMap()
  1968. #
  1969. # Returns 1 (true) if character is located in its lockmap.
  1970. # Returns 0 (false) if character is not located in lockmap.
  1971. sub inLockMap {
  1972. if ($field{'name'} eq $config{'lockMap'}) {
  1973. return 1;
  1974. } else {
  1975. return 0;
  1976. }
  1977. }
  1978. sub parseReload {
  1979. my ($args) = @_;
  1980. eval {
  1981. my $progressHandler = sub {
  1982. my ($filename) = @_;
  1983. message TF("Loading %s...n", $filename);
  1984. };
  1985. if ($args eq 'all') {
  1986. Settings::loadAll($progressHandler);
  1987. } else {
  1988. Settings::loadByRegexp(qr/$args/, $progressHandler);
  1989. }
  1990. Log::initLogFiles();
  1991. };
  1992. if (my $e = caught('UTF8MalformedException')) {
  1993. error TF(
  1994. "The file %s must be valid UTF-8 encoded, which it is n" .
  1995. "currently not. To solve this prolem, please use Notepadn" .
  1996. "to save that file as valid UTF-8.",
  1997. $e->textfile);
  1998. } elsif ($@) {
  1999. die $@;
  2000. }
  2001. }
  2002. sub MODINIT {
  2003. OpenKoreMod::initMisc() if (defined(&OpenKoreMod::initMisc));
  2004. }
  2005. return 1;