Welcome to the Builder Academy

Question oedit configurable object repop percentage

More
30 May 2025 07:18 - 30 May 2025 07:23 #10739 by Salty
Greetings,

Here's a snippet for oedit configurable object repop percentages, complete with happyhour bonuses.

add 
Code:
int rarity;
to struct obj_flag_data in structs.h

define
Code:
#define GET_OBJ_REPOP(obj) ((obj)->obj_flags.rarity)
in utils.h

define
Code:
#define MAX_OBJ_REPOP 100
in oasis.h

add
Code:
#define OEDIT_REPOP  <YOUR VALUE>
to the bottom of your Submodes of OEDIT connectedness defines

add
Code:
  send_to_char(ch, "Repop Percentage: %d\r\n", GET_OBJ_REPOP(j));
to your do_stat_object() in act.wizard.c

change:
Code:
  if ((retval = sscanf(line, "%d %d %d %d %d", t, t + 1, t + 2, t + 3, t + 4)) != 5) {     if (retval == 3) {       t[3] = 0;       t[4] = 0;     } else if (retval == 4)       t[4] = 0;     else {       log("SYSERR: Format error in third numeric line (expecting 5 args, got %d), %s", retval, buf2);       exit(1);     }   }
to
Code:
  if ((retval = sscanf(line, "%d %d %d %d %d %d", t, t + 1, t + 2, t + 3, t + 4, t + 5)) != 6)   {     if (retval == 4)     {       t[4] = 0;       t[5] = 100;     }     else if (retval == 5)       t[5] = 100;     else     {       log("SYSERR: Format error in third numeric line (expecting 6 args, got %d), %s", retval, buf2);       exit(1);     }   }
in *parse_object() in db.c

add
Code:
  GET_OBJ_REPOP(obj_proto + i) = t[5];
below the change we just made in db.c

the following is the modified cases in my reset_zone function in db.c
Code:
case 'O': /* read an object */       if (obj_index[ZCMD.arg1].number < ZCMD.arg2)       {         obj = read_object(ZCMD.arg1, REAL);         if (ZCMD.arg3 != NOWHERE)         {           rarity = rand_number(0, 99);           if (IS_HAPPYEQ && IS_HAPPYHOUR)             GET_OBJ_REPOP(obj) = MIN(GET_OBJ_REPOP(obj) + HAPPY_EQ, 100);                    if (GET_OBJ_REPOP(obj) > rarity)           {             obj_to_room(obj, ZCMD.arg3);             load_otrigger(obj);             last_cmd = 1;             tobj = obj;           }           else           {             extract_obj(obj);             last_cmd = 1;             tobj = obj;           }         }         else         {           obj = read_object(ZCMD.arg1, REAL);           IN_ROOM(obj) = NOWHERE;           last_cmd = 1;           tobj = obj;         }       }       else         last_cmd = 0;       tmob = NULL;       break;     case 'P': /* object to object */       if (obj_index[ZCMD.arg1].number < ZCMD.arg2)       {         obj = read_object(ZCMD.arg1, REAL);         if (!(obj_to = get_obj_num(ZCMD.arg3)))         {           ZONE_ERROR("target obj not found, command disabled");           ZCMD.command = '*';           break;         }         rarity = rand_number(0, 99);         if (IS_HAPPYEQ && IS_HAPPYHOUR)           GET_OBJ_REPOP(obj) = MIN(GET_OBJ_REPOP(obj) + HAPPY_EQ, 100);                     if (GET_OBJ_REPOP(obj) > rarity)         {           obj_to_obj(obj, obj_to);           last_cmd = 1;           load_otrigger(obj);           tobj = obj;         }         else         {           extract_obj(obj);           last_cmd = 1;           tobj = obj;         }       }       else         last_cmd = 0;       tmob = NULL;       break;     case 'G': /* obj_to_char */       if (!mob)       {         char error[MAX_INPUT_LENGTH];         snprintf(error, sizeof(error), "attempt to give obj #%d to non-existant mob, command disabled", obj_index[ZCMD.arg1].vnum);         ZONE_ERROR(error);         ZCMD.command = '*';         break;       }       if (obj_index[ZCMD.arg1].number < ZCMD.arg2)       {         obj = read_object(ZCMD.arg1, REAL);         rarity = rand_number(0, 99);         if (IS_HAPPYEQ && IS_HAPPYHOUR)           GET_OBJ_REPOP(obj) = MIN(GET_OBJ_REPOP(obj) + HAPPY_EQ, 100);                     if (GET_OBJ_REPOP(obj) > rarity)         {           obj_to_char(obj, mob);           last_cmd = 1;           load_otrigger(obj);           tobj = obj;         }         else         {           extract_obj(obj);           last_cmd = 1;           tobj = obj;         }       }       else         last_cmd = 0;       tmob = NULL;       break;     case 'E': /* object to equipment list */       if (!mob)       {         char error[MAX_INPUT_LENGTH];         snprintf(error, sizeof(error), "trying to equip non-existant mob with obj #%d, command disabled", obj_index[ZCMD.arg1].vnum);         ZONE_ERROR(error);         ZCMD.command = '*';         break;       }       if (obj_index[ZCMD.arg1].number < ZCMD.arg2)       {         obj = read_object(ZCMD.arg1, REAL);         if (ZCMD.arg3 < 0 || ZCMD.arg3 >= NUM_WEARS)         {           char error[MAX_INPUT_LENGTH];           snprintf(error, sizeof(error), "invalid equipment pos number (mob %s, obj %d, pos %d)", GET_NAME(mob), obj_index[ZCMD.arg2].vnum, ZCMD.arg3);           ZONE_ERROR(error);         }         rarity = rand_number(0, 99);         if (IS_HAPPYEQ && IS_HAPPYHOUR)           GET_OBJ_REPOP(obj) = MIN(GET_OBJ_REPOP(obj) + HAPPY_EQ, 100);                     if (GET_OBJ_REPOP(obj) > rarity)         {           IN_ROOM(obj) = IN_ROOM(mob);           load_otrigger(obj);           if (wear_otrigger(obj, mob, ZCMD.arg3))           {             IN_ROOM(obj) = NOWHERE;             equip_char(mob, obj, ZCMD.arg3);           }           else             obj_to_char(obj, mob);           tobj = obj;           last_cmd = 1;         }         else         {           extract_obj(obj);           last_cmd = 1;           tobj = obj;         }       }       else         last_cmd = 0;       tmob = NULL;       break;

