22
« on: October 14, 2010, 11:50:27 PM »
Updated Instructions to load my combat system into an out-of-the-box ds v3.0.
1) rename and move your stock combat.c to c:/ds/bin/combat.old
2) save the following as combat.c in /lib/
/*****************************************************************************
* Barebones combat for Dead Souls lib *
* Author: Detah, with huge thanks to Silenus for design outline *
* Thanks also to Cratylus and Raudhrskal for misc. advice *
* Modified: Detah@Arcania 05-01-08 *
* Modified: Detah@Arcania 09-30-08 *
* Modified: Detah@Arcania 12-07-08 *
* Modified: Detah@Arcania 01-29-09 *
* to update after a change: *
* update /lib/combat.c *
* update -r /lib/creator.c *
* update /domains/town/room/valley.c (testing room) *
* userload aura and any other tps *
*****************************************************************************/
#include <daemons.h>
inherit LIB_RACE;
inherit LIB_CLASSES;
inherit LIB_COMBATMSG;
static int rd;
int Dead, Wimpy;
private object *Enemies; /* All enemies who you have ever attacked, not killed and are not calmed */
private object *LocalEnemies; /* Those enemies in Enemies which are currently in attacker's env */
private object *CalmEnemies; /* Storage area for Enemies during Calm, Quell, or any other StopCombat event */
private object CurrentEnemy; /* This is the specific enemy which is targetted each round in melee */
int CombatRound(mixed target);
int ComputeToHit(object attacker, object target);
int ComputeDamage(object attacker, object target);
void DamageMessage(int dam, object target);
void ApplyDamage(int dam, object target);
int GetWimpy();
void Wimpy();
/*****************************************************************************
* Create function *
* Inputs: none *
* Output: none *
* initializes basic combat variables and starts the heartbeat function in *
* the current object *
*****************************************************************************/
static void create() {
race::create();
rd=1;
Dead=0; /*is this necessary, since its also in body.c create() */
Wimpy=20;
Enemies=({});
LocalEnemies=({});
CalmEnemies=({});
CurrentEnemy=0;
set_heart_beat(1);
}
/*****************************************************************************
* GetEnemy function *
* Inputs: none *
* Output: Enemies=all enemies in array *
* Returns an array of current targets/enemies *
*****************************************************************************/
object array GetEnemies() {
return Enemies;
}
object array GetLocalEnemies() {
return LocalEnemies;
}
object GetCurrentEnemy() {
return CurrentEnemy;
}
object array GetCalmEnemies() {
return CalmEnemies;
}
/*****************************************************************************
* AddEnemy function *
* Inputs: target=the current target *
* Output: none *
* target info sent from attack verb, adds enemies who are not already in *
* the Enemies array *
*****************************************************************************/
void AddEnemy(object target) {
if(member_array(target, Enemies)==-1) {
Enemies += ({target});
}
}
void AddLocalEnemy(object target) {
if(member_array(target, LocalEnemies)==-1) {
LocalEnemies += ({target});
}
}
void AddCurrentEnemy(object target) {
if(CurrentEnemy == 0) {
CurrentEnemy = target;
}
}
void AddCalmEnemies(object target) {
if(member_array(target, CalmEnemies)==-1) {
}
}
/*****************************************************************************
* RemoveEnemy function *
* Inputs: target=the current target *
* Output: none *
* target info sent from attack verb, remove enemies who have died, exited *
* room, etc *
*****************************************************************************/
void RemoveEnemy(object target) {
if(member_array(target, Enemies)!=-1) {
Enemies -= ({target});
}
}
void RemoveLocalEnemy(object target) {
if(member_array(target, LocalEnemies)!=-1) {
LocalEnemies -= ({target});
}
}
void RemoveCurrentEnemy() {
CurrentEnemy = 0;
}
void RemoveCalmEnemies(object target) {
if(member_array(target, CalmEnemies)!=-1) {
CalmEnemies -= ({target});
}
}
/*****************************************************************************
* SetAttack function *
* Inputs: target=the current enemy *
* Output: none *
* checks if attacked monster is already in Enemies array. if it is not then *
* it gets added to Enemies for both combatants *
*****************************************************************************/
void SetAttack(mixed target) {
if(objectp(target)) target=({target}); /*converts trivial case object into an array for processing in general case arrayp below*/
foreach(object ind in target) { /*reads in all objs in array target and adds them to both combatants if not already present*/
this_object()->AddEnemy(ind);
ind->AddEnemy(this_object());
}
}
/*****************************************************************************
* Heartbeat function *
* Inputs: none *
* Output: none *
* checks if there are enemies in the Enemies array, checks hit probability *
* function to hit, if a hit, applies damage with the eventReceiveDamage fun *
*****************************************************************************/
void heart_beat() {
object attacker;
attacker=this_object();
Enemies -= ({0});
race::heart_beat();
if(!clonep(this_object()) && !interactive(this_object())) return; /*prevents clones of combat.c from doing stuff*/
if(member_array(CurrentEnemy, all_inventory(environment(attacker) ) )==-1 ) { rd=1; } /*resets rd counter if you leave the room*/
/*Dead Clean Up*/
if(sizeof(Enemies)) { /* There are enemies in Enemies */
foreach(object ob in Enemies) { /* If anything in Enemies is dead, remove it from Enemies, LocalEnemies and CurrentEnemy */
if(ob->GetDead()) {
RemoveCurrentEnemy();
if(sizeof(LocalEnemies)) RemoveLocalEnemy(ob);
RemoveEnemy(ob);
if(sizeof(CalmEnemies)) RemoveCalmEnemies(ob);
}
}
}
LocalEnemies=Enemies & all_inventory(environment()); /*defines LocalEnemies*/
if( sizeof(LocalEnemies)==0 ) CurrentEnemy=0; /*if LocalEnemies is empty, then zero out CurrentEnemy*/
if( sizeof(LocalEnemies) ) {
if( member_array(CurrentEnemy, LocalEnemies) == -1 ) CurrentEnemy = LocalEnemies[0]; /*if CurrentEnemy is not an element of LocalEnemies, then change CurrentEnemy to the first enemy in LocalEnemies*/
if(userp(this_object())) { /* Only the player generates the Round counter*/
attacker->eventPrint("%^WHITE%^[%^RESET%^ RD: "+rd+" HP: "+this_object()->GetHP()+"/"+this_object()->GetMaxHP()+" %^WHITE%^]%^RESET%^");
if(this_object()->GetHP() < GetWimpy()) { /* BEGIN wimpy */
Wimpy();
} /* END wimpy */
}
CombatRound(CurrentEnemy);
rd++;
}
}
/*****************************************************************************
* CombatRound function *
* replaces eventExecuteAttack in DS stock *
* Inputs: target (should be an array) *
* Output: int 0/1 (success/failure) *
*****************************************************************************/
int CombatRound(object target) {
string damtype;
int dam;
object attacker;
attacker=this_object();
damtype=this_object()->GetWeaponType(); /*for use later when I make a complex tohit fun*/
Enemies -= ({0});
if(ComputeToHit(attacker, CurrentEnemy)==0) { /*missed*/
attacker->eventPrint("You miss.");
CurrentEnemy->eventPrint(GetName() + " misses you.");
tell_room( environment(attacker), attacker->GetName() + " misses " + CurrentEnemy->GetName()+".", ({attacker, CurrentEnemy}) );
} else { /*hit*/
dam=ComputeDamage(attacker, CurrentEnemy);
ApplyDamage(dam, CurrentEnemy);
DamageMessage(dam, CurrentEnemy);
}
return 1;
}
/*****************************************************************************
* ComputerToHit = calculates probability and whether you hit this round *
* Inputs: attacker=current attacker *
* target=current target *
* Output: 0=hit, 1=miss *
* checks if an attack hits or misses *
*****************************************************************************/
int ComputeToHit(object attacker, object target) {
int h;
h=50+attacker->GetStat("coordination")["points"]; /*overly simplified hitprob for now*/
if(h<0) h=0;
if(h>100) h=100;
if(h>(1+random(100))) {
return 1; /*hit*/
}
else {
return 0; /*miss*/
}
}
/*****************************************************************************
* ComputeDamage function *
* Inputs: attacker=the current attacker, *
* target=current target of attack *
* Output: dam=damage of attack *
*****************************************************************************/
int ComputeDamage(object attacker, object target) {
return to_int(1 + attacker->GetStat("strength")["points"]/10 );
}
/*****************************************************************************
* ApplyDamage function *
* Inputs: dam=damage done *
* target=current target of attack *
* Output: none *
*****************************************************************************/
void ApplyDamage(int dam, object target) {
target->AdjustHP(-dam, this_object());
}
/*****************************************************************************
* DamageMessage function *
* Inputs: dam=damage of attack *
* target=current target of attack *
* Output: none *
*****************************************************************************/
void DamageMessage(int dam, object target) {
string verb, verb2;
if(target->GetDead() || this_object()->GetDead()) return;
if(dam>29) { verb="bludgeon"; }
else if(dam>19) { verb="smash"; verb2="smashes"; }
else if(dam>9) { verb="crush"; verb2="crushes"; }
else if(dam>4) { verb="whack"; verb2="whacks"; }
else if(dam>2) { verb="poke"; verb2="pokes"; }
else if(dam=2) { verb="graze"; verb2="grazes"; }
else if(dam=1) { verb="tickle"; verb2="tickles"; }
this_object()->eventPrint(sprintf( "You %s %s.", verb, target->GetName() ) );
target->eventPrint(sprintf("%s %s you.", GetName(), verb2 ) );
tell_room(environment(),sprintf("%s %s %s.", GetName(), verb2, target->GetName() ), ({ this_object(), target }));
}
int GetDead(){
return Dead;
}
int SetDead(int i){
if(i==1) Dead=1;
else Dead = 0;
return 1;
}
/* BEGIN Wimpy section */
int GetWimpy() {
return Wimpy;
}
void SetWimpy(int x) {
if(x < 0) Wimpy=0;
if(x > this_object()->GetMaxHP()) write("You will always flee combat.");
Wimpy=x;
}
void Wimpy() {
string array exits;
int x;
exits=environment(this_player())->GetExits();
x=random(sizeof(exits)); /*selects random exit among room's exits*/
this_player()->eventPrint("You flee "+exits[x]+".\n");
this_player()->eventForce(exits[x]);
}
/* END Wimpy section */
/*To maintain compatibility with living.c eventFollow()*/
static int Destruct() {
/*if( GetParty() ) PARTY_D->eventLeaveParty(this_object());*/
return 1;
}
int GetBaseStatLevel(string stat){
return race::GetBaseStatLevel(stat);
}
varargs int eventDie(mixed agent){
object ob, env = environment();
int x;
if(this_object()->GetGodMode()) return 0;
if(Dead) return 1;
Dead = 1;
x = race::eventDie(agent);
if( x != 1 ){
return x;
}
foreach(ob in GetEnemies()){
if( ob ){
ob->eventEnemyDied(this_object());
}
}
Enemies = ({});
flush_messages();
return 1;
}
3) Save the following as hp.c and save into /cmds/players/hp.c
/****************************************************
* HP command, shows HP and MaxHP *
* Author: Detah@Arcania 02-04-09 *
* This file should reside in /cmds/players/ *
****************************************************/
#include <lib.h>
inherit LIB_DAEMON;
mixed cmd() {
this_player()->eventPrint("%^YELLOW%^[%^RESET%^ HP: "+this_player()->GetHP()+"/"+this_player()->GetMaxHP()+" %^YELLOW%^]%^RESET%^");
return 1;
}
update /cmds/players/hp.c
update /daemon/command.c
4) make the following changes
/verbs/players/attack.c
L102-3 in do_attack_lvs fun, change to
if(member_array(this_player(),subobj->GetEnemies()) != -1 ) {
/* && member_array(subobj,this_player()->GetNonTargets()) == -1){*/
/lib/player.c
L98 comment out living::eventKillEnemy(ob); in eventKillEnemy()
L105 changed to set_heart_beat(1); in eventReconnect()
L249 comment out SetSpecialTarget( ({}) ); in eventRevive()
L264 add this_object()->AdjustHP(this_object()->GetMaxHP(), this_object()); at the base level (ie. not in an if) in eventRevive, so players HP are reset after death, before final function bracket
L315 changed to set_heart_beat(1); in Setup()
/lib/npc.c
L64 changed to set_heart_beat(1); in create()
L78 changed to set_heart_beat(1); in CheckEncounter()
L85-90 comment out if( sizeof(enemies = GetEnemyNames()) ){ section in CheckEncounter()
L134 change GetInCombat to GetCurrentEnemy in heart_beat()
L138 change GetInCombat to GetCurrentEnemy in heart_beat()
L157 change GetInCombat to GetCurrentEnemy in heart_beat()
L185 comment out the if(GetParty()) line in Destruct()
L287 comment out living::eventEnemyDied(ob); line in eventEnemyDied()
/lib/race.c
L337 change GetInCombat to GetCurrentEnemy in GetHeartRate()
L359 changed to set_heart_beat(1); in heart_beat()
/lib/living.c
L240 change GetInCombat to GetCurrentEnemy in direct_steal_wrd_from_liv()
L253 change GetInCombat to GetCurrentEnemy in indirect_steal_obj_from_liv()
L628 change GetInCombat to GetCurrentEnemy in eventSteal()
/lib/genetics.c
L117 add this_object()->ComputeMaxHP(); in SetStat() last line
if(stat=="durability") this_object()->AdjustHP(this_object()->GetMaxHP()); in SetStat()
/lib/sentient.c
L447 change GetInCombat to GetCurrentEnemy in heart_beat()
/lib/body.c
L39 add private int HP, MaxHP, Dead, AC; to variable declare section
L59 add the follow to the prototype section after the variable declare section
int GetHP();
int GetMaxHP();
void ComputeMaxHP();
varargs int AdjustHP(int x, object source);
void NaturalHealing();
int GetAC();
void ComputeAC();
L85 add to create()
HP=0;
Dead=0;
AC=0;
L90 add to beginning of functions section:
GetHP() {
return HP;
}
GetMaxHP() {
return MaxHP;
}
ComputeMaxHP() {
MaxHP=50+GetStatLevel("durability")*8;
}
/*****************************************************
* AdjustHP fun *
* Inputs: HP adjustment, object that did the damage *
* Output: none *
* Applies the hp adj to the living's current HP and *
* checks whether HP are negative to set Death seq *
*****************************************************/
AdjustHP(int x, object source) {
HP+=x;
if( HP < 0 ) {
if(!this_object()->GetDead()){
call_out("eventDie", 0, source);
}
}
return 1;
}
/***************************************************************
* NaturalHealing fun *
* Inputs: none *
* Output: none *
* Increases HP by 1 every 20 seconds if HP are less than *
* than MaxHP. This requires the LastHeal variable declared at *
* top, initialized in create() and reset in eventReconnect *
***************************************************************/
NaturalHealing() {
if( time() - LastHeal > 20 && HP < MaxHP ) {
AdjustHP(1, this_object());
LastHeal=time();
this_object()->eventPrint("[ HP: "+HP+"/"+MaxHP+" ]"); /* either a simple tick prompt or just for testing */
}
}
GetAC() {
return AC;
}
ComputeAC() {
AC=0;
foreach(object ob in all_inventory(this_object())) {
if(ob->GetWorn()) {
AC+=ob->GetACValue();
}
}
}
comment out L386-388 if(!stringp(hobbled section in heart_beat()
L390 add following to heart_beat() at end of fun before final bracket
if(Dead==0) {
NaturalHealing();
}
L490 change GetInCombat to GetCurrentEnemy in eventCheckHealing()
L528 change GetInCombat to GetCurrentEnemy in eventCheckHealing()
L1010 add ComputeAC(); in eventWear() before final return 1
/*BEGIN ArmorClass section*/
/lib/std/armor.c (/lib/std/base_armor.c in post ds2.11 libs)
L40 add private int ACValue; to beginning of variable declare section
L54 add to beginning of functions section
void SetACValue(int x) {
ACValue=x;
}
int GetACValue() {
if(!ACValue) ACValue=0;
return ACValue;
}
/*END ArmorClass section*/
/lib/leader.c
L155 comment out whole fun eventPreAttack()
/lib/firearm.c
L287 change GetInCombat to GetCurrentEnemy in eventFire()
/lib/fishing.c
L50 change GetInCombat to GetCurrentEnemy in heart_beat()
L108 change GetInCombat to GetCurrentEnemy in CanCast()
/lib/magic.c
L16 change GetInCombat to GetCurrentEnemy in prototype section
L139 change GetInCombat to GetCurrentEnemy in eventPrepareCast()
/lib/lvs/position.c
L238-9 in eventLand() comment out
if(stringp(hobbled(this_object()))) Position = POSITION_STANDING;
else Position = POSITION_LYING;
L244-247 comment out if(!stringp(hobbled(this_object()))){ section in eventStand()
/cmds/creators/quell.c
L17 change GetInCombat to GetCurrentEnemy in cmd()
/domains/campus/npc/turret.c
L42 change GetInCombat to GetCurrentEnemy in eventShootDude()
/verbs/items/wear.c
L43 add this_object()->ComputeAC(); at end of do_wear_obj()
5) update /lib/body.c
update /lib/race.c
update /lib/combat.c
update /lib/player.c
update /lib/npc.c
update /lib/leader.c
update -r /lib/creator.c
reboot mud