Author Topic: DS combat  (Read 20066 times)

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: DS combat
« Reply #15 on: May 06, 2008, 12:29:30 pm »
I finished with a very bare combat object last night. It does not incorporate AC yet. I will perform tests tonight to get the combat object working and then start adding the core features, like AC, single HP and a simpler eventReceiveDamage.

WOOHOO!

Detah@Arcania

Offline daelaskai

  • BFF
  • ***
  • Posts: 174
    • View Profile
Re: DS combat
« Reply #16 on: May 06, 2008, 02:27:22 pm »
Great job Detah!  I know a lot of people will be thrilled with a
simpler (and hopefully easily customized) combat system.
Keep it up!

Daelas

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: DS combat
« Reply #17 on: September 19, 2008, 08:09:17 am »
Update

Basic combat is now operational. It is very clean code; just under 250 lines total. The code has already been well-commented. Only 2 files were altered, combat.c and npc.c.  There are still a few minor quirks to work out.

* normalize the heartbeats, so that players and npcs get one attack per round. You can easily adjust this to fit your own needs.
* add in a basic single variable AC system and integrate that into the damage computation
* clean up the hard returns in the damage messages
* speed up the real time of combat
* add in the promised features like wimpy, seamless unarmed or weapon combat, and basic spells

Detah@Arcania

I want to say a big thank you to Silenus, who has given me lots of advice, technical feedback and most importantly kept me motivated.
« Last Edit: September 19, 2008, 08:16:13 am by detah »

Offline shigs

  • Acquaintance
  • *
  • Posts: 37
    • View Profile
Re: DS combat
« Reply #18 on: September 19, 2008, 04:35:08 pm »
That's great news! I look forward to seeing it in action.

Offline Atomic

  • BFF
  • ***
  • Posts: 115
  • To infinity and beyond!
    • View Profile
Re: DS combat
« Reply #19 on: September 22, 2008, 08:03:48 am »
Indeed. Would love to see it in action... keep it up Detah.
Always remember that the early bird gets the worm first, but the fi...*SNAP*...second mouse gets the cheese.

Offline Xzystance

  • Acquaintance
  • *
  • Posts: 29
    • View Profile
Re: DS combat
« Reply #20 on: September 24, 2008, 06:38:23 pm »
So, where would i go to get these updates to the combat.c and npc.c files? :) I would much like simpler combat system.

X

Offline Nikeos

  • Acquaintance
  • *
  • Posts: 11
  • Studious Grecian
    • View Profile
Re: DS combat
« Reply #21 on: January 03, 2009, 08:47:43 pm »
Hey, Detah...

I'm helping develop a MUD here and I know that we were anxious to see what you've done with combat, as we were hoping to circumvent our own editing of the combat system. I'd love to see what you've gotten and I was curious where things were.

Hope things are going smashingly well!


Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: DS combat
« Reply #22 on: January 03, 2009, 11:56:00 pm »
Combat has a couple little bugs left. The basic tet-a-tet works. Once that is worked out, it will be a simple matter for me to alter the AC and HP variables to make the whole system LPMUD-like. The holidays have kept me diverted for several weeks now. But I expect life to return to normal starting monday. As soon as I make progress, I will post it here.

Detah

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
A good day
« Reply #23 on: January 29, 2009, 08:31:50 pm »
A featureless barebones combat is now complete.
* The player attacks once and each engaged monster attacks once per round.
* If you leave the room, combat stops.
* If you enter a room, start combat, leave the room, then reenter, the monster will 'remember' you and automatically engage you in combat again.
* If player or monster die, combat stops and both 'memories' are erased.

I will now begin adding the promised features: wimpy, single HP, single AC and perhaps a single example combat spell.

Detah
PS Sincere thank yous to Silenus and Raudhrskal for helping me find these last 2 logic errors.

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: DS combat
« Reply #24 on: March 05, 2009, 11:00:09 am »
The barebones combat system is complete.

This system includes the following features:
* combat hb hpinfo bar
* tick hpinfo bar (appears only when HP<MaxHP)
* single tohit function
* single damage function
* single HP trait (no limb hp)
* single MaxHP trait; default ties MaxHP to the Durability stat
* single AC trait
* hardcoded wimpy for players. requires additional mods to make this player-customizable
* natural healing of 1 hp / tick
* combat round occurs once per second