in genobj.c we need to change another long string:
the original string is:
Code:
fprintf(fp, "%d %s %s %s %s %s %s %s %s %s %s %s %s\n"           "%d %d %d %d\n"           "%d %d %d %d %d\n",   GET_OBJ_TYPE(obj),           ebuf1, ebuf2, ebuf3, ebuf4,           wbuf1, wbuf2, wbuf3, wbuf4,           pbuf1, pbuf2, pbuf3, pbuf4,           GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1),           GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 3),           GET_OBJ_WEIGHT(obj), GET_OBJ_COST(obj),           GET_OBJ_RENT(obj), GET_OBJ_LEVEL(obj), GET_OBJ_TIMER(obj)       );
we change it to:
Code:
fprintf(fp, "%d %s %s %s %s %s %s %s %s %s %s %s %s\n"           "%d %d %d %d\n"           "%d %d %d %d %d %d\n",   GET_OBJ_TYPE(obj),           ebuf1, ebuf2, ebuf3, ebuf4,           wbuf1, wbuf2, wbuf3, wbuf4,           pbuf1, pbuf2, pbuf3, pbuf4,           GET_OBJ_VAL(obj, 0), GET_OBJ_VAL(obj, 1),           GET_OBJ_VAL(obj, 2), GET_OBJ_VAL(obj, 3),           GET_OBJ_WEIGHT(obj), GET_OBJ_COST(obj),           GET_OBJ_RENT(obj), GET_OBJ_LEVEL(obj), GET_OBJ_TIMER(obj), GET_OBJ_REPOP(obj)       );
in objsave.c we change case 'R' of *objsave_parse_objects to be:
Code:
    case 'R':       if (!strcmp(tag, "Rare"))         GET_OBJ_REPOP(temp) = num;       else if (!strcmp(tag, "Rent"))         GET_OBJ_RENT(temp) = num;       break;
and add the following to objsave_save_obj_record() near where the if statement for "Rent" is:
Code:
  if (TEST_OBJN(rarity))     fprintf(fp, "Rare: %d\n", GET_OBJ_REPOP(obj));

Lastly, we have to add it to Oasis in oedit,c

