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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - WxWidgets Interface
  3. #  You need:
  4. #  * WxPerl (the Perl bindings for WxWidgets) - http://wxperl.sourceforge.net/
  5. #
  6. #  More information about WxWidgets here: http://www.wxwidgets.org/
  7. #
  8. #  Copyright (c) 2004,2005,2006,2007 OpenKore development team
  9. #
  10. #  This program is free software; you can redistribute it and/or modify
  11. #  it under the terms of the GNU General Public License as published by
  12. #  the Free Software Foundation; either version 2 of the License, or
  13. #  (at your option) any later version.
  14. #
  15. #  This program is distributed in the hope that it will be useful,
  16. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  18. #  GNU General Public License for more details.
  19. #
  20. #  $Revision: 6763 $
  21. #  $Id: Wx.pm 6763 2009-07-08 16:42:59Z eternalharvest $
  22. #
  23. #########################################################################
  24. package Interface::Wx;
  25. # Note: don't use wxTimer for anything important. It's known to cause reentrancy issues!
  26. BEGIN {
  27. require Wx::Perl::Packager if ($^O eq 'MSWin32');
  28. }
  29. use strict;
  30. use Wx ':everything';
  31. use Wx::Event qw(EVT_CLOSE EVT_MENU EVT_MENU_OPEN EVT_LISTBOX_DCLICK
  32. EVT_CHOICE EVT_TIMER EVT_TASKBAR_LEFT_DOWN EVT_KEY_DOWN
  33. EVT_BUTTON);
  34. use Time::HiRes qw(time sleep);
  35. use File::Spec;
  36. use FindBin qw($RealBin);
  37. use Globals;
  38. use Interface;
  39. use base qw(Wx::App Interface);
  40. use Modules;
  41. use Field;
  42. use Interface::Wx::Dock;
  43. use Interface::Wx::MapViewer;
  44. use Interface::Wx::LogView;
  45. use Interface::Wx::Console;
  46. use Interface::Wx::Input;
  47. use Interface::Wx::ItemList;
  48. use Interface::Wx::DockNotebook;
  49. use Interface::Wx::PasswordDialog;
  50. use AI;
  51. use Settings qw(%sys);
  52. use Plugins;
  53. use Misc;
  54. use Commands;
  55. use Utils;
  56. our $CVS;
  57. our ($iterationTime, $updateUITime, $updateUITime2);
  58. sub OnInit {
  59. my $self = shift;
  60. $CVS = ($Settings::SVN =~ /SVN/);
  61. $self->createInterface;
  62. $self->iterate;
  63. my $onChat = sub { $self->onChatAdd(@_); };
  64. $self->{hooks} = Plugins::addHooks(
  65. ['loadfiles',               sub { $self->onLoadFiles(@_); }],
  66. ['postloadfiles',           sub { $self->onLoadFiles(@_); }],
  67. ['parseMsg/addPrivMsgUser', sub { $self->onAddPrivMsgUser(@_); }],
  68. ['initialized',             sub { $self->onInitialized(@_); }],
  69. ['ChatQueue::add',          $onChat],
  70. ['packet_selfChat',         $onChat],
  71. ['packet_privMsg',          $onChat],
  72. ['packet_sentPM',           $onChat],
  73. ['mainLoop_pre',            sub { $self->onUpdateUI(); }]
  74. );
  75. $self->{history} = [];
  76. $self->{historyIndex} = -1;
  77. $self->{frame}->Update;
  78. return 1;
  79. }
  80. sub DESTROY {
  81. my $self = shift;
  82. Plugins::delHooks($self->{hooks});
  83. }
  84. ######################
  85. ## METHODS
  86. ######################
  87. sub mainLoop {
  88. my ($self) = @_;
  89. my $timer = new Wx::Timer($self, 246);
  90. # Start the real main loop in 100 msec, so that the UI has
  91. # the chance to layout correctly.
  92. EVT_TIMER($self, 246, sub { $self->realMainLoop(); });
  93. $timer->Start(100, 1);
  94. $self->MainLoop;
  95. }
  96. sub realMainLoop {
  97. my ($self) = @_;
  98. my $timer = new Wx::Timer($self, 247);
  99. my $sleepTime = $config{sleepTime};
  100. my $quitting;
  101. my $sub = sub {
  102. return if ($quitting);
  103. if ($quit) {
  104. $quitting = 1;
  105. $self->ExitMainLoop;
  106. $timer->Stop;
  107. return;
  108. } elsif ($self->{iterating}) {
  109. return;
  110. }
  111. $self->{iterating}++;
  112. if ($sleepTime ne $config{sleepTime}) {
  113. $sleepTime = $config{sleepTime};
  114. $timer->Start(($sleepTime / 1000) > 0
  115. ? ($sleepTime / 1000)
  116. : 10);
  117. }
  118. main::mainLoop();
  119. $self->{iterating}--;
  120. };
  121. EVT_TIMER($self, 247, $sub);
  122. $timer->Start(($sleepTime / 1000) > 0
  123. ? ($sleepTime / 1000)
  124. : 10);
  125. }
  126. sub iterate {
  127. my $self = shift;
  128. if ($self->{iterating} == 0) {
  129. $self->{console}->Refresh;
  130. $self->{console}->Update;
  131. }
  132. $self->Yield();
  133. $iterationTime = time;
  134. }
  135. sub getInput {
  136. my $self = shift;
  137. my $timeout = shift;
  138. my $msg;
  139. if ($timeout < 0) {
  140. while (!defined $self->{input} && !$quit) {
  141. $self->iterate;
  142. sleep 0.01;
  143. }
  144. $msg = $self->{input};
  145. } elsif ($timeout == 0) {
  146. $msg = $self->{input};
  147. } else {
  148. my $begin = time;
  149. until (defined $self->{input} || time - $begin > $timeout || $quit) {
  150. $self->iterate;
  151. sleep 0.01;
  152. }
  153. $msg = $self->{input};
  154. }
  155. undef $self->{input};
  156. undef $msg if (defined($msg) && $msg eq "");
  157. # Make sure we update the GUI. This is to work around the effect
  158. # of functions that block for a while
  159. $self->iterate if (timeOut($iterationTime, 0.05));
  160. return $msg;
  161. }
  162. sub query {
  163. my $self = shift;
  164. my $message = shift;
  165. my %args = @_;
  166. $args{title} = "Query" if (!defined $args{title});
  167. $args{cancelable} = 1 if (!exists $args{cancelable});
  168. $message = wrapText($message, 70);
  169. $message =~ s/n$//s;
  170. my $dialog;
  171. if ($args{isPassword}) {
  172. # WxPerl doesn't support wxPasswordEntryDialog :(
  173. $dialog = new Interface::Wx::PasswordDialog($self->{frame}, $message, $args{title});
  174. } else {
  175. $dialog = new Wx::TextEntryDialog($self->{frame}, $message, $args{title});
  176. }
  177. while (1) {
  178. my $result;
  179. if ($dialog->ShowModal == wxID_OK) {
  180. $result = $dialog->GetValue;
  181. }
  182. if (!defined($result) || $result eq '') {
  183. if ($args{cancelable}) {
  184. $dialog->Destroy;
  185. return undef;
  186. }
  187. } else {
  188. $dialog->Destroy;
  189. return $result;
  190. }
  191. }
  192. }
  193. sub showMenu {
  194. my $self = shift;
  195. my $message = shift;
  196. my $choices = shift;
  197. my %args = @_;
  198. $args{title} = "Menu" if (!defined $args{title});
  199. $args{cancelable} = 1 if (!exists $args{cancelable});
  200. $message = wrapText($message, 70);
  201. $message =~ s/n$//s;
  202. my $dialog = new Wx::SingleChoiceDialog($self->{frame},
  203. $message, $args{title}, $choices);
  204. while (1) {
  205. my $result;
  206. if ($dialog->ShowModal == wxID_OK) {
  207. $result = $dialog->GetSelection;
  208. }
  209. if (!defined($result)) {
  210. if ($args{cancelable}) {
  211. $dialog->Destroy;
  212. return -1;
  213. }
  214. } else {
  215. $dialog->Destroy;
  216. return $result;
  217. }
  218. }
  219. }
  220. sub writeOutput {
  221. my $self = shift;
  222. $self->{console}->add(@_);
  223. # Make sure we update the GUI. This is to work around the effect
  224. # of functions that block for a while
  225. $self->iterate if (timeOut($iterationTime, 0.05));
  226. }
  227. sub title {
  228. my $self = shift;
  229. my $title = shift;
  230. if (defined $title) {
  231. if ($title ne $self->{title}) {
  232. $self->{frame}->SetTitle($title);
  233. $self->{title} = $title;
  234. }
  235. } else {
  236. return $self->{title};
  237. }
  238. }
  239. sub displayUsage {
  240. my $self = shift;
  241. my $text = shift;
  242. print $text;
  243. }
  244. sub errorDialog {
  245. my $self = shift;
  246. my $msg = shift;
  247. my $fatal = shift;
  248. my $title = ($fatal) ? "Fatal error" : "Error";
  249. $self->{iterating}++;
  250. Wx::MessageBox($msg, "$title - $Settings::NAME", wxICON_ERROR, $self->{frame});
  251. $self->{iterating}--;
  252. }
  253. sub beep {
  254. Wx::Bell();
  255. }
  256. #########################
  257. ## INTERFACE CREATION
  258. #########################
  259. sub createInterface {
  260. my $self = shift;
  261. ### Main window
  262. my $frame = $self->{frame} = new Wx::Frame(undef, -1, $Settings::NAME);
  263. $self->{title} = $frame->GetTitle();
  264. ### Menu bar
  265. $self->createMenuBar;
  266. ### Vertical box sizer
  267. my $vsizer = $self->{vsizer} = new Wx::BoxSizer(wxVERTICAL);
  268. $frame->SetSizer($vsizer);
  269. ### Horizontal panel with HP/SP/Exp box
  270. $self->createInfoPanel;
  271. ## Splitter with console and another splitter
  272. my $splitter = new Wx::SplitterWindow($frame, 928, wxDefaultPosition, wxDefaultSize,
  273. wxSP_LIVE_UPDATE);
  274. $self->{splitter} = $splitter;
  275. $vsizer->Add($splitter, 1, wxGROW);
  276. # $splitter->SetMinimumPaneSize(50);
  277. $self->createSplitterContent;
  278. ### Input field
  279. $self->createInputField;
  280. ### Status bar
  281. my $statusbar = $self->{statusbar} = new Wx::StatusBar($frame, -1, wxST_SIZEGRIP);
  282. $statusbar->SetFieldsCount(3);
  283. $statusbar->SetStatusWidths(-1, 65, 175);
  284. $frame->SetStatusBar($statusbar);
  285. #################
  286. $frame->SetSizeHints(300, 250);
  287. $frame->SetClientSize(730, 400);
  288. $frame->SetIcon(Wx::GetWxPerlIcon);
  289. $frame->Show(1);
  290. EVT_CLOSE($frame, &onClose);
  291. # For some reason the input box doesn't get focus even if
  292. # I call SetFocus(), so do it in 100 msec.
  293. # And the splitter window's sash position is placed incorrectly
  294. # if I call SetSashGravity immediately.
  295. my $timer = new Wx::Timer($self, 73289);
  296. EVT_TIMER($self, 73289, sub {
  297. $self->{inputBox}->SetFocus;
  298. $self->{notebook}->switchPage('Console');
  299. # $splitter->SetSashGravity(1);
  300. });
  301. $timer->Start(500, 1);
  302. # Hide console on Win32
  303. if ($^O eq 'MSWin32' && $sys{wxHideConsole}) {
  304. eval 'use Win32::Console; Win32::Console->new(STD_OUTPUT_HANDLE)->Free();';
  305. }
  306. }
  307. sub createMenuBar {
  308. my $self = shift;
  309. my $menu = $self->{menu} = new Wx::MenuBar;
  310. my $frame = $self->{frame};
  311. $frame->SetMenuBar($menu);
  312. EVT_MENU_OPEN($self->{frame}, sub { $self->onMenuOpen; });
  313. # Program menu
  314. my $opMenu = new Wx::Menu;
  315. $self->{mPause}  = $self->addMenu($opMenu, '&Pause Botting', &onDisableAI, 'Pause all automated botting activity');
  316. $self->{mManual} = $self->addMenu($opMenu, '&Manual Botting', &onManualAI, 'Pause automated botting and allow manual control');
  317. $self->{mResume} = $self->addMenu($opMenu, '&Automatic Botting', &onEnableAI, 'Resume all automated botting activity');
  318. $opMenu->AppendSeparator;
  319. $self->addMenu($opMenu, 'Copy Last 100 Lines of Text', &onCopyLastOutput);
  320. $self->addMenu($opMenu, 'Minimize to &Tray', &onMinimizeToTray, 'Minimize to a small task bar tray icon');
  321. $opMenu->AppendSeparator;
  322. $self->addMenu($opMenu, 'E&xit Ctrl-W', &quit, 'Exit this program');
  323. $menu->Append($opMenu, 'P&rogram');
  324. # Info menu
  325. my $infoMenu = new Wx::Menu;
  326. $self->addMenu($infoMenu, '&Status Alt-S', sub { Commands::run("s"); });
  327. $self->addMenu($infoMenu, 'S&tatistics', sub { Commands::run("st"); });
  328. $self->addMenu($infoMenu, '&Inventory Alt-I', sub { Commands::run("i"); });
  329. $self->addMenu($infoMenu, 'S&kills', sub { Commands::run("skills"); });
  330. $infoMenu->AppendSeparator;
  331. $self->addMenu($infoMenu, '&Players Alt-P', sub { Commands::run("pl"); });
  332. $self->addMenu($infoMenu, '&Monsters Alt-M', sub { Commands::run("ml"); });
  333. $self->addMenu($infoMenu, '&NPCs', sub { Commands::run("nl"); });
  334. $infoMenu->AppendSeparator;
  335. $self->addMenu($infoMenu, '&Experience Report Alt+E', sub { Commands::run("exp"); });
  336. $menu->Append($infoMenu, 'I&nfo');
  337. # View menu
  338. my $viewMenu = $self->{viewMenu} = new Wx::Menu;
  339. $self->addMenu($viewMenu,
  340. '&Map Ctrl-M', &onMapToggle, 'Show where you are on the current map');
  341. $self->{infoBarToggle} = $self->addCheckMenu($viewMenu,
  342. '&Info Bar', &onInfoBarToggle, 'Show or hide the information bar.');
  343. $self->{chatLogToggle} = $self->addCheckMenu($viewMenu,
  344. 'Chat &Log', &onChatLogToggle, 'Show or hide the chat log.');
  345. $viewMenu->AppendSeparator;
  346. $self->addMenu($viewMenu,
  347. '&Font...', &onFontChange, 'Change console font');
  348. $viewMenu->AppendSeparator;
  349. $self->addMenu($viewMenu, 'Clear Console', sub {my $self = shift; $self->{console}->Remove(0, 40000)}, 'Clear content of console');
  350. $menu->Append($viewMenu, '&View');
  351. # Settings menu
  352. my $settingsMenu = new Wx::Menu;
  353. $self->createSettingsMenu($settingsMenu) if ($self->can('createSettingsMenu'));
  354. $self->addMenu($settingsMenu, '&Advanced...', &onAdvancedConfig, 'Edit advanced configuration options.');
  355. $menu->Append($settingsMenu, '&Settings');
  356. $self->createSettingsMenu2($settingsMenu) if ($self->can('createSettingsMenu2'));
  357. # Help menu
  358. my $helpMenu = new Wx::Menu();
  359. $self->addMenu($helpMenu, '&Manual F1', &onManual, 'Read the manual');
  360. $self->addMenu($helpMenu, '&Forum Shift-F1', &onForum, 'Visit the forum');
  361. $self->createHelpMenu($helpMenu) if ($self->can('createHelpMenu'));
  362. $menu->Append($helpMenu, '&Help');
  363. }
  364. sub createInfoPanel {
  365. my $self = shift;
  366. my $frame = $self->{frame};
  367. my $vsizer = $self->{vsizer};
  368. my $infoPanel = $self->{infoPanel} = new Wx::Panel($frame, -1);
  369. my $hsizer = new Wx::BoxSizer(wxHORIZONTAL);
  370. my $label = new Wx::StaticText($infoPanel, -1, "HP: ");
  371. $hsizer->Add($label, 0, wxLEFT, 3);
  372. ## HP
  373. my $hpBar = $self->{hpBar} = new Wx::Gauge($infoPanel, -1, 100,
  374. wxDefaultPosition, [0, $label->GetBestSize->GetHeight + 2],
  375. wxGA_HORIZONTAL | wxGA_SMOOTH);
  376. $hsizer->Add($hpBar, 1, wxRIGHT, 8);
  377. $label = new Wx::StaticText($infoPanel, -1, "SP: ");
  378. $hsizer->Add($label, 0);
  379. ## SP
  380. my $spBar = $self->{spBar} = new Wx::Gauge($infoPanel, -1, 100,
  381. wxDefaultPosition, [0, $label->GetBestSize->GetHeight + 2],
  382. wxGA_HORIZONTAL | wxGA_SMOOTH);
  383. $hsizer->Add($spBar, 1, wxRIGHT, 8);
  384. $label = new Wx::StaticText($infoPanel, -1, "Exp: ");
  385. $hsizer->Add($label, 0);
  386. ## Exp and job exp
  387. my $expBar = $self->{expBar} = new Wx::Gauge($infoPanel, -1, 100,
  388. wxDefaultPosition, [0, $label->GetBestSize->GetHeight + 2],
  389. wxGA_HORIZONTAL | wxGA_SMOOTH);
  390. $hsizer->Add($expBar, 1);
  391. my $jobExpBar = $self->{jobExpBar} = new Wx::Gauge($infoPanel, -1, 100,
  392. wxDefaultPosition, [0, $label->GetBestSize->GetHeight + 2],
  393. wxGA_HORIZONTAL | wxGA_SMOOTH);
  394. $hsizer->Add($jobExpBar, 1, wxRIGHT, 8);
  395. $label = new Wx::StaticText($infoPanel, -1, "Weight: ");
  396. $hsizer->Add($label, 0);
  397. ## Weight
  398. my $weightBar = $self->{weightBar} = new Wx::Gauge($infoPanel, -1, 100,
  399. wxDefaultPosition, [0, $label->GetBestSize->GetHeight + 2],
  400. wxGA_HORIZONTAL | wxGA_SMOOTH);
  401. $hsizer->Add($weightBar, 1);
  402. $infoPanel->SetSizerAndFit($hsizer);
  403. $vsizer->Add($infoPanel, 0, wxGROW);
  404. }
  405. sub createInputField {
  406. my $self = shift;
  407. my $vsizer = $self->{vsizer};
  408. my $frame = $self->{frame};
  409. my $hsizer = new Wx::BoxSizer(wxHORIZONTAL);
  410. $vsizer->Add($hsizer, 0, wxGROW);
  411. my $targetBox = $self->{targetBox} = new Wx::ComboBox($frame, -1, "", wxDefaultPosition,
  412. [115, 0]);
  413. $targetBox->SetName('targetBox');
  414. $hsizer->Add($targetBox, 0, wxGROW);
  415. EVT_KEY_DOWN($self, &onTargetBoxKeyDown);
  416. my $inputBox = $self->{inputBox} = new Interface::Wx::Input($frame);
  417. $inputBox->onEnter($self, &onInputEnter);
  418. $hsizer->Add($inputBox, 1, wxGROW);
  419. my $choice = $self->{inputType} = new Wx::Choice($frame, 456, wxDefaultPosition, wxDefaultSize,
  420. ['Command', 'Public chat', 'Party chat', 'Guild chat']);
  421. $choice->SetSelection(0);
  422. EVT_CHOICE($self, 456, sub { $inputBox->SetFocus; });
  423. $hsizer->Add($choice, 0, wxGROW);
  424. }
  425. sub createSplitterContent {
  426. my $self = shift;
  427. my $splitter = $self->{splitter};
  428. my $frame = $self->{frame};
  429. ## Dockable notebook with console and chat log
  430. my $notebook = $self->{notebook} = new Interface::Wx::DockNotebook($splitter, -1);
  431. $notebook->SetName('notebook');
  432. my $page = $notebook->newPage(0, 'Console');
  433. my $console = $self->{console} = new Interface::Wx::Console($page);
  434. $page->set($console);
  435. $page = $notebook->newPage(1, 'Chat Log', 0);
  436. my $chatLog = $self->{chatLog} = new Interface::Wx::LogView($page);
  437. $page->set($chatLog);
  438. $chatLog->addColor("selfchat", 0, 148, 0);
  439. $chatLog->addColor("pm", 142, 120, 0);
  440. $chatLog->addColor("p", 164, 0, 143);
  441. $chatLog->addColor("g", 0, 177, 108);
  442. $chatLog->addColor("warning", 214, 93, 0);
  443. ## Parallel to the notebook is another sub-splitter
  444. my $subSplitter = new Wx::SplitterWindow($splitter, 583,
  445. wxDefaultPosition, wxDefaultSize, wxSP_LIVE_UPDATE);
  446. ## Inside this splitter is a player/monster/item list, and a dock with map viewer
  447. my $itemList = $self->{itemList} = new Interface::Wx::ItemList($subSplitter);
  448. $itemList->onActivate(&onItemListActivate, $self);
  449. $self->customizeItemList($itemList) if ($self->can('customizeItemList'));
  450. $subSplitter->Initialize($itemList);
  451. # Dock
  452. my $mapDock = $self->{mapDock} = new Interface::Wx::Dock($subSplitter, -1, 'Map');
  453. $mapDock->Show(0);
  454. $mapDock->setHideFunc($self, sub {
  455. $subSplitter->Unsplit($mapDock);
  456. $mapDock->Show(0);
  457. $self->{inputBox}->SetFocus;
  458. });
  459. $mapDock->setShowFunc($self, sub {
  460. $subSplitter->SplitVertically($itemList, $mapDock, -$mapDock->GetBestSize->GetWidth);
  461. $mapDock->Show(1);
  462. $self->{inputBox}->SetFocus;
  463. });
  464. # Map viewer
  465. my $mapView = $self->{mapViewer} = new Interface::Wx::MapViewer($mapDock);
  466. $mapDock->setParentFrame($frame);
  467. $mapDock->set($mapView);
  468. # vcl code  $mapView->onMouseMove($self, &onMapMouseMove);
  469. # vcl code  $mapView->onClick->add($self, &onMapClick);
  470. # vcl code  $mapView->onMapChange($self, &onMap_MapChange, $mapDock);
  471. $mapView->onMouseMove(&onMapMouseMove, $self);
  472. $mapView->onClick(&onMapClick, $self);
  473. $mapView->onMapChange(&onMap_MapChange, $mapDock);
  474. $mapView->parsePortals(Settings::getTableFilename("portals.txt"));
  475. if ($field && $char) {
  476. $mapView->set($field->name(), $char->{pos_to}{x}, $char->{pos_to}{y}, $field);
  477. }
  478. my $position;
  479. if (Wx::wxMSW()) {
  480. $position = 600;
  481. } else {
  482. $position = 545;
  483. }
  484. $splitter->SplitVertically($notebook, $subSplitter, $position);
  485. }
  486. sub addMenu {
  487. my ($self, $menu, $label, $callback, $help) = @_;
  488. $self->{menuIDs}++;
  489. my $item = new Wx::MenuItem(undef, $self->{menuIDs}, $label, $help);
  490. $menu->Append($item);
  491. EVT_MENU($self->{frame}, $self->{menuIDs}, sub { $callback->($self); });
  492. return $item;
  493. }
  494. sub addCheckMenu {
  495. my ($self, $menu, $label, $callback, $help) = @_;
  496. $self->{menuIDs}++;
  497. my $item = new Wx::MenuItem(undef, $self->{menuIDs}, $label, $help, wxITEM_CHECK);
  498. $menu->Append($item);
  499. EVT_MENU($self->{frame}, $self->{menuIDs}, sub { $callback->($self); }) if ($callback);
  500. return $item;
  501. }
  502. ##########################
  503. ## INTERFACE UPDATING
  504. ##########################
  505. sub onUpdateUI {
  506. my $self = shift;
  507. if (timeOut($updateUITime, 0.15)) {
  508. $self->updateStatusBar;
  509. $self->updateMapViewer;
  510. $updateUITime = time;
  511. }
  512. if (timeOut($updateUITime2, 0.35)) {
  513. $self->updateItemList;
  514. $updateUITime2 = time;
  515. }
  516. }
  517. sub updateStatusBar {
  518. my $self = shift;
  519. my ($statText, $xyText, $aiText) = ('', '', '');
  520. if ($self->{loadingFiles}) {
  521. $statText = sprintf("Loading files... %.0f%%", $self->{loadingFiles}{percent} * 100);
  522. } elsif (!$conState) {
  523. $statText = "Initializing...";
  524. } elsif ($conState == 1) {
  525. $statText = "Not connected";
  526. } elsif ($conState > 1 && $conState < 5) {
  527. $statText = "Connecting...";
  528. } elsif ($self->{mouseMapText}) {
  529. $statText = $self->{mouseMapText};
  530. }
  531. if ($conState == 5) {
  532. $xyText = "$char->{pos_to}{x}, $char->{pos_to}{y}";
  533. if ($AI) {
  534. if (@ai_seq) {
  535. my @seqs = @ai_seq;
  536. foreach (@seqs) {
  537. s/^route_//;
  538. s/_/ /g;
  539. s/([a-z])([A-Z])/$1 $2/g;
  540. $_ = lc $_;
  541. }
  542. substr($seqs[0], 0, 1) = uc substr($seqs[0], 0, 1);
  543. $aiText = join(', ', @seqs);
  544. } else {
  545. $aiText = "";
  546. }
  547. } else {
  548. $aiText = "Paused";
  549. }
  550. }
  551. # Only set status bar text if it has changed
  552. my $i = 0;
  553. my $setStatus = sub {
  554. if (defined $_[1] && $self->{$_[0]} ne $_[1]) {
  555. $self->{$_[0]} = $_[1];
  556. $self->{statusbar}->SetStatusText($_[1], $i);
  557. }
  558. $i++;
  559. };
  560. $setStatus->('statText', $statText);
  561. $setStatus->('xyText', $xyText);
  562. $setStatus->('aiText', $aiText);
  563. }
  564. sub updateMapViewer {
  565. my $self = shift;
  566. my $map = $self->{mapViewer};
  567. return unless ($map && $field && $char);
  568. my $myPos;
  569. $myPos = calcPosition($char);
  570. $map->set($field->name(), $myPos->{x}, $myPos->{y}, $field);
  571. my $i = AI::findAction("route");
  572. my $args;
  573. if (defined $i && ($args = AI::args($i)) && $args->{dest} && $args->{dest}{pos}) {
  574. $map->setDest($args->{dest}{pos}{x}, $args->{dest}{pos}{y});
  575. } else {
  576. $map->setDest;
  577. }
  578. my @players = values %players;
  579. $map->setPlayers(@players);
  580. my @monsters = values %monsters;
  581. $map->setMonsters(@monsters);
  582. my @npcs = values %npcs;
  583. $map->setNPCs(@npcs);
  584. my @slaves = values %slaves;
  585. $map->setSlaves(@slaves);
  586. $map->update;
  587. $self->{mapViewTimeout}{time} = time;
  588. }
  589. sub updateItemList {
  590. my $self = shift;
  591. if ($conState == 5) {
  592. $self->{hpBar}->SetValue($char->{hp} / $char->{hp_max} * 100) if ($char->{hp_max});
  593. $self->{spBar}->SetValue($char->{sp} / $char->{sp_max} * 100) if ($char->{sp_max});
  594. $self->{expBar}->SetValue($char->{exp} / $char->{exp_max} * 100) if ($char->{exp_max});
  595. $self->{jobExpBar}->SetValue($char->{exp_job} / $char->{exp_job_max} * 100) if ($char->{exp_job_max});
  596. $self->{weightBar}->SetValue($char->{weight} / $char->{weight_max} * 100) if ($char->{weight_max});
  597. }
  598. }
  599. ##################
  600. ## Callbacks
  601. ##################
  602. sub onInputEnter {
  603. my $self = shift;
  604. my $text = shift;
  605. my $command;
  606. my $n = $self->{inputType}->GetSelection;
  607. if ($n == 0 || $text =~ /^/(.*)/) {
  608. my $command = ($n == 0) ? $text : $1;
  609. $self->{console}->add("input", "$commandn");
  610. $self->{inputBox}->Remove(0, -1);
  611. $self->{input} = $command;
  612. return;
  613. }
  614. if ($conState != 5) {
  615. $self->{console}->add("error", "You're not logged in.n");
  616. return;
  617. }
  618. if ($self->{targetBox}->GetValue ne "") {
  619. sendMessage($messageSender, "pm", $text, $self->{targetBox}->GetValue);
  620. } elsif ($n == 1) { # Public chat
  621. sendMessage($messageSender, "c", $text);
  622. } elsif ($n == 2) { # Party chat
  623. sendMessage($messageSender, "p", $text);
  624. } else { # Guild chat
  625. sendMessage($messageSender, "g", $text);
  626. }
  627. }
  628. sub onMenuOpen {
  629. my $self = shift;
  630. $self->{mPause}->Enable($AI);
  631. $self->{mManual}->Enable($AI != 1);
  632. $self->{mResume}->Enable($AI != 2);
  633. $self->{infoBarToggle}->Check($self->{infoPanel}->IsShown);
  634. $self->{chatLogToggle}->Check(defined $self->{notebook}->hasPage('Chat Log') ? 1 : 0);
  635. }
  636. sub onLoadFiles {
  637. my ($self, $hook, $param) = @_;
  638. if ($hook eq 'loadfiles') {
  639. $self->{loadingFiles}{percent} = $param->{current} / scalar(@{$param->{files}});
  640. } else {
  641. delete $self->{loadingFiles};
  642. }
  643. }
  644. sub onEnableAI {
  645. $AI = 2;
  646. }
  647. sub onManualAI {
  648. $AI = 1;
  649. }
  650. sub onDisableAI {
  651. $AI = 0;
  652. }
  653. sub onCopyLastOutput {
  654. my ($self) = @_;
  655. $self->{console}->copyLastLines(100);
  656. }
  657. sub onMinimizeToTray {
  658. my $self = shift;
  659. my $tray = new Wx::TaskBarIcon;
  660. my $title = ($char) ? "$char->{name} - $Settings::NAME" : "$Settings::NAME";
  661. $tray->SetIcon(Wx::GetWxPerlIcon, $title);
  662. EVT_TASKBAR_LEFT_DOWN($tray, sub {
  663. $tray->RemoveIcon;
  664. undef $tray;
  665. $self->{frame}->Show(1);
  666. });
  667. $self->{frame}->Show(0);
  668. }
  669. sub onClose {
  670. my ($self, $event) = @_;
  671. quit();
  672. if ($event->CanVeto) {
  673. $self->Show(0);
  674. }
  675. }
  676. sub onFontChange {
  677. my $self = shift;
  678. $self->{console}->selectFont($self->{frame});
  679. }
  680. sub onAdvancedConfig {
  681. my $self = shift;
  682. if ($self->{notebook}->hasPage('Advanced Configuration')) {
  683. $self->{notebook}->switchPage('Advanced Configuration');
  684. return;
  685. }
  686. my $page = $self->{notebook}->newPage(1, 'Advanced Configuration');
  687. my $panel = new Wx::Panel($page, -1);
  688. my $vsizer = new Wx::BoxSizer(wxVERTICAL);
  689. $panel->SetSizer($vsizer);
  690. require Interface::Wx::ConfigEditor;
  691. my $cfg = new Interface::Wx::ConfigEditor($panel, -1);
  692. $cfg->setConfig(%config);
  693. $cfg->addCategory('All', 'Grid');
  694. $cfg->addCategory('server', 'Grid', ['master', 'server', 'username', 'password', 'char', 'serverType']);
  695. $cfg->addCategory('X-Kore', 'Grid', ['XKore', 'XKore_silent', 'XKore_bypassBotDetection', 'XKore_exeName', 'XKore_listenIp', 'XKore_listenPort', 'XKore_publicIp', 'secureAdminPassword', 'adminPassword', 'callSign', 'commandPrefix']);
  696. $cfg->addCategory('lockMap', 'Grid', ['lockMap', 'lockMap_x', 'lockMap_y', 'lockMap_randX', 'lockMap_randY']);
  697. $cfg->addCategory('attack', 'Grid', ['attackAuto', 'attackAuto_party', 'attackAuto_onlyWhenSafe', 'attackAuto_followTarget', 'attackAuto_inLockOnly', 'attackDistance', 'attackDistanceAuto', 'attackMaxDistance', 'attackMaxRouteDistance', 'attackMaxRouteTime', 'attackMinPlayerDistance', 'attackMinPortalDistance', 'attackUseWeapon', 'attackNoGiveup', 'attackCanSnipe', 'attackCheckLOS', 'attackLooters', 'attackChangeTarget', 'aggressiveAntiKS']);
  698. $cfg->addCategory('route', 'Grid', ['route_escape_reachedNoPortal', 'route_escape_randomWalk', 'route_escape_shout', 'route_randomWalk', 'route_randomWalk_inTown', 'route_randomWalk_maxRouteTime', 'route_maxWarpFee', 'route_maxNpcTries', 'route_teleport', 'route_teleport_minDistance', 'route_teleport_maxTries', 'route_teleport_notInMaps', 'route_step']);
  699. $cfg->addCategory('teleport', 'Grid', ['teleportAuto_hp', 'teleportAuto_sp', 'teleportAuto_idle', 'teleportAuto_portal', 'teleportAuto_search', 'teleportAuto_minAggressives', 'teleportAuto_minAggressivesInLock', 'teleportAuto_onlyWhenSafe', 'teleportAuto_maxDmg', 'teleportAuto_maxDmgInLock', 'teleportAuto_deadly', 'teleportAuto_useSkill', 'teleportAuto_useChatCommand', 'teleportAuto_allPlayers', 'teleportAuto_atkCount', 'teleportAuto_atkMiss', 'teleportAuto_unstuck', 'teleportAuto_dropTarget', 'teleportAuto_dropTargetKS', 'teleportAuto_attackedWhenSitting', 'teleportAuto_totalDmg', 'teleportAuto_totalDmgInLock', 'teleportAuto_equip_leftAccessory', 'teleportAuto_equip_rightAccessory', 'teleportAuto_lostHomunculus']);
  700. $cfg->addCategory('follow', 'Grid', ['follow', 'followTarget', 'followEmotion', 'followEmotion_distance', 'followFaceDirection', 'followDistanceMax', 'followDistanceMin', 'followLostStep', 'followSitAuto', 'followBot']);
  701. $cfg->addCategory('items', 'Grid', ['itemsTakeAuto', 'itemsTakeAuto_party', 'itemsGatherAuto', 'itemsMaxWeight', 'itemsMaxWeight_sellOrStore', 'itemsMaxNum_sellOrStore', 'cartMaxWeight']);
  702. $cfg->addCategory('sellAuto', 'Grid', ['sellAuto', 'sellAuto_npc', 'sellAuto_standpoint', 'sellAuto_distance']);
  703. $cfg->addCategory('storageAuto', 'Grid', ['storageAuto', 'storageAuto_npc', 'storageAuto_distance', 'storageAuto_npc_type', 'storageAuto_npc_steps', 'storageAuto_password', 'storageAuto_keepOpen', 'storageAuto_useChatCommand', 'relogAfterStorage']);
  704. $cfg->addCategory('disconnect', 'Grid', ['dcOnDeath', 'dcOnDualLogin', 'dcOnDisconnect', 'dcOnEmptyArrow', 'dcOnMute', 'dcOnPM', 'dcOnZeny', 'dcOnStorageFull', 'dcOnPlayer']);
  705. $cfg->onChange(sub {
  706. my ($key, $value) = @_;
  707. configModify($key, $value) if ($value ne $config{$key});
  708. });
  709. $vsizer->Add($cfg, 1, wxGROW | wxALL, 8);
  710. my $sizer = new Wx::BoxSizer(wxHORIZONTAL);
  711. $vsizer->Add($sizer, 0, wxGROW | wxLEFT | wxRIGHT | wxBOTTOM, 8);
  712. my $revert = new Wx::Button($panel, 46, '&Revert');
  713. $revert->SetToolTip('Revert settings to before you opened the selected category');
  714. $sizer->Add($revert, 0);
  715. EVT_BUTTON($revert, 46, sub {
  716. $cfg->revert;
  717. });
  718. $revert->Enable(0);
  719. $cfg->onRevertEnable(sub {
  720. $revert->Enable($_[0]);
  721. });
  722. my $pad = new Wx::Window($panel, -1);
  723. $sizer->Add($pad, 1);
  724. my $close = new Wx::Button($panel, 47, '&Close');
  725. $close->SetToolTip('Close this panel/dialog');
  726. $close->SetDefault;
  727. $sizer->Add($close, 0);
  728. EVT_BUTTON($close, 47, sub {
  729. $self->{notebook}->closePage('Advanced Configuration');
  730. });
  731. $page->set($panel);
  732. }
  733. sub onMapToggle {
  734. my $self = shift;
  735. $self->{mapDock}->attach;
  736. }
  737. sub onInfoBarToggle {
  738. my $self = shift;
  739. $self->{vsizer}->Show($self->{infoPanel}, $self->{infoBarToggle}->IsChecked);
  740. $self->{frame}->Layout;
  741. }
  742. sub onChatLogToggle {
  743. my $self = shift;
  744. if (!$self->{chatLogToggle}->IsChecked) {
  745. $self->{notebook}->closePage('Chat Log');
  746. } elsif (!$self->{notebook}->hasPage('Chat Log')) {
  747. my $page = $self->{notebook}->newPage(1, 'Chat Log', 0);
  748. my $chatLog = $self->{chatLog} = new Interface::Wx::LogView($page);
  749. $page->set($chatLog);
  750. $chatLog->addColor("selfchat", 0, 148, 0);
  751. $chatLog->addColor("pm", 142, 120, 0);
  752. $chatLog->addColor("p", 164, 0, 143);
  753. $chatLog->addColor("g", 0, 177, 108);
  754. $chatLog->addColor("warning", 214, 93, 0);
  755. $page->set($chatLog);
  756. } else {
  757. $self->{notebook}->switchPage('Chat Log');
  758. }
  759. }
  760. sub onManual {
  761. my $self = shift;
  762. launchURL('http://openkore.sourceforge.net/manual/');
  763. }
  764. sub onForum {
  765. my $self = shift;
  766. launchURL('http://forums.openkore.com/');
  767. }
  768. sub onItemListActivate {
  769. my ($self, $actor) = @_;
  770. if ($actor->isa('Actor::Player')) {
  771. Commands::run("lookp " . $actor->{binID});
  772. Commands::run("pl " . $actor->{binID});
  773. } elsif ($actor->isa('Actor::Monster')) {
  774. main::attack($actor->{ID});
  775. } elsif ($actor->isa('Actor::Item')) {
  776. $self->{console}->add("message", "Taking item " . $actor->nameIdx . "n", "info");
  777. main::take($actor->{ID});
  778. } elsif ($actor->isa('Actor::NPC')) {
  779. Commands::run("nl " . $actor->{binID});
  780. }
  781. $self->{inputBox}->SetFocus;
  782. }
  783. sub onTargetBoxKeyDown {
  784. my $self = shift;
  785. my $event = shift;
  786. if ($event->GetKeyCode == WXK_TAB && !$event->ShiftDown) {
  787. $self->{inputBox}->SetFocus;
  788. } else {
  789. $event->Skip;
  790. }
  791. }
  792. sub onInitialized {
  793. my ($self) = @_;
  794. $self->{itemList}->init($npcsList, new Wx::Colour(103, 0, 162),
  795. $slavesList, new Wx::Colour(200, 100, 0),
  796. $playersList, undef,
  797. $monstersList, new Wx::Colour(200, 0, 0),
  798. $itemsList, new Wx::Colour(0, 0, 200));
  799. }
  800. sub onAddPrivMsgUser {
  801. my $self = shift;
  802. my $param = $_[1];
  803. $self->{targetBox}->Append($param->{user});
  804. }
  805. sub onChatAdd {
  806. my ($self, $hook, $params) = @_;
  807. my @tmpdate = localtime();
  808. if ($tmpdate[1] < 10) {$tmpdate[1] = "0".$tmpdate[1]};
  809. if ($tmpdate[2] < 10) {$tmpdate[2] = "0".$tmpdate[2]};
  810. return if (!$self->{notebook}->hasPage('Chat Log'));
  811. if ($hook eq "ChatQueue::add" && $params->{type} ne "pm") {
  812. my $msg = '';
  813. if ($params->{type} ne "c") {
  814. $msg = "[$params->{type}] ";
  815. }
  816. $msg .= "[$tmpdate[2]:$tmpdate[1]] $params->{user}: $params->{msg}n";
  817. $self->{chatLog}->add($msg, $params->{type});
  818. } elsif ($hook eq "packet_selfChat") {
  819. # only display this message if it's a real self-chat
  820. $self->{chatLog}->add("[$tmpdate[2]:$tmpdate[1]] $params->{user}: $params->{msg}n", "selfchat") if ($params->{user});
  821. } elsif ($hook eq "packet_privMsg") {
  822. $self->{chatLog}->add("([$tmpdate[2]:$tmpdate[1]] From: $params->{privMsgUser}): $params->{privMsg}n", "pm");
  823. } elsif ($hook eq "packet_sentPM") {
  824. $self->{chatLog}->add("([$tmpdate[2]:$tmpdate[1]] To: $params->{to}): $params->{msg}n", "pm");
  825. }
  826. }
  827. sub onMapMouseMove {
  828. # Mouse moved over the map viewer control
  829. #vcl code my ($self, undef, $args) = @_;
  830. #vcl code my ($x, $y) = @{$args};
  831. my ($self, $x, $y) = @_;
  832. my $walkable;
  833. $walkable = $field->isWalkable($x, $y);
  834. if ($x >= 0 && $y >= 0 && $walkable) {
  835. $self->{mouseMapText} = "Mouse over: $x, $y";
  836. } else {
  837. delete $self->{mouseMapText};
  838. }
  839. $self->{statusbar}->SetStatusText($self->{mouseMapText}, 0);
  840. }
  841. sub onMapClick {
  842. # Clicked on map viewer control
  843. #vcl code my ($self, undef, $args) = @_;
  844. #vcl code my ($x, $y) = @{$args};
  845. my ($self, $x, $y) = @_;
  846. my $checkPortal = 0;
  847. delete $self->{mouseMapText};
  848. if ($self->{mapViewer} && $self->{mapViewer}->{portals}
  849. && $self->{mapViewer}->{portals}->{$field->name()}
  850. && @{$self->{mapViewer}->{portals}->{$field->name()}}){
  851. foreach my $portal (@{$self->{mapViewer}->{portals}->{$field->name()}}){
  852. if (distance($portal,{x=>$x,y=>$y}) <= 5) {
  853. $x = $portal->{x};
  854. $y = $portal->{y};
  855. $self->writeOutput("message", "Moving to Portal $x, $yn", "info");
  856. $checkPortal = 1;
  857. last;
  858. }
  859. }
  860. }
  861. $self->writeOutput("message", "Moving to $x, $yn", "info") unless $checkPortal;
  862. AI::clear("mapRoute", "route", "move");
  863. main::ai_route($field->name(), $x, $y, attackOnRoute => 1);
  864. $self->{inputBox}->SetFocus;
  865. }
  866. sub onMap_MapChange {
  867. #vcl code my (undef, undef, undef, $mapDock) = @_;
  868. my ($mapDock) = @_;
  869. $mapDock->title($field->name());
  870. $mapDock->Fit;
  871. }
  872. 1;