ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/Deliantra-Client/DC.pm
Revision: 1.100
Committed: Sun Jul 16 20:04:07 2006 UTC (17 years, 9 months ago) by root
Branch: MAIN
Changes since 1.99: +10 -4 lines
Log Message:
*** empty log message ***

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.89 use Pod::POM ();
30 root 1.92 use Scalar::Util ();
31 root 1.89 use Storable (); # finally
32    
33     package CFClient::PodToPango;
34    
35     use base Pod::POM::View::Text;
36    
37     our $VERSION = 1; # bump if resultant formatting changes
38    
39     our $indent = 0;
40    
41     *view_seq_code =
42     *view_seq_bold = sub { "<b>$_[1]</b>" };
43     *view_seq_italic = sub { "<i>$_[1]</i>" };
44     *view_seq_space =
45     *view_seq_link =
46     *view_seq_index = sub { CFClient::UI::Label::escape ($_[1]) };
47    
48     sub view_seq_text {
49     my $text = $_[1];
50     $text =~ s/\s+/ /g;
51     CFClient::UI::Label::escape ($text)
52     }
53    
54     sub view_item {
55     ("\t" x ($indent / 4))
56     . $_[1]->title->present ($_[0])
57 root 1.91 . "\n\n"
58 root 1.89 . $_[1]->content->present ($_[0])
59     }
60    
61     sub view_verbatim {
62     (join "",
63     map +("\t" x ($indent / 2)) . "<tt>$_</tt>\n",
64     split /\n/, CFClient::UI::Label::escape ($_[1]))
65     . "\n"
66     }
67    
68     sub view_textblock {
69     ("\t" x ($indent / 2)) . "$_[1]\n\n"
70     }
71    
72     sub view_head1 {
73     "\n\n<span foreground='#ffff00' size='x-large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
74     . $_[1]->content->present ($_[0])
75     };
76    
77     sub view_head2 {
78     "\n<span foreground='#ccccff' size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
79     . $_[1]->content->present ($_[0])
80     };
81    
82     sub view_head3 {
83     "\n<span size='large'>" . $_[1]->title->present ($_[0]) . "</span>\n\n"
84     . $_[1]->content->present ($_[0])
85     };
86    
87     sub view_over {
88     local $indent = $indent + $_[1]->indent;
89     $_[1]->content->present ($_[0])
90     }
91    
92     package CFClient::Database;
93    
94     our @ISA = BerkeleyDB::Btree::;
95    
96     sub get($$) {
97     my $data;
98    
99     $_[0]->db_get ($_[1], $data) == 0
100     ? $data
101     : ()
102     }
103    
104     my %DB_SYNC;
105    
106     sub put($$$) {
107     my ($db, $key, $data) = @_;
108    
109     $DB_SYNC{$db} = AnyEvent->timer (after => 5, cb => sub { $db->db_sync });
110    
111     $db->db_put ($key => $data)
112     }
113    
114     package CFClient;
115 root 1.52
116 root 1.5 sub find_rcfile($) {
117     my $path;
118    
119 root 1.46 for (grep !ref, @INC) {
120 root 1.22 $path = "$_/CFClient/resources/$_[0]";
121 root 1.5 return $path if -r $path;
122     }
123    
124     die "FATAL: can't find required file $_[0]\n";
125     }
126    
127     sub read_cfg {
128     my ($file) = @_;
129    
130     open CFG, $file
131     or return;
132    
133     my $CFG;
134    
135     local $/;
136     $CFG = eval <CFG>;
137    
138     $::CFG = $CFG;
139    
140     close CFG;
141     }
142    
143     sub write_cfg {
144     my ($file) = @_;
145    
146     open CFG, ">$file"
147     or return;
148    
149     {
150 elmex 1.9 require Data::Dumper;
151 root 1.5 local $Data::Dumper::Purity = 1;
152     $::CFG->{VERSION} = $::VERSION;
153     print CFG Data::Dumper->Dump ([$::CFG], [qw/CFG/]);
154     }
155    
156     close CFG;
157     }
158    
159 root 1.77 our $DB_ENV;
160    
161 root 1.76 {
162     use strict;
163    
164 root 1.87 mkdir "$Crossfire::VARDIR/cfplus", 0777;
165 root 1.77 my $recover = $BerkeleyDB::db_version >= 4.4
166     ? eval "DB_REGISTER | DB_RECOVER"
167     : 0;
168    
169     $DB_ENV = new BerkeleyDB::Env
170 root 1.76 -Home => "$Crossfire::VARDIR/cfplus",
171     -Cachesize => 1_000_000,
172     -ErrFile => "$Crossfire::VARDIR/cfplus/errorlog.txt",
173 root 1.39 # -ErrPrefix => "DATABASE",
174 root 1.76 -Verbose => 1,
175 root 1.77 -Flags => DB_CREATE | DB_RECOVER | DB_INIT_MPOOL | DB_INIT_LOCK | DB_INIT_TXN | $recover,
176 root 1.78 -SetFlags => DB_AUTO_COMMIT | DB_LOG_AUTOREMOVE,
177 root 1.76 or die "unable to create/open database home $Crossfire::VARDIR/cfplus: $BerkeleyDB::Error";
178     }
179 root 1.34
180     sub db_table($) {
181 root 1.38 my ($table) = @_;
182    
183     $table =~ s/([^a-zA-Z0-9_\-])/sprintf "=%x=", ord $1/ge;
184 root 1.76
185 root 1.34 new CFClient::Database
186     -Env => $DB_ENV,
187 root 1.38 -Filename => $table,
188     # -Filename => "database",
189     # -Subname => $table,
190 root 1.51 -Property => DB_CHKSUM,
191 root 1.34 -Flags => DB_CREATE | DB_UPGRADE,
192 root 1.76 or die "unable to create/open database table $_[0]: $BerkeleyDB::Error"
193 root 1.34 }
194    
195 root 1.89 my $pod_cache = db_table "pod_cache";
196 root 1.52
197 root 1.89 sub load_pod($$$$) {
198     my ($path, $filtertype, $filterversion, $filtercb) = @_;
199 root 1.52
200 root 1.89 stat $path
201     or die "$path: $!";
202 root 1.60
203 root 1.89 my $phash = join ",", $filterversion, $CFClient::PodToPango::VERSION, (stat _)[7,9];
204 root 1.60
205 root 1.89 my ($chash, $pom) = eval { @{ Storable::thaw $pod_cache->get ("$path/$filtertype") } };
206 root 1.52
207 root 1.89 return $pom if $chash eq $phash;
208 root 1.52
209 root 1.89 my $pod = do {
210     local $/;
211     open my $pod, "<:utf8", $_[0]
212     or die "$_[0]: $!";
213     <$pod>
214     };
215 root 1.52
216 root 1.89 #utf8::downgrade $pod;
217 root 1.52
218 root 1.89 $pom = $filtercb-> (Pod::POM->new->parse_text ($pod));
219 root 1.52
220 root 1.89 $pod_cache->put ("$path/$filtertype" => Storable::nfreeze [$phash, $pom]);
221 root 1.52
222 root 1.89 $pom
223 root 1.53 }
224    
225 root 1.89 sub pod_to_pango($) {
226     my ($pom) = @_;
227 root 1.52
228 root 1.89 $pom->present ("CFClient::PodToPango")
229 root 1.52 }
230    
231 root 1.89 sub pod_to_pango_list($) {
232     my ($pom) = @_;
233 root 1.34
234 root 1.89 [
235     map s/^(\s*)// && [40 * length $1, length $_ ? $_ : " "],
236     split /\n/, $pom->present ("CFClient::PodToPango")
237     ]
238 root 1.34 }
239    
240 root 1.97 package CFClient::Layout;
241    
242     $CFClient::OpenGL::SHUTDOWN_HOOK{"CFClient::Layout"} = sub {
243 root 1.98 reset_glyph_cache;
244 root 1.97 };
245    
246 root 1.62 package CFClient::Item;
247    
248 root 1.71 use strict;
249     use Crossfire::Protocol::Constants;
250    
251 elmex 1.84 my $last_enter_count = 1;
252    
253 root 1.62 sub desc_string {
254     my ($self) = @_;
255    
256     my $desc =
257     $self->{nrof} < 2
258     ? $self->{name}
259     : "$self->{nrof} × $self->{name_pl}";
260    
261 root 1.71 $self->{flags} & F_OPEN
262 root 1.62 and $desc .= " (open)";
263 root 1.71 $self->{flags} & F_APPLIED
264 root 1.62 and $desc .= " (applied)";
265 root 1.71 $self->{flags} & F_UNPAID
266 root 1.62 and $desc .= " (unpaid)";
267 root 1.71 $self->{flags} & F_MAGIC
268 root 1.62 and $desc .= " (magic)";
269 root 1.71 $self->{flags} & F_CURSED
270 root 1.62 and $desc .= " (cursed)";
271 root 1.71 $self->{flags} & F_DAMNED
272 root 1.62 and $desc .= " (damned)";
273 root 1.71 $self->{flags} & F_LOCKED
274 root 1.62 and $desc .= " *";
275    
276     $desc
277     }
278    
279     sub weight_string {
280     my ($self) = @_;
281    
282     my $weight = ($self->{nrof} || 1) * $self->{weight};
283    
284     $weight < 0 ? "?" : $weight * 0.001
285     }
286    
287 elmex 1.84 sub do_n_dialog {
288     my ($cb) = @_;
289    
290 root 1.100 my $w = new CFClient::UI::FancyFrame
291     on_delete => sub { $_[0]->destroy; 1 },
292     has_close_button => 1,
293     ;
294    
295 elmex 1.84 $w->add (my $vb = new CFClient::UI::VBox x => "center", y => "center");
296     $vb->add (new CFClient::UI::Label text => "Enter item count:");
297     $vb->add (my $entry = new CFClient::UI::Entry
298     text => $last_enter_count,
299     on_activate => sub {
300     my ($entry) = @_;
301     $last_enter_count = $entry->get_text;
302     $cb->($last_enter_count);
303     $w->hide;
304 root 1.100 $w->destroy;
305    
306     0
307     },
308     on_escape => sub { $w->destroy; 1 },
309 elmex 1.84 );
310 root 1.93 $entry->grab_focus;
311 elmex 1.84 $w->show;
312     }
313    
314 root 1.62 sub update_widgets {
315     my ($self) = @_;
316    
317 root 1.92 # necessary to avoid cyclic references
318     Scalar::Util::weaken $self;
319    
320 root 1.63 my $button_cb = sub {
321     my (undef, $ev, $x, $y) = @_;
322    
323 elmex 1.84 my $targ = $::CONN->{player}{tag};
324 root 1.63
325 elmex 1.84 if ($self->{container} == $::CONN->{player}{tag}) {
326     $targ = $::CONN->{open_container};
327     }
328 root 1.63
329 elmex 1.84 if (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 1) {
330 root 1.79 $::CONN->send ("move $targ $self->{tag} 0")
331     if $targ || !($self->{flags} & F_LOCKED);
332 elmex 1.86 } elsif (($ev->{mod} & CFClient::KMOD_SHIFT) && $ev->{button} == 2) {
333     $self->{flags} & F_LOCKED
334     ? $::CONN->send ("lock " . pack "CN", 0, $self->{tag})
335     : $::CONN->send ("lock " . pack "CN", 1, $self->{tag})
336 root 1.63 } elsif ($ev->{button} == 1) {
337     $::CONN->send ("examine $self->{tag}");
338     } elsif ($ev->{button} == 2) {
339     $::CONN->send ("apply $self->{tag}");
340     } elsif ($ev->{button} == 3) {
341     my @menu_items = (
342     ["examine", sub { $::CONN->send ("examine $self->{tag}") }],
343     ["mark", sub { $::CONN->send ("mark ". pack "N", $self->{tag}) }],
344 elmex 1.99 ["ignite/thaw", # first try of an easier use of flint&steel
345     sub {
346     $::CONN->send ("mark ". pack "N", $self->{tag});
347     $::CONN->send ("command apply flint and steel");
348     }
349     ],
350 root 1.63 ["apply", sub { $::CONN->send ("apply $self->{tag}") }],
351     (
352 root 1.71 $self->{flags} & F_LOCKED
353 root 1.63 ? (
354     ["unlock", sub { $::CONN->send ("lock " . pack "CN", 0, $self->{tag}) }],
355     )
356     : (
357     ["lock", sub { $::CONN->send ("lock " . pack "CN", 1, $self->{tag}) }],
358 elmex 1.64 ["drop", sub { $::CONN->send ("move $::CONN->{open_container} $self->{tag} 0") }],
359 elmex 1.84 ["move n",
360     sub {
361     do_n_dialog (sub { $::CONN->send ("move $targ $self->{tag} $_[0]") })
362     }
363     ]
364 root 1.63 )
365     ),
366     );
367    
368     CFClient::UI::Menu->new (items => \@menu_items)->popup ($ev);
369     }
370    
371     1
372     };
373    
374 root 1.62 my $tooltip_std = "<small>"
375     . "Left click - examine item\n"
376     . "Shift-Left click - " . ($self->{container} ? "move or drop" : "take") . " item\n"
377     . "Middle click - apply\n"
378 elmex 1.86 . "Shift-Middle click - lock/unlock\n"
379 root 1.62 . "Right click - further options"
380     . "</small>\n";
381    
382 root 1.63 $self->{face_widget} ||= new CFClient::UI::Face
383     can_events => 1,
384     can_hover => 1,
385 root 1.67 anim => $self->{anim},
386 root 1.66 animspeed => $self->{animspeed}, # TODO# must be set at creation time
387 root 1.72 on_button_down => $button_cb,
388 root 1.63 ;
389 root 1.62 $self->{face_widget}{face} = $self->{face};
390     $self->{face_widget}{anim} = $self->{anim};
391 root 1.65 $self->{face_widget}{animspeed} = $self->{animspeed};
392 root 1.62 $self->{face_widget}->set_tooltip (
393     "<b>Face/Animation.</b>\n"
394     . "Item uses face #$self->{face}. "
395     . ($self->{animspeed} ? "Item uses animation #$self->{anim} at " . (1 / $self->{animspeed}) . "fps. " : "Item is not animated. ")
396     . "\n\n$tooltip_std"
397     );
398    
399 root 1.63 $self->{desc_widget} ||= new CFClient::UI::Label
400     can_events => 1,
401     can_hover => 1,
402     ellipsise => 2,
403 root 1.68 align => -1,
404 root 1.72 on_button_down => $button_cb,
405 root 1.63 ;
406     my $desc = CFClient::Item::desc_string $self;
407     $self->{desc_widget}->set_text ($desc);
408     $self->{desc_widget}->set_tooltip ("<b>$desc</b>.\n$tooltip_std");
409    
410     $self->{weight_widget} ||= new CFClient::UI::Label
411     can_events => 1,
412     can_hover => 1,
413     ellipsise => 0,
414 root 1.68 align => 0,
415 root 1.72 on_button_down => $button_cb,
416 root 1.63 ;
417 root 1.62 $self->{weight_widget}->set_text (CFClient::Item::weight_string $self);
418    
419     $self->{weight_widget}->set_tooltip (
420     "<b>Weight</b>.\n"
421     . ($self->{weight} >= 0 ? "One item weighs $self->{weight}g. " : "You have no idea how much this weighs. ")
422     . ($self->{nrof} ? "You have $self->{nrof} of it. " : "Item cannot stack with others of it's kind. ")
423     . "\n\n$tooltip_std"
424     );
425     }
426    
427 root 1.1 1;
428    
429     =back
430    
431     =head1 AUTHOR
432    
433     Marc Lehmann <schmorp@schmorp.de>
434     http://home.schmorp.de/
435    
436     =cut
437