in oedit_disp_menu() I have the following:
Code:
  write_to_output(d,                   "%s7%s) Wear flags  : %s%s\r\n"                   "%s8%s) Weight      : %s%d\r\n"                   "%s9%s) Cost        : %s%d\r\n"                   "%sA%s) Cost/Day    : %s%d\r\n"                   "%sB%s) Timer       : %s%d\r\n"                   "%sR%s) Repop Pecent: %s%d\r\n"                   "%sC%s) Values      : %s%d %d %d %d\r\n"                   "%sD%s) Applies menu\r\n"                   "%sE%s) Extra descriptions menu: %s%s%s\r\n"                   "%sM%s) Min Level   : %s%d\r\n"                   "%sP%s) Perm Affects: %s%s\r\n"                   "%sS%s) Script      : %s%s\r\n"                   "%sW%s) Copy object\r\n"                   "%sX%s) Delete object\r\n"                   "%sQ%s) Quit\r\n"                   "Enter choice : ",                   grn, nrm, cyn, buf1,                   grn, nrm, cyn, GET_OBJ_WEIGHT(obj),                   grn, nrm, cyn, GET_OBJ_COST(obj),                   grn, nrm, cyn, GET_OBJ_RENT(obj),                   grn, nrm, cyn, GET_OBJ_TIMER(obj),                   grn, nrm, cyn, GET_OBJ_REPOP(obj),                   grn, nrm, cyn, GET_OBJ_VAL(obj, 0),                   GET_OBJ_VAL(obj, 1),                   GET_OBJ_VAL(obj, 2),                   GET_OBJ_VAL(obj, 3),                   grn, nrm, grn, nrm, cyn, obj->ex_description ? "Set." : "Not Set.", grn,                   grn, nrm, cyn, GET_OBJ_LEVEL(obj),                   grn, nrm, cyn, buf2,                   grn, nrm, cyn, OLC_SCRIPT(d) ? "Set." : "Not Set.",                   grn, nrm,                   grn, nrm,                   grn, nrm);   OLC_MODE(d) = OEDIT_MAIN_MENU; }
in oedit_parse()
Code:
    case 'r':     case 'R':       write_to_output(d, "Enter repop percent (0-100): ");       OLC_MODE(d) = OEDIT_REPOP;       break;
and also in oedit_parse() further down:
Code:
  case OEDIT_REPOP:     GET_OBJ_REPOP(OLC_OBJ(d)) = LIMIT(atoi(arg), 0, MAX_OBJ_REPOP);     break;

Now, for happyhour integration:

add
Code:
int eq;
to struct happyhour in structs.h

define:
Code:
#define HAPPY_EQ     happy_data.eq
in utils.h

IIn act.other.c you need to edit the happyhour integration.  
I've removed gold and QP from my game so my show_happyhour looks like this:
Code:
static void show_happyhour(struct char_data *ch) {   char happyexp[80], happyeq[80];   int secs_left;   if ((IS_HAPPYHOUR) || (GET_LEVEL(ch) >= LVL_GRGOD))   {     if (HAPPY_TIME)       secs_left = ((HAPPY_TIME - 1) * SECS_PER_MUD_HOUR) + next_tick;     else       secs_left = 0;     sprintf(happyexp, "%s+%d%%%s to Experience per kill\r\n", CCYEL(ch, C_NRM), HAPPY_EXP, CCNRM(ch, C_NRM));     sprintf(happyeq, "%s+%d%%%s to equipment repop percentage\r\n", CCYEL(ch, C_NRM), HAPPY_EQ, CCNRM(ch, C_NRM));     send_to_char(ch, "\r\n\r\ntbaMUD Happy Hour!\r\n"                     "------------------\r\n"                     "%s%sTime Remaining: %s%d%s hours %s%d%s mins %s%d%s secs\r\n",                 (IS_HAPPYEXP || (GET_LEVEL(ch) >= LVL_GOD)) ? happyexp : "",                 (IS_HAPPYEQ || (GET_LEVEL(ch) >= LVL_GOD)) ? happyeq : "",                 CCYEL(ch, C_NRM), (secs_left / 3600), CCNRM(ch, C_NRM),                 CCYEL(ch, C_NRM), (secs_left % 3600) / 60, CCNRM(ch, C_NRM),                 CCYEL(ch, C_NRM), (secs_left % 60), CCNRM(ch, C_NRM)                 );   }   else   {     send_to_char(ch, "Sorry, there is currently no happy hour!\r\n");   } }

add
Code:
  if (is_abbrev(arg, "equipment"))   {     num = MIN(MAX((atoi(val)), 0), 100);     HAPPY_EQ = num;     send_to_char(ch, "Happy Hour equipment drop rate increased by +%d%%\r\n",HAPPY_EQ);   }
