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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. # This software is open source, licensed under the GNU General Public
  3. # License, version 2.
  4. # Basically, this means that you're allowed to modify and distribute
  5. # this software. However, if you distribute modified versions, you MUST
  6. # also distribute the source code.
  7. # See http://www.gnu.org/licenses/gpl.html for the full license.
  8. #########################################################################
  9. ##
  10. # MODULE DESCRIPTION: Processing of incoming chat commands
  11. #
  12. # Kore has a feature called 'chat commands': you command your bot to
  13. # do certain things by using PM or public chat. This module processes
  14. # chat input and executes chat commands when necessary.
  15. package ChatQueue;
  16. use strict;
  17. use Time::HiRes qw(time);
  18. use AI;
  19. use Commands;
  20. use Globals qw($accountID $AI %ai_v $char @chatResponses %cities_lut
  21. %config %field %itemChange @monsters_Killed %maps_lut $net %overallAuth
  22. %players %responseVars %skillsSP_lut $startTime_EXP %timeout
  23. $totalBaseExp $totalJobExp $messageSender
  24. );
  25. use Log qw(message error);
  26. use Misc qw(auth avoidGM_talk avoidList_talk configModify getIDFromChat
  27. getResponse quit relog sendMessage setTimeout look parseReload);
  28. use Plugins;
  29. use Translation;
  30. use Utils qw(formatNumber getFormattedDate parseArgs swrite timeConvert timeOut);
  31. our @queue;
  32. our $wordSplitters = qr/(b| |,|.|!)/;
  33. our %lastInput;
  34. # use SelfLoader; 1;
  35. # __DATA__
  36. ##
  37. # void ChatQueue::add(String type, Bytes userID, String user, String msg)
  38. # type: 'c' (public chat), 'pm' (private message), 'p' (party chat) or 'g' (guild chat)
  39. # userID: the account ID of the user who sent this message.
  40. # user: the name of the user who sent this message.
  41. # msg: the message.
  42. #
  43. # Add a chat message to the chat queue. The messages in the queue will be processed later.
  44. sub add {
  45. my %item = (
  46. type => shift,
  47. userID => shift,
  48. user => shift,
  49. msg => shift,
  50. time => time
  51. );
  52. push @queue, %item;
  53. Plugins::callHook('ChatQueue::add', %item);
  54. }
  55. ##
  56. # void ChatQueue::clear()
  57. #
  58. # Clear the chat queue, if there are any unprocessed messages.
  59. sub clear {
  60. @queue = ();
  61. %lastInput = ();
  62. }
  63. ##
  64. # void ChatQueue::processFirst()
  65. #
  66. # Process the first message in the queue, if any.
  67. # That message will be removed from the queue.
  68. sub processFirst {
  69. return unless @queue;
  70. my $cmd = shift @queue;
  71. return if ($cmd->{user} eq $char->{name});
  72. my $type = $cmd->{type};
  73. my $user = $cmd->{user};
  74. my $msg = $cmd->{msg};
  75. return if ( $user ne ""
  76. && (avoidGM_talk($user, $msg) || avoidList_talk($user, $msg, unpack("V1", $cmd->{userID}))) );
  77. # If the user is not authorized to use chat commands,
  78. # check whether he's trying to authenticate
  79. if (( $type eq "pm" || $type eq "p" || $type eq "g" ) && !$overallAuth{$user} && $config{adminPassword}) {
  80. if ($msg eq $config{adminPassword}) {
  81. auth($user, 1);
  82. sendMessage($messageSender, "pm", getResponse("authS"), $user);
  83. }
  84. # We don't notify the user if login failed; people use it
  85. # to check whether you're a bot.
  86. }
  87. # If the user is authorized to use chat commands,
  88. # check whether his message is a chat command, and execute it.
  89. my $callSign = quotemeta $config{callSign};
  90. if ($overallAuth{$user} && ( $type eq "pm" || $msg =~ /^b*$callSignb*/i )) {
  91. my $msg2 = $msg;
  92. $msg2 =~ s/^b*$callSignb* *//i;
  93. $msg2 =~ s/ *$//;
  94. return if processChatCommand($type, $user, $msg2);
  95. }
  96. # Not a chat command; attempt to reply with a message
  97. processChatResponse($cmd) if ($config{autoResponse});
  98. }
  99. sub processChatCommand {
  100. my ($type, $user, $msg) = @_;
  101. my ($switch, $after) = split / +/, $msg, 2;
  102. $after =~ s/ *$//;
  103. my @args = parseArgs($after);
  104. my $vars = %responseVars;
  105. $vars->{cmd_user} = $user;
  106. if ($switch eq "conf") {
  107. if ($args[0] eq "") {
  108. sendMessage($messageSender, $type, getResponse("confF1"), $user) if $config{verbose};
  109. } elsif (!exists $config{$args[0]}) {
  110. sendMessage($messageSender, $type, getResponse("confF2"), $user) if $config{verbose};
  111. } elsif ($args[1] eq "") {
  112. if (lc($args[0]) eq "username" || lc($args[0] eq "password")) {
  113. sendMessage($messageSender, $type, getResponse("confF3"), $user) if $config{verbose};
  114. } else {
  115. $vars->{key} = $args[0];
  116. $vars->{value} = $config{$args[0]};
  117. sendMessage($messageSender, $type, getResponse("confS1"), $user) if $config{verbose};
  118. $timeout{ai_thanks_set}{time} = time;
  119. }
  120. } else {
  121. $args[1] = "" if ($args[1] eq "none");
  122. configModify($args[0], $args[1]);
  123. sendMessage($messageSender, $type, getResponse("confS2"), $user) if $config{verbose};
  124. $timeout{ai_thanks_set}{time} = time;
  125. }
  126. } elsif ($switch eq "date") {
  127. $vars->{date} = getFormattedDate(int(time));
  128. sendMessage($messageSender, $type, getResponse("dateS"), $user) if $config{verbose};
  129. $timeout{ai_thanks_set}{time} = time;
  130. } elsif ($switch eq "exp") {
  131. if ($args[0] eq "") {
  132. my ($endTime_EXP, $w_sec);
  133. $endTime_EXP = time;
  134. $w_sec = int($endTime_EXP - $startTime_EXP);
  135. if ($w_sec > 0) {
  136. $vars->{bExpPerHour} = int($totalBaseExp / $w_sec * 3600);
  137. $vars->{jExpPerHour} = int($totalJobExp / $w_sec * 3600);
  138. if ($char->{exp_max} && $vars->{bExpPerHour}){
  139. $vars->{percentBExp} = "(".sprintf("%.2f",$totalBaseExp * 100 / $char->{exp_max})."%)";
  140. $vars->{percentBExpPerHour} = "(".sprintf("%.2f",$vars->{bExpPerHour} * 100 / $char->{exp_max})."%)";
  141. $vars->{bLvlUp} = timeConvert(int(($char->{exp_max} - $char->{exp})/($vars->{bExpPerHour}/3600)));
  142. }
  143. if ($char->{exp_job_max} && $vars->{jExpPerHour}){
  144. $vars->{percentJExp}  = "(".sprintf("%.2f",$totalJobExp * 100 / $char->{exp_job_max})."%)";
  145. $vars->{percentJExpPerHour} = "(".sprintf("%.2f",$vars->{jExpPerHour} * 100 / $char->{exp_job_max})."%)";
  146. $vars->{jLvlUp} = timeConvert(int(($char->{'exp_job_max'} - $char->{exp_job})/($vars->{jExpPerHour}/3600)));
  147. }
  148. }
  149. $vars->{time} = timeConvert($w_sec);
  150. $vars->{bExp} = formatNumber($totalBaseExp);
  151. $vars->{jExp} = formatNumber($totalJobExp);
  152. $vars->{bExpPerHour} = formatNumber($vars->{bExpPerHour});
  153. $vars->{jExpPerHour} = formatNumber($vars->{jExpPerHour});
  154. $vars->{numDeaths} = (defined $char->{deathCount})? $char->{deathCount} : 0;
  155. sendMessage($messageSender, $type, getResponse("expS"), $user) if $config{verbose};
  156. } elsif ($args[0] eq "monster") {
  157. $vars->{numKilledMonsters} = 0;
  158. sendMessage($messageSender, $type, getResponse("expMonsterS1"), $user) if $config{verbose};
  159. for (my $i = 0; $i < @monsters_Killed; $i++) {
  160. next if ($monsters_Killed[$i] eq "");
  161. $vars->{killedMonsters} = swrite(
  162. "@<< @<<<<<<<<<<<<<<<<<<<<<<<< @<<<<<<",
  163. [$i, $monsters_Killed[$i]{name}, $monsters_Killed[$i]{count}]);
  164. $vars->{killedMonsters} = substr($vars->{killedMonsters}, 0, length($vars->{killedMonsters}) - 1);
  165. $vars->{numKilledMonsters} += $monsters_Killed[$i]{count};
  166. sendMessage($messageSender, $type, getResponse("expMonsterS2"), $user) if $config{verbose};
  167. }
  168. sendMessage($messageSender, $type, getResponse("expMonsterS3"), $user) if $config{verbose};
  169. } elsif ($args[0] eq "item") {
  170. sendMessage($messageSender, $type, getResponse("expItemS1"), $user) if $config{verbose};
  171. for my $item (sort keys %itemChange) {
  172. next unless $itemChange{$item};
  173. $vars->{gotItems} = sprintf("%-40s %5d", $item, $itemChange{$item});
  174. sendMessage($messageSender, $type, getResponse("expItemS2"), $user) if $config{verbose};
  175. }
  176. } else {
  177. sendMessage($messageSender, $type, getResponse("expF"), $user) if $config{verbose};
  178. }
  179. } elsif ($switch eq "follow") {
  180. if ($args[0] eq "stop") {
  181. if ($config{follow}) {
  182. AI::clear("follow");
  183. configModify("follow", 0);
  184. sendMessage($messageSender, $type, getResponse("followStopS"), $user) if $config{verbose};
  185. $timeout{ai_thanks_set}{time} = time;
  186. } else {
  187. sendMessage($messageSender, $type, getResponse("followStopF"), $user) if $config{verbose};
  188. }
  189. } else {
  190. my $targetID = getIDFromChat(%players, $user, $after);
  191. if (defined $targetID) {
  192. AI::clear("follow");
  193. main::ai_follow($players{$targetID}{name});
  194. configModify("follow", 1);
  195. configModify("followTarget", $players{$targetID}{name});
  196. sendMessage($messageSender, $type, getResponse("followS"), $user) if $config{verbose};
  197. $timeout{ai_thanks_set}{time} = time;
  198. } else {
  199. sendMessage($messageSender, $type, getResponse("followF"), $user) if $config{verbose};
  200. }
  201. }
  202. } elsif ($switch eq "logout") {
  203. sendMessage($messageSender, $type, getResponse("quitS"), $user) if $config{verbose};
  204. quit();
  205. $timeout{ai_thanks_set}{time} = time;
  206. } elsif ($switch eq "look") {
  207. my ($body, $head) = @args;
  208. if ($body ne "") {
  209. look($body, $head);
  210. sendMessage($messageSender, $type, getResponse("lookS"), $user) if $config{verbose};
  211. $timeout{ai_thanks_set}{time} = time;
  212. } else {
  213. sendMessage($messageSender, $type, getResponse("lookF"), $user) if $config{verbose};
  214. }
  215. } elsif (($switch eq "move" && $args[0] eq "stop") || $switch eq "stop") {
  216. AI::clear("move", "route", "mapRoute");
  217. sendMessage($messageSender, $type, getResponse("moveS"), $user) if $config{verbose};
  218. $timeout{ai_thanks_set}{time} = time;
  219. } elsif ($switch eq "move") {
  220. my ($map, $x, $y);
  221. if ($args[0] =~ /^d+$/) {
  222.   # x y map
  223.   ($x, $y, $map) = @args;
  224. } else {
  225.   # map x y
  226.   ($map, $x, $y) = @args;
  227. }
  228. if ($map eq "") {
  229.   $x eq "" || $y eq ""
  230. } else {
  231.   $x ne "" && $y ne ""
  232. }
  233. if ($map ne "" || ($x ne "" && $y ne "")) {
  234. $map = $field{name} if ($map eq "");
  235. my $rsw = "${map}.rsw";
  236. if ($maps_lut{$rsw}) {
  237. if ($x ne "" && $y ne "") {
  238. message TF("Calculating route to: %s(%s): %d, %dn", $maps_lut{$rsw}, $map, $x, $y), "route";
  239. } else {
  240. message TF("Calculating route to: %s(%s)n", $maps_lut{$rsw}, $map), "route";
  241. }
  242. sendMessage($messageSender, $type, getResponse("moveS"), $user) if $config{verbose};
  243. main::ai_route($map, $x, $y, attackOnRoute => 1);
  244. $timeout{ai_thanks_set}{time} = time;
  245. } else {
  246. error TF("Map %s does not existn", $map);
  247. sendMessage($messageSender, $type, getResponse("moveF"), $user) if $config{verbose};
  248. }
  249. } else {
  250. sendMessage($messageSender, $type, getResponse("moveF"), $user) if $config{verbose};
  251. }
  252. } elsif ($switch eq "reload") {
  253. parseReload($after);
  254. sendMessage($messageSender, $type, getResponse("reloadS"), $user) if $config{verbose};
  255. $timeout{ai_thanks_set}{time} = time;
  256. } elsif ($switch eq "relog") {
  257. sendMessage($messageSender, $type, getResponse("relogS"), $user) if $config{verbose};
  258. relog($args[0]);
  259. $timeout{ai_thanks_set}{time} = time;
  260. } elsif ($msg =~ /bshut[sS]*upb/) {
  261. if ($config{verbose}) {
  262. configModify("verbose", 0);
  263. sendMessage($messageSender, $type, getResponse("verboseOffS"), $user);
  264. $timeout{ai_thanks_set}{time} = time;
  265. } else {
  266. sendMessage($messageSender, $type, getResponse("verboseOffF"), $user);
  267. }
  268. } elsif ($switch eq "speak") {
  269. if (!$config{verbose}) {
  270. configModify("verbose", 1);
  271. sendMessage($messageSender, $type, getResponse("verboseOnS"), $user);
  272. $timeout{ai_thanks_set}{time} = time;
  273. } else {
  274. sendMessage($messageSender, $type, getResponse("verboseOnF"), $user);
  275. }
  276. } elsif ($switch eq "sit") {
  277. Commands::run("sit");
  278. sendMessage($messageSender, $type, getResponse("sitS"), $user) if $config{verbose};
  279. $timeout{ai_thanks_set}{time} = time;
  280. } elsif ($switch eq "stand") {
  281. Commands::run("stand");
  282. sendMessage($messageSender, $type, getResponse("standS"), $user) if $config{verbose};
  283. $timeout{ai_thanks_set}{time} = time;
  284. } elsif ($switch eq "status") {
  285. $vars->{char_sp} = $char->{sp};
  286. $vars->{char_hp} = $char->{hp};
  287. $vars->{char_sp_max} = $char->{sp_max};
  288. $vars->{char_hp_max} = $char->{hp_max};
  289. $vars->{char_lv} = $char->{lv};
  290. $vars->{char_lv_job} = $char->{lv_job};
  291. $vars->{char_exp} = formatNumber($char->{exp});
  292. $vars->{char_exp_max} = formatNumber($char->{exp_max});
  293. $vars->{char_exp_job} = formatNumber($char->{exp_job});
  294. $vars->{char_exp_job_max} = formatNumber($char->{exp_job_max});
  295. $vars->{char_weight} = $char->{weight};
  296. $vars->{char_weight_max} = $char->{weight_max};
  297. $vars->{zenny} = formatNumber($char->{zenny});
  298. sendMessage($messageSender, $type, getResponse("statusS"), $user) if $config{verbose};
  299. } elsif ($switch eq "tank") {
  300. if ($args[0] eq "stop") {
  301. if (!$config{tankMode}) {
  302. sendMessage($messageSender, $type, getResponse("tankStopF"), $user) if $config{verbose};
  303. } else {
  304. sendMessage($messageSender, $type, getResponse("tankStopS"), $user) if $config{verbose};
  305. configModify("tankMode", 0);
  306. $timeout{ai_thanks_set}{time} = time;
  307. }
  308. } else {
  309. my $targetID = getIDFromChat(%players, $user, $after);
  310. if ($targetID ne "") {
  311. sendMessage($messageSender, $type, getResponse("tankS"), $user) if $config{verbose};
  312. configModify("tankMode", 1);
  313. configModify("tankModeTarget", $players{$targetID}{name});
  314. $timeout{ai_thanks_set}{time} = time;
  315. } else {
  316. sendMessage($messageSender, $type, getResponse("tankF"), $user) if $config{verbose};
  317. }
  318. }
  319. } elsif ($switch eq "thank" || $switch eq "thn" || $switch eq "thx") {
  320. if (!timeOut($timeout{ai_thanks_set})) {
  321. $timeout{ai_thanks_set}{time} -= $timeout{ai_thanks_set}{timeout};
  322. sendMessage($messageSender, $type, getResponse("thankS"), $user) if $config{verbose};
  323. }
  324. } elsif ($switch eq "timeout") {
  325. if ($args[0] eq "") {
  326. sendMessage($messageSender, $type, getResponse("timeoutF1"), $user) if $config{verbose};
  327. } elsif (!exists $timeout{$args[0]}{timeout}) {
  328. sendMessage($messageSender, $type, getResponse("timeoutF2"), $user) if $config{verbose};
  329. } elsif ($args[1] eq "") {
  330. $vars->{key} = $args[0];
  331. $vars->{value} = $timeout{$args[0]}{timeout};
  332. sendMessage($messageSender, $type, getResponse("timeoutS1"), $user) if $config{verbose};
  333. $timeout{ai_thanks_set}{time} = time;
  334. } else {
  335. $args[1] = "" if ($args[1] eq "none");
  336. setTimeout($args[0], $args[1]);
  337. sendMessage($messageSender, $type, getResponse("timeoutS2"), $user) if $config{verbose};
  338. $timeout{ai_thanks_set}{time} = time;
  339. }
  340. } elsif ($switch eq "town") {
  341. sendMessage($messageSender, $type, getResponse("moveS"), $user);
  342. main::useTeleport(2);
  343. } elsif ($switch eq "version") {
  344. $vars->{ver} = $Settings::VERSION;
  345. sendMessage($messageSender, $type, getResponse("versionS"), $user) if $config{verbose};
  346. } elsif ($switch eq "where") {
  347. my $rsw = "$field{name}.rsw";
  348. $vars->{x} = $char->{pos_to}{x};
  349. $vars->{y} = $char->{pos_to}{y};
  350. $vars->{map} = "$maps_lut{$rsw} ($field{name})";
  351. $timeout{ai_thanks_set}{time} = time;
  352. sendMessage($messageSender, $type, getResponse("whereS"), $user) if $config{verbose};
  353. # Support Skills
  354. } elsif ($switch eq "agi"){
  355. my $targetID = getIDFromChat(%players, $user, $after);
  356. if ($targetID eq "") {
  357. sendMessage($messageSender, $type, getResponse("healF1"), $user) if $config{verbose};
  358. } elsif ($char->{skills}{AL_INCAGI}{lv} > 0) {
  359. my $failed = 1;
  360. for (my $i = $char->{skills}{AL_INCAGI}{lv}; $i >=1; $i--) {
  361. if ($char->{sp} >= $skillsSP_lut{AL_INCAGI}{$i}) {
  362. main::ai_skillUse('AL_INCAGI', $i, 0, 0, $targetID);
  363. $failed = 0;
  364. last;
  365. }
  366. }
  367. if (!$failed) {
  368. sendMessage($messageSender, $type, getResponse("healS"), $user) if $config{verbose};
  369. }else{
  370. sendMessage($messageSender, $type, getResponse("healF2"), $user) if $config{verbose};
  371. }
  372. } else {
  373. sendMessage($messageSender, $type, getResponse("healF3"), $user) if $config{'verbose'};
  374. }
  375. $timeout{ai_thanks_set}{time} = time;
  376. } elsif ($switch eq "bless" || $switch eq "blessing"){
  377. my $targetID = getIDFromChat(%players, $user, $after);
  378. if ($targetID eq "") {
  379. sendMessage($messageSender, $type, getResponse("healF1"), $user) if $config{verbose};
  380. } elsif ($char->{skills}{AL_BLESSING}{lv} > 0) {
  381. my $failed = 1;
  382. for (my $i = $char->{skills}{AL_BLESSING}{lv}; $i >=1; $i--) {
  383. if ($char->{sp} >= $skillsSP_lut{AL_BLESSING}{$i}) {
  384. main::ai_skillUse('AL_BLESSING', $i, 0, 0, $targetID);
  385. $failed = 0;
  386. last;
  387. }
  388. }
  389. if (!$failed) {
  390. sendMessage($messageSender, $type, getResponse("healS"), $user) if $config{verbose};
  391. }else{
  392. sendMessage($messageSender, $type, getResponse("healF2"), $user) if $config{verbose};
  393. }
  394. } else {
  395. sendMessage($messageSender, $type, getResponse("healF3"), $user) if $config{verbose};
  396. }
  397. $timeout{ai_thanks_set}{time} = time;
  398. } elsif ($switch eq "heal") {
  399. my $amount;
  400. my $targetID;
  401. if ($args[0] =~ /^d+$/) {
  402. $amount = $args[0];
  403. $targetID = getIDFromChat(%players, $user, $args[1]);
  404. } else {
  405. $amount = $args[1];
  406. $targetID = getIDFromChat(%players, $user, $args[0]);
  407. }
  408. if ($targetID eq "") {
  409. sendMessage($messageSender, $type, getResponse("healF1"), $user) if $config{verbose};
  410. } elsif ($char->{skills}{AL_HEAL}{lv} > 0) {
  411. my $amount_healed;
  412. my $sp_needed;
  413. my $sp_used;
  414. my $failed;
  415. my @skillCasts;
  416. # Calculate what skill level is needed to heal $amount HP
  417. while ($amount_healed < $amount) {
  418. my ($sp, $amount_this);
  419. for (my $i = 1; $i <= $char->{skills}{AL_HEAL}{lv}; $i++) {
  420. $sp = 10 + ($i * 3);
  421. $amount_this = int(($char->{lv} + $char->{int}) / 8)
  422. * (4 + $i * 8);
  423. last if ($amount_healed + $amount_this >= $amount);
  424. }
  425. $sp_needed += $sp;
  426. $amount_healed += $amount_this;
  427. }
  428. while ($sp_used < $sp_needed && !$failed) {
  429. my ($lv, $sp);
  430. for (my $i = 1; $i <= $char->{skills}{AL_HEAL}{lv}; $i++) {
  431. $lv = $i;
  432. $sp = 10 + ($i * 3);
  433. if ($sp_used + $sp > $char->{sp}) {
  434. $lv--;
  435. $sp = 10 + ($lv * 3);
  436. last;
  437. }
  438. last if ($sp_used + $sp >= $sp_needed);
  439. }
  440. if ($lv > 0) {
  441. my %skill;
  442. $sp_used += $sp;
  443. $skill{skill} = 'AL_HEAL';
  444. $skill{lv} = $lv;
  445. $skill{maxCastTime} = 0;
  446. $skill{minCastTime} = 0;
  447. $skill{ID} = $targetID;
  448. unshift @skillCasts, %skill;
  449. } else {
  450. $vars->{char_sp} = $char->{sp} - $sp_used;
  451. sendMessage($messageSender, $type, getResponse("healF2"), $user) if $config{verbose};
  452. $failed = 1;
  453. }
  454. }
  455. if (!$failed) {
  456. sendMessage($messageSender, $type, getResponse("healS"), $user) if $config{verbose};
  457. }
  458. foreach (@skillCasts) {
  459. main::ai_skillUse($_->{skill}, $_->{lv}, $_->{maxCastTime}, $_->{minCastTime}, $_->{ID});
  460. }
  461. } else {
  462. sendMessage($messageSender, $type, getResponse("healF3"), $user) if $config{verbose};
  463. }
  464. } elsif ($switch eq "kyrie"){
  465. my $targetID = getIDFromChat(%players, $user, $after);
  466. if ($targetID eq "") {
  467. sendMessage($messageSender, $type, getResponse("healF1"), $user) if $config{verbose};
  468. } elsif ($char->{skills}{PR_KYRIE}{lv} > 0) {
  469. my $failed = 1;
  470. for (my $i = $char->{skills}{PR_KYRIE}{lv}; $i >= 1; $i--) {
  471. if ($char->{sp} >= $skillsSP_lut{PR_KYRIE}{$i}) {
  472. main::ai_skillUse('PR_KYRIE', $i, 0, 0, $targetID);
  473. $failed = 0;
  474. last;
  475. }
  476. }
  477. if (!$failed) {
  478. sendMessage($messageSender, $type, getResponse("healS"), $user) if $config{verbose};
  479. }else{
  480. sendMessage($messageSender, $type, getResponse("healF2"), $user) if $config{verbose};
  481. }
  482. } else {
  483. sendMessage($messageSender, $type, getResponse("healF3"), $user) if $config{verbose};
  484. }
  485. $timeout{ai_thanks_set}{time} = time;
  486. } elsif ($switch eq "mag"){
  487. my $targetID = $accountID;
  488. if ($char->{skills}{PR_MAGNIFICAT}{lv} > 0) {
  489. my $failed = 1;
  490. for (my $i = $char->{skills}{PR_MAGNIFICAT}{lv}; $i >= 1; $i--) {
  491. if ($char->{sp} >= $skillsSP_lut{PR_MAGNIFICAT}{$i}) {
  492. main::ai_skillUse('PR_MAGNIFICAT', $i, 0, 0, $targetID);
  493. $failed = 0;
  494. last;
  495. }
  496. }
  497. if (!$failed) {
  498. sendMessage($messageSender, $type, getResponse("healS"), $user) if $config{verbose};
  499. }else{
  500. sendMessage($messageSender, $type, getResponse("healF2"), $user) if $config{verbose};
  501. }
  502. } else {
  503. sendMessage($messageSender, $type, getResponse("healF3"), $user) if $config{verbose};
  504. }
  505. $timeout{ai_thanks_set}{time} = time;
  506. } else {
  507. return 0;
  508. }
  509. return 1;
  510. }
  511. # Automatically reply to a chat message
  512. sub processChatResponse {
  513. return unless ($AI == 2);
  514. my $cmd = shift;
  515. my $msg = lc $cmd->{msg};
  516. my $reply;
  517. my $repeating;
  518. # Check whether the user is repeating itself
  519. $msg =~ s/^ +//;
  520. $msg =~ s/ +$//;
  521. if (defined $lastInput{msg} && $lastInput{user} eq $cmd->{user} && $lastInput{msg} eq $msg) {
  522. $repeating = $cmd->{repeating} = 1;
  523. }
  524. # Determine a reply
  525. Plugins::callHook('ChatQueue::processChatResponse', $cmd);
  526. if (defined $cmd->{reply}) {
  527. $reply = $cmd->{reply};
  528. } elsif (!$repeating && ($cmd->{type} eq "c" || $cmd->{type} eq "pm") && !$cities_lut{$field{name}.'.rsw'}) {
  529. foreach my $item (@chatResponses) {
  530. my $word = quotemeta $item->{word};
  531. if ($msg =~ /${wordSplitters}${word}${wordSplitters}/) {
  532. my $max = @{$item->{responses}};
  533. $reply = $item->{responses}[rand($max)];
  534. last;
  535. }
  536. }
  537. }
  538. return unless (defined $reply);
  539. # Calculate a small delay (to simulate typing)
  540. # The average typing speed is 65 words per minute.
  541. # The average length of a word used by RO players is 4.25 characters (yes I measured it).
  542. # So the average user types 65 * 4.25 = 276.25 charcters per minute, or
  543. # 276.25 / 60 = 4.6042 characters per second
  544. # We also add a random delay of 0.5-1.5 seconds.
  545. my $timeout = 0.5 + rand(1) + length($reply) / 4.6042;
  546. my %args = (
  547. time => time,
  548. timeout => $timeout,
  549. type => $cmd->{type},
  550. from => $cmd->{user},
  551. reply => $reply
  552. );
  553. AI::queue("autoResponse", %args);
  554. $lastInput{msg} = $msg;
  555. $lastInput{user} = $cmd->{user};
  556. }
  557. 1;