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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore :: Vx Interface Module
  3. #  Based on OO
  4. #  Originally By Star-Kung - http://modkore.sourceforge.net.
  5. #
  6. #  Copyright (c) 2005 OpenKore development team 
  7. #
  8. #  This program is free software; you can redistribute it and/or modify
  9. #  it under the terms of the GNU General Public License as published by
  10. #  the Free Software Foundation; either version 2 of the License, or
  11. #  (at your option) any later version.
  12. #
  13. #  This program is distributed in the hope that it will be useful,
  14. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16. #  GNU General Public License for more details.
  17. #
  18. #  $Revision: 5989 $
  19. #  $Id: Vx.pm 5989 2007-09-30 11:36:16Z vcl_kore $
  20. #
  21. #########################################################################
  22. package Interface::Vx;
  23. use strict;
  24. use warnings;
  25. use AI;
  26. use Interface;
  27. use base qw/Interface/;
  28. use Plugins;
  29. use Globals;
  30. use Field;
  31. use Settings qw(%sys);
  32. use Misc;
  33. use Utils;
  34. #use Log qw(message warning);
  35. use Carp qw/carp croak confess/;
  36. use File::Spec;
  37. use Time::HiRes qw/time usleep/;
  38. use Tk;
  39. use Tk::ROText;
  40. use Tk::BrowseEntry;
  41. # parse panelTwo_domains into a hash
  42. my %panelTwo_domains;
  43. $sys{panelTwo_domains} ||= "publicchat, pm, guildchat, partychat, pm/sent, list, info, selfchat, schat, error, warning";
  44. my @array = split / *, */, $sys{panelTwo_domains};
  45. foreach (@array) {
  46. s/^s+//;
  47. s/s+$//;
  48. s/s+/ /g;
  49. $panelTwo_domains{$_} = 1;
  50. }
  51. my $buildType = 1;
  52. # main interface functions
  53. sub new {
  54. my $class = shift;
  55. my $self = {
  56. input_list => [], # input history
  57. input_offset => 0, # position while scrolling through input history
  58. input_que => [], # queued input data
  59. default_font => "MS Sans Serif",
  60. input_type => "Command",
  61. input_pm => undef,
  62. total_lines => {"panelOne" => 0, "panelTwo" => 0},
  63. last_line_end => {"panelOne" => 0, "panelTwo" => 0},
  64. line_limit => {"panelOne" => $sys{panelOne_lineLimit} || 900, "panelTwo" => $sys{panelTwo_lineLimit} || 100},
  65. mapDir => 'map'
  66. };
  67. if ($buildType == 0) {
  68. eval "use Win32::API;";
  69. $self->{ShellExecute} = new Win32::API("shell32", "ShellExecute",
  70. "NPPPPN", "V");
  71. }
  72. bless $self, $class;
  73. $self->initTk;
  74. $self->{hooks} = Plugins::addHooks(
  75. ['mainLoop_pre', &updateHook, $self],
  76. ['postloadfiles', &resetColors, $self],
  77. ['parseMsg/pre',  &packet, $self],
  78. ['attack_start', sub { $_[2]->followObj($_[1]->{ID}); }, $self]
  79. );
  80. return $self;
  81. }
  82. sub DESTROY {
  83. my $self = shift;
  84. Plugins::delHooks($self->{hooks});
  85. }
  86. sub update {
  87. my $self = shift;
  88. $self->{mw}->update();
  89. }
  90. sub getInput {
  91. my $self = shift;
  92. my $timeout = shift;
  93. my $msg;
  94. if ($timeout < 0) {
  95. until (defined $msg) {
  96. $self->update();
  97. if (@{ $self->{input_que} }) { 
  98. $msg = shift @{ $self->{input_que} }; 
  99. }
  100. } elsif ($timeout > 0) {
  101. my $end = time + $timeout;
  102. until ($end < time || defined $msg) {
  103. $self->update();
  104. if (@{ $self->{input_que} }) { 
  105. $msg = shift @{ $self->{input_que} }; 
  106. }
  107. } else {
  108. if (@{ $self->{input_que} }) { 
  109. $msg = shift @{ $self->{input_que} }; 
  110. }
  111. $self->update();
  112. $msg =~ s/n// if defined $msg;
  113. return $msg;
  114. }
  115. sub writeOutput {
  116. my $self = shift;
  117. my $type = shift || '';
  118. my $message = shift || '';
  119. my $domain = shift || '';
  120. my $panel;
  121. # FIXME: you can put message types like error and warning in the list because I wanted to see them
  122. # FIXME: a default list of domains should be given to the user if they didn't configure any
  123. if ($panelTwo_domains{$domain} || ($domain eq 'console' && $panelTwo_domains{$type})) {
  124. $panel = "panelTwo";
  125. } else {
  126. $panel = "panelOne";
  127. }
  128. my $scroll = 0;
  129. $scroll = 1 if (($self->{$panel}->yview)[1] == 1);
  130. #keep track of lines to limit the number of lines in the text widget
  131. $self->{total_lines}{panel} += $message =~ s/r?n/n/g;
  132. $self->{$panel}->insert('end', "n") if $self->{last_line_end}{$panel};
  133. $self->{last_line_end}{$panel} = $message =~ s/n$//;
  134. $self->{$panel}->insert('end', $message, "$type $type.$domain");
  135. #remove extra lines
  136. if ($self->{total_lines}{$panel} > $self->{line_limit}{$panel}) {
  137. my $overage = $self->{total_lines}{$panel} - $self->{line_limit}{$panel};
  138. $self->{$panel}->delete('1.0', $overage+1 . ".0");
  139. $self->{total_lines}{$panel} -= $overage;
  140. }
  141. $self->{$panel}->see('end') if $scroll;
  142. }
  143. sub title {
  144. my $self = shift;
  145. my $title = shift;
  146. if (defined $title) {
  147. if (!defined $self->{currentTitle} || $self->{currentTitle} ne $title) {
  148. $self->{mw}->title($title);
  149. $self->{currentTitle} = $title;
  150. }
  151. } else {
  152. return $self->{mw}->title();
  153. }
  154. }
  155. # interface construction
  156. sub initTk {
  157. my $self = shift;
  158. my $panelFont = $sys{panelFont} || 'Verdana';
  159. my $menuFont = $sys{menuFont} || 'Lucida Console';
  160. my $sbarFont = $sys{sbarFont} || 'Arial';
  161. # create main window
  162. $self->{mw} = MainWindow->new();
  163. #$self->{mw}->minsize(316,290);
  164. $self->{mw}->protocol('WM_DELETE_WINDOW', [&OnExit, $self]);
  165. #$self->{mw}->Icon(-image=>$self->{mw}->Photo(-file=>"hyb.gif"));
  166. $self->{mw}->title($Settings::NAME);
  167. # Main window menu
  168. $self->{mw}->configure(-menu => $self->{mw}->Menu(-menuitems=>
  169. [ map 
  170. ['cascade', $_->[0], -tearoff=> 0, -font=>[-family=>$menuFont,-size=>8], -menuitems => $_->[1]],
  171. ['~OpenKore',
  172. [[qw/command E~xit  -accelerator Alt+P/, -font=>[-family=>$menuFont,-size=>8], -command=>[&OnExit, $self]],]
  173. ],
  174. ['~View',
  175. [
  176. [qw/command Map  -accelerator Alt+M/, -font=>[-family=>$menuFont,-size=>8], -command=>[&OpenMap, $self]],
  177. '',
  178. [qw/command Status -accelerator Alt+D/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("s") }],
  179. [qw/command Storage -accelerator Alt+X/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("storage") }],
  180. [qw/command Skill -accelerator Alt+S/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("skills") }],
  181. [qw/command Stat -accelerator Alt+A/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("st") }],
  182. [qw/command Exp -accelerator Alt+Z/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("exp") }],
  183. [qw/command Usable -accelerator Alt+E/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("i u") }],
  184. [qw/command Equipped -accelerator Alt+Q/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("i eq") }],
  185. [qw/command Unequipped -accelerator Alt+C/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("i neq") }],
  186. [qw/command Non-Usable -accelerator Alt+W/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("i nu") }],
  187. '',
  188. [cascade=>"Guild", -tearoff=> 0, -font=>[-family=>$menuFont,-size=>8], -menuitems =>
  189. [
  190. [qw/command Info -accelerator ALT+F/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("guild info") }],
  191. [qw/command Member -accelerator ALT+G/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("guild member") }]
  192. ],
  193. ],
  194. '',
  195. [cascade=>"Font Weight", -tearoff=> 0, -font=>[-family=>$menuFont,-size=>8], -menuitems => 
  196. [
  197. [Checkbutton  => '~Bold', -variable => $self->{is_bold},-font=>[-family=>$sbarFont,-size=>8],-command => [&change_fontWeight, $self]],
  198. ]
  199. ],
  200. ],
  201. ],
  202. ['~Reload',
  203. [
  204. [qw/command config -accelerator Ctrl+Shift+C/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("reload config") }],
  205. [qw/command mon_control  -accelerator Ctrl+Shift+W/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("reload mon_control") }],
  206. [qw/command item_control  -accelerator Ctrl+Shift+Q/, -font=>[-family=>$menuFont,-size=>8], -command => sub{ Commands::run("reload items_control") }],
  207. [qw/command cart_control  -accelerator Ctrl+Shift+E/, -font=>[-family=>$menuFont,-size=>8], -command=>sub{ Commands::run("reload avoid") }],
  208. [qw/command timeouts  -accelerator Ctrl+Shift+Z/, -font=>[-family=>$menuFont,-size=>8], -command=>sub{ Commands::run("reload timeouts") }],
  209. [qw/command pickupitems  -accelerator Ctrl+Shift+V/, -font=>[-family=>$menuFont,-size=>8], -command=>sub{ Commands::run("reload pickupitems") }],
  210. [qw/command chatresp  -accelerator Ctrl+Shift+T/, -font=>[-family=>$menuFont,-size=>8], -command=>sub{ Commands::run("reload chat_resp") }],
  211. '',
  212. [qw/command All  -accelerator Ctrl+Shift+A/, -font=>[-family=>$menuFont,-size=>8], -command=>sub{ Commands::run("reload all") }],
  213. ]
  214. ],
  215. ['~Help',
  216. [[qw/command Manual  -accelerator Alt+H/, -font=>[-family=>$menuFont,-size=>8], -command=>[&showManual, $self]],]
  217. ]
  218. ]
  219. ));
  220. # subclasses of main window
  221. # status frame
  222. $self->{status_frame} = $self->{mw}->Frame()->pack(
  223. -side => 'bottom',
  224. -expand => 0,
  225. -fill => 'x',
  226. );
  227. #------ subclass in status frame
  228. $self->{status_gen} = $self->{status_frame}->Label(
  229. -anchor => 'w',
  230. -text => 'Ready',
  231. -font => [$sbarFont, 8],
  232. -bd=>0,
  233. -relief => 'sunken',
  234. )->pack(
  235. -side => 'left',
  236. -expand => 1,
  237. -fill => 'x',
  238. );
  239. $self->{status_ai} = $self->{status_frame}->Label(
  240. -text => 'Ai - Status',
  241. -font => [$sbarFont, 8],
  242. -width => 25,
  243. -relief => 'ridge',
  244. )->pack(
  245. -side => 'left',
  246. -expand => 0,
  247. -fill => 'x',
  248. );
  249. $self->{status_posx} = $self->{status_frame}->Label(
  250. -text => '0',
  251. -font => [$sbarFont, 8],
  252. -width => 4,
  253. -relief => 'ridge',
  254. )->pack(
  255. -side => 'left',
  256. -expand => 0,
  257. -fill => 'x',
  258. );
  259. $self->{status_posy} = $self->{status_frame}->Label(
  260. -text => '0',
  261. -font => [$sbarFont, 8],
  262. -width => 4,
  263. -relief => 'ridge',
  264. )->pack(
  265. -side => 'left',
  266. -expand => 0,
  267. -fill => 'x',
  268. );
  269. # input frame
  270. $self->{input_frame} = $self->{mw}->Frame(
  271. -bg=>'black'
  272. )->pack(
  273. -side => 'bottom',
  274. -expand => 0,
  275. -fill => 'x',
  276. );
  277. #------ subclass in input frame
  278. $self->{pminput} = $self->{input_frame}->BrowseEntry(
  279. -bg=>'black',
  280. -fg=>'grey',
  281. -variable => $self->{input_pm},
  282. -width => 8,
  283. -font=>[ -family => $panelFont ,-size=>10,],
  284. -autolimitheight => 1,
  285. -state =>'normal',
  286. -relief => 'flat',
  287. )->pack(
  288. -expand=>0,
  289. -fill => 'x',
  290. -side => 'left',
  291. );
  292. $self->{input} = $self->{input_frame}->Entry(
  293. -bg => 'black',
  294. -fg => 'grey',
  295. -insertbackground => 'grey',
  296. -relief => 'sunken',
  297. -font=>[ -family => $panelFont ,-size=>10,],
  298. )->pack(
  299. -expand=>1,
  300. -fill => 'x',
  301. -side => 'left',
  302. );
  303. $self->{sinput} = $self->{input_frame}->BrowseEntry(
  304. -bg=>'black',
  305. -fg=>'grey',
  306. -disabledbackground => 'black',
  307. -disabledforeground => 'grey',
  308. -variable => $self->{input_type},
  309. -autolimitheight => 1,
  310. -listwidth => 30,
  311. -font=>[ -family => $panelFont ,-size=>10,],
  312. -width => 8,
  313. -state => 'readonly',
  314. -relief => 'flat',
  315. )->pack (
  316. -expand=>0,
  317. -fill => 'x',
  318. -side => 'left',
  319. );
  320. $self->{sinput}->insert("end", qw(Command Public Party Guild));
  321. ### panelOne and panelTwo
  322. $self->{panelOne} = $self->{mw}->Scrolled('ROText',
  323. -bg=>'black',
  324. -fg=>'grey',
  325. -scrollbars => 'e',
  326. -height => $sys{panelOne_height} || 8,
  327. -width => $sys{panelOne_width} || 60,
  328. -wrap => 'word',
  329. -insertontime => 0,
  330. -background => 'black',
  331. -foreground => 'grey',
  332. -font=>[ -family => $panelFont ,-size=>$sys{panelOne_fontsize} || 8,],
  333. -relief => 'sunken',
  334. )->pack(
  335. -expand => 1,
  336. -fill => 'both',
  337. -side => $sys{panelOne_side} || 'top',
  338. );
  339. $self->{panelTwo} = $self->{mw}->Scrolled('ROText',
  340. -bg=>'black',
  341. -fg=>'grey',
  342. -scrollbars => 'e',
  343. -height => $sys{panelTwo_height} || 4,
  344. -width => $sys{panelTwo_width} || 40,
  345. -wrap => 'word',
  346. -insertontime => 0,
  347. -background => 'black',
  348. -foreground => 'grey',
  349. -font=>[ -family => $panelFont ,-size=>$sys{panelTwo_fontsize} || 8,],
  350. -relief => 'sunken',
  351. )->pack(
  352. -expand => 1,
  353. -fill => 'both',
  354. -side => $sys{panelTwo_side} || 'top',
  355. );
  356. # button frame, removed
  357. #$self->{btn_frame} = $self->{mw}->Frame(
  358. # #-bg=>'black'
  359. #)->pack(
  360. # -side => 'right',
  361. # -expand => 0,
  362. # -fill => 'y',
  363. #);
  364. ### Binding ###
  365. $self->{mw}->bind('all', '<Alt-p>' =>  [&OnExit, $self]);
  366. $self->{mw}->bind('all', '<Alt-m>' => [&OpenMap, $self]);
  367. $self->{mw}->bind('all', '<Control-Shift-C>' => sub{ Commands::run("reload config") });
  368. $self->{mw}->bind('all', '<Control-Shift-W>' => sub{ Commands::run("reload mon_control") });
  369. $self->{mw}->bind('all', '<Control-Shift-Q>' => sub{ Commands::run("reload items_control") });
  370. $self->{mw}->bind('all', '<Control-Shift-E>' => sub{ Commands::run("reload avoid") });
  371. $self->{mw}->bind('all', '<Control-Shift-Z>' => sub{ Commands::run("reload timeouts") });
  372. $self->{mw}->bind('all', '<Control-Shift-V>' => sub{ Commands::run("reload pickupitems") });
  373. $self->{mw}->bind('all', '<Control-Shift-T>' => sub{ Commands::run("reload chat_resp") });
  374. $self->{mw}->bind('all', '<Control-Shift-A>' => sub{ Commands::run("reload all") });
  375. $self->{mw}->bind('all', '<Alt-d>' =>  sub{ Commands::run("s") });
  376. $self->{mw}->bind('all', '<Alt-x>' =>  sub{ Commands::run("storage") });
  377. $self->{mw}->bind('all', '<Alt-s>' =>  sub{ Commands::run("skills") });
  378. $self->{mw}->bind('all', '<Alt-q>' =>  sub{ Commands::run("i eq") });
  379. $self->{mw}->bind('all', '<Alt-a>' =>  sub{ Commands::run("st") });
  380. $self->{mw}->bind('all', '<Alt-e>' =>  sub{ Commands::run("i u") });
  381. $self->{mw}->bind('all', '<Alt-w>' =>  sub{ Commands::run("i nu") });
  382. $self->{mw}->bind('all', '<Alt-z>' =>  sub{ Commands::run("exp") });
  383. $self->{mw}->bind('all', '<Alt-c>' =>  sub{ Commands::run("i neq") });
  384. $self->{mw}->bind('all', '<Alt-f>' =>  sub{ Commands::run("guild info") });
  385. $self->{mw}->bind('all', '<Alt-g>' =>  sub{ Commands::run("guild member") });
  386. $self->{mw}->bind('all', '<Alt-h>' =>  [&showManual, $self]);
  387. $self->{input}->bind('<Up>' => [&inputUp, $self]);
  388. $self->{input}->bind('<Down>' => [&inputDown, $self]);
  389. $self->{input}->bind('<Return>' => [&inputEnter, $self]);
  390. $self->{input}->focus();
  391. if ($buildType == 0) {
  392. $self->{input}->bind('<MouseWheel>' => [&w32mWheel, $self, Ev('k'), "panelTwo"]);
  393. $self->{panelTwo}->bind('<MouseWheel>' => [&w32mWheel, $self, Ev('k'), "panelTwo"]);
  394. $self->{panelOne}->bind('<MouseWheel>' => [&w32mWheel, $self, Ev('k'), "panelOne"]);
  395. my $console;
  396. eval 'use Win32::Console; $console = new Win32::Console(STD_OUTPUT_HANDLE);';
  397. $console->Free();
  398. }
  399. $self->{mw}->raise();
  400. }
  401. sub inputUp {
  402. my $inputarea = shift; #this is redundant =
  403. my $self = shift;
  404. my $line;
  405. chomp($line = $self->{input}->get);
  406. unless ($self->{input_offset}) {
  407. $self->{input_list}[$self->{input_offset}] = $line;
  408. }
  409. $self->{input_offset}++;
  410. $self->{input_offset} -= $#{$self->{input_list}} + 1 while $self->{input_offset} > $#{$self->{input_list}};
  411. $self->{input}->delete('0', 'end');
  412. $self->{input}->insert('end', "$self->{input_list}[$self->{input_offset}]");
  413. }
  414. sub w32mWheel {
  415. my $action_area = shift;
  416. my $self = shift;
  417. my $zDist = shift;
  418. my $panel = shift;
  419. $self->{$panel}->yview('scroll', -int($zDist/40), "units");
  420. }
  421. sub inputDown {
  422. my $inputarea = shift; #this is redundant =
  423. my $self = shift;
  424. my $line;
  425. chomp($line = $self->{input}->get);
  426. unless ($self->{input_offset}) {
  427. $self->{input_list}[$self->{input_offset}] = $line;
  428. }
  429. $self->{input_offset}--;
  430. $self->{input_offset} += $#{$self->{input_list}} + 1 while $self->{input_offset} < 0;
  431. $self->{input}->delete('0', 'end');
  432. $self->{input}->insert('end', "$self->{input_list}[$self->{input_offset}]");
  433. }
  434. sub inputEnter {
  435. my $inputarea = shift; #this is redundant =
  436. my $self = shift;
  437. my $line;
  438. $line = $self->{input}->get;
  439. $self->{input}->delete('0', 'end');
  440. # add input to input history
  441. $self->{input_list}[0] = $line;
  442. unshift(@{$self->{input_list}}, "");
  443. # modify the input based on what modes we are using
  444. if ($line =~ /^/(.*)/) {
  445. $line = $1;
  446. } else {
  447. if ($self->{input_pm} eq "") {
  448. if ($self->{input_type} eq "Public") {
  449. $line = "c ".$line;
  450. } elsif ($self->{input_type} eq "Party"){
  451. $line = "p ".$line;
  452. } elsif ($self->{input_type} eq "Guild"){
  453. $line = "g ".$line;
  454. }
  455. } else {
  456. $self->pm_add($self->{input_pm});
  457. $line = "pm "$self->{input_pm}" $line";
  458. }
  459. }
  460. return unless defined $line;
  461. $self->{input_offset} = 0;
  462. # add to interface input queue for processing
  463. push(@{ $self->{input_que} }, $line);
  464. }
  465. sub updateHook {
  466. my $hookname = shift;
  467. my $r_args = shift;
  468. my $self = shift;
  469. return unless defined $self->{mw};
  470. $self->updatePos();
  471. $self->{mw}->update();
  472. $self->setAiText("@ai_seq");
  473. #if ($field{name} eq $config{lockMap} || !$config{lockMap}) {
  474. # $self->status_update("On Map: $field{name}");
  475. #} else {
  476. # $self->status_update("On Map: $field{name} | LockMap: $config{lockMap}");
  477. #}
  478. }
  479. sub updatePos {
  480. my $self = shift;
  481. return unless (defined $char && defined $char->{pos_to});
  482. my ($x,$y) = @{$char->{pos_to}}{'x', 'y'};
  483. $self->{status_posx}->configure( -text =>$x);
  484. $self->{status_posy}->configure( -text =>$y);
  485. if ($self->mapIsShown()) {
  486. # show player coords
  487. $self->{map}{'canvas'}->delete($self->{map}{'player'}) if ($self->{map}{'player'});
  488. $self->{map}{'player'} = $self->{map}{'canvas'}->createOval(
  489. $x-2,$self->{map}{'map'}{'y'} - $y-2,
  490. $x+2,$self->{map}{'map'}{'y'} - $y+2,
  491. ,-fill => '#ffcccc', -outline=>'#ff0000');
  492. $self->{map}{'canvas'}->delete($self->{map}{'dest'}) if ($self->{map}{'dest'});
  493. # show route destination
  494. my $action = AI::findAction("route");
  495. if (defined $action) {
  496. my $args = AI::args($action);
  497. if ($args->{dest}{map} eq $field{name}) {
  498. my ($x,$y) = @{$args->{dest}{pos}}{'x', 'y'};
  499. $self->{map}{'dest'} = $self->{map}{'canvas'}->createOval(
  500. $x-2,$self->{map}{'map'}{'y'} - $y-2,
  501. $x+2,$self->{map}{'map'}{'y'} - $y+2,
  502. ,-fill => '#0000ff', -outline=>'#ccccff');
  503. }
  504. }
  505. # show circle of attack range
  506. $self->{map}{'canvas'}->delete($self->{map}{'range'}) if ($self->{map}{'range'});
  507. my $dis = $config{'attackDistance'};
  508. $self->{map}{'range'} = $self->{map}{'canvas'}->createOval(
  509. $x-$dis,$self->{map}{'map'}{'y'} - $y-$dis,
  510. $x+$dis,$self->{map}{'map'}{'y'} - $y+$dis,
  511. ,-outline=>'#ff0000');
  512. }
  513. }
  514. sub status_update {
  515. my $self = shift;
  516. my $text = shift;
  517. $self->{status_gen}->configure(-text => $text);
  518. }
  519. sub setAiText {
  520. my $self = shift;
  521. my ($text) = shift;
  522. $self->{status_ai}->configure(-text => $text);
  523. }
  524. sub OnExit {
  525. my $self = shift;
  526. if ($conState) {
  527. push(@{ $self->{input_que} }, "n");
  528. quit();
  529. } else {
  530. exit();
  531. }
  532. }
  533. sub showManual {
  534. my $self = shift;
  535. $self->{ShellExecute}->Call(0, '', 'http://openkore.sourceforge.net/manual/', '', '', 1);
  536. }
  537. sub change_fontWeight {
  538. my $self = shift;
  539. my $panelFont = $sys{panelFont} || 'Verdana';
  540. if ($self->{is_bold}) {
  541. $self->{panelOne}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'bold']);
  542. $self->{panelTwo}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'bold']);
  543. $self->{input}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'bold']);
  544. }else{
  545. $self->{panelOne}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'normal']);
  546. $self->{panelTwo}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'normal']);
  547. $self->{input}->configure(-font=>[-family => $panelFont ,-size=>10,-weight=>'normal']);
  548. }
  549. }
  550. sub pm_add {
  551. my $self = shift;
  552. my $name = shift;
  553. $self->{pminput}->insert("end",$name) if !defined binFind($self->{pminput}->get(0,'end'), $name);
  554. }
  555. # map functions
  556. sub OpenMap {
  557. my ($self, $param2) = @_;
  558. $self = $param2 if ($param2);
  559. if (!exists $self->{map}) {
  560. undef $self->{obj};
  561. my ($x,$y);
  562. $self->{map} = $self->{mw}->Toplevel();
  563. $self->{map}->transient($self->{mw});
  564. $self->{map}->title("Map View : ".$field{name});
  565. $self->{map}->protocol('WM_DELETE_WINDOW', 
  566. sub {
  567. undef $self->{obj};
  568. $self->{map}->destroy();
  569. delete $self->{map};
  570. }
  571. );
  572. $self->{map}->resizable(0,0);
  573. $self->{map}{'canvas'} = $self->{map}->Canvas(-width => 200, -height => 200,-background => 'white')->pack(-side => 'top');
  574. $self->loadMap;
  575. $x = $self->{status_posx}->cget(-text);
  576. $y = $self->{status_posy}->cget(-text);
  577. $self->{map}{'player'} = $self->{map}{'canvas'}->createOval(
  578. $x-2,$self->{map}{'map'}{'y'} - $y-2,
  579. $x+2,$self->{map}{'map'}{'y'} - $y+2,
  580. ,-fill => '#ffcccc', -outline=>'#ff0000');
  581. my $dis = $config{'attackDistance'};
  582. $self->{map}{'range'} = $self->{map}{'canvas'}->createOval(
  583. $x-$dis,$self->{map}{'map'}{'y'} - $y-$dis,
  584. $x+$dis,$self->{map}{'map'}{'y'} - $y+$dis,
  585. ,-outline=>'#ff0000');
  586. $self->{map}->bind('<1>', [&dblchk, $self, Ev('x') , Ev('y')]);
  587. $self->{map}->bind('<Motion>', [&pointchk, $self, Ev('x') , Ev('y')]); 
  588. } else {
  589. undef $self->{obj};
  590. $self->{map}->destroy();
  591. delete $self->{map};
  592. }
  593. }
  594. # map image loader functions
  595. sub _map {
  596. my $self = shift;
  597. return File::Spec->catfile($self->{mapDir}, @_);
  598. }
  599. sub loadMap {
  600. my $self = shift;
  601. $self->{map}{'canvas'}->delete('map');
  602. $self->{map}{'canvas'}->createText(50,20,-text =>'Processing..',-tags=>'map');
  603. my $name = $field{baseName};
  604. if (-f $self->_map("$name.jpg")) {
  605. require Tk::JPEG;
  606. $self->{map}{'map'} = $self->{map}{'canvas'}->Photo(-format => 'jpeg', -file=> $self->_map("$name.jpg"));
  607. } elsif (-f $self->_map("$name.png")) {
  608. require Tk::PNG;
  609. $self->{map}{'map'} = $self->{map}{'canvas'}->Photo(-format => 'png', -file=> $self->_map("$name.png"));
  610. } elsif (-f $self->_map("$name.gif")) {
  611. $self->{map}{'map'} = $self->{map}{'canvas'}->Photo(-format => 'gif', -file=> $self->_map("$name.gif"));
  612. } elsif (-f $self->_map("$name.bmp")) {
  613. $self->{map}{'map'} = $self->{map}{'canvas'}->Bitmap(-file => $self->_map("$name.bmp"));
  614. } else {
  615. $self->{map}{'map'} = $self->{map}{'canvas'}->Photo(-format => 'xpm', -data => Utils::xpmmake($field{width}, $field{height}, $field{rawMap}));
  616. }
  617. $self->{map}{'canvas'}->delete('map');
  618. $self->{map}{'canvas'}->createImage(2,2,-image =>$self->{map}{'map'},-anchor => 'nw',-tags=>'map');
  619. $self->{map}{'canvas'}->configure(
  620. -width => $field{'width'},
  621. -height => $field{'height'}
  622. );
  623. $self->{map}{'map'}{'x'} = $field{'width'};
  624. $self->{map}{'map'}{'y'} = $field{'height'};
  625. }
  626. # mouse moving over map viewer shows coordinates
  627. sub pointchk {
  628. my $actionArea = shift;
  629. my $self = shift;
  630. my $mvcpx = $_[0];
  631. my $mvcpy = $self->{map}{'map'}{'y'} - $_[1];
  632. $self->{map}->title("Map View : ".$field{'name'}." [$mvcpx , $mvcpy]");
  633. $self->{map}->update;
  634. }
  635. # click on map viewer to move to coordinates
  636. sub dblchk {
  637. my $actionarea = shift;
  638. my $self = shift;
  639. my $mvcpx = $_[0];
  640. my $mvcpy = $self->{map}{'map'}{'y'} - $_[1];
  641. push(@{$self->{input_que}}, "move $mvcpx $mvcpy"); 
  642. sub mapIsShown {
  643. my $self = shift;
  644. return defined $self->{map};
  645. }
  646. sub addObj {
  647. my $self = shift;
  648. my ($id,$type) = @_;
  649. my ($fg,$bg);
  650. return if (!$self->mapIsShown());
  651. if ($type eq "npc") {
  652. $fg = "#ABD5BD";
  653. $bg = "#005826";
  654. }elsif ($type eq "m") {
  655. $fg = "#A9D3E3";
  656. $bg = "#0076A3";
  657. }elsif ($type eq "p") {
  658. $fg = "#FFFFCC";
  659. $bg = "#FF6600";
  660. }else {
  661. $fg = "#666666";
  662. $bg = "#FF6600";
  663. }
  664. $self->{objc}{$id}[0] = $fg;
  665. $self->{objc}{$id}[1] = $bg;
  666. }
  667. sub moveObj {
  668. my $self = shift;
  669. return if (!$self->mapIsShown());
  670. my ($id,$type,$x,$y,$newx,$newy) = @_;
  671. my $range;
  672. if ($self->{obj}{$id}){
  673. $self->{map}{'canvas'}->delete($self->{obj}{$id});
  674. } else {
  675. $self->addObj($id,$type);
  676. }
  677. if (defined $newx && defined $newy) {
  678. $x = $newx;
  679. $y = $newy;
  680. }
  681. $self->{obj}{$id} = $self->{map}{'canvas'}->createOval(
  682. $x-2,$self->{map}{'map'}{'y'} - $y-2,
  683. $x+2,$self->{map}{'map'}{'y'} - $y+2,
  684. ,-fill => $self->{objc}{$id}[0], -outline=>$self->{objc}{$id}[1]); 
  685. }
  686. sub removeObj {
  687. my $self = shift;
  688. my ($id) = shift;
  689. return if (!$self->{obj}{$id} || !$self->mapIsShown());
  690. $self->{map}{'canvas'}->delete($self->{obj}{$id});
  691. undef $self->{obj}{$id};
  692. }
  693. sub removeAllObj {
  694. my $self = shift;
  695. return if (!$self->mapIsShown());
  696. foreach (keys %{$self->{obj}}) {
  697. $self->{map}{'canvas'}->delete($self->{obj}{$_}) if ($self->{obj}{$_});
  698. undef $self->{obj}{$_};
  699. }
  700. }
  701. # FIXME: the color specified here is never used
  702. sub followObj {
  703. my $self = shift;
  704. return if (!$self->mapIsShown());
  705. my ($id, $type) = @_;
  706. $self->{objc}{$id}[0] = "#FFCCFF";
  707. $self->{objc}{$id}[1] = "#CC00CC";
  708. }
  709. # load color tags
  710. sub resetColors {
  711. my $hookname = shift;
  712. my $r_args = shift;
  713. my $self = shift;
  714. return if $hookname ne 'postloadfiles';
  715. my $colors_loaded = 0;
  716. foreach my $filehash (@{ $r_args->{files} }) {
  717. if ($filehash->{file} =~ /consolecolors.txt$/) {
  718. $colors_loaded = 1;
  719. last;
  720. }
  721. }
  722. return unless $colors_loaded;
  723. my %gdefault = (-foreground => 'grey', -background => 'black');
  724. eval {
  725. $self->{panelOne}->configure(%gdefault);
  726. $self->{panelTwo}->configure(%gdefault);
  727. $self->{input}->configure(%gdefault);
  728. $self->{pminput}->configure(%gdefault);
  729. $self->{sinput}->configure(%gdefault);
  730. };
  731. if ($@) {
  732. if ($@ =~ /unknown color name "(.*)" at/) {
  733. Log::message("Color '$1' not recognised.n");
  734. return undef if !$consoleColors{''}{'useColors'}; #don't bother throwing a lot of errors in the next section.
  735. } else {
  736. die $@;
  737. }
  738. }
  739. foreach my $type (keys %consoleColors) {
  740. next if $type eq '';
  741. my %tdefault =%gdefault;
  742. if ($consoleColors{''}{'useColors'} && $consoleColors{$type}{'default'}) {
  743. $consoleColors{$type}{'default'} =~ m|([^/]*)(?:/(.*))?|;
  744. $tdefault{-foreground} = defined($1) && $1 ne 'default' ? $1 : $gdefault{-foreground};
  745. $tdefault{-background} = defined($2) && $2 ne 'default' ? $2 : $gdefault{-background};
  746. }
  747. eval {
  748. # FIXME: loading colors for both panels is pointless
  749. $self->{panelOne}->tagConfigure($type, %tdefault);
  750. $self->{panelTwo}->tagConfigure($type, %tdefault);
  751. };
  752. if ($@) {
  753. if ($@ =~ /unknown color name "(.*)" at/) {
  754. Log::message("Color '$1' not recognised in consolecolors.txt at [$type]: default.n");
  755. } else {
  756. die $@;
  757. }
  758. }
  759. foreach my $domain (keys %{ $consoleColors{$type} }) {
  760. my %color = %tdefault;
  761. if ($consoleColors{''}{'useColors'} && $consoleColors{$type}{$domain}) {
  762. $consoleColors{$type}{$domain} =~ m|([^/]*)(?:/(.*))?|;
  763. $color{-foreground} = defined($1) && $1 ne 'default' ? $1 : $tdefault{-foreground};
  764. $color{-background} = defined($2) && $2 ne 'default' ? $2 : $tdefault{-background};
  765. }
  766. eval {
  767. # FIXME: loading colors for both panels is pointless
  768. $self->{panelOne}->tagConfigure("$type.$domain", %color);
  769. $self->{panelTwo}->tagConfigure("$type.$domain", %color);
  770. };
  771. if ($@) {
  772. if ($@ =~ /unknown color name "(.*)" at/) {
  773. Log::message("Color '$1' not recognised in consolecolors.txt at [$type]: $domain.n");
  774. } else {
  775. die $@;
  776. }
  777. }
  778. }
  779. }
  780. # FIXME: find a better spot to fix the initial window scrolling
  781. $self->{panelOne}->see('end');
  782. $self->{panelTwo}->see('end');
  783. }
  784. # packet parsing hook
  785. # maybe this should be replaced by proper hooks in the future
  786. sub packet {
  787. my $hookName = shift;
  788. my $args = shift;
  789. my $self = shift;
  790. my $switch = $args->{switch};
  791. my $msg = $args->{msg};
  792. if ($switch eq "0071") {
  793. #0071 <character ID> l <map name> 16B <ip> l <port> w 
  794. #Character selection success & map name & game IP/port
  795. my ($map_name) = substr($msg, 6, 16) =~ /([sS]*?)00/;
  796. ($map_name) = $map_name =~ /([sS]*)./;
  797. if (!$config{lockMap} || $map_name eq $config{lockMap}) {
  798. $self->status_update("On Map : $map_name");
  799. } else {
  800. $self->status_update("On Map : $map_name | LockMap : $config{lockMap}");
  801. }
  802. #} elsif ($switch eq "0073") {
  803. # #0073 <server tick> l <coordinate> 3B? 2B 
  804. # #Game connection success & server side 1ms clock & appearance position
  805. # my %pos;
  806. # makeCoords(%pos, substr($msg, 6, 3));
  807. # $self->updatePos($pos{x},$pos{y});
  808. } elsif ($switch eq "0078" || $switch eq "01D8") {
  809. #0078 <ID> l <speed> w <opt1> w <opt2> w <option> w <class> w <hair> w <weapon> w <head option bottom> w <shield> w <head option top> w <head option mid> w <hair color> w? W <head dir> w <guild> l <emblem> l <manner> w <karma> B <sex> B <X_Y_dir> 3B? B? B <sit> B <Lv> B
  810. #01d8 <ID>.l <speed>.w <opt1>.w <opt2>.w <option>.w <class>.w <hair>.w <item id1>.w <item id2>.w <head option bottom>.w <head option top>.w <head option mid>.w <hair color>.w ?.w <head dir>.w <guild>.l <emblem>.l <manner>.w <karma>.B <sex>.B <X_Y_dir>.3B ?.B ?.B <sit>.B <Lv>.B ?.B
  811. #0078 mainly is monster , portal
  812. #01D8 = npc + player for episode 4+
  813. my $ID = substr($msg, 2, 4);
  814. my $type = unpack("v*",substr($msg, 14,  2));
  815. my $pet = unpack("C*",substr($msg, 16,  1));
  816. my %coords;
  817. makeCoords(%coords, substr($msg, 46, 3));
  818. if ($jobs_lut{$type}) {
  819. if (!$players{$ID}) {
  820. $self->addObj($ID,"p");
  821. }
  822. } elsif ($type >= 1000) {
  823. if ($pet) {
  824. if ($monsters{$ID}) {
  825. $self->removeObj($ID);
  826. }
  827. } else {
  828. $self->addObj($ID,"m");
  829. }
  830. } elsif ($type < 1000) {
  831. if (!$npcs{$ID}) {
  832. $self->addObj($ID,"npc");
  833. }
  834. }
  835. $self->moveObj($ID,"un",$coords{x},$coords{y}) if ($type != 45 && !$pet);
  836. } elsif ($switch eq "0079" || $switch eq "01D9") {
  837. #0079 <ID>.l <speed>.w <opt1>.w <opt2>.w <option>.w <class>.w <hair>.w <weapon>.w <head option bottom>.w <sheild>.w <head option top>.w <head option mid>.w <hair color>.w ?.w <head dir>.w <guild>.l <emblem>.l <manner>.w <karma>.B <sex>.B <X_Y_dir>.3B ?.B ?.B <Lv>.B
  838. #01d9 <ID>.l <speed>.w <opt1>.w <opt2>.w <option>.w <class>.w <hair>.w <item id1>.w <item id2>.w.<head option bottom>.w <head option top>.w <head option mid>.w <hair color>.w ?.w <head dir>.w <guild>.l <emblem>.l <manner>.w <karma>.B <sex>.B <X_Y_dir>.3B ?.B ?.B <Lv>.B ?.B
  839. #For boiling Character inside the indicatory range of teleport and the like, it faces and is not attached Character information? 
  840. my $ID = substr($msg, 2, 4);
  841. my %coords;
  842. makeCoords(%coords, substr($msg, 46, 3));
  843. $self->moveObj($ID,"p",$coords{x},$coords{y});
  844. } elsif ($switch eq "007B" || $switch eq "01DA" || $switch eq "0086") {
  845. #007b <ID> l <speed> w <opt1> w <opt2> w <option> w <class> w <hair> w <weapon> w <head option bottom> w <server tick> l <shield> w <head option top> w <head option mid> w <hair color> w? W <head dir> w <guild> l <emblem> l <manner> w <karma> B <sex> B <X_Y_X_Y> 5B? B? B? B <Lv> B 
  846. #01da <ID>.l <speed>.w <opt1>.w <opt2>.w <option>.w <class>.w <hair>.<item id1>.w <item id2>.w <head option bottom>.w <server tick>.l <head option top>.w <head option mid>.w <hair color>.w ?.w <head dir>.w <guild>.l <emblem>.l <manner>.w <karma>.B <sex>.B <X_Y_X_Y>.5B ?.B ?.B ?.B <Lv>.B ?.B
  847. #Information of Character movement inside indicatory range
  848. my $ID = substr($msg, 2, 4);
  849. my %coordsFrom;
  850. makeCoords(%coordsFrom, substr($msg, 50, 3));
  851. my %coordsTo;
  852. makeCoords2(%coordsTo, substr($msg, 52, 3));
  853. my $type = unpack("v1",substr($msg, 14,  2));
  854. my $pet = unpack("C1",substr($msg, 16,  1));
  855. if ($jobs_lut{$type}) {
  856. if (!$players{$ID}) {
  857. $self->addObj($ID,"p");
  858. }
  859. } elsif ($type >= 1000) {
  860. if ($pet) {
  861. if ($monsters{$ID}) {
  862. $self->removeObj($ID);
  863. }
  864. } else {
  865. if (!$monsters{$ID}) {
  866. $self->addObj($ID,"m");
  867. }
  868. }
  869. }
  870. $self->moveObj($ID,"un",$coordsFrom{x},$coordsFrom{y},$coordsTo{x},$coordsTo{y});
  871. } elsif ($switch eq "007C") {
  872. #007c <ID> l <speed> w? 6w <class> w? 7w <X_Y> 3B? 2B 
  873. #Character information inside the indicatory range for NPC
  874. my $ID = substr($msg, 2, 4);
  875. my %coords;
  876. makeCoords(%coords, substr($msg, 36, 3));
  877. my $type = unpack("v*",substr($msg, 20,  2));
  878. if ($jobs_lut{$type}) {
  879. $self->addObj($ID,"p");
  880. } elsif ($type >= 1000) {
  881. $self->addObj($ID,"m");
  882. }
  883. $self->moveObj($ID,"un",$coords{x},$coords{y});
  884. } elsif ($switch eq "0080") {
  885. #0080 <ID> l <type> B
  886. #Character Status (include other)
  887. my $ID = substr($msg, 2, 4);
  888. $self->removeObj($ID);
  889. #} elsif ($switch eq "0087") {
  890. # #0087 <server tick> l <X_Y_X_Y> 5B? B 
  891. # #Movement response 
  892. # my %coordsFrom;
  893. # makeCoords(%coordsFrom, substr($msg, 6, 3));
  894. # my %coordsTo;
  895. # makeCoords2(%coordsTo, substr($msg, 8, 3));
  896. # $self->updatePos($coordsTo{x},$coordsTo{y});
  897. } elsif ($switch eq "0091") {
  898. #0091 <map name> 16B <X> w <Y> w 
  899. #Business such as movement, teleport and fly between maps inside 
  900. my ($map_name) = substr($msg, 2, 16) =~ /([sS]*?)00/;
  901. ($map_name) = $map_name =~ /([sS]*)./;
  902. if ($map_name ne $field{name}) {
  903. eval {
  904. $field = new Field(name => $map_name);
  905. if (!$config{lockMap} || $map_name eq $config{lockMap}) {
  906. $self->status_update("On Map : $map_name");
  907. } else {
  908. $self->status_update("On Map : $map_name | LockMap : $config{lockMap}");
  909. }
  910. $self->loadMap() if ($self->mapIsShown());
  911. };
  912. if ($@) {
  913. undef $field;
  914. }
  915. }
  916. #my %coords;
  917. #$coords{x} = unpack("v1", substr($msg, 18, 2));
  918. #$coords{y} = unpack("v1", substr($msg, 20, 2));
  919. #$self->updatePos($coords{x},$coords{y});
  920. $self->removeAllObj();
  921. } elsif ($switch eq "0092") {
  922. #0092 <map name> 16B <X> w <Y> w <IP> l <port> w 
  923. #Movement between
  924. my ($map_name) = substr($msg, 2, 16) =~ /([sS]*?)00/;
  925. ($map_name) = $map_name =~ /([sS]*)./;
  926. if ($map_name ne $field{'name'}) {
  927. eval {
  928. $field = new Field(name => $map_name);
  929. $self->loadMap() if ($self->mapIsShown());
  930. $self->removeAllObj();
  931. if (!$config{lockMap} || $map_name eq $config{lockMap}) {
  932. $self->status_update("On Map : $map_name");
  933. } else {
  934. $self->status_update("On Map : $map_name | LockMap : $config{lockMap}");
  935. }
  936. };
  937. if ($@) {
  938. undef $field;
  939. }
  940. }
  941. } elsif ($switch eq "0097") {
  942. # Private message
  943. my $msg_size = length($msg);
  944. my $newmsg;
  945. main::decrypt($newmsg, substr($msg, 28, length($msg)-28));
  946. $msg = substr($msg, 0, 28) . $newmsg;
  947. my ($privMsgUser) = substr($msg, 4, 24) =~ /([sS]*?)00/;
  948. my $privMsg = substr($msg, 28, $msg_size - 29);
  949. $self->pm_add($privMsgUser);
  950. } elsif ($switch eq "01A4") {
  951. #01a4 < type >.B < ID >.l < val >.l 
  952. #pet spawn
  953. my $ID = substr($msg, 3, 4);
  954. if ($monsters{$ID}) {
  955. $self->removeObj($ID);
  956. }
  957. }
  958. }
  959. 1;