to do_happyhour in act.other.c

optionally add HAPPYEQ to "default"
Code:
  else if (is_abbrev(arg, "default"))   {     HAPPY_EXP = 100;     HAPPY_TIME = 1000;     HAPPY_EQ = <VALUE>;     game_info("A Happyhour has started!");   }


Feel free to use this snippet.  It works flawlessly.  All I ask is a credit in your comments.  Hope this helps someone.

Thanks.

Note:  This will require all you to edit your existing objects for this script to boot:  You can write a function adding the extra field (t + 5)  to your existing objects on boot before running parse_object(), write a shell script to do it for you, or manually edit your object files. 
Last edit: 30 May 2025 07:23 by Salty. Reason: Emphasis on Note

Please Log in or Create an account to join the conversation.

More
30 May 2025 20:44 #10741 by zi
nice! Does this handle zedit instances where a container doesn't load but the obj that goes in the container does? I'm thinking porting this over to mobs as well, but the same problem could derail...

Please Log in or Create an account to join the conversation.

More
30 May 2025 23:09 #10742 by Salty

Does this handle zedit instances where a container doesn't load but the obj that goes in the container does?


Good catch!  That's an oversight of mine.  I'm working on a fix.  I'll reply with a snippet for the case 'P' when I've got it.
The following user(s) said Thank You: zi

Please Log in or Create an account to join the conversation.

More
31 May 2025 05:07 #10743 by Salty
Here's a fix that works.  If Thomas or someone else can read the comments I wrote and tell me if I made a wrong turn in my logic, I'd appreciate it.
Code:
    case 'P': /* object to object */       if (obj_index[ZCMD.arg1].number < ZCMD.arg2)       {               obj = read_object(ZCMD.arg1, REAL);         rarity = rand_number(0, 99);         if (IS_HAPPYEQ && IS_HAPPYHOUR)           GET_OBJ_REPOP(obj) = MIN(GET_OBJ_REPOP(obj) + HAPPY_EQ, 100);         /*          * obj_to will already be parsed and thus loaded (or not) from case 'O' in this function,          *          * If obj_to is loaded, we check if the input object (obj) will load.           * If it does, cool beans.  We put it in the container and we attach any triggers it may have.          *          * If it doesn't.  We extract it.          *         */         if ((obj_to = get_obj_num(ZCMD.arg3)))         {           if (GET_OBJ_REPOP(obj) > rarity)           {             obj_to_obj(obj, obj_to);             load_otrigger(obj);             tobj = obj;           }           else           {             extract_obj(obj);             tobj = NULL;           }           last_cmd = 1;         }         /*          *          * Somewhat changing the logic of the way the handling of failures work in case 'P'          *          * The stock behavior is to delete the zedit command of an input object when the container          * object fails to load.  This is done by calling the ZONE_ERROR macro.          *          * We don't NEED to delete the zedit command when an input object tries to load in a container          * that did not load.  In the stock game we do it to keep the same bad zedit command from          * being run again & again (which sends the input item to NOWHERE - costing resources) or          * possibly introducing other bugs (ie: duplication).          *          * With rarity we don't WANT to delete the zedit command when an object tries to load in into          * a contianer that did not load.  By allowing the input item to be cleanly extracted without          * breaking out of the loop, we prevent the behaviors trying to be mitigated against in the stock          * version.          *          * I could be wrong, but this is working as I intend.  Perhaps someone wiser can chime in?          *          * -Salty          *         */         else         {           extract_obj(obj);           tobj = NULL;           break;         }       }       else         last_cmd = 0;       tmob = NULL;       break;

Zedit:
Code:
< Salty | 100(100)hp 100(100)mana 100(100)mv > zedit Room number: 1212 Room zone: 12 1) Builders       : CircleMUD Z) Zone name      : Immortal Complex L) Lifespan       : 60 minutes B) Bottom of zone : 1200 T) Top of zone    : 1299 R) Reset Mode     : Normal reset. F) Zone Flags     : NOBITS M) Level Range    : Levels 1 to 105 [Command list] 0 - Load Large desk [1297], Max : 1 1 -  then Put Elaseth's notebook [1295] in Large desk [1297], Max : 1 2 - <END OF LIST> N) Insert new command. E) Edit a command. D) Delete a command. Q) Quit Enter your choice :

