From: Andrey Fidrya Subject: Killing same mobs EXP gain limitation I succesfully added EXP gain limitation to my MUD and snippetized it. Here is the code snippet: === CUT HERE === "Killing same MOBs XP gain penalties" snippet was tested on CircleMUD version 3 beta patchlevel 11. This snippet limits XP gain when player is killing same mob too many times. Send bug reports, propositions about optimization and new XP limit formulas to: andrey@alex-ua.com Warning: This snippet adds extra 501 bytes of data to playerfile per each player to store mobs killed by him. If you install this snippet, you should do player wipe or convert your player base to new format. How it works: There is array in which mob vnums and number of kills stored. Array has 100 entries. If array becomes full, new monsters are overwrited over oldest ones. For example: if you killed a sailor for 30 times and became "maxxed" on him, to get full XP again you must kill 100 other types of mobs to kick sailor off the list and so remove all XP penalties for him. There is new optional E-type command for world builders: MaxFactor: n, where n = 1 to 255 and shows number of mobs that can be killed without large XP penalties. For example, when you kill mob who has 1000 XP and MaxFactor: 3, XP you receive will be arranged something like that: 333, 329, 326, (exceeded maxfactor) 218, 162, 130, 108, 93 ..... By default, MaxFactor is 255. How to determine at which value MaxFactor should be set: It depends on how many mobs of same type are in zone. For example: in low level zone "castle" there are 12 soldiers, 2 commanders, 1 high mage and 2 apprentices. MaxFactors should be arranged in way like that: soldier - MaxFactor around 18 (on second visit to castle player will notice that XP for soldiers is going down fast) commander and high mage - MaxFactor around 12 (player should always get good XP for hard and unique mobs) apprentices - MaxFactor around 2 (when player killed them once, they are no longer a challenge to him and shouldn't give much XP) Of course you can disagree with this example and set your own values. If you use this snippet, please credit: - Andrey Fidrya (andrey@alex-ua.com) aka Zmey//RMUD (Russian MUD project) Now, the snippet: ******************************************************************* *** Open edit file: STRUCTS.H ******************************************************************* ------------------------------------------------------------------- Around line 709, look for: ------------------------------------------------------------------- struct player_special_data_saved { byte skills[MAX_SKILLS+1]; /* array of skills plus skill 0 */ byte PADDING0; /* used to be spells_to_learn */ bool talks[MAX_TONGUE]; /* PC s Tongues 0 for NPC */ ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- /* Killing same mobs XP penalties arrays */ int kills_vnum[100]; /* Virtual numbers of the mobs killed */ byte kills_amount[100]; /* Number of mobs of that type killed */ byte kills_curpos; /* Current position in array */ ------------------------------------------------------------------- Around line 771, look for: ------------------------------------------------------------------- /* Specials used by NPCs, not PCs */ struct mob_special_data { byte last_direction; /* The last direction the monster went */ int attack_type; /* The Attack Type Bitvector for NPC's */ byte default_pos; /* Default position for NPC */ memory_rec *memory; /* List of attackers to remember */ byte damnodice; /* The number of damage dice's */ byte damsizedice; /* The size of the damage dice's */ int wait_state; /* Wait state for bashed mobs */ ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- byte maxfactor; /* Max number of kills of mob allowed */ ******************************************************************* *** Open edit file: UTILS.H ******************************************************************* ------------------------------------------------------------------- Around line 273, look for: ------------------------------------------------------------------- #define GET_ALIASES(ch) ((ch)->player_specials->aliases) #define GET_LAST_TELL(ch) ((ch)->player_specials->last_tell) #define GET_SKILL(ch, i) ((ch)->player_specials->saved.skills[i]) #define SET_SKILL(ch, i, pct) { (ch)->player_specials->saved.skills[i] = pct; } ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- /* Killing same mobs XP penalties macros */ #define GET_KILLS_VNUM(ch, a) ((ch)->player_specials->saved.kills_vnum[a-1]) #define GET_KILLS_AMOUNT(ch, a) ((ch)->player_specials->saved.kills_amount[a-1] ) #define GET_KILLS_CURPOS(ch) ((ch)->player_specials->saved.kills_curpos) #define GET_MOB_MAXFACTOR(ch) ((ch)->mob_specials.maxfactor) ******************************************************************* *** Open edit file: DB.C ******************************************************************* ------------------------------------------------------------------- Around line 836, look for: ------------------------------------------------------------------- void parse_simple_mob(FILE *mob_f, int i, int nr) { int j, t[10]; char line[256]; mob_proto[i].real_abils.str = 11; mob_proto[i].real_abils.intel = 11; mob_proto[i].real_abils.wis = 11; mob_proto[i].real_abils.dex = 11; mob_proto[i].real_abils.con = 11; mob_proto[i].real_abils.cha = 11; ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- mob_proto[i].mob_specials.maxfactor = 255; ------------------------------------------------------------------- Around line 915, look for: ------------------------------------------------------------------- void interpret_espec(char *keyword, char *value, int i, int nr) { int num_arg, matched = 0; num_arg = atoi(value); ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- CASE("MaxFactor") { RANGE(1, 255); mob_proto[i].mob_specials.maxfactor = num_arg; } ******************************************************************* *** Open edit file: FIGHT.C ******************************************************************* ------------------------------------------------------------------- Around line 28, look for: ------------------------------------------------------------------- /* External structures */ extern struct room_data *world; extern struct message_list fight_messages[MAX_MESSAGES]; extern struct obj_data *object_list; extern int pk_allowed; /* see config.c */ extern int auto_save; /* see config.c */ extern int max_exp_gain; /* see config.c */ ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- /* for killing same mob exp gain penalties */ extern struct index_data *mob_index; ------------------------------------------------------------------- Around line 336, look for: ------------------------------------------------------------------- void perform_group_gain(struct char_data * ch, int base, struct char_data * victim) { int share; ------------------------------------------------------------------- Right below it, put the following: ------------------------------------------------------------------- int exp_after_lim; if (!IS_NPC(ch) && IS_NPC(victim)) exp_after_lim = kills_limit_xpgain(ch, victim, base); ------------------------------------------------------------------- Right below it, _REPLACE_ the: ------------------------------------------------------------------- share = MIN(max_exp_gain, MAX(1, base)); ------------------------------------------------------------------- with: ------------------------------------------------------------------- share = MIN(max_exp_gain, MAX(1, exp_after_lim)); ------------------------------------------------------------------- Around line 730 in function damage, look for: ------------------------------------------------------------------- if (GET_POS(victim) == POS_DEAD) { if (IS_NPC(victim) || victim->desc) if (IS_AFFECTED(ch, AFF_GROUP)) group_gain(ch, victim); else { exp = MIN(max_exp_gain, GET_EXP(victim) / 3); ------------------------------------------------------------------- And _REPLACE_ this line: ------------------------------------------------------------------- exp = MIN(max_exp_gain, GET_EXP(victim) / 3); ------------------------------------------------------------------- with following piece of code: ------------------------------------------------------------------- exp = GET_EXP(victim) / 3; /* Limit up XP gain according on amount of kills of current mob */ if (!IS_NPC(ch) && IS_NPC(victim)) exp = kills_limit_xpgain(ch, victim, exp); exp = MIN(max_exp_gain, exp); ------------------------------------------------------------------- After change_alignment, but before death_cry, (around line 290) insert this function: ------------------------------------------------------------------- /* Killing same mobs XP gain limit function */ /* You can change formulas below if you wish */ #define GET_PERCENT(num, from_v) ((int)((float)(from_v) / 100 * (num))) /* This formula is responsible for very slow increase of XP penalties */ #define KILL_UNDER_MF_FORMULA(exp, num_of_kills) \ (MAX(1, GET_PERCENT((101 - num_of_kills), exp) \ ) \ ) /* This formula is responsible for fast increase of XP penalties */ #define KILL_ABOVE_MF_FORMULA(exp, num_of_kills) \ (MAX(1, GET_PERCENT((float)100/((float)num_of_kills/2+1), exp) \ ) \ ) int kills_limit_xpgain(struct char_data *ch, struct char_data *victim, int exp) { byte current_entry; int victim_vnum = GET_MOB_VNUM(victim); int new_exp; int i; /* Find corresponding to victim's vnum entry in array or create one */ i = GET_KILLS_CURPOS(ch); while (i >= 1 && GET_KILLS_VNUM(ch, i) != victim_vnum) --i; if (!i) { if (!GET_KILLS_VNUM(ch, GET_KILLS_CURPOS(ch))) { /* Array still isn't full - don't scan second part */ i = GET_KILLS_CURPOS(ch); } else { /* Array is full - must scan second part too */ i = 100; while (i > GET_KILLS_CURPOS(ch) && GET_KILLS_VNUM(ch, i) != victim_vnum) --i; } if (i == GET_KILLS_CURPOS(ch)) { /* We came back to search starting point: */ /* New type of mob killed, add it to list */ GET_KILLS_VNUM(ch, i) = victim_vnum; GET_KILLS_AMOUNT(ch, i) = 0; if (GET_KILLS_CURPOS(ch) < 100) ++GET_KILLS_CURPOS(ch); else GET_KILLS_CURPOS(ch) = 1; } } current_entry = i; /* Ok, now we have current_entry, pointing to current mob's entry in array */ /* Lets increment number of kills and reduce XP gain */ if (GET_KILLS_AMOUNT(ch, current_entry) < 255) ++GET_KILLS_AMOUNT(ch, current_entry); if (GET_KILLS_AMOUNT(ch, current_entry) > GET_MOB_MAXFACTOR(victim)) { new_exp = KILL_UNDER_MF_FORMULA(exp, GET_MOB_MAXFACTOR(victim)); new_exp = KILL_ABOVE_MF_FORMULA(new_exp, (GET_KILLS_AMOUNT(ch, current_entry) - GET_MOB_MAXFACTOR(victim))); } else if (GET_KILLS_AMOUNT(ch, current_entry) > 1) new_exp = KILL_UNDER_MF_FORMULA(exp, GET_KILLS_AMOUNT(ch, current_entry)) ; else new_exp = exp; /* MOB was killed for first time, just give the EXP */ return new_exp; }