ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.86
Committed: Fri Jun 2 16:27:27 2006 UTC (17 years, 11 months ago) by elmex
Branch: MAIN
Changes since 1.85: +46 -0 lines
Log Message:
implemented auto pickup dialog

File Contents

# User Rev Content
1 root 1.1 =head1 NAME
2    
3 root 1.22 CFClient - undocumented utility garbage for our crossfire client
4 root 1.1
5     =head1 SYNOPSIS
6    
7 root 1.22 use CFClient;
8 root 1.1
9     =head1 DESCRIPTION
10    
11     =over 4
12    
13     =cut
14    
15 root 1.22 package CFClient;
16 root 1.1
17     BEGIN {
18     $VERSION = '0.1';
19    
20 root 1.2 use XSLoader;
21 root 1.22 XSLoader::load "CFClient", $VERSION;
22 root 1.1 }
23    
24 root 1.62 use utf8;
25    
26 root 1.43 use Carp ();
27 root 1.52 use AnyEvent ();
28 root 1.34 use BerkeleyDB;
29 root 1.52
30 root 1.5 sub find_rcfile($) {
31     my $path;
32    
33 root 1.46 for (grep !ref, @INC) {
34 root 1.22 $path = "$_/CFClient/resources/$_[0]";
35 root 1.5 return $path if -r $path;
36     }
37    
38     die "FATAL: can't find required file $_[0]\n";
39     }
40    
41     sub read_cfg {
42     my ($file) = @_;
43    
44     open CFG, $file
45     or return;
46    
47     my $CFG;
48    
49     local $/;
50     $CFG = eval <CFG>;
51    
52     $::CFG = $CFG;
53    
54     close CFG;
55     }
56    
57     sub write_cfg {
58     my ($file) = @_;
59    
60     open CFG, ">$file"
61     or return;
62    
63     {
64 elmex 1.9 require Data::Dumper;
65 root 1.5 local $Data::Dumper::Purity = 1;
66     $::CFG->{VERSION} = $::VERSION;
67     print CFG Data::Dumper->Dump ([$::CFG], [qw/CFG/]);
68     }
69    
70     close CFG;
71     }
72    
73 root 1.75 mkdir "$Crossfire::VARDIR/cfplus", 0777;
74 root 1.34
75 root 1.77 our $DB_ENV;
76    
77 root 1.76 {
78     use strict;
79    
80 root 1.77 my $recover = $BerkeleyDB::db_version >= 4.4
81     ? eval "DB_REGISTER | DB_RECOVER"
82     : 0;
83    
84     $DB_ENV = new BerkeleyDB::Env
85 root 1.76 -Home => "$Crossfire::VARDIR/cfplus",
86     -Cachesize => 1_000_000,
87     -ErrFile => "$Crossfire::VARDIR/cfplus/errorlog.txt",
88 root 1.39 # -ErrPrefix => "DATABASE",
89 root 1.76 -Verbose => 1,
90 root 1.77 -Flags => DB_CREATE | DB_RECOVER | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | $recover,
91 root 1.78 -SetFlags => DB_AUTO_COMMIT | DB_LOG_AUTOREMOVE,
92 root 1.76 or die "unable to create/open database home $Crossfire::VARDIR/cfplus: $BerkeleyDB::Error";
93     }
94 root 1.34
95     sub db_table($) {
96 root 1.38 my ($table) = @_;
97    
98     $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
99 root 1.76
100 root 1.34 new CFClient::Database
101     -Env => $DB_ENV,
102 root 1.38 -Filename => $table,
103     # -Filename => "database",
104     # -Subname => $table,
105 root 1.51 -Property => DB_CHKSUM,
106 root 1.34 -Flags => DB_CREATE | DB_UPGRADE,
107 root 1.76 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error"
108 root 1.34 }
109    
110 root 1.52 sub pod_to_pango($) {
111     my ($pom) = @_;
112    
113     $pom->present ("CFClient::PodToPango")
114     }
115    
116 root 1.60 sub pod_to_pango_list($) {
117     my ($pom) = @_;
118    
119     [
120     map s/^(\s*)// && [40 * length $1, length $_ ? $_ : " "],
121     split /\n/, $pom->present ("CFClient::PodToPango")
122     ]
123     }
124    
125 root 1.52 package CFClient::PodToPango;
126    
127     use base Pod::POM::View::Text;
128    
129     our $indent = 0;
130    
131     *view_seq_code =
132     *view_seq_bold = sub { "<b>$_[1]</b>" };
133     *view_seq_italic = sub { "<i>$_[1]</i>" };
134     *view_seq_space =
135     *view_seq_link =
136     *view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
137    
138     sub view_seq_text {
139     my $text = $_[1];
140     $text =~ s/\s+/ /g;
141     CFClient::UI::Label::escape ($text)
142     }
143    
144     sub view_item {
145     ("\t" x ($indent / 4))
146     . $_[1]->title->present ($_[0])
147     . "\n"
148     . $_[1]->content->present ($_[0])
149     }
150    
151 root 1.53 sub view_verbatim {
152     (join "",
153 root 1.69 map +("\t" x ($indent / 2)) . "<tt>$_</tt>\n",
154 root 1.53 split /\n/, CFClient::UI::Label::escape ($_[1]))
155     . "\n"
156     }
157    
158 root 1.52 sub view_textblock {
159     ("\t" x ($indent / 2)) . "$_[1]\n\n"
160     }
161    
162 root 1.60 sub view_head1 {
163 root 1.69 "\n\n<span foreground='#ffff00' size='x-large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
164 root 1.60 . $_[1]->content->present ($_[0])
165     };
166    
167 root 1.52 sub view_head2 {
168 root 1.69 "\n<span foreground='#ccccff' size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
169     . $_[1]->content->present ($_[0])
170     };
171    
172     sub view_head3 {
173     "\n<span size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
174 root 1.52 . $_[1]->content->present ($_[0])
175     };
176    
177     sub view_over {
178     local $indent = $indent + $_[1]->indent;
179     $_[1]->content->present ($_[0])
180     }
181    
182 root 1.34 package CFClient::Database;
183    
184     our @ISA = BerkeleyDB::Btree::;
185    
186     sub get($$) {
187     my $data;
188    
189     $_[0]->db_get ($_[1], $data) == 0
190     ? $data
191 root 1.37 : ()
192 root 1.34 }
193    
194     my %DB_SYNC;
195    
196     sub put($$$) {
197     my ($db, $key, $data) = @_;
198    
199 root 1.37 $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
200 root 1.34
201 root 1.37 $db->db_put ($key => $data)
202 root 1.34 }
203    
204 root 1.62 package CFClient::Item;
205    
206 root 1.71 use strict;
207     use Crossfire::Protocol::Constants;
208    
209 elmex 1.84 my $last_enter_count = 1;
210    
211 root 1.62 sub desc_string {
212     my ($self) = @_;
213    
214     my $desc =
215     $self->{nrof} < 2
216     ? $self->{name}
217     : "$self->{nrof} × $self->{name_pl}";
218    
219 root 1.71 $self->{flags} & F_OPEN
220 root 1.62 and $desc .= " (open)";
221 root 1.71 $self->{flags} & F_APPLIED
222 root 1.62 and $desc .= " (applied)";
223 root 1.71 $self->{flags} & F_UNPAID
224 root 1.62 and $desc .= " (unpaid)";
225 root 1.71 $self->{flags} & F_MAGIC
226 root 1.62 and $desc .= " (magic)";
227 root 1.71 $self->{flags} & F_CURSED
228 root 1.62 and $desc .= " (cursed)";
229 root 1.71 $self->{flags} & F_DAMNED
230 root 1.62 and $desc .= " (damned)";
231 root 1.71 $self->{flags} & F_LOCKED
232 root 1.62 and $desc .= " *";
233    
234     $desc
235     }
236    
237     sub weight_string {
238     my ($self) = @_;
239    
240     my $weight = ($self->{nrof} || 1) * $self->{weight};
241    
242     $weight < 0 ? "?" : $weight * 0.001
243     }
244    
245 elmex 1.84 sub do_n_dialog {
246     my ($cb) = @_;
247    
248     my $w = new CFClient::UI::FancyFrame;
249     $w->add (my $vb = new CFClient::UI::VBox x => "center", y => "center");
250     $vb->add (new CFClient::UI::Label text => "Enter item count:");
251     $vb->add (my $entry = new CFClient::UI::Entry
252     text => $last_enter_count,
253     on_activate => sub {
254     my ($entry) = @_;
255     $last_enter_count = $entry->get_text;
256     $cb->($last_enter_count);
257     $w->hide;
258     $w = undef;
259     }
260     );
261     $entry->focus_in;
262     $w->show;
263    
264     }
265    
266 root 1.62 sub update_widgets {
267     my ($self) = @_;
268    
269 root 1.63 my $button_cb = sub {
270     my (undef, $ev, $x, $y) = @_;
271    
272 elmex 1.84 my $targ = $::CONN->{player}{tag};
273 root 1.63
274 elmex 1.84 if ($self->{container} == $::CONN->{player}{tag}) {
275     $targ = $::CONN->{open_container};
276     }
277 root 1.63
278 elmex 1.84 if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) {
279 root 1.79 $::CONN->send ("move $targ $self->{tag} 0")
280     if $targ || !($self->{flags} & F_LOCKED);
281 elmex 1.86 } elsif (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 2) {
282     $self->{flags} & F_LOCKED
283     ? $::CONN->send ("lock " . pack "CN", 0, $self->{tag})
284     : $::CONN->send ("lock " . pack "CN", 1, $self->{tag})
285 root 1.63 } elsif ($ev->{button} == 1) {
286     $::CONN->send ("examine $self->{tag}");
287     } elsif ($ev->{button} == 2) {
288     $::CONN->send ("apply $self->{tag}");
289     } elsif ($ev->{button} == 3) {
290     my @menu_items = (
291     ["examine", sub { $::CONN->send ("examine $self->{tag}") }],
292     ["mark", sub { $::CONN->send ("mark ". pack "N", $self->{tag}) }],
293     ["apply", sub { $::CONN->send ("apply $self->{tag}") }],
294     (
295 root 1.71 $self->{flags} & F_LOCKED
296 root 1.63 ? (
297     ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $self->{tag}) }],
298     )
299     : (
300     ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $self->{tag}) }],
301 elmex 1.64 ["drop", sub { $::CONN->send ("move $::CONN->{open_container} $self->{tag} 0") }],
302 elmex 1.84 ["move n",
303     sub {
304     do_n_dialog (sub { $::CONN->send ("move $targ $self->{tag} $_[0]") })
305     }
306     ]
307 root 1.63 )
308     ),
309     );
310    
311     CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev);
312     }
313    
314     1
315     };
316    
317 root 1.62 my $tooltip_std = "<small>"
318     . "Left click - examine item\n"
319     . "Shift-Left click - " . ($self->{container} ? "move or drop" : "take") . " item\n"
320     . "Middle click - apply\n"
321 elmex 1.86 . "Shift-Middle click - lock/unlock\n"
322 root 1.62 . "Right click - further options"
323     . "</small>\n";
324    
325 root 1.63 $self->{face_widget} ||= new CFClient::UI::Face
326     can_events => 1,
327     can_hover => 1,
328 root 1.67 anim => $self->{anim},
329 root 1.66 animspeed => $self->{animspeed}, # TODO# must be set at creation time
330 root 1.72 on_button_down => $button_cb,
331 root 1.63 ;
332 root 1.62 $self->{face_widget}{face} = $self->{face};
333     $self->{face_widget}{anim} = $self->{anim};
334 root 1.65 $self->{face_widget}{animspeed} = $self->{animspeed};
335 root 1.62 $self->{face_widget}->set_tooltip (
336     "<b>Face/Animation.</b>\n"
337     . "Item uses face #$self->{face}. "
338     . ($self->{animspeed} ? "Item uses animation #$self->{anim} at " . (1 / $self->{animspeed}) . "fps. " : "Item is not animated. ")
339     . "\n\n$tooltip_std"
340     );
341    
342 root 1.63 $self->{desc_widget} ||= new CFClient::UI::Label
343     can_events => 1,
344     can_hover => 1,
345     ellipsise => 2,
346 root 1.68 align => -1,
347 root 1.72 on_button_down => $button_cb,
348 root 1.63 ;
349     my $desc = CFClient::Item::desc_string $self;
350     $self->{desc_widget}->set_text ($desc);
351     $self->{desc_widget}->set_tooltip ("<b>$desc</b>.\n$tooltip_std");
352    
353     $self->{weight_widget} ||= new CFClient::UI::Label
354     can_events => 1,
355     can_hover => 1,
356     ellipsise => 0,
357 root 1.68 align => 0,
358 root 1.72 on_button_down => $button_cb,
359 root 1.63 ;
360 root 1.62 $self->{weight_widget}->set_text (CFClient::Item::weight_string $self);
361    
362     $self->{weight_widget}->set_tooltip (
363     "<b>Weight</b>.\n"
364     . ($self->{weight} >= 0 ? "One item weighs $self->{weight}g. " : "You have no idea how much this weighs. ")
365     . ($self->{nrof} ? "You have $self->{nrof} of it. " : "Item cannot stack with others of it's kind. ")
366     . "\n\n$tooltip_std"
367     );
368     }
369    
370 elmex 1.81 package CFClient::Binder;
371 elmex 1.73
372     my @ALLOWED_MODIFIER_KEYS = (
373 root 1.85 CFClient::SDLK_LSHIFT,
374     CFClient::SDLK_LCTRL ,
375     CFClient::SDLK_LALT ,
376     CFClient::SDLK_LMETA ,
377    
378     CFClient::SDLK_RSHIFT,
379     CFClient::SDLK_RCTRL ,
380     CFClient::SDLK_RALT ,
381     CFClient::SDLK_RMETA ,
382 elmex 1.73 );
383    
384     my %ALLOWED_MODIFIERS = (
385 root 1.85 CFClient::KMOD_LSHIFT => "LSHIFT",
386     CFClient::KMOD_LCTRL => "LCTRL",
387     CFClient::KMOD_LALT => "LALT",
388     CFClient::KMOD_LMETA => "LMETA",
389    
390     CFClient::KMOD_RSHIFT => "RSHIFT",
391     CFClient::KMOD_RCTRL => "RCTRL",
392     CFClient::KMOD_RALT => "RALT",
393     CFClient::KMOD_RMETA => "RMETA",
394 elmex 1.73 );
395    
396     my %DIRECT_BIND_CHARS = map { $_ => 1 } qw/0 1 2 3 4 5 6 7 8 9/;
397     my @DIRECT_BIND_KEYS = (
398     CFClient::SDLK_F1,
399     CFClient::SDLK_F2,
400     CFClient::SDLK_F3,
401     CFClient::SDLK_F4,
402     CFClient::SDLK_F5,
403     CFClient::SDLK_F6,
404     CFClient::SDLK_F7,
405     CFClient::SDLK_F8,
406     CFClient::SDLK_F9,
407     CFClient::SDLK_F10,
408     CFClient::SDLK_F11,
409     CFClient::SDLK_F12,
410     CFClient::SDLK_F13,
411     CFClient::SDLK_F14,
412     CFClient::SDLK_F15,
413     );
414    
415     # this binding dialog asks for a key-combo to be pressed
416 elmex 1.81 # and if successful it calls the $cb with $mod and $sym as args.
417 elmex 1.73 sub open_binding_dialog {
418 elmex 1.81 my ($cb) = @_;
419 elmex 1.73
420     my $w = new CFClient::UI::FancyFrame
421 elmex 1.80 title => "Bind Action",
422     x => "center",
423     y => "center";
424 elmex 1.73
425     $w->add (my $vb = new CFClient::UI::VBox);
426     $vb->add (new CFClient::UI::Label
427     text => "Press a modifier (CTRL, ALT and/or SHIFT) and a key."
428     ."You can only bind 0-9 and F1-F15 without modifiers."
429     );
430     $vb->add (my $entry = new CFClient::UI::Entry
431     text => "",
432     on_key_down => sub {
433     my ($entry, $ev) = @_;
434    
435     my $mod = $ev->{mod};
436     my $sym = $ev->{sym};
437    
438     # XXX: This seems a little bit hackisch to me, but i have to ignore them
439     if (grep { $_ == $sym } @ALLOWED_MODIFIER_KEYS) {
440     return;
441     }
442    
443     if ($mod == CFClient::KMOD_NONE
444     and not $DIRECT_BIND_CHARS{chr ($ev->{unicode})}
445     and not grep { $sym == $_ } @DIRECT_BIND_KEYS)
446     {
447     $::STATUSBOX->add (
448     "Can't bind key ".CFClient::SDL_GetKeyName ($sym)
449     ." directly without modifier! It would damage the completer handling."
450     );
451     return;
452     }
453    
454     $entry->focus_out;
455    
456 elmex 1.81 $cb->($mod, $sym);
457 elmex 1.73
458     $w->destroy
459     });
460    
461     $entry->focus_in;
462     $w->show;
463     }
464    
465     sub keycombo_to_name {
466     my ($mod, $sym) = @_;
467    
468     my $mods = join '+',
469     map { $ALLOWED_MODIFIERS{$_} }
470 elmex 1.81 grep { ($_ + 0) & ($mod + 0) }
471 elmex 1.73 keys %ALLOWED_MODIFIERS;
472     $mods .= "+" if $mods ne '';
473    
474     return $mods . CFClient::SDL_GetKeyName ($sym);
475     }
476    
477 elmex 1.86 package CFClient::Pickup;
478     # some pickup constants
479     sub PU_NOTHING { 0x00000000 }
480    
481     sub PU_DEBUG { 0x10000000 }
482     sub PU_INHIBIT { 0x20000000 }
483     sub PU_STOP { 0x40000000 }
484     sub PU_NEWMODE { 0x80000000 }
485    
486     sub PU_RATIO { 0x0000000F }
487    
488     sub PU_FOOD { 0x00000010 }
489     sub PU_DRINK { 0x00000020 }
490     sub PU_VALUABLES { 0x00000040 }
491     sub PU_BOW { 0x00000080 }
492    
493     sub PU_ARROW { 0x00000100 }
494     sub PU_HELMET { 0x00000200 }
495     sub PU_SHIELD { 0x00000400 }
496     sub PU_ARMOUR { 0x00000800 }
497    
498     sub PU_BOOTS { 0x00001000 }
499     sub PU_GLOVES { 0x00002000 }
500     sub PU_CLOAK { 0x00004000 }
501     sub PU_KEY { 0x00008000 }
502    
503     sub PU_MISSILEWEAPON { 0x00010000 }
504     sub PU_ALLWEAPON { 0x00020000 }
505     sub PU_MAGICAL { 0x00040000 }
506     sub PU_POTION { 0x00080000 }
507    
508     sub PU_SPELLBOOK { 0x00100000 }
509     sub PU_SKILLSCROLL { 0x00200000 }
510     sub PU_READABLES { 0x00400000 }
511     sub PU_MAGIC_DEVICE { 0x00800000 }
512    
513     sub PU_NOT_CURSED { 0x01000000 }
514    
515     sub PU_JEWELS { 0x02000000 }
516    
517    
518 root 1.1 1;
519    
520     =back
521    
522     =head1 AUTHOR
523    
524     Marc Lehmann <schmorp@schmorp.de>
525     http://home.schmorp.de/
526    
527     =cut
528