roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * update.cpp
 *   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-2012 Jason Mitchell, Randi Mitchell
 * 	   Contributions by Tim Callahan, Jonathan Hseu
 *  Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
 *
 */


#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 <sys/signal.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;

	gServer->parseDelayedActions(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 won't 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 >= 1) {
		pulseTicks(t);
   		pulseCreatureEffects(t);
    }

    // pulse effects
	if(t - lastUserUpdate >= 20) {
		updateUsers(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)
		gServer->updateWeather(t);
	if(t - last_action_update >= Action_update_interval)
		gServer->updateAction(t);
//		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));
}

//*********************************************************************
//						weather
//*********************************************************************
// turn the weather enum into a hook event

bstring weatherToEvent(WeatherString w) {
	switch(w) {
		case WEATHER_SUNRISE:
			return("weatherSunrise");
		case WEATHER_SUNSET:
			return("weatherSunset");

		case WEATHER_EARTH_TREMBLES:
			return("weatherEarthTrembles");
		case WEATHER_HEAVY_FOG:
			return("weatherHeavyFog");

		case WEATHER_BEAUTIFUL_DAY:
			return("weatherBeautifulDay");
		case WEATHER_BRIGHT_SUN:
			return("weatherBrightSun");
		case WEATHER_GLARING_SUN:
			return("weatherGlaringSun");
		case WEATHER_HEAT:
			return("weatherHeat");

		case WEATHER_STILL:
			return("weatherStill");
		case WEATHER_LIGHT_BREEZE:
			return("weatherLightBreeze");
		case WEATHER_STRONG_WIND:
			return("weatherStrongWind");
		case WEATHER_WIND_GUSTS:
			return("weatherWindGusts");
		case WEATHER_GALE_FORCE:
			return("weatherGaleForce");

		case WEATHER_CLEAR_SKIES:
			return("weatherClearSkies");
		case WEATHER_LIGHT_CLOUDS:
			return("weatherLightClouds");
		case WEATHER_THUNDERHEADS:
			return("weatherThunderheads");

		case WEATHER_LIGHT_RAIN:
			return("weatherLightRain");
		case WEATHER_HEAVY_RAIN:
			return("weatherHeavyRain");
		case WEATHER_SHEETS_RAIN:
			return("weatherSheetsRain");
		case WEATHER_TORRENT_RAIN:
			return("weatherTorrentRain");

		case WEATHER_NO_MOON:
			return("weatherNoMoon");
		case WEATHER_SLIVER_MOON:
			return("weatherSliverMoon");
		case WEATHER_HALF_MOON:
			return("weatherHalfMoon");
		case WEATHER_WAXING_MOON:
			return("weatherWaxingMoon");
		case WEATHER_FULL_MOON:
			return("weatherFullMoon");
	}
	return("");
}

//*********************************************************************
//						weather
//*********************************************************************

void Server::weather(WeatherString w) {
	bstring season = gConfig->calendar->getCurSeason()->getName().toLower();
	const bstring& event = weatherToEvent(w);
	bstring weather;
	char color;

	if(w == WEATHER_SUNRISE)
		color = 'Y';
	else if(w == WEATHER_SUNSET)
		color = 'D';
	else
		color = 'w';


	for(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->getRoomParent()->isOutdoors())
				continue;
		}

		weather = gConfig->weatherize(w, player->getRoomParent());
		if(weather != "") {
			player->printColor("^%c%s\n", color, weather.c_str());
			player->hooks.execute(event, 0, season);
		}
	}


	MonsterList::iterator it = activeList.begin();
	while(it != activeList.end()) {
		// Increment the iterator in case this monster dies during the update and is removed from the active list
		Monster* monster = (*it++);

		if(!monster)
			continue;

		if(	(w == WEATHER_SUNRISE || w == WEATHER_SUNSET) &&
			monster->isEffected("vampirism"))
		{
			// if sunrise/sunset, vampires always see it
		} else {
			if(!monster->getRoomParent()->isOutdoors())
				continue;
		}

		// monsters don't need to see the message, but we need to know if there is a message or not
		// as that will decide whether or not we tell them to execute a hook
		weather = gConfig->weatherize(w, monster->getRoomParent());
		if(weather != "")
			monster->hooks.execute(event, 0, season);
	}
}

