/* * update.c * Routines to handle game updates * ____ _ * | _ \ ___ __ _| |_ __ ___ ___ * | |_) / _ \/ _` | | '_ ` _ \/ __| * | _ < __/ (_| | | | | | | \__ \ * |_| \_\___|\__,_|_|_| |_| |_|___/ * * Permission to use, modify and distribute is granted via the * Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License * http://creativecommons.org/licenses/by-nc-sa/3.0/ * * Copyright (C) 2007-2009 Jason Mitchell, Randi Mitchell * Contributions by Tim Callahan, Jonathan Hseu * Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman * */ #include <sys/signal.h> #include "mud.h" #include "login.h" #include "commands.h" #include "move.h" #include "factions.h" #include "ships.h" #include "calendar.h" #include "web.h" #include <libxml/parser.h> // xmlCleanupParser bool firstLoop=true; long last_track_update=0; long last_time_update=0; long last_weather_update=0; static long lastTickUpdate=0; static long last_update=0; static long last_shutdown_update=0; static long last_action_update=0; long TX_interval = 4200; short Random_update_interval = 6; short Action_update_interval = 6; extern msparse Mob[MMAX]; // over the course of time, ships begin to become a few seconds // off from the main clock. this variable will help us keep // things in check //********************************************************************* // update_game //********************************************************************* // This function handles all the updates that occur while players are // typing. void Server::updateGame() { long t = time(0); if(t == last_update) return; last_update = t; updateFishing(t); // update on the hour: ie, 3:00 // Sometimes on startup, we don't get to this section of the code in 1 second, // meaning this wont run until 1 hour after the game has started. Throwing in // or-firstLoop gives us 2 seconds of time. if(!gConfig->currentMinutes() && (!(t%2) || firstLoop)) update_time(t); // Run ships every other second. if(t%2) update_ships(); // Prune Dns once a day if(t - lastDnsPrune >= 86400) pruneDns(); if(t - lastTickUpdate >= 5) pulseTicks(t); // pulse effects if(t - lastUserUpdate >= 20) { updateUsers(t); pulseCreatureEffects(t); } // same cycle length as creature pulses, but offset 10 seconds if(t - lastRoomPulseUpdate >= 20) pulseRoomEffects(t); if(t - last_track_update >= 20) update_track(t); if(t - lastRandomUpdate >= Random_update_interval) updateRandom(t); if(t != lastActiveUpdate) updateActive(t); if(t - last_weather_update >= 60) updateWeather(t); if(t - last_action_update >= Action_update_interval) update_action(t); if(last_dust_output && last_dust_output < t) update_dust_oldPrint(t); if(t > gConfig->getLotteryRunTime()) gConfig->runLottery(); if(Shutdown.ltime && t - last_shutdown_update >= 30) if(Shutdown.ltime + Shutdown.interval <= t+500) update_shutdown(t); } //********************************************************************* // weatherize //********************************************************************* bstring Config::weatherize(WeatherString w, const BaseRoom* room) const { const CatRefInfo* cri=0; const cWeather* weather=0; bstring txt = ""; // get weather data out of catrefinfo cri = gConfig->getCatRefInfo(room, 0); if(cri) { weather = cri->getWeather(); // if the requested string doesn't exist, default to config weather if(weather && weather->get(w) == "") weather = 0; } // try parent if(!weather) { cri = gConfig->getCatRefInfo(room, 1); if(cri) { weather = cri->getWeather(); // if the requested string doesn't exist, default to config weather if(weather && weather->get(w) == "") weather = 0; } } // if that fails, we grab out of config if(!weather) weather = gConfig->getWeather(); if(!weather) return(""); return(weather->get(w)); } //********************************************************************* // pulseCreatureEffects //********************************************************************* // lastUserUpdate is set in updateUsers void Server::pulseCreatureEffects(long t) { Monster *monster=0; const Socket *sock=0; Player* player=0; ctag *cp = first_active; std::list<Socket*>::const_iterator it; for(it = sockets.begin(); it != sockets.end() ; ) { sock = *it; it++; if(!sock->isConnected()) continue; player = sock->getPlayer(); if(player) player->pulseEffects(t); } while(cp) { monster = cp->crt->getMonster(); cp = cp->next_tag; monster->pulseEffects(t); } } //********************************************************************* // pulseRoomEffects //********************************************************************* void Server::pulseRoomEffects(long t) { std::list<BaseRoom*>::iterator it; for(it = effectsIndex.begin() ; it != effectsIndex.end() ; ) { (*it)->pulseEffects(t); if(!(*it)->needsEffectsIndex()) it = effectsIndex.erase(it); else it++; } lastRoomPulseUpdate = t; } //********************************************************************* // addEffectsIndex //********************************************************************* void BaseRoom::addEffectsIndex() { if(!needsEffectsIndex()) return; gServer->addEffectsIndex(this); } void Server::addEffectsIndex(BaseRoom* room) { // you can only be in the list once! std::list<BaseRoom*>::const_iterator it; for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) { if((*it) == room) return; } effectsIndex.push_back(room); } //********************************************************************* // needsEffectsIndex //********************************************************************* bool BaseRoom::needsEffectsIndex() const { // any room effects? if(effects.list.size()) return(true); // any exit effects? xtag* xp = first_ext; while(xp) { if(xp->ext->effects.list.size()) return(true); xp = xp->next_tag; } return(false); } //********************************************************************* // removeEffectsIndex //********************************************************************* bool BaseRoom::removeEffectsIndex() { if(needsEffectsIndex()) return(false); gServer->removeEffectsIndex(this); return(true); } void Server::removeEffectsIndex(BaseRoom* room) { std::list<BaseRoom*>::iterator it; for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) { if((*it) == room) { effectsIndex.erase(it); return; } } } //********************************************************************* // removeEffectsOwner //********************************************************************* // on suicide, we remove the owner of the effect void Server::removeEffectsOwner(const Creature* owner) { std::list<BaseRoom*>::iterator it; xtag* xp=0; for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) { (*it)->effects.removeOwner(owner); xp = (*it)->first_ext; while(xp) { xp->ext->effects.removeOwner(owner); xp = xp->next_tag; } } } //********************************************************************* // weather //********************************************************************* void weather(WeatherString w) { bstring weather; char color; if(w == WEATHER_SUNRISE) color = 'Y'; else if(w == WEATHER_SUNSET) color = 'D'; else color = 'w'; foreach(Socket* sock, gServer->sockets) { Player* player = sock->getPlayer(); if(!player) continue; if(player->fd < 0) continue; if( (w == WEATHER_SUNRISE || w == WEATHER_SUNSET) && player->isEffected("vampirism") ) { // if sunrise/sunset, vampires always see it } else { if(!player->getRoom()->isOutdoors()) continue; } weather = gConfig->weatherize(w, player->getRoom()); if(weather != "") player->printColor("^%c%s\n", color, weather.c_str()); } } //********************************************************************* // updateFishing //********************************************************************* void updateFishing(long t) { bool someoneFishing=false, isFishing = false; // don't loop through players unless someone is fishing if(!gConfig->isFishing()) return; Player* player=0; foreach(Socket* sock, gServer->sockets) { player = sock->getPlayer(); if(!player) continue; if(player->getFishing()) { // this player is fishing isFishing = true; if(player->getFishing() <= t) { // actually, this player is done fishing isFishing = false; player->setFishing(false); doFish(player); } // if someone is still fishing, keep track of it someoneFishing = someoneFishing || isFishing; } } // if nobody is fishing, let this function know it can stop looping over players if(!someoneFishing) gConfig->setFishing(false); } //********************************************************************* // update_time //********************************************************************* // This function updates the game time in hours. When it is 6am a getSunrise() // message is broadcast. When it is 8pm a getSunset() message is broadcast. void update_time(long t) { int daytime=0; last_time_update = t; // do not do this immeditately on startup, // but do everything below this if(!firstLoop) { if(!gConfig->currentHour()) gConfig->calendar->advance(); // save on the hour gConfig->calendar->save(); gConfig->saveShips(); } firstLoop = false; std::pair<bstring, Player*> p; daytime = gConfig->currentHour(); if(daytime == SUNRISE-1) { foreach(p, gServer->players) { if(p.second->isEffected("vampirism")) p.second->printColor("^YThe sun is about to rise.\n"); } } else if(daytime == SUNRISE) { weather(WEATHER_SUNRISE); gConfig->killMortalObjects(); } else if(daytime == SUNSET-1) { foreach(p, gServer->players) { if(p.second->isEffected("vampirism")) p.second->printColor("^DThe sun is about to set.\n"); } } else if(daytime == SUNSET) { weather(WEATHER_SUNSET); } foreach(p, gServer->players) { if(p.second->isEffected("vampirism")) p.second->computeAC(); } // run at midnight if(!daytime && !gServer->isValgrind()) { runDemographics(); gConfig->uniqueDecay(); gConfig->cleanUpAreas(); } } //********************************************************************* // update_shutdown //********************************************************************* // This function broadcasts a shutdown message every 30 seconds until // shutdown is achieved. Then it saves off all rooms and players, // and exits the game. void update_shutdown(long t) { long i; last_shutdown_update = t; i = Shutdown.ltime + Shutdown.interval; // fix for strange bug where *shut 0 gives you 1 second if(i-t==1) i = t; if(i > t) { if(i-t > 60) broadcast("### Game shutdown in %d:%02d minutes.", (i-t)/60L, (i-t)%60L); else broadcast("### Game shutdown in %d seconds.", i-t); } else { broadcast("### Shutting down now."); gServer->processOutput(); loge("--- Game shut down ---\n"); // run moveRoomAbort before rooms/players get saved gConfig->moveRoomAbort(); gConfig->resaveAllRooms(1); save_all_ply(); disconnect_all_ply(); gConfig->save(); cleanUpMemory(); printf("Goodbye.\n"); kill(getpid(), 9); } } //********************************************************************* // updateWeather //********************************************************************* void updateWeather(long t) { int j=0, n=0; last_weather_update = t; for(j=0; j<5; j++) { if(Weather[j].ltime + Weather[j].interval < t) { switch (j) { case 0: if(mrand(1,100) > 80) { n = mrand(0,2); n -= 1; n = Weather[j].misc + n; if(n < 0 || n > 4) n = 0; switch (n) { case 0: Weather[j].ltime = t; weather(WEATHER_EARTH_TREMBLES); break; case 1: Weather[j].ltime = t; weather(WEATHER_HEAVY_FOG); break; } Weather[j].misc = (short)n; Weather[j].interval = 3200; } break; case 1: if(mrand(1,100) > 50 && isDay()) { n = mrand(0,2); n -= 1; n = Weather[j].misc + n; if(n < 0 || n > 4) n = 0; switch (n) { case 0: Weather[j].ltime = t; weather(WEATHER_BEAUTIFUL_DAY); break; case 1: Weather[j].ltime = t; weather(WEATHER_BRIGHT_SUN); break; case 3: Weather[j].ltime = t; weather(WEATHER_GLARING_SUN); break; case 4: Weather[j].ltime = t; weather(WEATHER_HEAT); break; } Weather[j].misc = (short)n; Weather[j].interval = 1600; } break; case 2: if(mrand(1,100) > 50) { n = mrand(0,2); n -= 1; n = Weather[j].misc +n; if(n< 0 || n> 4) n = 0; switch (n) { case 0: Weather[j].ltime = t; weather(WEATHER_STILL); break; case 1: Weather[j].ltime = t; weather(WEATHER_LIGHT_BREEZE); break; case 2: Weather[j].ltime = t; weather(WEATHER_STRONG_WIND); break; case 3: Weather[j].ltime = t; weather(WEATHER_WIND_GUSTS); break; case 4: Weather[j].ltime = t; weather(WEATHER_GALE_FORCE); break; } Weather[j].misc = (short)n; Weather[j].interval = 900; } break; case 3: if(mrand(1,100) > 50) { n = mrand(0,2); n -= 1; n = Weather[j].misc+n; if(n< 0 || n> 6) n = 0; switch (n) { case 0: Weather[j].ltime = t; weather(WEATHER_CLEAR_SKIES); break; case 1: Weather[j].ltime = t; weather(WEATHER_LIGHT_CLOUDS); break; case 2: Weather[j].ltime = t; weather(WEATHER_THUNDERHEADS); break; case 3: Weather[j].ltime =t; weather(WEATHER_LIGHT_RAIN); break; case 4: Weather[j].ltime = t; weather(WEATHER_HEAVY_RAIN); break; case 5: Weather[j].ltime = t; weather(WEATHER_SHEETS_RAIN); break; case 6: Weather[j].ltime = t; weather(WEATHER_TORRENT_RAIN); break; } Weather[j].misc = (short)n; Weather[j].interval = 1100; } break; case 4: if(mrand(1,100) > 50 && !isDay()) { n = mrand(0,2); n -= 1; n += Weather[j].misc; if(n< 0 || n> 4) n = 0; switch (n) { case 0: Weather[j].ltime = t; weather(WEATHER_NO_MOON); break; case 1: Weather[j].ltime = t; weather(WEATHER_SLIVER_MOON); break; case 2: Weather[j].ltime = t; weather(WEATHER_HALF_MOON); break; case 3: Weather[j].ltime = t; weather(WEATHER_WAXING_MOON); break; case 4: Weather[j].ltime = t; weather(WEATHER_FULL_MOON); break; } Weather[j].misc = (short)n; Weather[j].interval = 1200; } break; } } } n = 11 - Weather[1].misc - Weather[2].misc - (Weather[3].misc - 2) + Weather[4].misc; if(n>25 || n < 2) n=11; //Random_update_interval = (short)n; Random_update_interval = 5; return; } //********************************************************************* // update_action //********************************************************************* // update function for logic scripts void update_action(long t) { Creature* creature=0, *victim=0; Object *object=0; BaseRoom* room=0; const ctag *cp=0; ctag *vcp=0; ttag *act=0, *tact=0; int i=0, on_cmd=0, thresh=0; int xdir=0, num=0; char *resp; const char *xits[] = { "n","ne","e","se","s","sw","w","nw","u","d" }; cmd cmnd; last_action_update = t; for(cp = gServer->getFirstActive();cp;cp = cp->next_tag) { creature = cp->crt; if(creature) { room = creature->getRoom(); if(room && creature->flagIsSet(M_LOGIC_MONSTER)) { if(!cp->crt->first_tlk) loadCreature_actions(cp->crt); else { act = cp->crt->first_tlk; on_cmd = act->on_cmd; on_cmd--; i = 0; if(on_cmd) while(i < on_cmd) { act = act->next_tag; i++; } on_cmd+=2; // set for next command, can be altered later // proccess commands based on a higharcy if(act->test_for) { switch(act->test_for) { case 'P': // test for player victim = room->findPlayer(creature, act->response, 1); if(victim) { if(creature->first_tlk->target) delete[] creature->first_tlk->target; creature->first_tlk->target = new char[strlen(act->response)+1]; strcpy(creature->first_tlk->target, act->response); act->success = 1; } else { if(creature->first_tlk->target) free(creature->first_tlk->target); creature->first_tlk->target = 0; act->success = 0; } break; case 'C': // test for a player with class case 'R': // test for a player with race for(vcp = room->first_ply; vcp; vcp = vcp->next_tag) { if(act->test_for == 'C') if(vcp->crt->getClass() == act->arg1) { if(creature->first_tlk->target) delete[] creature->first_tlk->target; creature->first_tlk->target = new char[strlen(vcp->crt->name)+1]; strcpy(creature->first_tlk->target, vcp->crt->name); act->success = 1; break; } if(act->test_for == 'R') if(vcp->crt->getRace() == act->arg1) { if(creature->first_tlk->target) delete creature->first_tlk->target; creature->first_tlk->target = new char[strlen(vcp->crt->name)+1]; strcpy(creature->first_tlk->target, vcp->crt->name); act->success = 1; break; } } if(!vcp) { if(creature->first_tlk->target) delete creature->first_tlk->target; creature->first_tlk->target = 0; act->success = 0; } break; case 'O': // test for object in room object = findObject(creature, room->first_obj, act->response, 1); if(object) { if(creature->first_tlk->target) delete creature->first_tlk->target; creature->first_tlk->target = new char[strlen(act->response)+1]; strcpy(creature->first_tlk->target, act->response); act->success = 1; // loge(victim->name); } else { if(creature->first_tlk->target) free(creature->first_tlk->target); creature->first_tlk->target = 0; act->success = 0; } break; case 'o': // test for object on players break; case 'M': // test for monster victim = room->findMonster(creature, act->response, 1); if(victim) { if(creature->first_tlk->target) delete creature->first_tlk->target; creature->first_tlk->target = new char[strlen(act->response)+1]; strcpy(creature->first_tlk->target, act->response); act->success = 1; } else { if(creature->first_tlk->target) free(creature->first_tlk->target); creature->first_tlk->target = 0; act->success = 0; } break; } } if(act->if_cmd) { // test to see if command was successful for(tact = creature->first_tlk; tact; tact = tact->next_tag) { if(tact->type == act->if_cmd) break; } if(tact) { if(act->if_goto_cmd && tact->success) on_cmd = act->if_goto_cmd; if(act->not_goto_cmd && !tact->success) on_cmd = act->not_goto_cmd; } else { if(act->not_goto_cmd) on_cmd = act->not_goto_cmd; } } // run a action if(act->do_act) { // num = 0; // thresh = 0; act->success = 1; resp = act->response; if(isdigit(*(resp))) { num = 10*(atoi(resp)); ++resp; num = (num == 0) ? 100:num; thresh = mrand(1,200); // broadcast(isDm, -1, cp->crt->getRoom(), "*DM* NUM: %d Thresh: %d", num, thresh); } switch(act->do_act) { case 'E': // broadcast response to room if(thresh <= num) broadcast(NULL, cp->crt->getRoom(), "%s", resp); break; case 'S': // say to room if(thresh <= num) broadcast(NULL, cp->crt->getRoom(), "%M says, \"%s\"", creature, resp); break; case 'T': // Mob Trash-talk if(mrand(1,100) <= 10) { if(countTotalEnemies(creature) > 0 && !creature->getMonster()->nearEnemy()) { if(creature->daily[DL_BROAD].cur > 0) { broadcast("### %M broadcasted, \"%s\"", creature, resp); subtractMobBroadcast(creature, 0); } } } break; case 'B': // Mob general random broadcasts if(mrand(1,100) <= 10) { if(countTotalEnemies(creature) < 1 && thresh <= num) { if(creature->daily[DL_BROAD].cur > 0) { broadcast("### %M broadcasted, \"%s\"", creature, resp); subtractMobBroadcast(creature, 0); } } } break; case 'A': // attack monster in target string if(creature->first_tlk->target && !creature->first_enm) { victim = room->findMonster(creature, creature->first_tlk->target, 1); if(!victim) return; // if(victim) // addEnmCrt(victim->name, creature); //broadcast(NULL, creature->getRoom(), "%M attacks %s.",creature,victim); victim->getMonster()->monsterCombat((Monster*)creature); if(creature->first_tlk->target) free(creature->first_tlk->target); creature->first_tlk->target = 0; } break; case 'a': // attack player target break; case 'c': // cast a spell on target break; case 'F': // force target to do somthing break; case '|': // set a flag on target break; case '&': // remove flag on target break; case 'P': // perform social break; case 'O': // open door break; case 'C': // close door break; case 'D': // delay action break; case 'G': // go into a keyword exit break; case '0': // go n case '1': // go ne case '2': // go e case '3': // go se case '4': // go s case '5': // go sw case '6': // go w case '7': // go nw case '8': // go up case '9': // go down xdir = act->do_act - '0'; strcpy(cmnd.str[0], xits[xdir]); // TODO: Dom: fix update printf("action move\n"); //move(creature, &cmnd); break; } } // unconditional jump if(act->goto_cmd) { act->success = 1; cp->crt->first_tlk->on_cmd = act->goto_cmd; } else cp->crt->first_tlk->on_cmd = on_cmd; } } } } } //********************************************************************* // update_dust_oldPrint //********************************************************************* void update_dust_oldPrint(long t) { last_dust_output=0; broadcast("Ominous thunder rumbles in the distance."); } //********************************************************************* // handle_pipe //********************************************************************* void handle_pipe( int sig) { // What should we do here? // Should just continue on } //********************************************************************* // doCrash //********************************************************************* void doCrash(int sig) { long t = time(0); std::ostringstream oStr; Crash++; if(Crash > 1) exit(-12); // Uninstall signal handlers so we don't get back here again signal(SIGALRM, SIG_DFL); signal(SIGFPE, SIG_DFL); signal(SIGSEGV, SIG_DFL); gServer->sendCrash(); gConfig->save(); broadcast(" C R A S H !\n"); broadcast(isStaff, "%s", ctime(&t)); oStr << "People online: "; std::pair<bstring, Player*> p; Player* player=0; int i=0; foreach(Socket* sock, gServer->sockets) { player = sock->getPlayer(); if(!player) continue; if(i++) oStr << ", "; if(!sock->isConnected()) oStr << player->name << " (connecting)"; else oStr << player->name << " (^xcmd:" + player->getLastCommand() << ":" << (t-sock->ltime) << "^W)"; } if(!i) oStr << "No one"; oStr << "."; broadcast(isDm, "^W%s", oStr.str().c_str()); gServer->processOutput(); logn("log.inbytes", "--- %ld\n", InBytes); logn("log.outbytes", "-- %ld\n", OutBytes); logn("log.crash","--- !CRASH! Game closed ---\n"); loge("--- !CRASH! Game closed ---\n"); // for some reason, on cygwin this will (sometimes) corrupt room files #ifndef __CYGWIN__ // Turning this off because of the xp->ext = NULL bug which is erasing exits // gConfig->resaveAllRooms(1); #endif save_all_ply(); cleanUpMemory(); oStr << "\n"; //delimit(who); logn("log.crash", oStr.str().c_str()); printf("The mud has crashed :(.\n"); // abort(); exit(77); } //********************************************************************* // crash //********************************************************************* void crash(int sig) { if(gConfig->sendTxtOnCrash()) webCrash(""); doCrash(sig); } void cleanup_spelling(void); //********************************************************************* // cleanUpMemory //********************************************************************* // Clean up all memory we know is in use -- this way valgrind and any other // tools can accurately find memory leaks void cleanUpMemory() { gConfig->clearQuestTable(); gConfig->flushRoom(); gConfig->flushObject(); gConfig->flushMonster(); //flush_ext(); Config::destroyInstance(); Server::destroyInstance(); // Clean up xml memory xmlCleanupParser(); cleanup_spelling(); // TODO: Clean up spelling memory printf("Memory cleaned up.\n"); } //********************************************************************* // subtractMobBroadcast //********************************************************************* void subtractMobBroadcast(Creature *monster, int num) { if(monster->daily[DL_BROAD].cur > 0) { if(num) monster->daily[DL_BROAD].cur -= num; else monster->daily[DL_BROAD].cur --; } } //********************************************************************* // countTotalEnemies //********************************************************************* int countTotalEnemies(Creature *monster) { int count = 0; etag *ep=0; if(monster->isPlayer()) return(0); ep = monster->first_enm; while(ep) { count++; ep = ep->next_tag; } return(count); } //********************************************************************* // clearEnemyPlayer //********************************************************************* // This function removes a player's name from every single monster // on the active list when the player dies, suicides, or is dusted. void Player::clearEnemyPlayer() { const ctag *cp=0; Creature* creature=0; if(!(cp = gServer->getFirstActive())) return; while(cp) { if(!cp) { merror("cp in active", NONFATAL); break; } if(!(cp->crt)) { merror("cp in active", NONFATAL); break; } creature = cp->crt; if(!creature) { merror("creature in active", NONFATAL); break; } if(!(creature->parent_rom && creature->parent_rom->info.id) && !creature->area_room) break; creature->getMonster()->delEnmCrt(name); cp = cp->next_tag; } return; } //********************************************************************* // dmInRoom //********************************************************************* // This function will return 1 if anyone is in the room while dmInvis int BaseRoom::dmInRoom() const { ctag *cp = first_ply; while(cp) { if(cp->crt->flagIsSet(P_DM_INVIS)) return(1); cp = cp->next_tag; } return(0); } //********************************************************************* // checkOutlawAggro //********************************************************************* void Player::checkOutlawAggro() { if(flagIsSet(P_OUTLAW_WILL_BE_ATTACKED)) { ctag *outlawmob; outlawmob = getRoom()->first_mon; while(outlawmob) { if(outlawmob->crt->flagIsSet(M_OUTLAW_AGGRO) && !outlawmob->crt->first_enm) { printColor("^r%M attacks you.\n", outlawmob->crt); broadcast(getSock(), getRoom(), "%M attacks %N.", outlawmob->crt, this); outlawmob->crt->updateAttackTimer(true, DEFAULT_WEAPON_DELAY); outlawmob->crt->getMonster()->addEnmCrt(this); } outlawmob = outlawmob->next_tag; } } } //********************************************************************* // update_track //********************************************************************* void update_track(long t) { std::list<Area*>::iterator it; if(last_track_update) for(it = gConfig->areas.begin() ; it != gConfig->areas.end() ; it++) (*it)->updateTrack((int)t - last_track_update); last_track_update = t; } //********************************************************************* // update_ships //********************************************************************* void update_ships() { std::list<Ship*>::iterator it; Ship *ship=0; ShipStop *stop=0; // we must delay ships for a while if(gConfig->calendar->shipUpdates > gConfig->expectedShipUpdates()) return; gConfig->calendar->shipUpdates++; for(it = gConfig->ships.begin() ; it != gConfig->ships.end() ; it++) { ship = (*it); ship->timeLeft--; stop = ship->stops.front(); // only do last call if we're in port if(ship->inPort && ship->timeLeft==60) shipBroadcastRange(ship, stop, stop->lastcall); // while loop because a ship might take 0 time to get to the next stop while(ship->timeLeft <= 0) { if(ship->inPort) { ship->timeLeft = stop->to_next; shipDeleteExits(ship, stop); ship->stops.push_back(stop); ship->stops.pop_front(); } else { ship->timeLeft = stop->in_dock; shipSetExits(ship, stop); } stop = ship->stops.front(); ship->inPort = !ship->inPort; } } // we haven't updated enough! if(gConfig->calendar->shipUpdates < gConfig->expectedShipUpdates()) update_ships(); } //********************************************************************* // list_act //********************************************************************* int list_act(Player* player, cmd* cmnd) { const ctag *cp = gServer->getFirstActive(); player->print("### Active monster list ###\n"); player->print("Monster - Room Number\n"); while(cp) { if( (!cp->crt->parent_rom || !cp->crt->parent_rom->info.id) && !cp->crt->area_room ) { if(!cp->crt->name) player->print("Bad Mob - Room %s.\n", cp->crt->getRoom()->fullName().c_str()); else player->print("Bad Mob - %s - Room %s.\n", cp->crt->name, cp->crt->getRoom()->fullName().c_str()); cp = cp->next_tag; continue; } if(!cp->crt->name) { player->print("Bad Mob - Room %d.\n", cp->crt->getRoom()->fullName().c_str()); cp = cp->next_tag; continue; } player->print("%s - %s.\n", cp->crt->name, cp->crt->getRoom()->fullName().c_str()); cp = cp->next_tag; } gServer->processOutput(); return(0); }