/***************************************************************************
Ranged weapon code ... should include _all_ fired weapons
Radix - March 3, 1996
Khelben - Juny 23, 1996
****************************************************************************/
inherit "/obj/weapon.c";
#include "path.h"
#define RANGE "/obj/handlers/range.c"
// info[] 0- range 1- speed 2- accuracy 3-container 4-ammo 5-hands
mixed info,tmp;
string wtype;
object ammo, me, him, env;
int round;
string firemess, readymess;
object get_ammo();
int do_hit(object me, object him, object ammo);
void do_random_target(object me, object him, object ammo);
void missed_shot(object me, object him, object ammo);
void lose_concentration();
void set_readymess(string arg) { readymess = arg; }
void set_firemess(string arg) { firemess = arg; }
create()
{
::create();
info = allocate(6);
if(wtype)
info = TABLE->get_info(wtype);
ammo, me, him = ({ });
round = 0;
if ( !readymess )
readymess = "Skillfully, you draw the bow ready and aim at";
if ( !firemess )
firemess = "Finishing your aim, you shoot off an arrow at";
// Why this? Doesn't need to be in base object - Radix
// add_property("guild","ranger"); This must be put in every weapon.
set_holdable(1);
if ( (int)info[5] == 2 )
{ set_twohanded(1); }
}
void init()
{
::init();
add_action("ranged_shot","shoot");
}
void set_ranged_weapon(string t)
{
wtype = TABLE->look_up(t);
info = TABLE->get_info(wtype);
}
int ranged_shot(string victim)
{
object old = him;
if(!this_object()->query_in_use())
{
write("You must be holding this weapon to use it!\n");
return 1;
}
if(!victim || !stringp(victim))
{
write("Syntax: "+query_verb()+" <target>\n");
return 1;
}
if(environment()) me = environment(this_object());
if(info[4] != "itself" && sizeof(me->query_attacker_list()))
{
write("You cannot load your "+query_short()+
" while in combat!\n");
return 1;
}
victim = me->expand_nickname(victim);
him = find_living(victim);
if(!me || !him)
{
write(capitalize(victim)+" does not exist or is a ghost.\n");
return 1;
}
if(him && him == me)
{
write("Suicide is not possible, try to kill something else.\n");
return 1;
}
if(him->query_property("dead"))
{
write(him->query_cap_name()+" is a ghost.\n");
return 1;
}
if(!RANGE->check_line_of_sight(me,him,info[0]) && ENV(me) != ENV(him))
{
write("Could not find "+him->query_cap_name()+" in range.\n");
return 1;
}
if(him->query_hidden()||him->query_invis()||him->query_hide_shadow())
{
write("Could not find "+him->query_cap_name()+" in range.\n");
return 1;
}
ammo = get_ammo();
if(!ammo)
{
write("Could not find "+info[4]+" in "+info[3]+".\n");
return 1;
}
if(!round)
write(readymess+" "+him->query_cap_name()+".\n");
else
{
if(him == old)
write("You hesitate and aim at "+him->query_cap_name()+
" again.\n");
else
write("You change aim from "+old->query_cap_name()+" to "+
him->query_cap_name()+".\n");
}
env = environment(me);
round = 0;
set_heart_beat(1);
return 1;
}
heart_beat()
{
int hit;
if(!him || !me)
{
if(!me)
{
// die in horror
dest_me();
return;
}
tell_object(me,"You lost sight of "+him->query_cap_name()+".\n");
me, him, ammo = 0;
round = 0;
set_heart_beat(0);
return;
}
if(env != environment(me))
lose_concentration();
if(!this_object()->query_in_use())
{
tell_object(me,"As you low your weapon you lose your target.\n");
set_heart_beat(0);
return;
}
if(him->query_property("dead"))
{
write(him->query_cap_name()+" is a ghost.\n");
set_heart_beat(0);
return;
}
if(!RANGE->check_line_of_sight(me,him,info[0])&& env != environment(him))
{
tell_object(me,him->query_cap_name()+" is out of range!\n");
me, him, ammo = 0;
round = 0;
set_heart_beat(0);
return;
}
if(him->query_hidden() || him->query_invis() || him->query_hide_shadow())
{
tell_object(me,"Could not find "+him->query_cap_name()+" in range.\n");
me, him, ammo = 0;
round = 0;
set_heart_beat(0);
return;
}
if(round == info[1])
{
tell_object(me,firemess+" "+him->query_cap_name()+".\n");
tell_room(ENV(me), me->query_cap_name()+" holds a "+query_short()+
" and shoot off a projectile.\n", ({me}));
if(!do_hit(me,him,ammo))
missed_shot(me,him,ammo); // Wrong the shot.
me, him, ammo = 0;
round = 0;
set_heart_beat(0);
return;
}
round++;
tell_object(me,"You continue your aim at "+him->query_cap_name()+".\n");
}
void lose_concetration()
{
tell_object(me,"Moving, you lose your concentration and begin "
"again aiming at "+him->query_cap_name()+".\n");
env = environment(me);
round = 0;
}
// May give this a number arg to return *objs.. fire more than one arrow?
object get_ammo()
{
object *all;
int i;
// missiles from containers...
all = find_match(info[4], find_match(info[3], me));
for(i=0; i<sizeof(all); i++)
if(all[i]->query_ranged_ammo())
return all[i];
return 0;
}
int check_accuracy(object archer, object enemy, object proj, int tmp)
{
int bonus, malus, ac, thac0, acc, distance, dex;
int needed, shoot;
if( enemy->query_property("NOBUDGE") ||
environment(enemy)->query_static_property("noguild") ||
environment(enemy)->query_static_property("nocast") )
{
return -1; // We'll do them lose ammo.
}
if( environment(archer)->query_static_property("noguild") ||
environment(archer)->query_static_property("nocast") )
{
return -1; // We'll do them lose ammo.
}
ac = enemy->query_total_ac();
thac0 = archer->query_thac0();
acc = info[2];
distance = tmp*2; // Will be hardest if the target is far
distance *= 10;
dex = (int)archer->query_dex()-16;
dex *= 10;
bonus = acc + dex - distance;
needed = thac0 - bonus - acc;
shoot = random(200);
if ( shoot >= needed )
return 1;
else
return 0;
}
void missed_shot(object me, object him, object proj)
{
int bad;
if(!ammo)
{
return;
}
tell_object(me,"You missed "+him->query_cap_name()+"!\n");
bad = random(200);
switch (bad)
{
case 0..10:
ammo->move(environment(him));
break;
case 11..198:
ammo->dest_me();
break;
default: // 199
do_random_target(me,him,proj); // Now we can hit
// another target
break;
}
}
int do_hit(object archer, object enemy, object proj)
{
int damage,i, dest;
int shoot;
tmp = RANGE->get_direction_delta(enemy, archer);
if(!proj)
{
return 1;
}
shoot = check_accuracy(archer, enemy, proj, tmp[1]);
if ( shoot == -1)
{
tell_object(me,"You missed "+enemy->query_cap_name()+"!\n");
proj->dest_me();
return 1; // In this way We'll avod the miss_shoot function.
}
if(shoot == 1)
{
damage = proj->do_ranged_weapon_damage(archer,enemy,tmp,info[4]);
tell_object(archer,"You hit "+enemy->query_cap_name()+" with your "+
proj->query_short()+" doing "+damage+"!\n");
tell_object(enemy,"You have been struck by a flying "+proj->query_short()+
" from the "+tmp[0]+" doing "+damage+"!\n");
tell_room(ENV(enemy), enemy->query_cap_name()+" is hited by a "
"projectile from the "+tmp[0]+".\n", ({archer,enemy}));
if ( !userp(enemy) )
{
switch((string)proj->query_proj_name())
{
case "arrow":
dest = ARROW_BREAK; break;
case "bolt":
dest = BOLT_BREAK; break;
case "needle":
dest = NEEDLE_BREAK; break;
default:
dest=200; break;
}
if ( random(100) <= dest )
{
proj->dest_me();
}
else
proj->move(enemy);
}
return 1;
}
else if( shoot == 0)
return 0; // Wrong the shoot
}
void do_random_target(object archer, object enemy, object proj)
{
int damage, j, i, dest;
int deb=0, di;
object *all;
all = all_inventory(environment(enemy));
j = sizeof(all);
if ( j == 1 && ENV(archer) != ENV(enemy) )
{
// Only our target is in the room
proj->dest_me();
}
else if ( j == 2 && ENV(archer) == ENV(enemy) )
{
// Only our target and the archer are in the room
proj->dest_me();
}
else
{
for(di=0; di<sizeof(all); di++)
i = random(sizeof(all));
while ( all[i] == enemy || all[i] == archer )
{
i = random(sizeof(all));
deb++;
}
}
if ( userp(all[i]) || all[i]->query_npc() )
{
damage = proj->do_ranged_weapon_damage(archer,all[i],tmp,info[4]);
tell_object(archer,"Opps...you wrong the shot and you now "+
"hit "+all[i]->query_cap_name()+" with your "+
proj->query_short()+" doing "+damage+"!\n");
tell_object(all[i],"You have been struck by a flying "+proj->query_short()+
" from the "+tmp[0]+" doing "+damage+"!\n");
tell_object(all[i],"Ahora se te clona una flecha clavada.\n");
tell_room(ENV(enemy), enemy->query_cap_name()+" is hited by a "
"projectile from the "+tmp[0]+".\n", ({archer,enemy}));
}
else
proj->dest_me();
if ( !userp(all[i]) )
{
switch((string)proj->query_proj_name())
{
case "arrow":
dest = ARROW_BREAK; break;
case "bolt":
dest = BOLT_BREAK; break;
case "needle":
dest = NEEDLE_BREAK; break;
}
if ( random(100) <= dest )
{
proj->dest_me();
}
else
proj->move(enemy);
}
}