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

外挂编程

开发平台:

Windows_Unix

  1. #########################################################################
  2. #  OpenKore - WxWidgets Interface
  3. #  Map viewer control
  4. #
  5. #  Copyright (c) 2004 OpenKore development team
  6. #
  7. #  This program is free software; you can redistribute it and/or modify
  8. #  it under the terms of the GNU General Public License as published by
  9. #  the Free Software Foundation; either version 2 of the License, or
  10. #  (at your option) any later version.
  11. #
  12. #  This program is distributed in the hope that it will be useful,
  13. #  but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15. #  GNU General Public License for more details.
  16. #
  17. #
  18. #  $Revision: 6763 $
  19. #  $Id: MapViewer.pm 6763 2009-07-08 16:42:59Z eternalharvest $
  20. #
  21. #########################################################################
  22. package Interface::Wx::MapViewer;
  23. use strict;
  24. use Wx ':everything';
  25. # vcl code use Wx::Event qw(EVT_PAINT EVT_LEFT_DOWN EVT_MOTION EVT_ERASE_BACKGROUND);
  26. use Wx::Event qw(EVT_PAINT EVT_LEFT_DOWN EVT_RIGHT_DOWN EVT_MOTION EVT_ERASE_BACKGROUND);
  27. use File::Spec;
  28. use base qw(Wx::Panel);
  29. use FastUtils;
  30. # vcl code use Utils::CallbackList;
  31. use Log qw(message);
  32. use Globals;
  33. use Translation qw(TF);
  34. our %addedHandlers;
  35. sub new {
  36. my $class = shift;
  37. my $self = $class->SUPER::new(@_);
  38. $self->{mapDir} = 'map';
  39. $self->{points} = [];
  40. $self->SetBackgroundColour(new Wx::Colour(0, 0, 0));
  41. $self->{destBrush}    = new Wx::Brush(new Wx::Colour(255, 110, 245), wxSOLID);
  42. $self->{playerBrush}  = new Wx::Brush(new Wx::Colour(0, 200, 0), wxSOLID);
  43. $self->{monsterBrush} = new Wx::Brush(new Wx::Colour(215, 0, 0), wxSOLID);
  44. $self->{npcBrush}     = new Wx::Brush(new Wx::Colour(180, 0, 255), wxSOLID);
  45. $self->{portalBrush}  = new Wx::Brush(new Wx::Colour(255, 128, 64), wxSOLID);
  46. $self->{slaveBrush}     = new Wx::Brush(new Wx::Colour(0, 0, 215), wxSOLID);
  47. # vcl code  EVT_PAINT($self, &_handlePaintEvent);
  48. # vcl code  EVT_LEFT_DOWN($self, &_handleLeftDownEvent);
  49. # vcl code  EVT_MOTION($self, &_handleMotionEvent);
  50. # vcl code  EVT_ERASE_BACKGROUND($self, &_handleEraseEvent);
  51. EVT_PAINT($self, &_onPaint);
  52. EVT_LEFT_DOWN($self, &_onClick);
  53. EVT_RIGHT_DOWN($self, &_onRightClick);
  54. EVT_MOTION($self, &_onMotion);
  55. EVT_ERASE_BACKGROUND($self, &_onErase);
  56. # vcl code  $self->{onClick} = new CallbackList();
  57. # vcl code  $self->{onMouseMove} = new CallbackList();
  58. # vcl code  $self->{onMapChange} = new CallbackList();
  59. return $self;
  60. }
  61. #### Events ####
  62. # vcl code sub onClick {
  63. # vcl code  return $_[0]->{onClick};
  64. # vcl code }
  65. # vcl code sub onMouseMove {
  66. # vcl code  return $_[0]->{onMouseMove};
  67. # vcl code }
  68. # vcl code sub onMapChange {
  69. # vcl code  return $_[0]->{onMapChange};
  70. # vcl code }
  71. sub onClick {
  72. my $self = shift;
  73. my $callback = shift;
  74. my $user_data = shift;
  75. $self->{clickCb} = $callback;
  76. $self->{clickData} = $user_data;
  77. }
  78. sub onMouseMove {
  79. my $self = shift;
  80. my $callback = shift;
  81. my $user_data = shift;
  82. $self->{mouseMoveCb} = $callback;
  83. $self->{mouseMoveData} = $user_data;
  84. }
  85. sub onMapChange {
  86. my $self = shift;
  87. my $callback = shift;
  88. my $user_data = shift;
  89. $self->{mapChangeCb} = $callback;
  90. $self->{mapChangeData} = $user_data;
  91. }
  92. #### Public methods ####
  93. sub set {
  94. my ($self, $map, $x, $y, $field) = @_;
  95. $self->{field}{width} = $field->width() if ($field && $field->width());
  96. $self->{field}{height} = $field->height() if ($field && $field->height());
  97. if ($map && $map ne $self->{field}{name}) {
  98. # Map changed
  99. undef $self->{bitmap};
  100. $self->{field}{name} = $map;
  101. $self->{field}{x} = $x;
  102. $self->{field}{y} = $y;
  103. my $bitmap = $self->{bitmap} = $self->_loadMapImage($field);
  104. return unless $bitmap;
  105. $self->SetSizeHints($bitmap->GetWidth, $bitmap->GetHeight);
  106. if ($self->GetParent && $self->GetParent->GetSizer) {
  107. my $sizer = $self->GetParent->GetSizer;
  108. $sizer->SetItemMinSize($self, $bitmap->GetWidth, $bitmap->GetHeight);
  109. }
  110. # vcl code  $self->{onMapChange}->call($self);
  111. $self->{mapChangeCb}->($self->{mapChangeData}) if ($self->{mapChangeCb});
  112. $self->{needUpdate} = 1;
  113. } elsif ($x ne $self->{field}{x} || $y ne $self->{field}{y}) {
  114. # Position changed
  115. $self->{field}{x} = $x;
  116. $self->{field}{y} = $y;
  117. $self->{needUpdate} = 1;
  118. }
  119. }
  120. sub setDest {
  121. my ($self, $x, $y) = @_;
  122. if (defined $x) {
  123. if ($self->{dest}{x} ne $x && $self->{dest}{y} ne $y) {
  124. $self->{dest}{x} = $x;
  125. $self->{dest}{y} = $y;
  126. $self->{needUpdate} = 1;
  127. }
  128. } elsif (defined $self->{dest}) {
  129. undef $self->{dest};
  130. $self->{needUpdate} = 1;
  131. }
  132. }
  133. sub setMonsters {
  134. my $self = shift;
  135. my $monsters = shift;
  136. my $old = $self->{monsters};
  137. if (!$old || @{$monsters} != @{$old}) {
  138. $self->{needUpdate} = 1;
  139. $self->{monsters} = $monsters;
  140. return;
  141. }
  142. for (my $i = 0; $i < @{$monsters}; $i++) {
  143. my $pos1 = $monsters->[$i]{pos_to};
  144. my $pos2 = $old->[$i]{pos_to};
  145. if ($pos1->{x} != $pos2->{x} && $pos1->{y} != $pos2->{y}) {
  146. $self->{needUpdate} = 1;
  147. $self->{monsters} = $monsters;
  148. return;
  149. }
  150. }
  151. }
  152. sub setPortals {
  153. my $self = shift;
  154. $self->{portals} = shift;
  155. $self->{needUpdate} = 1;
  156. }
  157. sub setPlayers {
  158. my $self = shift;
  159. my $players = shift;
  160. my $old = $self->{players};
  161. if (!$old || @{$players} != @{$old}) {
  162. $self->{needUpdate} = 1;
  163. $self->{players} = $players;
  164. return;
  165. }
  166. for (my $i = 0; $i < @{$players}; $i++) {
  167. my $pos1 = $players->[$i]{pos_to};
  168. my $pos2 = $old->[$i]{pos_to};
  169. if ($pos1->{x} != $pos2->{x} && $pos1->{y} != $pos2->{y}) {
  170. $self->{needUpdate} = 1;
  171. $self->{players} = $players;
  172. return;
  173. }
  174. }
  175. }
  176. sub setNPCs {
  177. my $self = shift;
  178. my $npcs = shift;
  179. my $old = $self->{npcs};
  180. if (!$old || @{$npcs} != @{$old}) {
  181. $self->{needUpdate} = 1;
  182. $self->{npcs} = $npcs;
  183. return;
  184. }
  185. for (my $i = 0; $i < @{$npcs}; $i++) {
  186. my $pos1 = $npcs->[$i]{pos};
  187. my $pos2 = $old->[$i]{pos};
  188. if ($pos1->{x} != $pos2->{x} && $pos1->{y} != $pos2->{y}) {
  189. $self->{needUpdate} = 1;
  190. $self->{npcs} = $npcs;
  191. return;
  192. }
  193. }
  194. }
  195. sub setSlaves {
  196. my $self = shift;
  197. my $slaves = shift;
  198. my $old = $self->{slaves};
  199. if (!$old || @{$slaves} != @{$old}) {
  200. $self->{needUpdate} = 1;
  201. $self->{slaves} = $slaves;
  202. return;
  203. }
  204. for (my $i = 0; $i < @{$slaves}; $i++) {
  205. my $pos1 = $slaves->[$i]{pos_to};
  206. my $pos2 = $old->[$i]{pos_to};
  207. if ($pos1->{x} != $pos2->{x} && $pos1->{y} != $pos2->{y}) {
  208. $self->{needUpdate} = 1;
  209. $self->{slaves} = $slaves;
  210. return;
  211. }
  212. }
  213. }
  214. sub update {
  215. my $self = shift;
  216. if ($self->{needUpdate}) {
  217. $self->{needUpdate} = 0;
  218. $self->Refresh;
  219. }
  220. }
  221. sub mapSize {
  222. my $self = shift;
  223. if ($self->{bitmap}) {
  224. return ($self->{bitmap}->GetWidth, $self->{bitmap}->GetHeight);
  225. } else {
  226. return (50, 50);
  227. }
  228. }
  229. sub setMapDir {
  230. my $self = shift;
  231. $self->{mapDir} = shift;
  232. }
  233. sub parsePortals {
  234. my $self = shift;
  235. my $file = shift;
  236. return unless (-r $file);
  237. open FILE, "< $file";
  238. $self->{portals} = {};
  239. while (my $line = <FILE>) {
  240. next if $line =~ /^#/;
  241. $line =~ s/cM|cJ//g;
  242. $line =~ s/s+/ /g;
  243. $line =~ s/^s+|s+$//g;
  244. my @args = split /s/, $line, 8;
  245. if (@args > 5) {
  246. $self->{portals}->{$args[0]} = [] unless defined $self->{portals}->{$args[0]};
  247. push (@{$self->{portals}->{$args[0]}},{x=>$args[1],y=>$args[2]});
  248. }
  249. }
  250. close FILE;
  251. }
  252. #### Private ####
  253. # vcl code sub _handleLeftDownEvent {
  254. sub _onClick {
  255. my $self = shift;
  256. my $event = shift;
  257. # vcl code  if (!$self->{onClick}->empty() && $self->{field}{width} && $self->{field}{height}) {
  258. if ($self->{clickCb} && $self->{field}{width} && $self->{field}{height}) {
  259. my ($x, $y, $xscale, $yscale);
  260. $xscale = $self->{field}{width} / $self->{bitmap}->GetWidth();
  261. $yscale = $self->{field}{height} / $self->{bitmap}->GetHeight();
  262. $x = $event->GetX * $xscale;
  263. $y = $self->{field}{height} - ($event->GetY * $yscale);
  264. # vcl code  $self->{onClick}->call($self, [int $x, int $y]);
  265. $self->{clickCb}->($self->{clickData}, int $x, int $y);
  266. }
  267. }
  268. # vcl code sub _handleMotionEvent {
  269. sub _onRightClick {
  270. my $self = shift;
  271. my $event = shift;
  272. # vcl code  if (!$self->{onMouseMove}->empty() && $self->{field}{width} && $self->{field}{height}) {
  273. if ($self->{clickCb} && $self->{field}{width} && $self->{field}{height}) {
  274. my ($x, $y, $xscale, $yscale);
  275. $xscale = $self->{field}{width} / $self->{bitmap}->GetWidth();
  276. $yscale = $self->{field}{height} / $self->{bitmap}->GetHeight();
  277. $x = $event->GetX * $xscale;
  278. $y = $self->{field}{height} - ($event->GetY * $yscale);
  279. my $coord = "$x $y";
  280. my $map = $field{name};
  281. AI::clear(qw/move route mapRoute/);
  282. message TF("Walking to waypoint: $x, $yn"), "success";
  283. main::ai_route($map, $x, $y,
  284. attackOnRoute => 2,
  285. noSitAuto => 1,
  286. notifyUponArrival => 1);
  287. }
  288. }
  289. sub _onMotion {
  290. my $self = shift;
  291. my $event = shift;
  292. if ($self->{mouseMoveCb} && $self->{field}{width} && $self->{field}{height}) {
  293. my ($x, $y, $xscale, $yscale);
  294. $xscale = $self->{field}{width} / $self->{bitmap}->GetWidth;
  295. $yscale = $self->{field}{height} / $self->{bitmap}->GetHeight;
  296. $x = $event->GetX * $xscale;
  297. $y = $self->{field}{height} - ($event->GetY * $yscale);
  298. # vcl code  $self->{onMouseMove}->call($self, [int $x, int $y]);
  299. $self->{mouseMoveCb}->($self->{mouseMoveData}, int $x, int $y);
  300. }
  301. }
  302. # vcl code sub _handleEraseEvent {
  303. sub _onErase {
  304. my $self = shift;
  305. if ($self->{bitmap}) {
  306. # Do nothing; prevent flickering when drawing
  307. } else {
  308. my $event = shift;
  309. $event->Skip;
  310. }
  311. }
  312. sub _loadImage {
  313. my $file = shift;
  314. my ($ext) = $file =~ /.*(..*?)$/;
  315. my ($handler, $mime);
  316. # Initialize required image handler
  317. if (!$addedHandlers{$ext}) {
  318. $ext = lc $ext;
  319. if ($ext eq '.png') {
  320. $handler = new Wx::PNGHandler();
  321. } elsif ($ext eq '.jpg' || $ext eq '.jpeg') {
  322. $handler = new Wx::JPEGHandler();
  323. } elsif ($ext eq '.bmp') {
  324. $handler = new Wx::BMPHandler();
  325. } elsif ($ext eq '.xpm') {
  326. $handler = new Wx::XPMHandler();
  327. }
  328. return unless $handler;
  329. Wx::Image::AddHandler($handler);
  330. $addedHandlers{$ext} = 1;
  331. }
  332. my $image = Wx::Image->newNameType($file, wxBITMAP_TYPE_ANY);
  333. my $bitmap = new Wx::Bitmap($image);
  334. return ($bitmap && $bitmap->Ok()) ? $bitmap : undef;
  335. }
  336. sub _map {
  337. my $self = shift;
  338. return File::Spec->catfile($self->{mapDir}, @_);
  339. }
  340. sub _f {
  341. return File::Spec->catfile(@_);
  342. }
  343. sub _loadMapImage {
  344. my $self = shift;
  345. my $field = shift;
  346. my $name = $field->{baseName};
  347. if (-f $self->_map("$name.jpg")) {
  348. return _loadImage($self->_map("$name.jpg"));
  349. } elsif (-f $self->_map("$name.png")) {
  350. return _loadImage($self->_map("$name.png"));
  351. } elsif (-f $self->_map("$name.bmp")) {
  352. return _loadImage($self->_map("$name.bmp"));
  353. } else {
  354. my $file = _f(File::Spec->tmpdir(), "map.xpm");
  355. return unless (open(F, ">", $file));
  356. binmode F;
  357. print F Utils::xpmmake($field->width(), $field->height(), $field->{rawMap});
  358. close F;
  359. my $bitmap = _loadImage($file);
  360. unlink $file;
  361. return $bitmap;
  362. }
  363. }
  364. sub _posXYToView {
  365. my ($self, $x, $y) = @_;
  366. my ($xscale, $yscale);
  367. $xscale = $self->{bitmap}->GetWidth / $self->{field}{width};
  368. $yscale = $self->{bitmap}->GetHeight / $self->{field}{height};
  369. $x *= $xscale;
  370. $y = ($self->{field}{height} - $y) * $yscale;
  371. return ($x, $y);
  372. }
  373. # vcl code sub _handlePaintEvent {
  374. sub _onPaint {
  375. my $self = shift;
  376. my $dc = new Wx::PaintDC($self);
  377. return unless ($self->{bitmap});
  378. my ($x, $y);
  379. $dc->SetPen(wxBLACK_PEN);
  380. $dc->SetBrush(wxBLACK_BRUSH);
  381. my ($h, $w) = ($self->{bitmap}->GetHeight, $self->{bitmap}->GetWidth);
  382. $dc->DrawRectangle($w, 0,
  383. $self->GetSize->GetWidth - $w,
  384. $self->GetSize->GetHeight);
  385. $dc->DrawRectangle(0, $h,
  386. $w, $self->GetSize->GetHeight - $h);
  387. $dc->DrawBitmap($self->{bitmap}, 0, 0, 1);
  388. if ($self->{portals} && $self->{portals}->{$self->{field}{name}} && @{$self->{portals}->{$self->{field}{name}}}) {
  389. $dc->SetBrush($self->{portalBrush});
  390. foreach my $pos (@{$self->{portals}->{$self->{field}{name}}}) {
  391. ($x, $y) = $self->_posXYToView($pos->{x}, $pos->{y});
  392. $dc->DrawEllipse($x - 3, $y - 3, 6, 6);
  393. }
  394. }
  395. if ($self->{players} && @{$self->{players}}) {
  396. $dc->SetBrush($self->{playerBrush});
  397. foreach my $pos (@{$self->{players}}) {
  398. ($x, $y) = $self->_posXYToView($pos->{pos_to}{x}, $pos->{pos_to}{y});
  399. $dc->DrawEllipse($x - 2, $y - 2, 4, 4);
  400. }
  401. }
  402. if ($self->{monsters} && @{$self->{monsters}}) {
  403. $dc->SetBrush($self->{monsterBrush});
  404. foreach my $pos (@{$self->{monsters}}) {
  405. ($x, $y) = $self->_posXYToView($pos->{pos_to}{x}, $pos->{pos_to}{y});
  406. $dc->DrawEllipse($x - 2, $y - 2, 4, 4);
  407. }
  408. }
  409. if ($self->{npcs} && @{$self->{npcs}}) {
  410. $dc->SetBrush($self->{npcBrush});
  411. foreach my $pos (@{$self->{npcs}}) {
  412. ($x, $y) = $self->_posXYToView($pos->{pos}{x}, $pos->{pos}{y});
  413. $dc->DrawEllipse($x - 2, $y - 2, 4, 4);
  414. }
  415. }
  416. if ($self->{slaves} && @{$self->{slaves}}) {
  417. $dc->SetBrush($self->{slaveBrush});
  418. foreach my $pos (@{$self->{slaves}}) {
  419. ($x, $y) = $self->_posXYToView($pos->{pos_to}{x}, $pos->{pos_to}{y});
  420. $dc->DrawEllipse($x - 2, $y - 2, 4, 4);
  421. }
  422. }
  423. if ($self->{dest}) {
  424. $dc->SetPen(wxWHITE_PEN);
  425. $dc->SetBrush($self->{destBrush});
  426. ($x, $y) = $self->_posXYToView($self->{dest}{x}, $self->{dest}{y});
  427. $dc->DrawEllipse($x - 3, $y - 3, 6, 6);
  428. }
  429. if (!$self->{selfDot}) {
  430. my $file = $self->_map("kore.png");
  431. $self->{selfDot} = _loadImage($file) if (-f $file);
  432. }
  433. ($x, $y) = $self->_posXYToView($self->{field}{x}, $self->{field}{y});
  434. if ($self->{selfDot}) {
  435. $dc->DrawBitmap($self->{selfDot},
  436. $x - ($self->{selfDot}->GetHeight() / 2),
  437. $y - ($self->{selfDot}->GetHeight() / 2),
  438. 1);
  439. } else {
  440. $dc->SetBrush(wxCYAN_BRUSH);
  441. $dc->DrawEllipse($x - 5, $y - 5, 10, 10);
  442. }
  443. }
  444. 1;