/************************************************************************** * # # # ## # # ### ## ## ### http://www.lyonesse.it * * # # # # # ## # # # # # * * # # # # # ## ## # # ## ## ## # # ## * * # # # # # ## # # # # # # # # # # # * * ### # ## # # ### ## ## ### # # #### ## Ver. 1.0 * * * * -Based on CircleMud & Smaug- Copyright (c) 2001-2002 by Mithrandir * * * * ********************************************************************** * * * * File: wild.life.c * * * * Virtual Ecosystem module * * * **************************************************************************/ #include "conf.h" #include "sysdep.h" #include "structs.h" #include "utils.h" #include "comm.h" #include "db.h" #include "interpreter.h" #include "handler.h" #include "constants.h" /* external funcs */ WILD_DATA *get_wd(COORD_DATA *coord); /* local funcs */ void save_life(WILD_DATA *wd); /* globals */ LIFE_DATA *base_life_table[SECT_HASH]; /* default encounters table */ /* * * Global switch for using encounter charts * * TRUE = use encounter charts * FALSE = do not use encounter charts (no mob in wild) * */ bool use_life = TRUE; /* * * Global switch for using custom wildsector chart * * TRUE = use custom wildsector chart * FALSE = use global chart base_life_table for all wildsectors * */ bool use_custom_life = TRUE; /* ================================================================= */ // add the life data to the right hash table void new_life(LIFE_DATA *pLife, WILD_DATA *wd) { int iHash = pLife->sect % SECT_HASH; // add to the right hash table if (wd) { pLife->next = wd->life_table[iHash]; wd->life_table[iHash] = pLife; } else { pLife->next = base_life_table[iHash]; base_life_table[iHash] = pLife; } } /* ================================================================= */ // duplicate default data into the given wildsector void clone_base_life(WILD_DATA *wd) { LIFE_DATA *pLife, *wLife; int iHash; for (iHash = 0; iHash < SECT_HASH; iHash++) { for (pLife = base_life_table[iHash]; pLife; pLife = pLife->next) { CREATE(wLife, LIFE_DATA, 1); // copy numbers *wLife = *pLife; // duplicate string wLife->name = str_dup(pLife->name); // nullify next pointer wLife->next = NULL; new_life(wLife, wd); } } } /* ================================================================= */ /* * * wandering monster encounter chart -- Base Chart * * fields: * * <sector num> <mob vnum> <mob name>~ <max number> <min number> (1)<current number> (2)<regen rate> * * (1) in base charts always set to 0 * * (2) regen_rate is halved by 100 in loading, so if you want to regenerate 1 mob per mud hour * you have to write 100, for 3 mob per hours write 300, for 1 mob every 2 hours write 50, * and so on.. * */ // load global default life data void load_base_life(void) { FILE *fp; LIFE_DATA *pLife; char fname[128]; char letter; int sect, num; float calc; use_life = TRUE; sprintf(fname, "%slife.data", LIB_DATA); if (!(fp = fopen(fname, "r"))) { log("SYSERR: cannot open encounter chart file %s", fname); use_life = FALSE; return; } for ( ;; ) { letter = fread_letter( fp ); if (feof(fp)) letter = '$'; if ( letter == '$' ) break; if ( letter == '*' ) { fread_to_eol( fp ); continue; } /* * supported types.. * * M = mobile * = */ if ( strchr("M", letter) == NULL ) { log("SYSERR: load_base_life() - invalid type %c.", letter); fread_to_eol( fp ); continue; } sect = fread_number(fp); if (sect < 0 || sect > MAX_SECT) { log("SYSERR: load_base_life() - invalid sector number %d.", sect); fread_to_eol( fp ); continue; } CREATE(pLife, LIFE_DATA, 1); pLife->next = NULL; pLife->sect = sect; pLife->vnum = fread_number(fp); pLife->name = fread_string_nospace(fp); pLife->num_max = fread_number(fp); pLife->num_min = fread_number(fp); fread_number(fp); // pLife->num_curr num = fread_number(fp); calc = (float) num / 100; pLife->regen_rate = calc; pLife->num_curr = pLife->num_max; new_life(pLife, NULL); } fclose(fp); } /* ================================================================= */ // load a wildsector life file void load_life(WILD_DATA *wd) { FILE *fp; LIFE_DATA *pLife; char fname[128]; char letter; int sect, num; float calc; if (!use_life || !use_custom_life) return; sprintf(fname, "%slife/%d-%d.life", WLS_PREFIX, wd->coord->y, wd->coord->x); if (!(fp = fopen(fname, "r"))) { clone_base_life(wd); // save current time wd->last_load = time(0); // save it save_life(wd); return; } wd->last_load = 0; for ( ;; ) { letter = fread_letter( fp ); if (feof(fp)) letter = '$'; if ( letter == '$' ) break; if ( letter == '*' ) { fread_to_eol( fp ); continue; } /* * supported types.. * * M = mobile * T = time (last time the wildsector has been loaded) */ if ( strchr("MT", letter) == NULL ) { log("SYSERR: load_life() - invalid type %c.", letter); fread_to_eol( fp ); continue; } if ( letter == 'T' ) { wd->last_load = fread_number(fp); continue; } sect = fread_number(fp); if (sect < 0 || sect > MAX_SECT) { log("SYSERR: load_base_life() - invalid sector number %d.", sect); fread_to_eol( fp ); continue; } CREATE(pLife, LIFE_DATA, 1); pLife->next = NULL; pLife->sect = sect; pLife->vnum = fread_number(fp); pLife->name = fread_string_nospace(fp); pLife->num_max = fread_number(fp); pLife->num_min = fread_number(fp); pLife->num_curr = fread_number(fp); num = fread_number(fp); calc = (float) num / 100; pLife->regen_rate = calc; // add to the wildsector table new_life(pLife, wd); // update life data...... if needed, of course if (wd->last_load && pLife->num_curr < pLife->num_max) { time_t days = days_passed(wd->last_load); if (days) { int nnum = pLife->regen_rate * days; pLife->num_curr += nnum; pLife->num_curr = URANGE(pLife->num_min, pLife->num_curr, pLife->num_max); } } } // save current time wd->last_load = time(0); fclose(fp); } // save wildsector life to file void save_life(WILD_DATA *wd) { FILE *fp; LIFE_DATA *pLife; char fname[128]; int iHash, rrate; if (!use_life || !use_custom_life) return; sprintf(fname, "%slife/%d-%d.life", WLS_PREFIX, wd->coord->y, wd->coord->x); if (!(fp = fopen(fname, "w"))) { log("SYSERR: save_life() - cannot create life file %s", fname); return; } fprintf(fp, "T %ld\n", wd->last_load); for (iHash = 0; iHash < SECT_HASH; iHash++) { for (pLife = wd->life_table[iHash]; pLife; pLife = pLife->next) { rrate = pLife->regen_rate * 100; fprintf(fp, "M %hd %d %s~ %hd %hd %hd %d\n", pLife->sect, pLife->vnum, pLife->name, pLife->num_max, pLife->num_min, pLife->num_curr, rrate); } } fprintf(fp, "$\n"); fclose(fp); } /* ================================================================= */ /* */ void encounter(ROOM_DATA *pRoom) { LIFE_DATA *pLife, *pLifeTable; WILD_DATA *wd; CHAR_DATA *mob; int iHash, tot; // no life? no party.. if (!use_life) return; // no wild? no party.. if (!IS_WILD(pRoom)) return; // 2 chanches out of 20 if (number(1, 20) > 2) return; // get the wildsector if (!(wd = get_wd(pRoom->coord))) return; iHash = pRoom->sector_type % SECT_HASH; if (use_custom_life) pLifeTable = wd->life_table[iHash]; else pLifeTable = base_life_table[iHash]; // get total number of possible beasts in sector for (tot = 0, pLife = pLifeTable; pLife; pLife = pLife->next) { if (pLife->sect == pRoom->sector_type) tot += (use_custom_life ? pLife->num_curr : pLife->num_max); } // no life.. exit if (!tot) return; // each beast take a chance to be loaded in room for (pLife = pLifeTable; pLife; pLife = pLife->next) { if (pLife->sect == pRoom->sector_type) if (number(0, 100) < percentage((use_custom_life ? pLife->num_curr : pLife->num_max), tot)) break; } // no luck, exit if (!pLife) return; // aaarggghh.. invalid vnum here?? you, bastard! if (!(mob = read_mobile(pLife->vnum, VIRTUAL))) return; if (use_custom_life) { // _NEVER_ go under minimum.. extinction is not allowed.. :-)) if (pLife->num_curr > pLife->num_min) pLife->num_curr--; } // set the mob as an encounter mob SET_BIT(MOB_FLAGS(mob), MOB_ENCOUNTER); // this means 100 seconds of permanence in world... (see mobact.c) mob->mob_specials.timer = 10; // place mob in room char_to_room(mob, pRoom); } /* ================================================================= */ ACMD(do_life) { LIFE_DATA *pLife; int nsect, iHash; one_argument(argument, arg); if (!*arg || !is_number(arg)) { send_to_char("Usage: life <sector number>\r\n", ch); return; } nsect = atoi(arg); if (nsect < 0 || nsect > MAX_SECT) { ch_printf(ch, "Valid sector number goes from 0 to %d.\r\n", MAX_SECT); return; } iHash = nsect % SECT_HASH; ch_printf(ch, "Encounter table for '%s' sectors.\r\n", terrain_type[nsect]->name); for (pLife = base_life_table[iHash]; pLife; pLife = pLife->next) { if (pLife->sect != nsect) continue; ch_printf(ch, "Mob vnum: [%6d] Name: [%-30s] Max: [%3d] Min: [%3d] Regen Rate: [%6.2f]\r\n", pLife->vnum, pLife->name, pLife->num_max, pLife->num_min, pLife->regen_rate); } }