*** BEGIN EVENT REGEN WALKTHROUGH BEGIN ACT.COMM.C In do_gen_comm in the section for SCMD_HOLLER, change the modification of GET_MOVE to: + alter_move(ch, holler_move_cost); END ACT.COMM.C BEGIN ACT.MOVEMENT.C In do_simple_move, change all modifications to GET_MOVE (stock there is only one) to: + alter_move(ch, need_movement); Note that the above modification is needed three times if you have the mount code installed. END ACT.MOVEMENT.C BEGIN ACT.WIZARD.C In do_wizutil SCMD_UNAFFECT, immediately after it sends the message to the victim, add: + check_regen_rates(vict); In perform_set, cases 4 through 9, add a comment to the affect_total lines: + affect_total(vict); /* affect_total() handles regen updates */ In perform_set, case 31, immediately before the break; add: + check_regen_rates(vict); In perform_set, case 39, immediately before the break; add: + check_regen_rates(vict); END ACT.WIZARD.C BEGIN CLASS.C Add an include at the top: #include "handler.h" Near the end of advance_level, just before snoop_check and save_char, add: + check_regen_rates(ch); /* start regening new points */ END CLASS.C BEGIN DB.C At the top of save_char where variables are declared, add: sh_int hit, mana, move; In save_char, right before the comment to unaffect everything, add: + /* + * save current values, because unequip_char will call + * affect_total(), which will reset points to lower values + * if player is wearing +points items. We will restore old + * points level after reequiping. + */ + hit = GET_HIT(ch); + mana = GET_MANA(ch); + move = GET_MOVE(ch); For STOCK (no ascii pfiles) in save_char, right after the commented out affect_total, add: + /* restore our points to previous level */ + GET_HIT(ch) = hit; + GET_MANA(ch) = mana; + GET_MOVE(ch) = move; For ASCII PFILES (modified stock) in save char, right before it starts to print the file, add: + /* restore our points to previous level */ + GET_HIT(ch) = hit; + GET_MANA(ch) = mana; + GET_MOVE(ch) = move; In reset_char, right before GET_LAST_TELL, add: + check_regen_rates(ch); /* start regening points */ END DB.C BEGIN FIGHT.C In update_pos, change the else if statements for DEAD, MORTALLYW and INCAP to: ! else if (GET_HIT(victim) <= HIT_DEAD) ! GET_POS(victim) = POS_DEAD; ! else if (GET_HIT(victim) <= HIT_MORTALLYW) ! GET_POS(victim) = POS_MORTALLYW; ! else if (GET_HIT(victim) <= HIT_INCAP) ! GET_POS(victim) = POS_INCAP; In int damage, change the adjustment to GET_HIT (before exp for the hit is calced) to: + alter_hit(victim, dam); END FIGHT.C BEGIN HANDLER.C For older versions of the DG_EVENTS package, add an include to events.h at the top: #include "events.h" If you have a MODERN (pl8+) DG_SCRIPTS, the include is dg_events.h: #include "dg_events.h" In affect_total, immediately before the closing bracket, add: + check_regen_rates(ch); /* update regen rates (for age) */ In extract_char_final, right after stopping fights, add: + /* cancel point updates */ + for (i = 0; i < 3; i++) + if (GET_POINTS_EVENT(ch, i)) { + event_cancel(GET_POINTS_EVENT(ch, i)); + GET_POINTS_EVENT(ch, i) = NULL; + } END HANDLER.C BEGIN HANDLER.H At the end of the file, add: +/* prototypes from regen.c */ +void alter_hit(struct char_data *ch, int amount); +void alter_mana(struct char_data *ch, int amount); +void alter_move(struct char_data *ch, int amount); +void check_regen_rates(struct char_data *ch); END HANDLER.H BEGIN LIMITS.C In gain_condition, right after the macro call to GET_COND(ch, condition) += value;, add: + /* update regen rates if we were just on empty */ + if ((condition != DRUNK) && (value > 0) && + (GET_COND(ch, condition) == value)) + check_regen_rates(ch); In point_update, remove the calls to GET_HIT, et al like so: - GET_HIT(i) = MIN(GET_HIT(i) + hit_gain(i), GET_MAX_HIT(i)); - GET_MANA(i) = MIN(GET_MANA(i) + mana_gain(i), GET_MAX_MANA(i)); - GET_MOVE(i) = MIN(GET_MOVE(i) + move_gain(i), GET_MAX_MOVE(i)); END LIMITS.C BEGIN MAGIC.C At the end of mag_points, remove GET_HIT, et al and add as follows: - GET_HIT(victim) = MIN(GET_MAX_HIT(victim), GET_HIT(victim) + healing); - GET_MOVE(victim) = MIN(GET_MAX_MOVE(victim), GET_MOVE(victim) + move); - update_pos(victim); + alter_hit(victim, -healing); + alter_move(victim, -move); At the end of mag_unaffects, right before the closing bracket, add: + if (spellnum == SPELL_REMOVE_POISON) + check_regen_rates(victim); /* speed up regen rate immediately */ END MAGIC.C BEGIN MAKEFILE Add regen.c and regen.o to the makefile. You can do this in your template Makefile too. + mobact.o modify.o objsave.o olc.o random.o regen.o shop.o spec_assign.o \ + mobact.c modify.c objsave.c olc.c random.c regen.c shop.c spec_assign.c \ END MAKEFILE BEGIN REGEN.C Add the entire regen.c file to your src directory: /* ************************************************************************ * File: regen.c * * * * Usage: Contains routines to handle event based point regeneration * * * * Written by Eric Green (ejg3@cornell.edu) * ************************************************************************ */ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "spells.h" #include "handler.h" #include "events.h" /* For older events packages */ /* #include "dg_event.h" */ /* For modern (pl8+) DG_SCRIPTS packages */ /* Player point types for events */ #define REGEN_HIT 0 #define REGEN_MANA 1 #define REGEN_MOVE 2 #define PULSES_PER_MUD_HOUR (SECS_PER_MUD_HOUR*PASSES_PER_SEC) /* event object structure for point regen */ struct regen_event_obj { struct char_data *ch; /* character regening */ int type; /* HIT, MOVE, or MANA */ }; EVENTFUNC(points_event) { struct regen_event_obj *regen = (struct regen_event_obj *) event_obj; struct char_data *ch; int type, gain; char logbuf[100]; ch = regen->ch; type = regen->type; if (GET_POS(ch) >= POS_STUNNED) { /* no help for the dying */ /* * Increment type of points by one. * If not at max, reenqueue the event. */ switch (type) { case REGEN_HIT: GET_HIT(ch) = MIN(GET_HIT(ch) + 1, GET_MAX_HIT(ch)); if (GET_POS(ch) <= POS_STUNNED) update_pos(ch); if (GET_HIT(ch) < GET_MAX_HIT(ch)) { /* reenqueue the event */ gain = hit_gain(ch); return (PULSES_PER_MUD_HOUR / (gain ? gain : 1)); } break; case REGEN_MANA: GET_MANA(ch) = MIN(GET_MANA(ch) + 1, GET_MAX_MANA(ch)); if (GET_MANA(ch) < GET_MAX_MANA(ch)) { /* reenqueue the event */ gain = mana_gain(ch); return (PULSES_PER_MUD_HOUR / (gain ? gain : 1)); } break; case REGEN_MOVE: GET_MOVE(ch) = MIN(GET_MOVE(ch) + 1, GET_MAX_MOVE(ch)); if (GET_MOVE(ch) < GET_MAX_MOVE(ch)) { /* reenqueue the event */ gain = move_gain(ch); return (PULSES_PER_MUD_HOUR / (gain ? gain : 1)); } break; default: sprintf(logbuf, "SYSERR: Unknown points event type %d", type); log(logbuf); free(event_obj); return 0; } } /* kill this event */ GET_POINTS_EVENT(ch, type) = NULL; free(event_obj); return 0; } /* * subtracts amount of hitpoints from ch's current and starts points event */ void alter_hit(struct char_data *ch, int amount) { struct regen_event_obj *regen; long time; int gain; GET_HIT(ch) = MIN(GET_HIT(ch) - amount, GET_MAX_HIT(ch)); if (GET_HIT(ch) <= HIT_INCAP) return; if (GET_HIT(ch) < GET_MAX_HIT(ch) && !GET_POINTS_EVENT(ch, REGEN_HIT)) { CREATE(regen, struct regen_event_obj, 1); regen->ch = ch; regen->type = REGEN_HIT; gain = hit_gain(ch); time = PULSES_PER_MUD_HOUR / (gain ? gain : 1); GET_POINTS_EVENT(ch, REGEN_HIT) = event_create(points_event, regen, time); if (amount >= 0) { /* * if the character gained hp, update position and * restart mana and move regeneration if needed. */ update_pos(ch); alter_mana(ch, 0); alter_move(ch, 0); } } } /* * subtracts amount of mana from ch's current and starts points event */ void alter_mana(struct char_data *ch, int amount) { struct regen_event_obj *regen; long time; int gain; GET_MANA(ch) = MIN(GET_MANA(ch) - amount, GET_MAX_MANA(ch)); if (!GET_POINTS_EVENT(ch, REGEN_MANA) && (GET_MANA(ch) < GET_MAX_MANA(ch))) { /* make sure the character isn't dying */ if (GET_POS(ch) >= POS_STUNNED) { CREATE(regen, struct regen_event_obj, 1); regen->ch = ch; regen->type = REGEN_MANA; gain = mana_gain(ch); time = PULSES_PER_MUD_HOUR / (gain ? gain : 1); GET_POINTS_EVENT(ch, REGEN_MANA) = event_create(points_event, regen, time); } } } /* * subtracts amount of moves from ch's current and starts points event */ void alter_move(struct char_data *ch, int amount) { struct regen_event_obj *regen; long time; int gain; GET_MOVE(ch) = MIN(GET_MOVE(ch) - amount, GET_MAX_MOVE(ch)); if (!GET_POINTS_EVENT(ch, REGEN_MOVE) && (GET_MOVE(ch) < GET_MAX_MOVE(ch))) { /* make sure the character isn't dying */ if (GET_POS(ch) >= POS_STUNNED) { CREATE(regen, struct regen_event_obj, 1); regen->ch = ch; regen->type = REGEN_MOVE; gain = move_gain(ch); time = PULSES_PER_MUD_HOUR / (gain ? gain : 1); GET_POINTS_EVENT(ch, REGEN_MOVE) = event_create(points_event, regen, time); } } } /* updates regen rates. Use when big regen rate changes are made */ void check_regen_rates(struct char_data *ch) { struct regen_event_obj *regen; int type, gain = 0; long time; if (GET_HIT(ch) <= HIT_INCAP) return; for (type = REGEN_HIT; type <= REGEN_MOVE; type++) { switch (type) { case REGEN_HIT: if (GET_HIT(ch) >= GET_MAX_HIT(ch)) continue; gain = hit_gain(ch); break; case REGEN_MANA: if (GET_MANA(ch) >= GET_MAX_MANA(ch)) continue; gain = mana_gain(ch); break; case REGEN_MOVE: if (GET_MOVE(ch) >= GET_MAX_MOVE(ch)) continue; gain = move_gain(ch); break; } time = PULSES_PER_MUD_HOUR / (gain ? gain : 1); if (!GET_POINTS_EVENT(ch, type) || (time < event_time(GET_POINTS_EVENT(ch, type)))) { /* take off old event, create updated event */ if (GET_POINTS_EVENT(ch, type)) event_cancel(GET_POINTS_EVENT(ch, type)); CREATE(regen, struct regen_event_obj, 1); regen->ch = ch; regen->type = type; GET_POINTS_EVENT(ch, type) = event_create(points_event, regen, time); } } } END REGEN.C BEGIN SPELL_PARSER.C In do_cast, near the end where mana is calculated, make the following changes: ! /* You throws the dice and you takes your chances.. 101% is total failure */ ! if (rand_number(0, 101) > GET_SKILL(ch, spellnum)) { ! WAIT_STATE(ch, PULSE_VIOLENCE); ! if (!tch || !skill_message(0, ch, tch, spellnum)) ! send_to_char(ch, "You lost your concentration!\r\n"); ! if (mana > 0) ! alter_mana(ch, mana / 2); ! if (SINFO.violent && tch && IS_NPC(tch)) ! hit(tch, ch, TYPE_UNDEFINED); ! } else { /* cast spell returns 1 on success; subtract mana & set waitstate */ ! if (cast_spell(ch, tch, tobj, spellnum)) { ! WAIT_STATE(ch, PULSE_VIOLENCE); ! if (mana > 0) ! alter_mana(ch, mana); ! } ! } END SPELL_PARSER.C BEGIN STRUCTS.H After the position defines, add: +/* Hitpoint levels */ +#define HIT_INCAP -3 /* The hit level for incapacitation */ +#define HIT_MORTALLYW -6 /* The hit level for mortally wounded */ +#define HIT_DEAD -11 /* The point you never want to get to */ In struct char_data, add the following: + struct event *points_event[3]; /* events for regening H/M/V */ END STRUCTS.H BEGIN UTILS.H Add the following defines: +#define GET_POINTS_EVENT(ch, i) ((ch)->points_event[i]) +#define GET_DAMAGE_EVENTS(ch) ((ch)->damage_events) END UTILS.H END EVENT REGEN WALKTHROUGH *** Circle 3.0 Event Based Regeneration Patch 1.0 ============================================= First release, 9 March 1998 This patch implements event based regeneration for CircleMUD. This makes it so each point type (hit, mana, or move) is regenerated one point at a time, rather than having one big jump once per tick. The rate of regeneration is still the same with this patch, it just does it gradually in order to make the game run "smoother" and to defeat people with tick counters who sleep right before the tick. This patch does not add as much processor overhead as one might think at first glance. Stock CircleMUD recalculates the regeneration of every character in the game (mobs and players) once per tick. This patch changes regeneration so only the characters whose points are less than their maximum are checked. Even with multiple checks per tick, if most characters are at their full point level, the over head could be lower than with stock Circle. Installation ------------ 1. Install DG Events (version 1.1 or later), following the instructions provided with that patch. 2. Copy the regen.c file to your circle/src directory. 3. In your circle directory (not circle/src), run "patch -p1 < regen.patch". 4. Rerun configure (to update your Makefile). 5. If your mud has been modified, see the "Using" section below and make any necessary changes. 5. Recompile your MUD. The patch is based on Circle 3.0 pl12, so you might have to do some patching by hand if you have modified your source or are using it on a different version. This has only been testing on Linux, but hopefully will work on all systems unmodified. Please let me know if you have to make any changes for it to work on your system. Using ----- Stock CircleMUD allows a character's points to be altered by directly manipulating the points variable, such as by setting GET_HIT(ch) or ch->points.hit. When you use this patch, any changes must be done using the functions: void alter_hit(struct char_data *ch, int amount); void alter_mana(struct char_data *ch, int amount); void alter_move(struct char_data *ch, int amount); The first argument is the character to alter. The second argument is the number of points to subtract from the points. To add points, use a negative amount. If the points variable must be manipulated directly, call the appropriate alter function with amount of 0 to make sure there are the proper regeneration events. Remember to do this when increasing the maximum value also. Another function is provided to update regeneration on all three points types: void check_regen_rates(struct char_data *ch); This will make sure that any point category below maximum has an event to regenerate it. It also will recalculate the regeneration rate if the rate has been increased. It should be called whenever there is a significant change in regeneration rates, or if you want to check that all three points types have the proper regen events. Possible Problems ----------------- If the mud fails to compile with this patch due to errors like: fight.o(.text+0xaf8): undefined reference to `alter_hit' handler.o(.text+0x0bc): undefined reference to `check_regen_rates' then you did not properly update your Makefile, either by rerunning configure, or copying over the Makefile.win to Makefile. regen.o must be added to OBJFILES in order for it to compile properly. If you get an error compiling about undefined references to the functions event_create, event_time, and event_cancel, you did not properly install the dgevents patch. Refer to the documentation with that package to check your installation. If a player stops regenerating properly, you probably forgot to use an alter_xxx() call and instead manipulated the variable directly. You need to track down where this occurred and use the alter_xxx() or check_regen_rates(). Warning: Once you get used to event based regen, it is hard playing on a mud using ticks. Location -------- The latest version of this regen patch should be available from the CircleMUD ftp site (ftp://ftp.circlemud.org/pub/CircleMUD/contrib/code). The DG Events patch can be found in the same location, named dgevents. If you find a problem with this package, please email me. Eric Green ejg3@cornell.edu http://www.imaxx.net/~thrytis