# Copyright (c)2000-2013, Chris Pressey, Cat's Eye Technologies.
# All rights reserved.
# Distributed under a BSD-style license; see the file LICENSE for more info.
sub attack
{
my $self = shift;
my $other = shift;
# $self swings. Did they hit?
my $attr = $self->{melee_attacks};
my $att;
if (defined($self->{$self->{domhand}}))
{
$attr = $self->{$self->{domhand}}->{melee_attacks};
}
my $best = 0;
my $i = 0;
for ($i=0;$i<=$#{$attr};$i++)
{
$att = $attr->[$i];
next if not defined $other->{location}; # i.e. other died
$self->{totalswings}++;
my $targ = 20 + int($other->{op}{dexterity} * 0.75) - $att->{accuracy}
- int(($other->{blurry}+9)/10); # target die: 1d(20+...)
if ($targ <= $self->{op}{dexterity}) { $targ = $self->{op}{dexterity} + 1; }
my $r = ::d(1,$targ); # basic roll to hit
my $ta = undef; my $fail = 0; my $sky = '';
if ($i == 0 and $ta = $self->has(Talent::weapon_proficiency($self->{$self->{domhand}})))
{
# roll talent
if (::d(1,100) <= $ta->{prof})
{
# $self->seen("<self> uses <his> weapon proficiency!");
$sky = "skillfully ";
my $r2 = ::d(1,$targ); $r = $r2 if $r2 < $r;
} else
{
$fail = 1;
}
}
if ($att->{autofollow} > 0)
{
if ($best >= $att->{autofollow})
{
$r = $self->{op}{dexterity};
} else
{
$self->{totalswings}--;
next;
}
} elsif ($att->{followup} > 0)
{
if ($best < $att->{followup})
{
$self->{totalswings}--;
next;
}
}
if ($r <= $self->{op}{dexterity}) # a hit, a palpable hit!
{
# where on $other did we hit? choose body part.
my $b = $Distribution::bp{$self->{body_aim}}->pick;
my $d = $other->hurt($att, $self, $b, $sky);
$best++;
if ($ta and not $fail)
{
if(::d(1,100) <= $ta->{lesson})
{
$ta->{lesson} = 0;
$ta->{prof}++;
$self->review('talents');
$self->seen("<self> gets a little more proficient at using <his> $self->{$self->{domhand}}->{name}.");
}
}
} else
{
$self->seen($other, "<self> ${sky}$att->{attemptverb} <other> but misses.");
$best = 0;
if ($fail)
{
$ta->{lesson}++;
$self->review('talents');
}
}
}
}
sub death
{
my $self = shift;
my $other = shift;
my $x;
if (defined $other)
{
$self->seen($other, "<self> is slain by <other>!");
$other->{experience} += $self->reward;
} else
{
$self->seen($self, "<self> dies!");
}
foreach $x (keys %{$wtable})
{
my $method = $wtable->{$x}->[0];
if (defined $self->{$method})
{
$self->take_off($method, 1); # force takeoff even of cursed items
}
}
foreach $x (@{$self->{belongings}})
{
$x->{x} = $self->{x};
$x->{y} = $self->{y};
$x->{location} = $self->{location};
unshift @{$self->{location}{map}[$self->{x}][$self->{y}]}, $x;
}
foreach $x (@{$self->{location}{actors}})
{
next if not defined $x;
next if not defined $x->{target};
if ($x->{target} eq $self) { $x->{target} = undef; }
}
if ($self->{carcass})
{
unshift @{$self->{location}{map}[$self->{x}][$self->{y}]},
$self->carcass;
}
if (defined $self->{party})
{
$self->{party}->remove($self);
}
if ($self eq $::leader)
{
::moremsg();
::color('grey','black');
::clrscr();
::color('white','grey');
::draw_box(30,11,50,22);
::gotoxy(34,13);
::color('black','grey');
::display('R.I.P ' . $::leader->{name});
::gotoxy(10,22);
::color('lime','green');
::display("^`'^'`^'`'" x 6);
::getkey;
::normal;
::clrscr;
exit(0);
}
$self->{location}->relieve($self);
$self->{location} = undef;
}
sub carcass
{
my $self = shift;
return
Item->new('name' => 'carcass',
'identity' => $self->{name} . ' carcass',
'plural' => 'carcasses',
'pluralid' => $self->{name} . ' carcasses',
'appearance' => 'carcass',
'color' => $self->{color},
'weight' => $self->{max}{constitution} * 10,
'soul' => $self,
'x' => $self->{x},
'y' => $self->{y},
'location' => $self->{location},
'type' => 'food')->implies($Adj::edible);
}
sub rest
{
my $self = shift;
my $scale = shift;
my @attrib = ('blind', 'deaf', 'dumb', 'confused', 'paralyzed', 'placid', 'blurry');
my @release= ('can see', 'can hear', 'can speak', 'can think clearly', 'can move', 'can make decisions', 'is distinct');
my $i;
my $f = 0;
for($i = 0; $i <= $#attrib; $i++)
{
if ($self->{$attrib[$i]} > 0)
{
$self->{$attrib[$i]} -= $scale;
if ($self->{$attrib[$i]} <= 0)
{
$self->{$attrib[$i]} = 0;
$self->seen($self, "<self> $release[$i] again!");
$f = 1;
}
}
}
my $s = {
'strength' => 240,
'constitution' => 160,
'dexterity' => 200,
'intelligence' => 280,
'spirit' => 120,
'charisma' => 100,
};
my $k;
foreach $k (keys %{$s})
{
if (::d(1,$s->{$k}/$scale) <= $self->{op}{$k})
{
my $m = ::sgn($self->{max}{$k} - $self->{op}{$k});
$self->adjust($k, $m, 'healing') if $m;
$f = abs($m) if not $f;
}
}
if ($f and $self eq $::leader) { $self->review('character'); }
}
sub step
{
my $self = shift;
my $xd = shift;
my $yd = shift;
my $oi = 0;
if ($xd == 0 and $yd == 0)
{
$self->rest(2);
return;
}
$self->rest(1);
if ($self->{confused} or ($self->{blind} and $self ne $::leader))
{
my $mxd = ::d(1,3)-2;
my $myd = ::d(1,3)-2;
if ($mxd != 0 or $myd != 0)
{
$xd = $mxd;
$yd = $myd;
}
}
if ($self->{x}+$xd < 0)
{
return if $self ne $::leader;
if ($self->{location}{worldx} == 0)
{
$self->seen($self, "<self> cannot cross over the edge of the world.");
return;
}
my $a = $::reg{$::wmap->[$self->{location}{worldy}][$self->{location}{worldx} - 1]};
# return if not defined $a;
my $y = $self->{y};
$self->{location}->queue_follow($self, $a, $a->{sizex}-1, $y);
$self->{location}->relieve($self);
$a->enter($self, $a->{sizex}-1, $y);
$self->{location}->display($self);
$self->display;
return;
} elsif ($self->{y}+$yd < 0)
{
return if $self ne $::leader;
if ($self->{location}{worldy} == 0)
{
$self->seen($self, "<self> cannot cross over the edge of the world.");
return;
}
my $a = $::reg{$::wmap->[$self->{location}{worldy} - 1][$self->{location}{worldx}]};
# return if not defined $a;
my $x = $self->{x};
$self->{location}->queue_follow($self, $a, $x, $a->{sizey}-1);
$self->{location}->relieve($self);
$a->enter($self, $x, $a->{sizey}-1);
$self->{location}->display($self);
$self->display;
return;
} elsif ($self->{x}+$xd >= $self->{location}{sizex})
{
return if $self ne $::leader;
if ($self->{location}{worldx} == $#{$::wmap->[0]})
{
$self->seen($self, "<self> cannot cross over the edge of the world.");
return;
}
my $a = $::reg{$::wmap->[$self->{location}{worldy}][$self->{location}{worldx} + 1]};
# return if not defined $a;
my $y = $self->{y};
$self->{location}->queue_follow($self, $a, 0, $y);
$self->{location}->relieve($self);
$a->enter($self, 0, $y);
$self->{location}->display($self);
$self->display;
return;
} elsif ($self->{y}+$yd >= $self->{location}{sizey})
{
return if $self ne $::leader;
if ($self->{location}{worldy} == $#{$::wmap})
{
$self->seen($self, "<self> cannot cross over the edge of the world.");
return;
}
my $a = $::reg{$::wmap->[$self->{location}{worldy} + 1][$self->{location}{worldx}]};
# return if not defined $a;
my $x = $self->{x};
$self->{location}->queue_follow($self, $a, $x, 0);
$self->{location}->relieve($self);
$a->enter($self, $x, 0);
$self->{location}->display($self);
$self->display;
return;
}
$self->{location}->gel($self->{x}+$xd,$self->{y}+$yd);
my $thing = $self->{location}->get_terrain($self->{x}+$xd,$self->{y}+$yd);
if (not $thing->allows($self))
{
if ($self eq $::leader)
{
if ($self->{blind} or not $self->{lit})
{
$self->seen($self, "Something blocks <self>'s progress.");
} else
{
$self->seen($thing, "<self> can't move through <other>.");
}
}
} else
{
my $q = $self->{location}->actor_at($self->{x}+$xd, $self->{y}+$yd);
if (defined $q) # fight/encounter
{
# if ($q eq $self) { carp "Weird: cannot encounter yourself"; }
if ($self eq $::leader)
{
if ($self->{blind} or not $self->{lit})
{
$self->seen($self, "Something blocks <self>'s progress.");
} else
{
$self->seen($q, "<self> can't move through <other>.");
}
} else
{
$self->attack($q);
}
return; # if not defined $self; # might have been killed if $q reflects attack
} else # move
{
my $t = $self->{location}->get_terrain($self->{x},$self->{y});
::script $t->{on_exit}, $t, $self;
$self->{location}{_collmap}[$self->{x}][$self->{y}] = 0;
$self->undisplay;
$self->{x} += $xd;
$self->{y} += $yd;
$self->{location}->gel($self->{x}, $self->{y});
$self->display;
$self->{location}{_collmap}[$self->{x}][$self->{y}] = 1;
}
my $string = '';
my $stuff = $self->{location}->get_top($self->{x},$self->{y});
while (ref($stuff) eq 'Item' and $self->{party})
{
if ($self->{blind} or not $self->{lit})
{
$self->seen($self, "<self> notes something on the ground here.");
last;
}
elsif ($stuff->{count} > 1)
{
$string .= "$stuff->{count} " . $stuff->plural . ", ";
} else
{
$string .= "a $stuff->{name}, ";
}
$stuff = $self->{location}{map}[$self->{x}][$self->{y}][++$oi];
}
$string =~ s/\,\s*$//g if $string;
$string =~ s/\,\s*([^,]*?)$/ and $1/g if $string;
$self->seen("<self> notes $string here.") if $string;
::script $thing->{on_enter}, $thing, $self;
if ($self eq $::leader and defined $thing->{encounter})
{
$thing->{encounter}->begin;
if (not $thing->{encounter}->{persistent})
{
$thing->{encounter} = undef;
}
}
}
}
# holds Actor's internal logic for "believable" movement.
# called by Region->tick, et al.
sub move
{
my $self = shift;
return if $self->{incapacitated} or $self->{paralyzed};
if ($self->{sleeping} > 0)
{
$self->{sleeping}--;
if ($self->{sleeping} <= 0)
{
$self->seen("<self> wakes up.");
$self->{sleeping} = -::d(100,3);
}
return;
} elsif ($self->{sleeping} < 0)
{
$self->{sleeping}++;
if ($self->{sleeping} >= 0)
{
$self->seen("<self> falls asleep.");
$self->{sleeping} = ::d(50,3);
return;
}
} else
{
$self->{sleeping} = -::d(100,3);
}
if ($self->{on_move})
{
::script $item->{on_move}, $self;
return;
}
if (defined $self->{using_talent})
{
$self->{using_talent}[0]--;
if ($self->{using_talent}[0] == 0)
{
# should probably be encapsulated in talent...
if (defined $self->{using_talent}[1])
{
::script $self->{using_talent}[1]->{on_perform},
$self, $self->{using_talent}[2];
}
$self->{using_talent} = undef;
}
} elsif (defined $self->{target})
{
my $t; my $p = undef;
foreach $t (@{$self->{talents}})
{
next if $t->{recharge} > -1 and $t->{lastuse} >= $::game_time - $t->{recharge};
if ($self->{op}{spirit} > $t->{cost})
# and $self->dist($self->{target}) <= $self->{talents}[0]{range})
{
if(::script $t->{on_consider}, $t, $self)
{
if (defined $p and $p->{lastuse} < $t->{lastuse}) { }
else { $p = $t; }
}
}
}
if(defined $p)
{
$p->use($self, undef, $self->{target});
return;
}
my $dx = ::sgn($self->{target}{x} - $self->{x});
my $dy = ::sgn($self->{target}{y} - $self->{y});
my $aa = $self->{location}->actor_at($self->{x}+$dx,$self->{y}+$dy);
if ($self->{combat} eq 'Flee')
{
$dx *= -1;
$dy *= -1;
}
while ((not $self->in_bounds($self->{x}+$dx,$self->{y}+$dy)) or
(not $self->{location}->get_terrain($self->{x}+$dx,$self->{y}+$dy)->allows($self)) or
(defined $aa and $aa ne $self->{target}))
{
$dx = ::d(1,3)-2;
$dy = ::d(1,3)-2;
$aa = $self->{location}->actor_at($self->{x}+$dx,$self->{y}+$dy);
$aa = undef if defined $aa and $aa eq $self;
}
$self->step($dx,$dy);
} elsif(defined $self->{party})
{
# follow party leader
my $dx = ::sgn($::leader->{x} - $self->{x});
my $dy = ::sgn($::leader->{y} - $self->{y});
if (::d(1,20) > $::leader->{op}{charisma})
{
$dx = ::d(1,3)-2;
$dy = ::d(1,3)-2;
}
my $i = 0;
follow_argggh:
my $aa = $self->{location}->actor_at($self->{x}+$dx,$self->{y}+$dy);
if ((not $self->in_bounds($self->{x}+$dx,$self->{y}+$dy)) or
(not $self->{location}->get_terrain($self->{x}+$dx,$self->{y}+$dy)->allows($self)) or
defined $aa)
{
$dx = ::d(1,3)-2;
$dy = ::d(1,3)-2;
$i++;
if ($i < 100)
{
goto follow_argggh;
} else
{
$self->rest; # bash?
}
} else
{
$self->step($dx, $dy);
}
} else
{
if ($self->{noncombat} eq 'Wander')
{
# wander around randomly
my $dx = ::d(1,3)-2;
my $dy = ::d(1,3)-2;
if ($self->{location}->actor_at($self->{x}+$dx,$self->{y}+$dy)) { $dx = 0; $dy = 0; }
$self->step($dx, $dy);
if ($self->{hostile} and $self->los($::leader, 4))
{
$self->{target} = $::leader;
}
} else
{
$self->step(0, 0);
}
}
}
sub look_dir
{
my $self = shift;
my $x = shift;
my $y = shift;
if ($self->{blind})
{
$self->seen($self, "<self> is blind.");
return;
}
if (not $self->{lit})
{
$self->seen($self, "<self> cannot see in the dark.");
return;
}
$x += $self->{x};
$y += $self->{y};
my $t = $self->{location}->get_top($x,$y);
my $a = $self->{location}->actor_at($x,$y);
if (defined $a)
{
$a->view('impression');
$self->seen($a, "<self> can see <a other> on a $t->{name} there.");
} else
{
$self->seen($t, "<self> can see <a other> there.");
}
if ($t->{graffiti})
{
$self->seen($self, "<self> sees scratched in it: $t->{graffiti}");
}
}
1;