oedit 1297:
Code:
< Salty | 100(100)hp 100(100)mana 100(100)mv > oedit 1297 -- Item number : [1297] 1) Keywords : Desk 2) S-Desc   : Large desk 3) L-Desc   :- A large blackened desk 4) A-Desc   :- Not Set. 5) Type        : CONTAINER 6) Extra flags : NOBITS 7) Wear flags  : NOBITS 8) Weight      : 0 9) Cost        : 0 A) Cost/Day    : 0 B) Timer       : 0 R) Repop Pecent: 55 C) Values      : 20000 3 0 0 D) Applies menu E) Extra descriptions menu: Not Set. M) Min Level   : 0 P) Perm Affects: NOBITS S) Script      : Not Set. W) Copy object X) Delete object Q) Quit Enter choice :

oedit 1295:
Code:
< Salty | 100(100)hp 100(100)mana 100(100)mv > oedit 1295 -- Item number : [1295] 1) Keywords : notebook notes book 2) S-Desc   : Elaseth's notebook 3) L-Desc   :- A book entitled "Elaseth's Notes: A Madwoman's Account" lies here. 4) A-Desc   :- Not Set. 5) Type        : ARMOR 6) Extra flags : NOBITS 7) Wear flags  : TAKE OFF_HAND 8) Weight      : 1 9) Cost        : 1 A) Cost/Day    : 0 B) Timer       : 5 R) Repop Pecent: 50 C) Values      : 50 0 0 0 D) Applies menu E) Extra descriptions menu: Not Set. M) Min Level   : 0 P) Perm Affects: NOBITS S) Script      : Not Set. W) Copy object X) Delete object Q) Quit Enter choice :


Now, a test:
Code:
< Salty | 100(100)hp 100(100)mana 100(100)mv > l [ 1212] A can of nuts [ MAGIC_DAMPEN ] [ Inside ] You are in a can of mixed nuts. [ Exits: None!] [1297] A large blackened desk < Salty | 100(100)hp 100(100)mana 100(100)mv > l in desk Desk (here): [1295] Elaseth's notebook < Salty | 100(100)hp 100(100)mana 100(100)mv > zpurge;zreset Purged zone #12: Immortal Complex. [ (GC) Salty purged zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > Reset zone #12: Immortal Complex. [ (GC) Salty reset zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > l [ 1212] A can of nuts [ MAGIC_DAMPEN ] [ Inside ] You are in a can of mixed nuts. [ Exits: None!] [1297] A large blackened desk < Salty | 100(100)hp 100(100)mana 100(100)mv > l in desk Desk (here):   Nothing. < Salty | 100(100)hp 100(100)mana 100(100)mv > zpurge;zreset Purged zone #12: Immortal Complex. [ (GC) Salty purged zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > Reset zone #12: Immortal Complex. [ (GC) Salty reset zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > l in desk Desk (here):   Nothing. < Salty | 100(100)hp 100(100)mana 100(100)mv > zpurge;zreset Purged zone #12: Immortal Complex. [ (GC) Salty purged zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > Reset zone #12: Immortal Complex. [ (GC) Salty reset zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > l in desk Desk (here): [1295] Elaseth's notebook < Salty | 100(100)hp 100(100)mana 100(100)mv > zpurge;zreset; Purged zone #12: Immortal Complex. [ (GC) Salty purged zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > Reset zone #12: Immortal Complex. [ (GC) Salty reset zone 12 (Immortal Complex) ] < Salty | 100(100)hp 100(100)mana 100(100)mv > l [ 1212] A can of nuts [ MAGIC_DAMPEN ] [ Inside ] You are in a can of mixed nuts. [ Exits: None!] < Salty | 100(100)hp 100(100)mana 100(100)mv > where notebook Couldn't find any such thing. < Salty | 100(100)hp 100(100)mana 100(100)mv > where desk Couldn't find any such thing. < Salty | 100(100)hp 100(100)mana 100(100)mv >

In the test we have a desk & a notebook, each with different rarity.  

At the beginning they're both loaded.
Next reset:  Desk loaded, notebook not loaded.
Next reset:  Desk loaded, notebook not loaded.
Next reset:  Both loaded.
Last reset:  Desk not loaded therefore notebook not loaded.

Notebook not on 'where', desk not on 'where' as NOWHERE would show "in an unknown location"

Hope this helps.  I'd be happy if I can get another set of eyes on this fix, however.

Thanks for finding that edge case for me, Zi !

-Salty

Please Log in or Create an account to join the conversation.

Time to create page: 0.268 seconds