inherit "/obj/monster";
#include <armoury.h>
#include <weapon.h>
#include <tasks.h>
#include <combat.h>
int told_attack, told_parry, ability;
string *misc;
object trainer, trainee;
#define DODGE "fighting.combat.dodging.melee"
#define PARRY "fighting.combat.parry.melee"
#define PERSON_DODGE_FAILURE "As the dummy spins around, an arm swings towards you and hits you before you can dodge out of the way!\n"
#define ROOM_DODGE_FAILURE " isn't fast enough to dodge a spinning arm! Ouch!\n"
#define PERSON_DODGE_SUCESS "As the dummy spins around, an arm swings towards you but you see it in time and dodge it.\n"
#define ROOM_DODGE_SUCESS " dodges the arm of the wooden training dummy.\n"
#define PERSON_PARRY_FAILURE "As the dummy spins around, an arm swings towards you but you are unable to block it!\n"
#define ROOM_PARRY_FAILURE " isn't fast enough to avoid the training dummy! Ouch!\n"
#define PERSON_PARRY_SUCCESS "As the dummy spins around, an arm swings towards you but you see it in time and block it.\n"
#define ROOM_PARRY_SUCCESS " parries the training dummy.\n"
#define MISC_MESSAGES ({ "Generally, heavier weapons are harder to attack and "\
"parry with but their attacks are harder to parry.", \
"Being burdened in combat, by carrying lots of things or wearing lots of " \
"armour, reduces your effectiveness. This is especially true for dodging.", \
"When injured it is harder to fight, so watch your health closely.", \
"Holding a two-handed weapon in one hand can be useful when using a shield "\
"but is hard to do unless you have high strength.", \
"You can focus on specific parts of your opponents body to take advantage " \
"of weaknesses in their armour but this is harder to do than attacking " \
"normally."})
#define DIFF 15
#define MOD 10
int assault_dummy();
mixed *callback(int stage, class attack att, mixed data);
void failed_defense( object );
void failed_attack( object );
void tell_misc( object );
void spin_arm( object, object );
object query_trainer() {
return trainer;
}
object reset_trainer() {
trainer = 0;
}
object set_trainer( object thing ) {
return trainer = thing;
}
object query_trainee() {
return trainee;
}
object reset_trainee() {
trainee = 0;
}
object set_trainee( object thing ) {
return trainee = thing;
}
int query_skill_bonus( string words ) {
return 10;
}
void setup() {
set_name( "dummy" );
add_adjective( ({ "wood", "wooden", "training" }) );
// add_property( "determinate", "A" );
set_short( "training dummy" );
set_long( "A fighters dummy. It is looking pretty worn from having "+
"all the stuffing knocked out of it all day.\n");
basic_setup( "human", "warrior", 10 );
set_gender( 0 );
add_property( "run away", -1 );
set_dex(6);
set_str(6);
set_con(18);
set_int(6);
set_wis(6);
remove_ac( "sharp" );
remove_ac( "pierce" );
remove_ac( "blunt" );
remove_ac( "unarmed" );
add_skill_level( "other.health", 1000 );
add_combat_action( 100, "spin_arm", (: spin_arm :) );
ARMOURY->request_item( "dirty rags", 30 )->move( this_object() );
} /* setup() */
// Don't let the dummy attack;
int attack_this_time() {
return 0;
}
void init() {
if( this_player() ) {
this_player()->add_command( "kill", this_object(), "{dummy}",
(: assault_dummy() :) );
this_player()->add_command( "attack", this_object(), "{dummy}",
(: assault_dummy() :) );
}
}/*init*/
int assault_dummy() {
object *weapons, weapon;
int i;
if( trainer->query_fighting() ) {
write( "You beat at and tear up " + this_object()->the_short() +
" for a while until you get bored.\n" );
say( this_player()->query_short() + " hacks at " +
this_object()->the_short() + " for a while.\n", this_player() );
}
if( trainee && !interactive( trainee ) )
reset_trainee();
if( trainee ) {
if( trainee->query_fighting() ) {
if( trainee == this_player() ) {
tell_object( this_player(), "You are already using " +
this_object()->the_short() + ".\n" );
return notify_fail( "" );
}
tell_object( this_player(), trainee->one_short() + " is currently "
"using " + this_object()->the_short() + ".\n" );
return notify_fail( "" );
}
}
weapon = 0;
ability = 0;
told_attack = 0;
told_parry = 0;
misc = copy(MISC_MESSAGES);
weapons = (object *)this_player()->query_weapons();
if( sizeof( weapons ) > 1 ) {
trainer->stop_them( previous_object(), this_object() );
trainer->do_command( "say Just use one weapon, I get confused if you use "
"more than one!" );
return 1;
}
if( sizeof( weapons ) && !weapon )
weapon = weapons[ 0 ];
this_player()->attack_ob( this_object() );
write( "You attack " + this_object()->the_short() + ".\n" );
say( this_player()->the_short() + " attacks " + this_object()->a_short() +
".\n", this_player() );
trainee = this_player();
for( i = 0; i < sizeof( ( class combat_special )this_object()->
query_specials() ); i++ ) {
this_object()->remove_special( ( class combat_special )this_object()->
query_specials()[i]->id );
}
this_object()->register_special( T_DEFENSIVE,
E_AFTER_ATTACK,
({ this_object(), "callback" }),
({ 0, 0, weapon }) );
return 1;
}/*attack_dummy*/
int attack_by(object ob) {
if(!sizeof(query_specials()))
call_out("attack_dummy", 0);
return ::attack_by(ob);
}
mixed *callback( int stage, class attack att, mixed data ) {
int *rating, number, damage, last_damage;
object dumdum, newbie, *weapons, weapon;
string what, skill;
mixed *attacks;
newbie = att->attacker;
dumdum = att->opponent;
/*
* The data array is the list of information specific to a particular
* special for a particular player.
*
* data[0] == Times tried
* data[1] == Damage
* data[2] == Weapon
*/
weapons = (object *)this_player()->query_weapons();
if( sizeof( weapons ) > 1 ) {
trainer->stop_them( newbie, dumdum );
trainer->do_command( "say Just use one weapon, I get confused if you use "
"more than one!" );
debug_printf( "More than one weapon, removing special.\n" );
return({ R_CONTINUE | R_REMOVE_ME, att, data });
}
if( sizeof( weapons ) && !weapon )
weapon = weapons[ 0 ];
if( weapon != data[2] ) {
trainer->stop_them( newbie, dumdum );
trainer->do_command( "say You've changed weapons!" );
debug_printf( "Changed weapons, removing special.\n" );
return({ R_CONTINUE | R_REMOVE_ME, att, data });
}
last_damage = data[1];
damage = att->damage;
data[1] = damage;
if( !last_damage) {
failed_attack(newbie);
}
if(last_damage || !random(4)) {
// This hands out skill advances very generously up to lvl 5 then it
// stops.
if( weapon ) {
attacks = weapon->query_attack_data();
skill = attacks[random( sizeof( attacks ) / W_ARRAY_SIZE ) *
W_ARRAY_SIZE + W_SKILL];
} else {
skill = "unarmed";
}
if( newbie->query_skill( "fighting.combat.melee." + skill ) < 5 &&
!random( 4 * ( newbie->query_skill( "fighting.combat.melee." +
skill ) ) ) &&
newbie->add_skill_level( "fighting.combat.melee." + skill, 1, 1 ) ) {
if( skill == "unarmed" )
tell_object( newbie, "%^YELLOW%^You feel you've learned something "
"about unarmed combat.%^RESET%^\n" );
tell_object( newbie, "%^YELLOW%^You feel you've learned something about "
"using " + weapon->a_short() + ".%^RESET%^\n" );
}
}
// Check if we have used up all our chances.
if( data[0]++ > 15 ) {
number = ( 2 * (ability + data[0]) ) / ( 2 * data[0] );
if( weapon )
what = convert_message( ( string )weapon->the_short() );
else
what = "whatever it was you were using";
trainer->stop_them( newbie, dumdum );
trainer->do_command( "think" );
switch ( number ) {
case 0..1 :
if( skill == "unarmed" )
trainer->do_command( "say Well, you're rather hopeless." );
else {
if( strsrch( what, "knife" ) == -1 && strsrch( what, "dagger" ) == -1)
trainer->do_command( "say Well, you're rather hopeless with " + what +
", really. A lighter, simpler weapon may help." );
else
trainer->do_command( "say Well, you're rather hopeless with " + what +
", really, but with practice you'll get better.");
}
break;
case 2 .. 3 :
if( skill == "unarmed" )
trainer->do_command( "say Hmmm... You have some skill." );
trainer->do_command( "say Hmmm... You have some skill with " + what +
", at any rate, as you practice you'll get better." );
break;
case 4 .. 5 :
if( skill == "unarmed" )
trainer->do_command( "say You're getting there. Keep training and "
"you'll be pretty good soon." );
trainer->do_command( "say You're getting there. Keep training with " +
what + " and you'll be pretty good with it soon." );
break;
default :
if( skill == "unarmed" )
trainer->do_command( "say I'd say you've more or less mastered unarmed "
"combat. Well done!" );
trainer->do_command( "say I'd say you've more or less mastered " + what +
". Well done!" );
}
trainee = 0;
return({ R_CONTINUE | R_REMOVE_ME, att, data });
}
tell_misc(newbie);
if( !damage) {
return ({ R_CONTINUE, att, data });
}
if( skill == "unarmed" ) {
rating = (int *)"/cmds/guild-race/other/judge"->calc_rating( newbie );
} else {
rating = (int *)"/cmds/guild-race/other/judge"->calc_rating( weapon );
}
// Ratings for unarmed. I'm making them up!
if( !rating[0] )
rating [0] = random( 10 ) + 30;
if( !rating[1] )
rating [1] = random( 80 ) + 60;
number = ( ( 40 * damage ) / rating[ 0 ] +
( 20 * damage ) / rating[ 1 ] ) / 5;
debug_printf("%d", number);
ability += number;
if (number > 14)
number = 14;
trainer->do_command( "say That's " + ({ "an atrocious", "an extremely poor",
"a very poor", "a rather poor", "a poor", "a pretty poor", "quite a poor",
"a reasonable", "quite a good", "a pretty good", "a good", "a rather "
"good", "a very good", "an extremely good", "an excellent" })
[ number ] + " attack." );
return ({ R_CONTINUE, att, data });
}
void spin_arm( object newbie, object dumdum ) {
int diff, which, failed;
//debug_printf( "specials %O\n", this_player()->query_specials() );
if( environment( newbie ) != environment( this_object() ) )
return;
switch( ( ( class tactics )newbie->query_tactics() )->attitude ) {
case "offensive":
diff = DIFF + MOD;
break;
case "defensive":
diff = DIFF - MOD;
break;
default:
diff = DIFF;
}
switch( newbie->query_combat_response() ) {
case "dodge" :
which = 1;
break;
case "parry":
which = 0;
break;
default:
which = random( 2 );
}
// Dodge
if( which ) {
switch( TASKER->perform_task( newbie, DODGE, diff, TM_FIXED ) ) {
case FAIL :
if( newbie->query_skill( DODGE ) >= 5 ||
random( 3 * ( newbie->query_skill( DODGE ) ) ) ||
!newbie->add_skill_level( DODGE, 1, 1 ) ) {
tell_object( newbie, PERSON_DODGE_FAILURE );
tell_room( environment( newbie ),newbie->one_short() +
ROOM_DODGE_FAILURE, ({ newbie }) );
failed = 1;
break;
}
case AWARD :
tell_object( newbie, "%^YELLOW%^You feel better at dodging "
"things.%^RESET%^\n" );
case SUCCEED :
tell_object( newbie,PERSON_DODGE_SUCESS );
tell_room( environment( newbie ), newbie->one_short() +
ROOM_DODGE_SUCESS, ({ newbie }) );
break;
}
} else {
// Parry
switch( TASKER->perform_task( newbie, PARRY, diff, TM_FIXED ) ) {
case FAIL :
if( newbie->query_skill( PARRY ) >= 5 ||
random( 3 * ( newbie->query_skill( PARRY ) ) ) ||
!newbie->add_skill_level( PARRY, 1, 1 ) ) {
tell_object( newbie, PERSON_PARRY_FAILURE );
tell_room( environment( newbie ), newbie->one_short() +
ROOM_PARRY_FAILURE, ({ newbie }) );
failed = 1;
break;
}
case AWARD :
tell_object( newbie, "%^YELLOW%^You feel better at parrying things."
"%^RESET%^\n" );
case SUCCEED :
tell_object( newbie, PERSON_PARRY_SUCCESS );
tell_room( environment( newbie ), newbie->one_short() +
ROOM_PARRY_SUCCESS, ({ newbie }) );
break;
}
}
if( trainer && failed )
failed_defense(newbie);
return;
}
void tell_misc(object newbie) {
int which;
if(!sizeof(misc) || random(5))
return;
which = random(sizeof(misc));
trainer->do_command("say " + misc[which]);
misc -= ({ misc[which] });
}
void failed_attack( object newbie ) {
string attitude;
if(told_attack)
return;
attitude = (newbie->query_tactics())->attitude;
if(attitude != "offensive" && attitude != "insane") {
told_attack = 1;
trainer->do_command( "say You can improve your chances of hitting your "
"opponent by setting your tactics to a more "
"offensive attitude.");
}
}
void failed_defense( object newbie ) {
string attitude;
if(told_parry)
return;
attitude = (newbie->query_tactics())->attitude;
if(attitude != "defensive" && attitude != "wimp") {
told_parry = 1;
trainer->do_command( "say You can improve your chances of defending by "
"setting your tactics to a more defensive "
"attitude." );
}
}
void adjust_hp() {
return this_object()->query_max_hp();
}