To implement:
* move your current /lib/combat.c to some backup dir and rename it combat.old
* save the following as combat.c and place it in /lib/
Code: [Select]
/*****************************************************************************
 * Barebones combat for Dead Souls lib                                       *
 * Author: Detah@Arcania, with huge thanks to Silenus for design outline.    *
 *   The author wishes to thank the following people for advice, feedback    *
 *   and information which directly helped to complete this project:         *
 *   Silenus, Raudhrskal and Cratylus                                        *
 * Completed: Detah@Arcania 03-04-09                                         *
 *****************************************************************************/
#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)                                         *
 * This is the core handler of combat. Hit determination occurs here. A miss *
 * immediately reports a miss, otherwise, damage is computed and passed on   *
 * to the target and reported to the room                                    *
 *****************************************************************************/
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                                                     *
 * Contains the tohit formula.                                               *
 *****************************************************************************/
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                                              *
 * Contains the damage formula.                                              *
 *****************************************************************************/
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                                                              *
 * Customizable damage messages reported to attacker, target and room        *
 *****************************************************************************/
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;
}

* place the following in a file called hp.c in /cmds/players/hp.c
Code: [Select]
/****************************************************
 * 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

Make the following changes to the following files:
Code: [Select]
/lib/player.c
L84 comment out living::eventKillEnemy(ob); in eventKillEnemy()
L91 changed to set_heart_beat(1); in eventReconnect()
L235 comment out SetSpecialTarget( ({}) ); in eventRevive()
L239 this_object()->AdjustHP(this_object()->GetMaxHP(), this_object()); in eventRevive, so players HP are reset after death
L332 changed to set_heart_beat(1); in Setup()

/lib/npc.c
L63 changed to set_heart_beat(1); in create()
L71 changed to set_heart_beat(1); in CheckEncounter()
L73-78 comment out if( sizeof(enemies = GetEnemyNames()) ){ section
L123 change GetInCombat to GetCurrentEnemy
L127 change GetInCombat to GetCurrentEnemy
L146 change GetInCombat to GetCurrentEnemy
L174 comment out GetParty() line in Destruct() fun
L278 comment out living::eventEnemyDied(ob); line

/lib/race.c
L337 change GetInCombat to GetCurrentEnemy
L360 changed to set_heart_beat(1); in heart_beat()

/lib/living.c
L222 change GetInCombat to GetCurrentEnemy
L235 change GetInCombat to GetCurrentEnemy
L610 change GetInCombat to GetCurrentEnemy

/lib/genetics.c in SetStat() add
L98 this_object()->ComputeMaxHP();
L99 if(stat=="durability") this_object()->AdjustHP(this_object()->GetMaxHP());

/lib/sentient.c
L386 change GetInCombat to GetCurrentEnemy

/lib/body.c
L41 private int HP, MaxHP, Dead, AC;
L59 int GetHP();
    int GetMaxHP();
    void ComputeMaxHP();
    varargs int AdjustHP(int x, object source);
    void NaturalHealing();
    int GetAC();
    void ComputeAC();
L85 HP=0;
    Dead=0;
    AC=0;
L90
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();
      }
    }
}

L363 in heart_beat()
if(Dead==0) {
NaturalHealing();
}
comment out L363-365 hobbled lines in heart_beat()
L456 change GetInCombat to GetCurrentEnemy in eventCheckHealing()
L494 change GetInCombat to GetCurrentEnemy in eventCheckHealing()
L989 in eventWear add ComputeAC();
/*BEGIN ArmorClass section*/
/lib/std/armor.c
L40 private int ACValue;
L57 void SetACValue(int x) {
        ACValue=x;
    }
L61 int GetACValue() {
        if(!ACValue) ACValue=0;
        return ACValue;
    }
/*END ArmorClass section*/
/lib/leader.c
L148 comment out whole fun eventPreAttack()

/lib/firearm.c
L299 change GetInCombat to GetCurrentEnemy

/lib/fishing.c
L50 change GetInCombat to GetCurrentEnemy
L108 change GetInCombat to GetCurrentEnemy

/lib/magic.c
L16 change GetInCombat to GetCurrentEnemy
L139 change GetInCombat to GetCurrentEnemy

