File Coverage

blib/lib/Games/Traveller/Animals.pm
Criterion Covered Total %
statement 4 6 66.6
branch n/a
condition n/a
subroutine 2 2 100.0
pod n/a
total 6 8 75.0


line stmt bran cond sub pod time code
1             package Games::Traveller::Animals;
2              
3 1     1   19857 use 5.008003;
  1         4  
  1         37  
4 1     1   407 use Games::Traveller::Animals::AnimalEncounter;
  0            
  0            
5             use YAML;
6             use strict;
7             use warnings;
8              
9             our $VERSION = '0.50';
10              
11             srand (time ^ (($$ << 15) + $$));
12              
13             {
14             sub new { bless {}, shift }
15             sub dice { $_[1] += int(rand(6)+1) for 1..$_[0]; return $_[1]; }
16              
17             my %terrainTables;
18              
19             sub terrainTables :lvalue { $terrainTables{+shift} }
20            
21             # get a list of the terrain types
22             sub terrainTypes { keys %{$terrainTables{+shift}} }
23              
24             # $self->encounterTable{ 'terrain type' }
25             sub encounterTable { @{$terrainTables{+shift}->{$_[1]}} }
26              
27             sub DESTROY
28             {
29             my $sref = 0+shift;
30             delete $terrainTables{$sref};
31             }
32            
33             my ($terrainTypes,$catalog) = YAML::Load( <<'...' );
34             ---
35             Default attrs: [0,0,0,0,0,0,0,0,0,0,'f-6','f-6','f-3']
36             Terrain:
37             - { name: Clear ,type: 3, mass: 0 }
38             - { name: Prairie ,type: 4, mass: 0 }
39             - { name: Rough ,type: 0, mass: 0 }
40             - { name: Broken ,type: -3, mass: -3 }
41             - { name: Mountain ,type: 0, mass: 0 }
42             - { name: Forest ,type: -4, mass: -4 }
43             - { name: Jungle ,type: -3, mass: -2 }
44             - name: River
45             type: 1
46             mass: 1
47             attrs: [0,0,'s1','a1',0,0,0,0,0,0,0,'f-6','f-5']
48             - name: Swamp
49             type: -2
50             mass: 4
51             attrs: [0,0,'s-3','a1','a1',0,0,0,0,0,0,'f-6','f-5']
52             - name: Marsh
53             type: 0
54             mass: -1
55             attrs: [0,0,'s-6','a2','a1',0,0,0,0,0,0,'f-6','f-5']
56             - { name: Desert ,type: 3, mass: -3 }
57             - name: Beach
58             type: 3
59             mass: 2
60             attrs: [0,0,'s1','a2','a2',0,0,0,0,0,0,'f-6','f-5']
61             - name: Surface
62             type: 2
63             mass: 3
64             attrs: [0,0,'s2','s2','s2','a2','a0','s1','s-1','t-7','t-6','f-6','f-5']
65             - name: Shallows
66             type: 2
67             mass: 2
68             attrs: [0,0,'s2','s2','s2','a2','a0','s1','s-1','t-7','t-6','f-6','f-5']
69             - { name: Depths ,type: -4, mass: 0 }
70             - { name: Bottom ,type: -2, mass: 0 }
71             - { name: Sea cave ,type: -2, mass: 0 }
72             - name: Sargasso
73             type: -4
74             mass: -2
75             attrs: [0,0,'s2','s2','s2','a2','a0','s1','s-1','t-7','t-6','f-6','f-5']
76             - { name: Ruins ,type: -3, mass: 0 }
77             - { name: Cave ,type: -4, mass: 1 }
78             - { name: Chasm ,type: -1, mass: -3 }
79             - { name: Crater ,type: 0, mass: -1 }
80             ---
81             Subtypes:
82             - &flt { name: Filter, F: 2, A: i, S: -5, flee first: true }
83             - &int { name: Intermittent, F: 3, A: 3, S: -4, flee first: true }
84             - &grz { name: Grazer, F: -1, A: 2, S: -2, flee first: true }
85             - >h { name: Gatherer, A: 3, F: 2, S: -3 }
86             - &hnt { name: Hunter, A: 0, F: 2, S: -4 }
87             - &etr { name: Eater, A: 0, F: 3, S: -3 }
88             - &pnc { name: Pouncer, A: s, F: s, S: -4 }
89             - &chs { name: Chaser, A: m, F: 3, S: -2 }
90             - &trp { name: Trapper, A: s, F: 2, S: -5 }
91             - &srn { name: Siren, A: s, F: 3, S: -4 }
92             - &kll { name: Killer, A: 0, F: 3, S: -3 }
93             - &hjc { name: Hijacker, A: 1, F: 2, S: -4 }
94             - &inm { name: Intimidator, A: 2, F: 1, S: -4 }
95             - &crr { name: Carrion Eater, A: 3, F: 2, S: -3 }
96             - &rdc { name: Reducer, A: 3, F: 2, S: -4 }
97              
98             Categories:
99             - &scavenger
100             name: Scavenger
101             WeaponDM: 0
102             ArmorDM: 1
103             List:
104             - [ *crr, 1 ]
105             - [ *crr, 2 ]
106             - [ *rdc, 1 ]
107             - [ *hjc, 1 ]
108             - [ *crr, 2 ]
109             - [ *inm, 1 ]
110             - [ *rdc, 0 ]
111             - [ *crr, 1 ]
112             - [ *rdc, 0 ]
113             - [ *hjc, 0 ]
114             - [ *inm, 0 ]
115             - [ *rdc, 1 ]
116             - [ *hjc, 0 ]
117             - [ *inm, 1 ]
118             - &omnivore
119             name: Omnivore:
120             WeaponDM: 4
121             ArmorDM: 0
122             List:
123             - [ *gth, 0 ]
124             - [ *gth, 0 ]
125             - [ *etr, 0 ]
126             - [ *gth, 0 ]
127             - [ *etr, 2 ]
128             - [ *gth, 0 ]
129             - [ *hnt, 0 ]
130             - [ *hnt, 1 ]
131             - [ *hnt, 0 ]
132             - [ *gth, 0 ]
133             - [ *etr, 1 ]
134             - [ *hnt, 1 ]
135             - [ *gth, 0 ]
136             - [ *gth, 0 ]
137             - &herbivore
138             name: Herbivore
139             WeaponDM: -3
140             ArmorDM: 2
141             List:
142             - [ *flt, 1 ]
143             - [ *flt, 0 ]
144             - [ *flt, 0 ]
145             - [ *int, 0 ]
146             - [ *int, 0 ]
147             - [ *int, 0 ]
148             - [ *int, 0 ]
149             - [ *grz, 0 ]
150             - [ *grz, 0 ]
151             - [ *grz, 1 ]
152             - [ *grz, 2 ]
153             - [ *grz, 3 ]
154             - [ *grz, 4 ]
155             - [ *grz, 5 ]
156             - &carnivore
157             name: Carnivore
158             WeaponDM: 8
159             ArmorDM: -1
160             List:
161             - [ *srn, 0 ]
162             - [ *pnc, 0 ]
163             - [ *srn, 0 ]
164             - [ *pnc, 0 ]
165             - [ *kll, 1 ]
166             - [ *trp, 0 ]
167             - [ *pnc, 0 ]
168             - [ *chs, 0 ]
169             - [ *chs, 3 ]
170             - [ *chs, 0 ]
171             - [ *kll, 0 ]
172             - [ *chs, 2 ]
173             - [ *srn, 0 ]
174             - [ *chs, 1 ]
175             - &event
176             name: Event
177             List:
178             - Chameleon
179             - Psionic Assaulters
180             - Circling Flyers
181             - Poisonous Pests
182             - Stampede
183             - Rutting Season
184             - Lair
185             - Hallucinogenic Pollen
186             - Carnivorous Plants
187             - Wirebrushes
188             - Dense Fog
189             - Sandstorm
190             - Cold Snap
191             - Tornado
192             - Rainstorm
193             - Prairie Fire
194             - Flash Flood
195             - Volcano
196             - Seismic Quake
197             - Broken Ground
198             - Oasis
199             - Crevasse
200             - Radiation Area
201             - Quicksand
202             - Ford
203             - Statues
204             - Jungle Drums
205             - Marsh Gas
206             - Dust Pool
207             - Solar Storm
208             - Magnetic Anomaly
209             - Tracks
210             - Pressure Tent
211              
212             Main Table:
213             - *scavenger
214             - *omnivore
215             - *scavenger
216             - *omnivore
217             - *herbivore
218             - *herbivore
219             - *herbivore
220             - *carnivore
221             - *event
222             - *carnivore
223             - *carnivore
224              
225             ...
226            
227              
228             sub toString
229             {
230             my $self = shift;
231             my $tables = $self->terrainTables;
232             my @types = $self->terrainTypes;
233             my $out = '';
234            
235             foreach my $terrain (@types)
236             {
237             my @list = @{$tables->{$terrain}};
238            
239             $out .= "\n\nTerrain: $terrain\n";
240             $out .= " Category Size Ht Weapon Mod Armor Behaviour\n";
241             $out .= '-' x 79, "\n";
242              
243             foreach my $entry (@list)
244             {
245             $out .= $entry->toString();
246             }
247             }
248             return $out;
249             }
250            
251             sub generateAnimalTable
252             {
253             my $self = shift;
254             my $sref = 0+$self;
255             my $worldSize = shift;
256             my $worldAtmosphere = shift;
257            
258             my %terrainTables = ();
259            
260             foreach my $terrain (@{$terrainTypes->{Terrain}})
261             {
262             my $name = $terrain->{name};
263             my $tdm = $terrain->{type};
264             my $wdm = $terrain->{mass};
265             my $attrs = $terrain->{attrs} || $terrainTypes->{'Default attrs'};
266            
267             # print "\n\nTerrain: $name\n";
268             # print " Category Size Ht Weapon Mod Armor Behaviour\n";
269             # print '-' x 79, "\n";
270            
271             my @list = ();
272              
273             for my $index ( 2..12 )
274             {
275             my $encounter = new Games::Traveller::Animals::AnimalEncounter;
276            
277             my ($type, $category, $count, $wdm, $adm, $behaviour) = _getAnimal( $tdm );
278              
279             $encounter->index = $index;
280             $encounter->category = $type;
281            
282             if ( $category =~ /Event/ )
283             {
284             $encounter->attribute = 'Event';
285             push @list, $encounter;
286            
287             # printf( "%2d Event: $type\n", $index );
288             next;
289             }
290              
291             my $attr = _attributes($worldSize, $worldAtmosphere, $attrs);
292            
293             if ($attr =~ /(\w)(.+)/)
294             {
295             $encounter->attribute = $1;
296             # $type = "$1 $type";
297             $wdm += int($2);
298             }
299             else
300             {
301             $encounter->attribute = '';
302             # $type = " $type";
303             }
304            
305             my ($mass, $hit, $dead, $weaponMod) = _weightEffects($wdm);
306             my $weapon = _weaponryTable($wdm);
307             my $armor = _armorTable($adm);
308              
309             $encounter->mass = $mass;
310             $encounter->hits = $hit;
311             $encounter->dead = $dead;
312             $encounter->weapon = $weapon;
313             $encounter->damageMod = $weaponMod;
314             $encounter->armor = $armor;
315             $encounter->behavior = $behaviour;
316            
317             push @list, $encounter;
318            
319             # printf ("%2d%2s %-14s %4s %3.3s/%-2.2s %9.9s %3.3s %-7s %s\n",
320             # $index,
321             # $encounter->attribute,
322             # $encounter->category,
323             # $encounter->mass,
324             # $encounter->hits,
325             # $encounter->dead,
326             # $encounter->weapon,
327             # $encounter->damageMod,
328             # $encounter->armor,
329             # $encounter->behaviour);
330            
331             }
332             $terrainTables{$name} = \@list;
333             }
334             $self->terrainTables = \%terrainTables;
335             }
336            
337             sub _getAnimal
338             {
339             my $typeDM = shift || 0;
340            
341             my @types = @{$catalog->{'Main Table'}};
342            
343             my $categoryref = $types[ int(rand(6)) + int(rand(6)) ];
344             my $category = $categoryref->{name};
345             my $wdm = $categoryref->{WeaponDM};
346             my $adm = $categoryref->{ArmorDM};
347             my @list = @{$categoryref->{List}};
348            
349             my $roll = $typeDM + int(rand(6)) + int(rand(6));
350             $roll = 0 if $roll < 0;
351             $roll = 10 if $roll > 10;
352            
353             my $subtyperef = $list[ $roll ];
354            
355             return ( $subtyperef, 'Event' ) unless ref $subtyperef ;
356              
357             my ($subtyperef, $countIter) = @$subtyperef;
358              
359             my $type = $subtyperef->{name};
360            
361             my $a = int(rand(6)+1) + $subtyperef->{A};
362             my $f = int(rand(6)+1) + $subtyperef->{F};
363             my $s = int(rand(6)+1) + $subtyperef->{S};
364            
365             $s = 0 if $s < 0;
366             $a = 0 if $a < 0;
367             $f = 0 if $f < 0;
368            
369             my $flee1st = $subtyperef->{'flee first'};
370            
371             my $behaviour = "A$a F$f S$s";
372             $behaviour = "F$f A$a S$s" if $flee1st;
373            
374             my $count = 0;
375             $count += int(rand(6)+1) for( 1..$countIter );
376             $count = 1 if $count == 0;
377            
378             return ($type, $category, $count, $wdm, $adm, $behaviour);
379             }
380              
381              
382             ########################################################
383             #
384             # sub : attributes
385             #
386             # desc: determines special attributes for animal
387             #
388             # in : terrain type, world size (L/M/S), atmosphere (Thin/Dense/Exotic)
389             #
390             # out : attribute and DM
391             #
392             ########################################################
393             sub _attributes
394             {
395             my $size = shift;
396             my $atmosphere = shift;
397             my $attrs = shift;
398            
399             my $dm = 0;
400            
401             $dm-- if $size =~ /[89A]/;
402             $dm++ if $size =~ /[4567]/;
403             $dm+=2 if $size =~ /[0S123]/;
404             $dm-- if $atmosphere =~ /[123]/;
405             $dm+=2 if $atmosphere =~ /[9ABCDEF]/;
406            
407             my $roll = &dice(2, $dm);
408             $roll = ($roll < 0 )? 0 :
409             ($roll > 12)? 12 : $roll;
410            
411             return $attrs->[ $roll ];
412             }
413              
414             ########################################################
415             #
416             # sub : weightEffects
417             #
418             # desc: returns mass, hits, and wounds modifier
419             #
420             # in : weight DM
421             #
422             # out :
423             #
424             ########################################################
425             sub _weightEffects
426             {
427             my $weightDM = shift;
428            
429             my @effects =
430             (
431             #
432             # hit wound
433             #
434             ' 1 1 0 -2d',
435             ' 3 1 1 -2d',
436             ' 6 1 2 -1d',
437             ' 12 2 2 -',
438             ' 25 3 2 -',
439             ' 50 4 2 -',
440             '100 5 2 -',
441             '200 5 3 +1d',
442             '400 6 3 +2d',
443             '800 7 3 +3d',
444             ' 2k 8 3 +4d',
445             ' 3k 8 4 +5d',
446             -1,
447             ' 6k 9 4 x2',
448             '12k 10 5 x2',
449             '24k 12 6 x3',
450             '30k 14 7 x4',
451             '36k 15 7 x4',
452             '40k 16 8 x5',
453             '44k 17 9 x6'
454             );
455            
456             my $roll = &dice(2, $weightDM - 2);
457             $roll = 0 if $roll < 0;
458             $roll = 19 if $roll > 19;
459            
460             $roll = &dice(2, $weightDM + 6) while ($roll == 12) || ($roll > 19);
461            
462             my @effect = split(' ', $effects[$roll]);
463            
464             $effect[1] = &dice($effect[1]);
465             $effect[2] = &dice($effect[2]);
466            
467             return @effect;
468             }
469            
470             ########################################################
471             #
472             # sub : weaponryTable
473             #
474             # desc:
475             #
476             # in : weapon DM
477             #
478             # out : weapons and hits
479             #
480             ########################################################
481             sub _weaponryTable
482             {
483             my $wDM = shift;
484            
485             my @weaponTable =
486             ( '',
487             'Hrns,hvs ',
488             'Horns ',
489             'Hvs,tth ',
490             'Hooves ',
491             'Hrns,tth ',
492             'Thrasher ',
493             'Clws,tth ',
494             'Teeth ',
495             'Claws ',
496             'Claws ',
497             'Thrasher ',
498             'Clws, tth',
499             'Claws+1 ',
500             'Stinger ',
501             'Clw/tth+1',
502             'Teeth+1 ',
503             'Blade(1d)',
504             'Blade(2d)',
505             'Pstl(4d) '
506             );
507            
508             return $weaponTable[&dice(2, $wDM-2)];
509             }
510            
511            
512             ########################################################
513             #
514             # sub : armorTable
515             #
516             # desc: returns armor type
517             #
518             # in : armor DM
519             #
520             # out :
521             #
522             ########################################################
523             sub _armorTable
524             {
525             my $adm = shift;
526            
527             my @at =
528             (
529             '', 6, '', '', 'jack', '','','','','', 'jack', '', 6,
530             'mesh+1',
531             'clth+1',
532             'mesh',
533             'cloth',
534             'cbt+4',
535             'reflec',
536             'ablat',
537             'battle'
538             );
539            
540             my $roll = &dice(2, $adm-2);
541             $roll = &dice(2, $adm+4) while ($at[$roll] eq '6');
542            
543             return $at[$roll];
544             }
545             }
546              
547             1;
548              
549             __END__