ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC/MapWidget.pm
Revision: 1.37
Committed: Thu May 25 01:26:53 2006 UTC (18 years ago) by root
Branch: MAIN
Changes since 1.36: +12 -0 lines
Log Message:
split intro.pod intro intro.pod and manual.pod, misc fixes

File Contents

# User Rev Content
1 root 1.1 package CFClient::MapWidget;
2    
3     use strict;
4 root 1.14 use utf8;
5 root 1.1
6     use List::Util qw(min max);
7    
8 root 1.4 use CFClient::OpenGL;
9 root 1.1
10     our @ISA = CFClient::UI::Base::;
11    
12     sub new {
13     my $class = shift;
14    
15 root 1.20 my $self = $class->SUPER::new (
16 root 1.1 z => -1,
17     can_focus => 1,
18 root 1.4 list => glGenList,
19 root 1.1 @_
20 root 1.20 );
21    
22 root 1.36 $self->{completer} = new CFClient::MapWidget::Command::
23     command => $self->{command},
24     can_focus => 1,
25 root 1.37 tooltip => "<b>The Command Completer</b>\n\n"
26     . "This is your central interface to send text commands to the server. "
27     . "To enter a verbatim command to send to the server, just type the command, "
28     . "followed by a space, and press return. "
29     . "Typing the initial letters of words (or just any letters) displays guesses "
30     . "for commands you might want to use.\n"
31     . "You can use the cursor-up and cursor-down keys to select between those guesses.\n"
32     . "<b>Right-Click</b> opens a menu where you cna select further options, sich as redefining keybindings.",
33 root 1.36 ;
34    
35 root 1.20 $self
36 root 1.1 }
37    
38 root 1.36 sub add_command {
39     my ($self, $command, $tooltip, $widget, $cb) = @_;
40    
41     (my $data = $command) =~ s/\\//g;
42    
43     $tooltip =~ s/^\s+//;
44    
45     $tooltip = "<big>$data</big>\n\n$tooltip";
46    
47     $tooltip =~ s/\s+$//;
48    
49     $self->{completer}{command}{$command} = [$data, $tooltip, $widget, $cb, ++$self->{command_id}];
50     }
51 root 1.4
52 root 1.36 sub clr_commands {
53     my ($self) = @_;
54 root 1.4
55 root 1.36 %{$self->{completer}{command}} = ();
56 root 1.4 }
57    
58 root 1.1 sub button_down {
59     my ($self, $ev, $x, $y) = @_;
60    
61     $self->focus_in;
62    
63 root 1.5 if ($ev->{button} == 2) {
64     my ($ox, $oy) = ($ev->{x}, $ev->{y});
65 root 1.1 my ($bw, $bh) = ($::CFG->{map_shift_x}, $::CFG->{map_shift_y});
66    
67     $self->{motion} = sub {
68     my ($ev, $x, $y) = @_;
69    
70 root 1.5 ($x, $y) = ($ev->{x}, $ev->{y});
71 root 1.1
72     $::CFG->{map_shift_x} = $bw + $x - $ox;
73     $::CFG->{map_shift_y} = $bh + $y - $oy;
74    
75     $self->update;
76     };
77     }
78     }
79    
80     sub button_up {
81     my ($self, $ev, $x, $y) = @_;
82    
83     delete $self->{motion};
84     }
85    
86     sub mouse_motion {
87     my ($self, $ev, $x, $y) = @_;
88    
89     $self->{motion}->($ev, $x, $y) if $self->{motion};
90     }
91    
92     sub size_request {
93     (
94     1 + 32 * int $::WIDTH / 32,
95     1 + 32 * int $::HEIGHT / 32,
96     )
97     }
98    
99     sub update {
100     my ($self) = @_;
101    
102     $self->{need_update} = 1;
103     $self->SUPER::update;
104     }
105    
106 root 1.36 my %DIR = (
107     CFClient::SDLK_KP8, [1, "north"],
108     CFClient::SDLK_KP9, [2, "northeast"],
109     CFClient::SDLK_KP6, [3, "east"],
110     CFClient::SDLK_KP3, [4, "southeast"],
111     CFClient::SDLK_KP2, [5, "south"],
112     CFClient::SDLK_KP1, [6, "southwest"],
113     CFClient::SDLK_KP4, [7, "west"],
114     CFClient::SDLK_KP7, [8, "northwest"],
115    
116     CFClient::SDLK_UP, [1, "north"],
117     CFClient::SDLK_RIGHT, [3, "east"],
118     CFClient::SDLK_DOWN, [5, "south"],
119     CFClient::SDLK_LEFT, [7, "west"],
120     );
121    
122     sub key_down {
123     my ($self, $ev) = @_;
124    
125     return unless $::CONN;
126    
127     my $mod = $ev->{mod};
128     my $sym = $ev->{sym};
129     my $uni = $ev->{unicode};
130    
131     if ($sym == CFClient::SDLK_KP5) {
132     $::CONN->user_send ("stay fire");
133     } elsif ($uni == ord ",") {
134     $::CONN->user_send ("take");
135     } elsif ($uni == ord " ") {
136     $::CONN->user_send ("apply");
137 root 1.37 } elsif ($uni == ord ".") {
138     $::CONN->user_send ($self->{completer}{last_command})
139     if exists $self->{completer}{last_command};
140 root 1.36 } elsif ($uni == ord "\t") {
141     # TODO: toggle inventory
142     } elsif ($sym == CFClient::SDLK_KP_PLUS || $uni == ord "+") {
143     $::CONN->user_send ("rotateshoottype +");
144     } elsif ($sym == CFClient::SDLK_KP_MINUS || $uni == ord "-") {
145     $::CONN->user_send ("rotateshoottype -");
146     } elsif ($uni == ord '"') {
147     $self->{completer}->set_prefix ("$::CFG->{say_command} ");
148     $self->{completer}->show;
149     } elsif ($uni == ord "'") {
150     $self->{completer}->set_prefix ("");
151     $self->{completer}->show;
152     } elsif (exists $DIR{$sym}) {
153     if ($mod & CFClient::KMOD_SHIFT) {
154     $self->{shft}++;
155     $::CONN->user_send ("fire $DIR{$sym}[0]");
156     } elsif ($mod & CFClient::KMOD_CTRL) {
157     $self->{ctrl}++;
158     $::CONN->user_send ("run $DIR{$sym}[0]");
159     } else {
160     $::CONN->user_send ("$DIR{$sym}[1]");
161     }
162     } elsif ($ev->{unicode}) {
163     $self->{completer}->key_down ($ev);
164     $self->{completer}->show;
165     }
166     }
167    
168     sub key_up {
169     my ($self, $ev) = @_;
170    
171     my $mod = $ev->{mod};
172     my $sym = $ev->{sym};
173    
174     if (!($mod & CFClient::KMOD_SHIFT) && delete $self->{shft}) {
175     $::CONN->user_send ("fire_stop");
176     }
177     if (!($mod & CFClient::KMOD_CTRL ) && delete $self->{ctrl}) {
178     $::CONN->user_send ("run_stop");
179     }
180     }
181    
182 root 1.1 sub draw {
183     my ($self) = @_;
184    
185 root 1.36 my $focused = $CFClient::UI::FOCUS == $self
186     || $CFClient::UI::FOCUS == $self->{completer}{entry};
187    
188 root 1.26 return
189 root 1.36 unless $focused || !$::FAST;
190 root 1.26
191 root 1.1 if (delete $self->{need_update}) {
192 root 1.4 glNewList $self->{list};
193 root 1.1
194     if ($::MAP) {
195 root 1.17 my $sw = int $::WIDTH / (32 * $::CFG->{map_scale}) + 0.99;
196     my $sh = int $::HEIGHT / (32 * $::CFG->{map_scale}) + 0.99;
197 root 1.1
198 root 1.18 my $sx = $::CFG->{map_shift_x} / $::CFG->{map_scale}; my $sx0 = $sx & 31; $sx = ($sx - $sx0) / 32;
199     my $sy = $::CFG->{map_shift_y} / $::CFG->{map_scale}; my $sy0 = $sy & 31; $sy = ($sy - $sy0) / 32;
200    
201 root 1.16 glPushMatrix;
202 root 1.9 glScale $::CFG->{map_scale}, $::CFG->{map_scale};
203    
204 root 1.1 glTranslate $sx0 - 32, $sy0 - 32, 0;
205    
206     my ($w, $h, $data) = $::MAP->draw ($sx, $sy, 0, 0, $sw + 1, $sh + 1);
207    
208     if ($::CFG->{fow_enable}) {
209     if ($::CFG->{fow_smooth} && $CFClient::GL_VERSION >= 1.2) { # smooth fog of war
210     glConvolutionParameter (GL_CONVOLUTION_2D, GL_CONVOLUTION_BORDER_MODE, GL_CONSTANT_BORDER);
211     glConvolutionFilter2D (
212     GL_CONVOLUTION_2D,
213     GL_ALPHA,
214     3, 3,
215     GL_ALPHA, GL_FLOAT,
216     pack "f*",
217 root 1.7 0.05, 0.13, 0.05,
218     0.13, 0.30, 0.13,
219     0.05, 0.13, 0.05,
220 root 1.1 );
221     glEnable GL_CONVOLUTION_2D;
222     }
223    
224 root 1.35 $self->{fow_texture_name} ||= glGenTexture;
225 root 1.31 # try to re-use the texture name: TODO improve texture class instead
226    
227 root 1.1 $self->{fow_texture} = new CFClient::Texture
228     w => $w,
229     h => $h,
230     data => $data,
231 root 1.35 name => $self->{fow_texture_name},
232 root 1.1 internalformat => GL_ALPHA,
233     format => GL_ALPHA;
234    
235     glDisable GL_CONVOLUTION_2D if $::CFG->{fow_smooth};
236    
237     glEnable GL_TEXTURE_2D;
238     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE;
239    
240 root 1.7 glColor +($::CFG->{fow_intensity}) x 3, 0.8;
241 root 1.31 $self->{fow_texture}->draw_quad_alpha (0, 0, $w * 32, $h * 32);
242 root 1.1
243     glDisable GL_TEXTURE_2D;
244     }
245    
246 root 1.18 glPopMatrix;
247 root 1.1 }
248    
249     glEndList;
250     }
251    
252     glPushMatrix;
253     glCallList $self->{list};
254     glPopMatrix;
255    
256 root 1.29 # TNT2 emulates logops in software (or worse :)
257 root 1.36 if ($focused) {
258 root 1.32 (delete $self->{out_of_focus})->destroy
259     if $self->{out_of_focus};
260     } else {
261     glColor 0.4, 0.2, 0.2, 0.6;
262 root 1.29 glEnable GL_BLEND;
263     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
264 root 1.1 glBegin GL_QUADS;
265     glVertex 0, 0;
266     glVertex 0, $::HEIGHT;
267     glVertex $::WIDTH, $::HEIGHT;
268     glVertex $::WIDTH, 0;
269     glEnd;
270 root 1.29 glDisable GL_BLEND;
271 root 1.32
272     $self->{out_of_focus} ||= do {
273     my $label = new CFClient::UI::Label
274     x => 0,
275     y => 0,
276     z => 1,
277     ellipsise => 0,
278 root 1.33 text => "map out of focus (click map to play)";
279 root 1.32
280     $label->show;
281     $label->update;
282    
283     $CFClient::UI::ROOT->on_post_alloc ("$self$label" => sub {
284     $label->move (
285     ($::WIDTH - $label->{w}) * 0.5,
286     ($::HEIGHT - $label->{h}) * 0.5,
287     );
288     });
289    
290     $label
291     };
292 root 1.1 }
293     }
294    
295 root 1.36 sub DESTROY {
296     my $self = shift;
297 root 1.3
298 root 1.36 glDeleteList $self->{list};
299 root 1.1
300 root 1.36 $self->SUPER::DESTROY;
301 root 1.8 }
302    
303 root 1.18 package CFClient::MapWidget::MapMap;
304    
305 root 1.19 our @ISA = CFClient::UI::Base::;
306 root 1.18
307     use Time::HiRes qw(time);
308     use CFClient::OpenGL;
309    
310     sub size_request {
311     ($::HEIGHT * 0.25, $::HEIGHT * 0.25)
312     }
313    
314     sub size_allocate {
315     my ($self, $w, $h) = @_;
316    
317     $self->SUPER::size_allocate ($w, $h);
318     $self->update;
319     }
320    
321     sub update {
322     my ($self) = @_;
323    
324     delete $self->{texture_atime};
325     $self->SUPER::update;
326     }
327    
328     sub _draw {
329     my ($self) = @_;
330    
331     $::MAP or return;
332    
333     my ($w, $h) = @$self{qw(w h)};
334    
335     my $sw = int $::WIDTH / (32 * $::CFG->{map_scale}) + 0.99;
336     my $sh = int $::HEIGHT / (32 * $::CFG->{map_scale}) + 0.99;
337    
338     my $sx = int $::CFG->{map_shift_x} / 32;
339     my $sy = int $::CFG->{map_shift_y} / 32;
340    
341     my $ox = 0.5 * ($w - $sw);
342     my $oy = 0.5 * ($h - $sh);
343    
344     glEnable GL_BLEND;
345     glBlendFunc GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA;
346     glEnable GL_TEXTURE_2D;
347     glTexEnv GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE;
348    
349     if ($self->{texture_atime} < time) {
350     $self->{texture_atime} = time + 1/3;
351    
352     $self->{texture} =
353     new CFClient::Texture
354     w => $w,
355     h => $h,
356     data => $::MAP->mapmap (-$ox, -$oy, $w, $h),
357     type => $CFClient::GL_VERSION >= 1.2 ? GL_UNSIGNED_INT_8_8_8_8_REV : GL_UNSIGNED_BYTE;
358     }
359    
360     $self->{texture}->draw_quad (0, 0);
361    
362     glDisable GL_TEXTURE_2D;
363    
364     glTranslate 0.375, 0.375;
365    
366     #TODO: map scale is completely borked
367    
368     my $x0 = int $ox - $sx + 0.5;
369     my $y0 = int $oy - $sy + 0.5;
370    
371     glColor 1, 1, 0, 1;
372     glBegin GL_LINE_LOOP;
373     glVertex $x0 , $y0 ;
374     glVertex $x0 , $y0 + $sh;
375     glVertex $x0 + $sw, $y0 + $sh;
376     glVertex $x0 + $sw, $y0 ;
377     glEnd;
378    
379     glDisable GL_BLEND;
380     }
381    
382 root 1.8 package CFClient::MapWidget::Command;
383    
384     use strict;
385    
386     use CFClient::OpenGL;
387    
388 root 1.23 our @ISA = CFClient::UI::Frame::;
389 root 1.8
390     sub new {
391     my $class = shift;
392    
393     my $self = $class->SUPER::new (
394 root 1.23 bg => [0, 0, 0, 0.8],
395 root 1.8 @_,
396 root 1.23 );
397    
398     $self->add ($self->{vbox} = new CFClient::UI::VBox);
399    
400     $self->{label} = [
401     map
402 root 1.8 CFClient::UI::Label->new (
403 root 1.25 can_hover => 1,
404     can_events => 1,
405     tooltip_width => 0.33,
406     fontsize => $_,
407 root 1.27 ), (0.8) x 16
408 root 1.23 ];
409    
410     $self->{entry} = new CFClient::UI::Entry
411     connect_changed => sub {
412     $self->update_labels;
413 root 1.36 },
414     connect_key_down => sub {
415     my ($entry, $ev) = @_;
416    
417     my $self = $entry->{parent}{parent};
418    
419     if ($ev->{sym} == 13) {
420     if (exists $self->{select}) {
421 root 1.37 $self->{last_command} = $self->{select};
422 root 1.36 $::CONN->user_send ($self->{select});
423     $self->hide;
424     }
425     } elsif ($ev->{sym} == 27) {
426     $self->hide;
427     return;
428     } elsif ($ev->{sym} == CFClient::SDLK_DOWN) {
429     ++$self->{select_offset}
430     if $self->{select_offset} < $#{ $self->{last_match} || [] };
431     $self->update_labels;
432     } elsif ($ev->{sym} == CFClient::SDLK_UP) {
433     --$self->{select_offset}
434     if $self->{select_offset};
435     $self->update_labels;
436     } else {
437     return 0;
438     }
439    
440     1
441     }
442     ;
443 root 1.23
444     $self->{vbox}->add (
445     $self->{entry},
446     @{$self->{label}},
447 root 1.8 );
448    
449     $self
450     }
451    
452 root 1.36 sub set_prefix {
453     my ($self, $prefix) = @_;
454    
455     $self->{entry}->set_text ($prefix);
456     $self->show;
457     }
458    
459 root 1.8 sub size_allocate {
460     my ($self, $w, $h) = @_;
461    
462     $self->SUPER::size_allocate ($w, $h);
463     $self->move (($::WIDTH - $w) * 0.5, ($::HEIGHT - $h) * 0.6, 10);
464     }
465    
466 root 1.36 sub show {
467     my ($self) = @_;
468    
469     $self->SUPER::show;
470     $self->{entry}->focus_in;
471     }
472    
473     sub hide {
474     my ($self) = @_;
475    
476     $self->SUPER::hide;
477     $self->{entry}->set_text ("");
478     }
479    
480 root 1.23 sub key_down {
481     my ($self, $ev) = @_;
482    
483 root 1.36 $self->{entry}->key_down ($ev);
484 root 1.23 }
485    
486 root 1.8 sub update_labels {
487     my ($self) = @_;
488    
489 root 1.23 my $text = $self->{entry}->get_text;
490    
491     length $text
492 root 1.36 or return $self->hide;
493 root 1.23
494     my ($cmd, $arg) = $text =~ /^\s*([^[:space:]]*)(.*)$/;
495    
496 root 1.36 if ($text ne $self->{last_search}) {
497     my @match;
498 root 1.23
499 root 1.36 if ($text =~ /^(.*?)\s+$/) {
500     @match = [$cmd, "(appended whitespace suppresses completion)"];
501     } else {
502     my $regexp = do {
503     my ($beg, @chr) = split //, lc $cmd;
504 root 1.23
505 root 1.36 # the following regex is used to match our "completion entry"
506     # to an actual command - the parentheses match kind of "overhead"
507     # - the more characters the parentheses match, the less attractive
508     # is the match.
509     my $regexp = "^\Q$beg\E"
510     . join "", map "(?:.*?[ \\\\]\Q$_\E|(.*?)\Q$_\E)", @chr;
511     qr<$regexp>
512     };
513    
514     my @penalty;
515    
516     for (keys %{$self->{command}}) {
517     if (@penalty = $_ =~ $regexp) {
518     push @match, [$_, length join "", map "::$_", grep defined, @penalty];
519     }
520 root 1.23 }
521 root 1.36
522     @match = map $self->{command}{$_->[0]},
523     sort {
524     $a->[1] <=> $b->[1]
525     or $self->{command}{$a->[0]}[4] <=> $self->{command}{$b->[0]}[4]
526     or (length $a->[0]) <=> (length $b->[0])
527     } @match;
528 root 1.8 }
529 root 1.23
530     $self->{last_search} = $cmd;
531     $self->{last_match} = \@match;
532    
533     $self->{select_offset} = 0;
534 root 1.8 }
535    
536 root 1.23 my @labels = @{ $self->{label} };
537     my @matches = @{ $self->{last_match} || [] };
538 root 1.8
539 root 1.23 if ($self->{select_offset}) {
540     splice @matches, 0, $self->{select_offset}, ();
541 root 1.8
542 root 1.23 my $label = shift @labels;
543     $label->set_text ("...");
544     $label->set_tooltip ("Use Cursor-Up to view previous matches");
545 root 1.8 }
546    
547 root 1.23 for my $label (@labels) {
548     $label->{fg} = [1, 1, 1, 1];
549     $label->{bg} = [0, 0, 0, 0];
550     }
551    
552     if (@matches) {
553     $self->{select} = "$matches[0][0]$arg";
554    
555     $labels[0]->{fg} = [0, 0, 0, 1];
556     $labels[0]->{bg} = [1, 1, 1, 0.8];
557     } else {
558 root 1.24 $self->{select} = "$cmd$arg";
559 root 1.23 }
560    
561     for my $match (@matches) {
562     my $label = shift @labels;
563    
564     if (@labels) {
565     $label->set_text ("$match->[0]$arg");
566     $label->set_tooltip ($match->[1]);
567     } else {
568     $label->set_text ("...");
569     $label->set_tooltip ("Use Cursor-Down to view more matches");
570     last;
571     }
572     }
573 root 1.8
574 root 1.23 for my $label (@labels) {
575     $label->set_text ("");
576     $label->set_tooltip ("");
577 root 1.8 }
578    
579 root 1.23 $self->update;
580     ###
581 root 1.8 }
582    
583 root 1.23 sub _draw {
584     my ($self) = @_;
585 root 1.8
586 root 1.23 # hack
587     local $CFClient::UI::FOCUS = $self->{entry};
588 root 1.10
589 root 1.23 $self->SUPER::_draw;
590 root 1.2 }
591    
592 root 1.1 1