// For our global aggression (every 5 minutes) // parses players only, but because of it, can cause aggression towards monsters // which is just cool if you ask me! Monsters fighting monsters, monsters fighting // people, Just makes sense. FUll world aggression! void aggro_update(void) { // we only parse players to make this go fast! FOREACH(Lexi::List, player_list, player_iter) { CHAR_DATA *ch = (*player_iter); aggression(ch); // cause mass aggression! } return; } // Aggression never looked so good! // stock aggression code searches the entire character list, then parses the people list // for every mob, and then another people list check within that check. Eating up according // to merc, 25%-35% of ALL cpu usage. This function, called on movement into a room // and once every 5 minutes (globally) incase people sit and don't move but meanwhile the aggressor // does or what-have-you. This affectively cuts out 25-35% of ALL CPU usage, until movement, then it // causes about 0.1-0.5% cpu usage, and vanish's again. Affectively saving MASSIVE amounts of CPU usage. // even if every character on the mud, (including npc's) moved at the same time, this would eat up // no more then mercs projected 35% cpu usage. Making this a far more effective method. // Merc said it was too tricky to do it room based, apparently, they don't think like me. // -- Darien 21:01 PM 2008-12-14 void aggression( CHAR_DATA *ch ){ CHAR_DATA *victims[10000]; CHAR_DATA *aggressor[10000]; register CHAR_DATA *wch = NULL, *wch_next = NULL; register CHAR_DATA *vch = NULL, *vch_next = NULL; // Register usage, though the compiler may disregard them // putting these value's into the register has the potential // to speed up processing, and considering the high-level of // usage, we expect the best performance, so we register with // hopes that the compiler will accept. int cnt = 0; // Lets protect vs crashing if(!ch) { log_string("%s: called on NULL character, preventing bad things(tm)", __PRETTY_FUNCTION__); return; } // stranger things have happened, but safety persists! if(!ch->in_room) { log_string("%s called on a character(%s) will a Null Room!", __PRETTY_FUNCTION__, IS_NPC(ch) ? ch->short_descr : ch->name); return; } // safe room, no aggression, period! if(IS_SET(ch->in_room->room_flags, ROOM_SAFE)) return; // Only person in the room, lets not bother hitting any loops // characters are always put at the start of the list when they enter a room // this is called whenever you change rooms, thus making you the first person // in the room. No aggression. Only works off-world-map, as on-world-map // you never change room, just x/y. if(!ON_MAP(ch)) { if(ch->in_room->people == ch && ch->next_in_room == NULL) { return; } } // chance to stop aggression if your sneaking! // 100% sneak with 25 dex >= 100% chance of non-aggression. (atleast you won't cause it) // but someone-else may cause you to get suckered into it if they aggress. if(IS_AFFECTED(ch, AFF_SNEAK)) { int chance; chance = get_skill(ch,gsn_sneak); chance += get_curr_stat(ch,STAT_DEX) * 3/2; chance -= number_range(0,ch->level) /2; // random chance! if (number_percent() < chance) return; } // we are hidden, no aggression here, despite can_see has a chance to see you, we want // to ensure that we don't call this function too much, so things like this help. // hide stops on movement, so this is only affecting when the 5-minute interval aggression // check happens. if(IS_AFFECTED(ch, AFF_HIDE)) return; // look for an aggressive monster (add to the aggressive monster list.) for ( wch = ch->in_room->people; wch != NULL; wch = wch_next ) { int count; wch_next = wch->next_in_room; if(IS_NPC(wch) && IS_SET(wch->act, ACT_AGGRESSIVE) && !is_fighting(wch) && IS_AWAKE(wch) && (ch->x == wch->x && ch->y == wch->y) // ensure we are the same x/y for map interaction && number_bits(1) == 0) // chance we don't aggress { aggressor[cnt] = wch; cnt++; if(cnt == 10000) break; // safety! } } // we didn't count any monsters! No aggression! if(cnt == 0) return; // pick the aggressor. (yay randomized aggression) CHAR_DATA *monster = aggressor[number_range(0,cnt-1)]; // 0 0 results in 0 return. if(!monster) return; // we got a null monster, but there was a monster list // lets safely assume something *bad* happened and we // move to save the day here. cnt = 0; // reset our counter for the victims list. // find our random victims. Other monsters can be victims! // so-long as they match the requirements. (sects not matching, alliance vs rebellion goes on) for(vch = monster->in_room->people; vch; vch = vch_next) { if(vch->rebel != monster->rebel && can_see(monster, vch) && !is_same_group(monster, vch) // potential if aggressive pet! (beastmaster class can set aggro on pet) && (vch->level <= (monster->level +10)) // over 10 levels, we don't aggress anymore! && (vch->x == wch->x && vch->y == wch->y) // safety net again! && !is_fighting(vch)) { victims[cnt] = vch; cnt++; if(cnt == 10000) break; // safety! } } // we didn't find any victims we could attack with our random monster! No aggression! if(cnt == 0) return; // lets find our victim to feel the pain! CHAR_DATA *feel_n_pain = victims[number_range(0, cnt-1)]; // 0 0 results in 0 return (only one person found) if(!feel_n_pain) { if(ch->rebel != monster->rebel && (ch->x == monster->x && monster->y == ch->y) && (ch->level <= monster->level+10) // really, monsters aren't stupid enough to attack them! && can_see(monster, ch)) feel_n_pain = ch; // haven't escaped the pain yet! else return; } if(!feel_n_pain) return; // can only happen if ch gets nulled, as this is the double-check // nulled ch should *NOT* beable to happen after this has been // called, but, stranger things have happened. Safety persists. // We've done allot of work, lets finally check and see if we are good to go for // aggression. We hope to aggress here, as this was a very painstaking adventure in // building a list of aggressors and victims to aggress against. hehehehe // notibly, we don't want to always aggress, thus we leave it up to chance. if(number_bits(1) == 0) { // here we determine our methods of attacking (based on monster class/level eventually) // as it stands, this is a simple method to start aggression. switch(monster->iclass) { default: multi_hit(monster, feel_n_pain, TYPE_UNDEFINED); break; case 3: switch(number_range(0, 3)) { case 2: do_function(monster, do_backstab, feel_n_pain->name); break; default: multi_hit(monster, feel_n_pain, TYPE_UNDEFINED); break; } } } return; }