//*********************************************************************
//						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;


	daytime = gConfig->currentHour();
	if(daytime == SUNRISE-1) {
		for(std::pair<bstring, Player*> p : gServer->players) {
			if(p.second->isEffected("vampirism"))
				p.second->printColor("^YThe sun is about to rise.\n");
		}
	} else if(daytime == SUNRISE) {
		gServer->weather(WEATHER_SUNRISE);
		gConfig->killMortalObjects();
	} else if(daytime == SUNSET-1) {
		for( std::pair<bstring, Player*> p : gServer->players) {
			if(p.second->isEffected("vampirism"))
				p.second->printColor("^DThe sun is about to set.\n");
		}
	} else if(daytime == SUNSET) {
		gServer->weather(WEATHER_SUNSET);
	}

	for(std::pair<bstring, Player*> 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 swapAbort before rooms/players get saved
		gConfig->swapAbort();
		gConfig->resaveAllRooms(1);
		gServer->saveAllPly();
		gServer->disconnectAll();
		gConfig->save();
		cleanUpMemory();

		printf("Goodbye.\n");
		exit(0);
		//kill(getpid(), 9);
	}
}


//*********************************************************************
//						updateWeather
//*********************************************************************

void Server::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 Server::updateAction(long t) {
	Creature* victim=0;
	Object	*object=0;
	BaseRoom* room=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;

	Monster	*monster=0;
	MonsterList::iterator it = activeList.begin();
	while(it != activeList.end()) {
		monster = (*it++);
		if(monster) {
			room = monster->getRoomParent();
			if(room && monster->flagIsSet(M_LOGIC_MONSTER)) {
				if(!monster->first_tlk)
					loadCreature_actions(monster);
				else {
					act = monster->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(monster, act->response, 1);
							if(victim) {
								if(monster->first_tlk->target)
									delete[] monster->first_tlk->target;
								monster->first_tlk->target = new char[strlen(act->response)+1];
								strcpy(monster->first_tlk->target, act->response);
								act->success = 1;
							} else {
								if(monster->first_tlk->target)
									free(monster->first_tlk->target);
								monster->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(Player* ply : room->players) {
								if(act->test_for == 'C')
									if(ply->getClass() == act->arg1) {
										if(monster->first_tlk->target)
											delete[] monster->first_tlk->target;
										monster->first_tlk->target = new char[ply->getName().length()+1];
										strcpy(monster->first_tlk->target, ply->getCName());
										act->success = 1;
										break;
									}
								if(act->test_for == 'R')
									if(ply->getRace() == act->arg1) {
										if(monster->first_tlk->target)
											delete monster->first_tlk->target;
										monster->first_tlk->target = new char[ply->getName().length()+1];
										strcpy(monster->first_tlk->target, ply->getCName());
										act->success = 1;
										break;
									}
							}
							if(!act->success) {
								if(monster->first_tlk->target)
									delete monster->first_tlk->target;
								monster->first_tlk->target = 0;
								act->success = 0;
							}
							break;
						case 'O': // test for object in room
							object = room->findObject(monster, act->response, 1);

							if(object) {
								if(monster->first_tlk->target)
									delete monster->first_tlk->target;
								monster->first_tlk->target = new char[strlen(act->response)+1];
								strcpy(monster->first_tlk->target, act->response);
								act->success = 1;
								// loge(victim->name);
							} else {
								if(monster->first_tlk->target)
									free(monster->first_tlk->target);
								monster->first_tlk->target = 0;
								act->success = 0;
							}
							break;
						case 'o': // test for object on players
							break;
						case 'M': // test for monster
							victim = room->findMonster(monster, act->response, 1);
							if(victim) {
								if(monster->first_tlk->target)
									delete monster->first_tlk->target;
								monster->first_tlk->target = new char[strlen(act->response)+1];
								strcpy(monster->first_tlk->target, act->response);
								act->success = 1;
							} else {
								if(monster->first_tlk->target)
									free(monster->first_tlk->target);
								monster->first_tlk->target = 0;
								act->success = 0;
							}
							break;
						}

					}
					if(act->if_cmd) {
						// test to see if command was successful
						for(tact = monster->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, monster->getRoomParent(), "%s", resp);

							break;
						case 'S': // say to room
							if(thresh <= num)
								broadcast(NULL, monster->getRoomParent(), "%M says, \"%s\"", monster, resp);
							break;
						case 'T':	// Mob Trash-talk
							if(mrand(1,100) <= 10) {

								if(countTotalEnemies(monster) > 0 && !monster->getAsMonster()->nearEnemy()) {
									if(monster->daily[DL_BROAD].cur > 0) {
										broadcast("### %M broadcasted, \"%s\"", monster, resp);
										subtractMobBroadcast(monster, 0);
									}
								}
							}
							break;
						case 'B':	// Mob general random broadcasts
							if(mrand(1,100) <= 10) {

								if(countTotalEnemies(monster) < 1 && thresh <= num) {
									if(monster->daily[DL_BROAD].cur > 0) {
										broadcast("### %M broadcasted, \"%s\"", monster, resp);
										subtractMobBroadcast(monster, 0);
									}
								}
							}
							break;
						case 'A': // attack monster in target string
							if(monster->first_tlk->target && !monster->getAsMonster()->hasEnemy()) {
								victim = room->findMonster(monster, monster->first_tlk->target, 1);
								if(!victim)
									return;
								victim->getAsMonster()->monsterCombat((Monster*)monster);
								if(monster->first_tlk->target)
									free(monster->first_tlk->target);
								monster->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;
						monster->first_tlk->on_cmd = act->goto_cmd;
					} else
						monster->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();
	gServer->saveIds();
	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;
	for(Socket* sock : gServer->sockets) {
		player = sock->getPlayer();
		if(!player)
			continue;
		if(i++)
			oStr << ", ";

		if(!sock->isConnected())
			oStr << player->getName() << " (connecting)";
		else
			oStr << player->getName() << " (^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
	gServer->saveAllPly();

	cleanUpMemory();


	oStr << "\n";
	//delimit(who);
	logn("log.crash", oStr.str().c_str());

	printf("The mud has crashed :(.\n");

	if(sig != -69) {
//		char dbx[160];
//
//		sprintf(dbx, "gstack %d > realms.%d.dump", getpid(), getpid());
//		system(dbx);
//
	//	abort();
		signal(sig, SIG_DFL);
		kill(getpid(), sig);
	} else {
		// Crashing from *bane, don't bother with core dumps or backtraces
		exit(sig);
	}
}

//*********************************************************************
//						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) {
	if(monster->isPlayer())
		return(0);
	Monster* mons = monster->getAsMonster();
	return(mons->threatTable->size());
}

//*********************************************************************
//						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 Server::clearAsEnemy(Player* player) {

	Monster	*monster=0;
	MonsterList::iterator it = activeList.begin();
	while(it != activeList.end()) {
		// Increment the iterator in case this monster dies during the update and is removed from the active list
		monster = (*it++);

		if(!(monster->inUniqueRoom() && monster->getUniqueRoomParent()->info.id) && !monster->inAreaRoom()) continue;

		monster->clearEnemy(player);
	}

	return;
}

//*********************************************************************
//						dmInRoom
//*********************************************************************
// This function will return 1 if anyone is in the room while dmInvis

int BaseRoom::dmInRoom() const {
    for(Player* ply : players) {
		if(ply->flagIsSet(P_DM_INVIS))
			return(1);
	}
	return(0);
}

//*********************************************************************
//						checkOutlawAggro
//*********************************************************************

void Player::checkOutlawAggro() {
	if(!flagIsSet(P_OUTLAW_WILL_BE_ATTACKED))
		return;

	for(Monster* mons : getRoomParent()->monsters) {
		if(mons->flagIsSet(M_OUTLAW_AGGRO) && !mons->getAsMonster()->hasEnemy()) {
			mons->updateAttackTimer(true, DEFAULT_WEAPON_DELAY);
			mons->getAsMonster()->addEnemy(this, true);
		}
	}
}


//*********************************************************************
//						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
//*********************************************************************
bstring Server::showActiveList() {
	Monster* monster;
	MonsterList::iterator it = activeList.begin();
	std::ostringstream oStr;
	oStr << "### Active monster list ###\n";
	oStr << "Monster    -    Room Number\n";

	while(it != activeList.end()) {
		monster = (*it++);
		if((!monster->inUniqueRoom() || !monster->getUniqueRoomParent()->info.id) && !monster->inAreaRoom()) {
			if(monster->getName()[0])
				oStr << "Bad Mob " << monster->getName() << ".\n";
			else
				oStr << "Bad Mob\n";
			continue;
		}
		if(!monster->getName()[0]) {
			oStr << "Bad Mob - Room " << monster->getRoomParent()->fullName() << "\n";
			continue;
		}
		oStr << monster->getName() << " - " << monster->getRoomParent()->fullName() << "\n";
	}
	return(oStr.str());

}
int list_act(Player* player, cmd* cmnd) {
	player->print("%s", gServer->showActiveList().c_str());
	gServer->processOutput();
	return(0);
}