/lib/lvs/position.c
comment out L238-9 hobbled lines

/cmds/creators/quell.c
L14 change GetInCombat to GetCurrentEnemy

/domains/campus/npc/turret.c
L42 change GetInCombat to GetCurrentEnemy

/verbs/items/wear.c at end of do_wear_obj()
L43 this_object()->ComputeAC();

/verbs/players/attack.c in do_attack_lvs fun, change to
        if(member_array(this_player(),subobj->GetEnemies()) != -1 ) { 
/*        &&  member_array(subobj,this_player()->GetNonTargets()) == -1){*/

* 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
Done//

All newly created characters after this point will automatically generate their HP and MaxHP appropriately. However, existing characters will have 0/0 HP/MaxHP. To correct this do
Code: [Select]
eval return this_player()->ComputeMaxHP();
or
Code: [Select]
eval return find_player("playername")->ComputeMaxHP();

You can also use
Code: [Select]
eval return this_player()->AdjustHP(100);
to increase the HP of any player at any time.

Substantive comments are welcome; especially if you know a way to improve efficiency.

Detah@Arcania
This code is PD. Merry Christmas.


Offline Atomic

  • BFF
  • ***
  • Posts: 115
  • To infinity and beyond!
    • View Profile
Re: DS combat
« Reply #25 on: March 06, 2009, 05:35:59 am »
Great work Detah!

How does this code handle the following situations:
- MaxHP is altered by magically enhanced items (let's say you're wearing a +10 constitution armor)
- same question for regeneration. What if you're under the influence of alcohol and heal faster, or wear a magic ring that
  speeds up the process by adding +5 hp's per round ?

...or is that out of scope/apart from this combat code?
Always remember that the early bird gets the worm first, but the fi...*SNAP*...second mouse gets the cheese.

Offline saquivor

  • BFF
  • ***
  • Posts: 111
    • View Profile
Re: DS combat
« Reply #26 on: March 06, 2009, 06:51:43 am »
Congratulations on your excellent progress.

Saq

Offline detah

  • BFF
  • ***
  • Posts: 190
  • Ruler of 2D
    • View Profile
Re: DS combat
« Reply #27 on: March 06, 2009, 08:49:46 am »
Great work Detah!

How does this code handle the following situations:
- MaxHP is altered by magically enhanced items (let's say you're wearing a +10 constitution armor)
- same question for regeneration. What if you're under the influence of alcohol and heal faster, or wear a magic ring that
  speeds up the process by adding +5 hp's per round ?
...or is that out of scope/apart from this combat code?
* There is no code for handling advanced combat issues like magical enhancements. This is a barebones combat with only the features I outlined above. However, if you write the +10 durability armor, the MaxHP code will automatically update your MaxHP to the appropriate amount. You will want to include a line like:
Code: [Select]
this_object()->ComputeMaxHP();
in the wear/equip/remove code. This should handle everything as far as MaxHP.

* There is also no code at all for alcohol or increased regen. You will need to code that yourself. However, the NaturalHealing function can be easily altered to set your regen to anything you wish. This is a trivial mod. eg. suppose you want to let every player have regen equal to 20% of their Speed stat. Then just change the AdjustHP() line in NaturalHealing() to
Code: [Select]
  AdjustHP(this_object()->GetStatLevel("speed")/5, this_object());
Alcohol- increased regen is also an easy mod, altho not as trivial as linking it to a stat. To add alcohol as a modifier to regen I would recommend creating a new function for alcohol regen. Then call that from the 'drink alcohol' command, so the bonus regen it is added to BaseNaturalHealing (renamed). Then have a call_out to remove the alcohol regen after some appropriate timeperiod. There are other ways to accomplish this, but are not as easy.

Offline Fooze

  • Acquaintance
  • *
  • Posts: 1
    • View Profile
Re: DS combat
« Reply #28 on: March 21, 2009, 05:54:07 pm »
Absolutely beautiful... nice work, Detah!

Offline shigs

  • Acquaintance
  • *
  • Posts: 37
    • View Profile
Re: DS combat
« Reply #29 on: March 22, 2009, 06:40:53 pm »
Again, Detah, Thanks from both myself and the AmethystiumMUD team. You've saved us reinventing the wheel!