line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Games::Roguelike::World; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# purpose of library: |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# keep track of map/location |
6
|
|
|
|
|
|
|
# convenience for collision, line of sight, path-finding |
7
|
|
|
|
|
|
|
# assume some roguelike concepts (mobs/items) |
8
|
|
|
|
|
|
|
# allow someone to make 7-day rl's in 7-days |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
=head1 NAME |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
Games::Roguelike::World - Roguelike World |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=head1 SYNOPSIS |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
package myWorld; |
17
|
|
|
|
|
|
|
use base 'Games::Roguelike::World'; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
$r = myWorld->new(w=>80,h=>50,dispw=>40,disph=>18); # creates a world with specified width/height & map display width/height |
20
|
|
|
|
|
|
|
$r->area(new Games::Roguelike::Area(name=>'1')); # create a new area in this world called "1" |
21
|
|
|
|
|
|
|
$r->area->genmaze2(); # make a cavelike maze |
22
|
|
|
|
|
|
|
$char = Games::Roguelike::Mob->new($r->area, sym=>'@', pov=>8); # add a mobile object with symbol '@' |
23
|
|
|
|
|
|
|
$r->setvp($char); # set viewpoint to be from $char's perspective |
24
|
|
|
|
|
|
|
$r->drawmap(); # draw the active area map from the current perspective |
25
|
|
|
|
|
|
|
while (!((my $c = $r->getch()) eq 'q')) { |
26
|
|
|
|
|
|
|
$char->kbdmove($c); |
27
|
|
|
|
|
|
|
$r->drawmap(); |
28
|
|
|
|
|
|
|
} |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=head1 DESCRIPTION |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
General pupose object which pulls together field of view, item, mob handling and map drawing code. |
33
|
|
|
|
|
|
|
|
34
|
|
|
|
|
|
|
* contains a hash of Games::Roguelike::Area's for each "level" or "region" in the game |
35
|
|
|
|
|
|
|
* uses the Games::Roguelike::Console library to draw the current area |
36
|
|
|
|
|
|
|
* assumes the user will be using overridden Games::Roguelike::Mob's as characters in the game |
37
|
|
|
|
|
|
|
* assumes the user will be using overridden Games::Roguelike::Item's as items in the game |
38
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
=head2 METHODS |
40
|
|
|
|
|
|
|
|
41
|
|
|
|
|
|
|
=over 4 |
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
=cut |
44
|
|
|
|
|
|
|
|
45
|
3
|
|
|
3
|
|
30027
|
use strict; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
145
|
|
46
|
3
|
|
|
3
|
|
600
|
use Games::Roguelike::Utils qw(:all); |
|
3
|
|
|
|
|
13
|
|
|
3
|
|
|
|
|
1496
|
|
47
|
3
|
|
|
3
|
|
300
|
use Games::Roguelike::Console; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
141
|
|
48
|
3
|
|
|
3
|
|
19
|
use Games::Roguelike::Mob; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
78
|
|
49
|
|
|
|
|
|
|
|
50
|
3
|
|
|
3
|
|
9413
|
use Math::Trig; |
|
3
|
|
|
|
|
130200
|
|
|
3
|
|
|
|
|
689
|
|
51
|
3
|
|
|
3
|
|
42
|
use Data::Dumper; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
158
|
|
52
|
3
|
|
|
3
|
|
19
|
use Carp qw(croak confess carp); |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
13227
|
|
53
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
our $AUTOLOAD; |
55
|
|
|
|
|
|
|
our $VERSION = '0.4.' . [qw$Revision: 256 $]->[1]; |
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=item new(OPT1=>VAL1, OPT2=>VAL2...) |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
Options can also all be set/get as class accessors: |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
vp => undef # Games::Roguelike::Mob that is the 'viewpoint' |
62
|
|
|
|
|
|
|
dispx, dispy => (0,1) # x/y location, of the map |
63
|
|
|
|
|
|
|
dispw, disph => (60,24) # width & height of the map |
64
|
|
|
|
|
|
|
msgx, msgy => (0,0) # x/y location of the "scrolling message box" |
65
|
|
|
|
|
|
|
msgw, msgh => (60, 1) # width & height of the "scrolling message box" |
66
|
|
|
|
|
|
|
maxlog => 80, # maximum number of rows stored message log |
67
|
|
|
|
|
|
|
msgoldcolor => 'gray', # color of non-curent messages (if left blank, color is left alone) |
68
|
|
|
|
|
|
|
wsym => '#', # default wall symbol |
69
|
|
|
|
|
|
|
fsym => '.', # default floor symbol |
70
|
|
|
|
|
|
|
dsym => '+', # default door symbol |
71
|
|
|
|
|
|
|
debugmap => 0, # turn on map coordinate display |
72
|
|
|
|
|
|
|
debug => 0, # debug level (higher = more) |
73
|
|
|
|
|
|
|
noview => '#+', # list of symbols that block view |
74
|
|
|
|
|
|
|
nomove => '#', # list of symbols that block movement |
75
|
|
|
|
|
|
|
area => undef, # Games::Roguelike::Area that contains the currrent map |
76
|
|
|
|
|
|
|
|
77
|
|
|
|
|
|
|
None of these features have to be used, and can be easily ignored or overridden. |
78
|
|
|
|
|
|
|
|
79
|
|
|
|
|
|
|
=cut |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub new { |
82
|
5
|
|
|
5
|
1
|
15381
|
my $pkg = shift; |
83
|
5
|
50
|
|
|
|
31
|
croak "usage: Games::Roguelike::World->new()" unless $pkg; |
84
|
|
|
|
|
|
|
|
85
|
5
|
|
|
|
|
19
|
my $self = bless {}, $pkg; |
86
|
5
|
|
|
|
|
45
|
$self->init(@_); |
87
|
5
|
|
|
|
|
92
|
return $self; |
88
|
|
|
|
|
|
|
} |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
sub init { |
91
|
5
|
|
|
5
|
0
|
10
|
my $self = shift; |
92
|
|
|
|
|
|
|
|
93
|
5
|
|
|
|
|
53
|
$self->{hasmem} = 1; |
94
|
5
|
|
|
|
|
17
|
$self->{dispy} = 1; |
95
|
5
|
|
|
|
|
16
|
$self->{dispx} = 0; |
96
|
5
|
|
|
|
|
14
|
$self->{h} = 40; |
97
|
5
|
|
|
|
|
14
|
$self->{w} = 80; |
98
|
5
|
|
|
|
|
15
|
$self->{maxlog} = 80; |
99
|
5
|
|
|
|
|
12
|
$self->{msgx} = 0; |
100
|
5
|
|
|
|
|
19
|
$self->{msgoldcolor} = 'gray'; |
101
|
5
|
|
|
|
|
16
|
$self->{msgy} = 0; |
102
|
5
|
|
|
|
|
11
|
$self->{msgh} = 1; |
103
|
5
|
|
|
|
|
28
|
$self->{noview} = '#+'; |
104
|
5
|
|
|
|
|
15
|
$self->{wsym} = '#'; # default wall symbol |
105
|
5
|
|
|
|
|
12
|
$self->{fsym} = '.'; # default floor symbol |
106
|
5
|
|
|
|
|
17
|
$self->{dsym} = '+'; |
107
|
5
|
|
|
|
|
11
|
$self->{debugmap} = 0; |
108
|
5
|
|
|
|
|
20
|
$self->{vp} = undef; |
109
|
5
|
|
|
|
|
15
|
$self->{dn} = 0; |
110
|
5
|
|
|
|
|
16
|
$self->{memcolor} = 'gray'; |
111
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
# allow all of the above to be overridden by params |
113
|
5
|
|
|
|
|
43
|
while( my ($k, $v) = splice(@_, 0, 2)) { |
114
|
16
|
|
|
|
|
58
|
$self->{$k} = $v; |
115
|
|
|
|
|
|
|
} |
116
|
|
|
|
|
|
|
|
117
|
5
|
50
|
|
|
|
28
|
$self->{nomove} = $self->{wsym} unless $self->{nomove}; # by default, can't move through walls |
118
|
5
|
100
|
|
|
|
31
|
$self->{disph} = min(24, $self->{h}) unless $self->{disph}; # default display sizes |
119
|
5
|
100
|
|
|
|
26
|
$self->{dispw} = min(60,$self->{w}) unless $self->{dispw}; |
120
|
5
|
50
|
|
|
|
46
|
$self->{msgw} = min(60,$self->{dispw}) unless $self->{msgw}; # default message window size |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
# create console object |
123
|
5
|
100
|
66
|
|
|
91
|
$self->{con} = new Games::Roguelike::Console(noinit=>$self->{noinit}, type=>$self->{console_type}) |
124
|
|
|
|
|
|
|
unless $self->{con} || $self->{noconsole}; |
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
=item area([name or Games::Roguelike::Area]) |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
No arguments: returns the current area |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
Specify a scalar name: returns an area with that name |
132
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Specify an Games::Roguelike::Area object: stores that object in the area hash, |
134
|
|
|
|
|
|
|
overwriting any with the same name, then makes it the active area |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
=cut |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
sub area { |
139
|
12
|
|
|
12
|
1
|
27
|
my $self = shift; |
140
|
12
|
100
|
|
|
|
36
|
if (@_) { |
141
|
5
|
100
|
|
|
|
14
|
if (ref($_[0])) { |
142
|
4
|
|
|
|
|
17
|
my $area = shift; |
143
|
4
|
|
|
|
|
30
|
$self->addarea($area); |
144
|
4
|
|
|
|
|
10
|
$self->{area} = $area; |
145
|
|
|
|
|
|
|
} else { |
146
|
1
|
|
|
|
|
13
|
return $self->{areas}->{$_[0]}; |
147
|
|
|
|
|
|
|
} |
148
|
|
|
|
|
|
|
} |
149
|
11
|
|
|
|
|
228
|
return $self->{area}; |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
sub areas { |
153
|
0
|
|
|
0
|
0
|
0
|
my $self = shift; |
154
|
0
|
|
|
|
|
0
|
return values(%{$self->{areas}}); |
|
0
|
|
|
|
|
0
|
|
155
|
|
|
|
|
|
|
} |
156
|
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
sub addarea { |
158
|
4
|
|
|
4
|
0
|
6
|
my $self = shift; |
159
|
4
|
|
|
|
|
7
|
my $area = shift; |
160
|
4
|
50
|
33
|
|
|
24
|
confess("this world already has an area named $area->{name}") |
161
|
|
|
|
|
|
|
if $self->{areas}->{$area->{name}} && $self->{areas}->{$area->{name}} != $area; |
162
|
4
|
|
|
|
|
14
|
$self->{areas}->{$area->{name}} = $area; |
163
|
|
|
|
|
|
|
} |
164
|
|
|
|
|
|
|
|
165
|
|
|
|
|
|
|
# perl accessors are slow compared to just accessing the hash directly |
166
|
|
|
|
|
|
|
# autoload is even slower |
167
|
|
|
|
|
|
|
sub AUTOLOAD { |
168
|
3
|
|
|
3
|
|
66
|
my $self = shift; |
169
|
3
|
50
|
|
|
|
15
|
my $pkg = ref($self) or croak "$self is not an object"; |
170
|
|
|
|
|
|
|
|
171
|
3
|
|
|
|
|
9
|
my $name = $AUTOLOAD; |
172
|
3
|
|
|
|
|
20
|
$name =~ s/.*://; # strip fully-qualified portion |
173
|
|
|
|
|
|
|
|
174
|
3
|
50
|
33
|
|
|
36
|
$name =~ s/^set// if @_ && !exists $self->{$name}; |
175
|
|
|
|
|
|
|
|
176
|
3
|
50
|
|
|
|
14
|
unless (exists $self->{$name}) { |
177
|
0
|
|
|
|
|
0
|
croak "Can't access `$name' field in class $pkg"; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
3
|
50
|
|
|
|
13
|
if (@_) { |
181
|
3
|
|
|
|
|
51
|
return $self->{$name} = $_[0]; |
182
|
|
|
|
|
|
|
} else { |
183
|
0
|
|
|
|
|
0
|
return $self->{$name}; |
184
|
|
|
|
|
|
|
} |
185
|
|
|
|
|
|
|
} |
186
|
|
|
|
|
|
|
|
187
|
1
|
|
|
1
|
|
108
|
sub DESTROY { |
188
|
|
|
|
|
|
|
} |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=item dprint ( msg1 [,msg2...msgn] [,level] ) |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Debug print messages |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
For now, hard coded to far right side of screen, at col 82, past most terminal game widths |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
=cut |
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub dprint { |
199
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
200
|
|
|
|
|
|
|
|
201
|
0
|
|
|
|
|
0
|
my $level = 1; |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
# last arg is an integer number |
204
|
0
|
0
|
|
|
|
0
|
$level = pop if int(0+$_[$#_]) eq $_[$#_]; |
205
|
|
|
|
|
|
|
|
206
|
0
|
0
|
|
|
|
0
|
return unless $self->{debug} >= $level; |
207
|
|
|
|
|
|
|
|
208
|
|
|
|
|
|
|
#windows cant have a "wide" console |
209
|
0
|
0
|
0
|
|
|
0
|
if ($self->{con} && ref($self->{con}) !~ /win32/i && ref($self->{con}) !~ /dump/i) { |
|
|
|
0
|
|
|
|
|
210
|
0
|
|
|
|
|
0
|
my $msg = substr(join("\t",@_),0,40); |
211
|
0
|
|
|
|
|
0
|
$self->{con}->addstr($self->{dn},82,$msg . (" " x (40-length($msg)))); |
212
|
0
|
|
|
|
|
0
|
++$self->{dn}; |
213
|
0
|
0
|
|
|
|
0
|
$self->{dn} = 0 if $self->{dn} > 30; |
214
|
|
|
|
|
|
|
} else { |
215
|
0
|
|
|
|
|
0
|
my $msg = join("\t",@_); |
216
|
0
|
|
|
|
|
0
|
open DEBUG, ">>rll-debug.txt"; |
217
|
0
|
|
|
|
|
0
|
print DEBUG scalar(localtime), "\t", $msg, "\n"; |
218
|
0
|
|
|
|
|
0
|
close DEBUG; |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
} |
221
|
|
|
|
|
|
|
|
222
|
|
|
|
|
|
|
=item getch () |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
Read one character, blocks until a char is pressed. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=cut |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
sub getch { |
229
|
3
|
|
|
3
|
1
|
45
|
my $self = shift; |
230
|
3
|
|
|
|
|
23
|
$self->{con}->getch(); |
231
|
|
|
|
|
|
|
} |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=item getstr ([echo=>1[,empty=>0]]) |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
Calls getch repeatedly, optionally echoing characters to the active console. If "empty" is not |
236
|
|
|
|
|
|
|
set to true, it will not return empty strings. |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
=cut |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
sub getstr { |
241
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
242
|
0
|
|
|
|
|
0
|
my %opts = @_; |
243
|
0
|
0
|
|
|
|
0
|
$opts{max} = 40 if !defined $opts{max}; |
244
|
0
|
0
|
|
|
|
0
|
$opts{echo} = 1 if !defined $opts{echo}; |
245
|
0
|
0
|
|
|
|
0
|
$opts{empty} = 0 if !defined $opts{empty}; |
246
|
|
|
|
|
|
|
|
247
|
0
|
|
|
|
|
0
|
$self->{con}->cursor(1); |
248
|
0
|
|
|
|
|
0
|
my ($c, $str); |
249
|
0
|
|
|
|
|
0
|
while (1) { |
250
|
0
|
|
|
|
|
0
|
$c = $self->{con}->getch(); |
251
|
0
|
0
|
|
|
|
0
|
if ($c =~ /[\n\r]/) { |
252
|
0
|
0
|
0
|
|
|
0
|
last if length($str) > 0 || $opts{empty}; |
253
|
|
|
|
|
|
|
} |
254
|
0
|
0
|
0
|
|
|
0
|
if ($opts{echo} && length($str) < $opts{max}) { |
255
|
0
|
0
|
0
|
|
|
0
|
if ($c eq 'BACKSPACE') { |
|
|
0
|
0
|
|
|
|
|
256
|
0
|
|
|
|
|
0
|
$self->{con}->addch(chr(8)); |
257
|
0
|
|
|
|
|
0
|
$self->{con}->addch(' '); |
258
|
0
|
|
|
|
|
0
|
$self->{con}->addch(chr(8)); |
259
|
|
|
|
|
|
|
} elsif ((length($c)==1) && (ord($c) > 30) && (ord($c) < 128)) { |
260
|
0
|
|
|
|
|
0
|
$self->{con}->addch($c); |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
} |
263
|
0
|
|
|
|
|
0
|
$self->{con}->refresh(); |
264
|
0
|
0
|
0
|
|
|
0
|
if ($c eq 'BACKSPACE') { |
|
|
0
|
0
|
|
|
|
|
265
|
0
|
|
|
|
|
0
|
$str = substr($str, 0, -1); |
266
|
|
|
|
|
|
|
} elsif ((length($c)==1) && (ord($c) > 30) && (ord($c) < 128)) { |
267
|
0
|
|
|
|
|
0
|
$str .= $c; |
268
|
|
|
|
|
|
|
}; |
269
|
0
|
0
|
|
|
|
0
|
$c = '' if !length($str); |
270
|
|
|
|
|
|
|
} |
271
|
|
|
|
|
|
|
|
272
|
0
|
|
|
|
|
0
|
$self->{con}->cursor(0); |
273
|
0
|
|
|
|
|
0
|
chomp $str; |
274
|
0
|
|
|
|
|
0
|
return $str; |
275
|
|
|
|
|
|
|
} |
276
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
=item refresh () |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
Refreshes the console display. |
281
|
|
|
|
|
|
|
|
282
|
|
|
|
|
|
|
=cut |
283
|
|
|
|
|
|
|
|
284
|
|
|
|
|
|
|
sub refresh { |
285
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
286
|
0
|
|
|
|
|
0
|
$self->{con}->refresh(); |
287
|
|
|
|
|
|
|
} |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=item nbgetch () |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
Read one character, nonblocking, returns undef if none are available. |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
=cut |
294
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
sub nbgetch { |
296
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
297
|
0
|
|
|
|
|
0
|
$self->{con}->nbgetch(); |
298
|
|
|
|
|
|
|
} |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=item findfeature (symbol) |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
searches "map feature list" for the given symbol, returns coordinates if found |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=cut |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
sub findfeature { |
307
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
308
|
0
|
|
|
|
|
0
|
return $self->{area}->findfeature(@_); |
309
|
|
|
|
|
|
|
} |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=item dispclear () |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
Erases the "display world", and resets the "display line" (used by dispstr) |
314
|
|
|
|
|
|
|
|
315
|
|
|
|
|
|
|
Useful for displaying an in-game menu, inventory, ability or skill list, etc. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
=cut |
318
|
|
|
|
|
|
|
|
319
|
|
|
|
|
|
|
sub dispclear { |
320
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
321
|
|
|
|
|
|
|
|
322
|
0
|
|
|
|
|
0
|
my ($y) = @_; |
323
|
0
|
0
|
|
|
|
0
|
$y = $self->{dispy} if ! defined $y; |
324
|
|
|
|
|
|
|
|
325
|
0
|
|
|
|
|
0
|
for (my $i = $y; $i < ($self->{disph}+$self->{dispy}); ++$i) { |
326
|
0
|
|
|
|
|
0
|
$self->{con}->addstr($i,$self->{dispx}," " x ($self->{dispw})); |
327
|
|
|
|
|
|
|
} |
328
|
0
|
|
|
|
|
0
|
$self->{displine} = $self->{dispy}; |
329
|
|
|
|
|
|
|
} |
330
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
=item dispstr (str[, line]) |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
Draws a tagged string at the "displine" position and increments the "displine". |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
Return value: 0 (offscreen, did not draw), 1 (ok), 2 (ok, but next call will be offscreen). |
336
|
|
|
|
|
|
|
|
337
|
|
|
|
|
|
|
=cut |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
sub dispstr { |
340
|
0
|
|
|
0
|
1
|
0
|
my $self = shift; |
341
|
0
|
|
|
|
|
0
|
my ($str, $line) = @_; |
342
|
|
|
|
|
|
|
|
343
|
0
|
|
|
|
|
0
|
my $ret = 1; |
344
|
|
|
|
|
|
|
|
345
|
0
|
0
|
|
|
|
0
|
if ($line) { |
346
|
0
|
|
|
|
|
0
|
$self->{displine} = $line; |
347
|
|
|
|
|
|
|
} |
348
|
|
|
|
|
|
|
|
349
|
0
|
|
|
|
|
0
|
for (split(/\n/, $str)) { |
350
|
0
|
0
|
|
|
|
0
|
if ($self->{displine} >= ($self->{dispy} + $self->{disph})) { |
351
|
0
|
|
|
|
|
0
|
return 0; |
352
|
|
|
|
|
|
|
} |
353
|
0
|
|
|
|
|
0
|
$self->{con}->tagstr($self->{displine}, $self->{dispx}, rpad($_, $self->{dispw})); |
354
|
0
|
|
|
|
|
0
|
$self->{con}->move($self->{displine}, $self->{dispx}+length($_)); |
355
|
0
|
|
|
|
|
0
|
$self->{displine} += 1; |
356
|
|
|
|
|
|
|
} |
357
|
|
|
|
|
|
|
|
358
|
0
|
0
|
|
|
|
0
|
if ($self->{displine} >= ($self->{dispy} + $self->{disph})) { |
359
|
0
|
|
|
|
|
0
|
$ret = 2; |
360
|
|
|
|
|
|
|
} |
361
|
|
|
|
|
|
|
|
362
|
0
|
|
|
|
|
0
|
return $ret; |
363
|
|
|
|
|
|
|
} |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
=item drawmap () |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Draws the map, usually do this after each move |
368
|
|
|
|
|
|
|
|
369
|
|
|
|
|
|
|
=cut |
370
|
|
|
|
|
|
|
|
371
|
|
|
|
|
|
|
sub drawmap { |
372
|
3
|
|
|
3
|
1
|
27
|
my $self = shift; |
373
|
3
|
|
|
|
|
23
|
$self->{area}->draw($self); |
374
|
|
|
|
|
|
|
} |
375
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
=item prompt (msg[, match]) |
377
|
|
|
|
|
|
|
|
378
|
|
|
|
|
|
|
Same as showmsg, but also shows the cursor, and gets a character response, optionally waiting until it matches. |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
=cut |
381
|
|
|
|
|
|
|
|
382
|
|
|
|
|
|
|
sub prompt { |
383
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
384
|
0
|
|
|
|
|
|
my ($msg, $match) = @_; |
385
|
0
|
0
|
|
|
|
|
$match = '.' if !$match; |
386
|
0
|
|
|
|
|
|
$self->showmsg($msg); |
387
|
0
|
|
|
|
|
|
$self->{con}->cursor(1); |
388
|
0
|
|
|
|
|
|
$self->{con}->move($self->{msgy},$self->{msgx}+length($msg)+1); |
389
|
0
|
|
|
|
|
|
my $c; |
390
|
0
|
|
|
|
|
|
do { |
391
|
0
|
|
|
|
|
|
$c = $self->getch(); |
392
|
|
|
|
|
|
|
} while ($c !~ /$match/); |
393
|
0
|
|
|
|
|
|
$self->{con}->cursor(0); |
394
|
0
|
|
|
|
|
|
return $c; |
395
|
|
|
|
|
|
|
} |
396
|
|
|
|
|
|
|
|
397
|
|
|
|
|
|
|
=item cursor (bool) |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
Turn on/off display of cursor for next operation. |
400
|
|
|
|
|
|
|
|
401
|
|
|
|
|
|
|
=cut |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
sub cursor { |
404
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
405
|
0
|
|
|
|
|
|
$self->{con}->cursor(@_); |
406
|
|
|
|
|
|
|
} |
407
|
|
|
|
|
|
|
|
408
|
|
|
|
|
|
|
=item pushmsg (msg, color) |
409
|
|
|
|
|
|
|
|
410
|
|
|
|
|
|
|
Shows a message and pushes it into the log. Use of color argument is deprecated. Prefer to use "<$color>$msg" tagged strings. |
411
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
=cut |
413
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
sub pushmsg { |
415
|
0
|
|
|
0
|
1
|
|
return showmsg(@_[0..2],1); |
416
|
|
|
|
|
|
|
} |
417
|
|
|
|
|
|
|
|
418
|
|
|
|
|
|
|
=item showmsg (msg, color[, push]) |
419
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
Shows a message at msgx, msgy coorinates and optionally logs it. Also displays up to (msgh-1) old messages. |
421
|
|
|
|
|
|
|
|
422
|
|
|
|
|
|
|
=cut |
423
|
|
|
|
|
|
|
|
424
|
|
|
|
|
|
|
sub showmsg { |
425
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
426
|
0
|
|
|
|
|
|
my ($msg, $color, $keep) = @_; |
427
|
0
|
|
|
|
|
|
$msg = substr($msg, 0, $self->{msgw}); |
428
|
|
|
|
|
|
|
|
429
|
|
|
|
|
|
|
# use the character's log, unless there is none |
430
|
0
|
0
|
|
|
|
|
my $msglog = $self->{vp} ? $self->{vp}->{msglog} : $self->{msglog} ? $self->{msglog} : ($self->{msglog} = []); |
|
|
0
|
|
|
|
|
|
431
|
|
|
|
|
|
|
|
432
|
0
|
|
|
|
|
|
push @$msglog, [$msg, $color]; |
433
|
|
|
|
|
|
|
|
434
|
0
|
0
|
|
|
|
|
if (@$msglog > $self->{maxlog}) { |
435
|
0
|
|
|
|
|
|
shift @$msglog; |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
0
|
|
|
|
|
|
my $mlx = $#{$msglog}; |
|
0
|
|
|
|
|
|
|
439
|
0
|
|
|
|
|
|
for (my $i = 0; $i < $self->{msgh}; ++$i) { |
440
|
0
|
0
|
|
|
|
|
next unless $i <= $mlx; # no more messages in log |
441
|
0
|
|
|
|
|
|
my ($m, $a) = @{$msglog->[$mlx-$i]}; |
|
0
|
|
|
|
|
|
|
442
|
0
|
0
|
|
|
|
|
if ($self->{msgoldcolor}) { |
443
|
0
|
0
|
|
|
|
|
$a = $self->{msgoldcolor} if $i > 0; |
444
|
0
|
|
|
|
|
|
$m =~ s/<[^<>]*>//g; |
445
|
|
|
|
|
|
|
} |
446
|
0
|
0
|
|
|
|
|
$m = "<$a>$m" if $a; |
447
|
0
|
|
|
|
|
|
$self->{con}->tagstr($self->{msgy}+$i, $self->{msgx}, $m.(' 'x($self->{msgw}-length($m)))); |
448
|
|
|
|
|
|
|
} |
449
|
|
|
|
|
|
|
|
450
|
0
|
|
|
|
|
|
$self->{con}->move($self->{msgy},$self->{msgx}+length($msglog->[0]->[0])); |
451
|
|
|
|
|
|
|
|
452
|
0
|
0
|
|
|
|
|
if (!$keep) { |
453
|
0
|
|
|
|
|
|
pop @$msglog; |
454
|
|
|
|
|
|
|
} |
455
|
|
|
|
|
|
|
|
456
|
0
|
|
|
|
|
|
$self->{con}->cursor(0); |
457
|
0
|
|
|
|
|
|
$self->{con}->refresh(); |
458
|
|
|
|
|
|
|
} |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
sub showmsglog { |
461
|
0
|
|
|
0
|
0
|
|
my @sort; |
462
|
0
|
|
|
|
|
|
my $self = shift; |
463
|
0
|
|
|
|
|
|
my $x = $self->{dispx}; |
464
|
0
|
|
|
|
|
|
my $y = $self->{dispy}; |
465
|
0
|
|
|
|
|
|
my $h = $self->{disph}; |
466
|
0
|
0
|
0
|
|
|
|
if ($x == $self->{msgx} && ($self->{msgy} + $self->{msgh}) == $y) { |
467
|
0
|
|
|
|
|
|
$y=$self->{msgy}; |
468
|
|
|
|
|
|
|
} |
469
|
0
|
0
|
0
|
|
|
|
if ($x == $self->{msgx} && ($y + $self->{disph}) == $self->{msgy}) { |
470
|
0
|
|
|
|
|
|
$h = $self->{disph} + $self->{msgh}; |
471
|
|
|
|
|
|
|
} |
472
|
0
|
|
|
|
|
|
for (@{$self->{vp}->{msglog}}) { |
|
0
|
|
|
|
|
|
|
473
|
0
|
|
|
|
|
|
my ($msg,$color) = @$_; |
474
|
0
|
|
|
|
|
|
$self->{con}->attrstr($color,$y,$x,$msg.(' 'x($self->{dispw}-length($msg)))); |
475
|
0
|
|
|
|
|
|
++$y; |
476
|
0
|
0
|
|
|
|
|
last if $y >= $h; |
477
|
|
|
|
|
|
|
} |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
=item save ([file]) |
481
|
|
|
|
|
|
|
|
482
|
|
|
|
|
|
|
Saves the world (!), optionally specify filename which defaults to "rll.world". |
483
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
=cut |
485
|
|
|
|
|
|
|
|
486
|
|
|
|
|
|
|
sub save { |
487
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
488
|
0
|
|
|
|
|
|
my $fn = shift; |
489
|
0
|
0
|
|
|
|
|
$fn = "rll.world" if (!$fn); |
490
|
3
|
|
|
3
|
|
5772
|
use Storable; |
|
3
|
|
|
|
|
13389
|
|
|
3
|
|
|
|
|
519
|
|
491
|
0
|
|
|
|
|
|
local $self->{con} = undef; |
492
|
0
|
|
|
|
|
|
store $self,$fn; |
493
|
|
|
|
|
|
|
} |
494
|
|
|
|
|
|
|
|
495
|
|
|
|
|
|
|
=item load ([file]) |
496
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
Loads a world, optionally specify filename, returns a reference to the new world. |
498
|
|
|
|
|
|
|
|
499
|
|
|
|
|
|
|
Console is not initialized, and is, instead, copied from the current world. |
500
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
=cut |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
sub load { |
504
|
0
|
|
|
0
|
1
|
|
my $self = shift; |
505
|
0
|
|
|
|
|
|
my $fn = shift; |
506
|
0
|
0
|
|
|
|
|
$fn = "rll.world" if (!$fn); |
507
|
3
|
|
|
3
|
|
34
|
use Storable; |
|
3
|
|
|
|
|
6
|
|
|
3
|
|
|
|
|
347
|
|
508
|
|
|
|
|
|
|
|
509
|
0
|
|
|
|
|
|
my $n = retrieve $fn; |
510
|
|
|
|
|
|
|
|
511
|
0
|
|
|
|
|
|
$n->{con} = $self->{con}; |
512
|
0
|
|
|
|
|
|
$n->{console_type} = $self->{console_type}; |
513
|
|
|
|
|
|
|
|
514
|
0
|
|
|
|
|
|
return $n; |
515
|
|
|
|
|
|
|
} |
516
|
|
|
|
|
|
|
|
517
|
|
|
|
|
|
|
=back |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
=head1 SEE ALSO |
520
|
|
|
|
|
|
|
|
521
|
|
|
|
|
|
|
L, L, L |
522
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
=head1 AUTHOR |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
Erik Aronesty C |
526
|
|
|
|
|
|
|
|
527
|
|
|
|
|
|
|
=head1 LICENSE |
528
|
|
|
|
|
|
|
|
529
|
|
|
|
|
|
|
This program is free software; you can redistribute it and/or |
530
|
|
|
|
|
|
|
modify it under the same terms as Perl itself. |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
See L or the included LICENSE file. |
533
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
=cut |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
1; |
537
|
|
|
|
|
|
|
|