From 8a71861846c41c5f432570262b398c7627743c91 Mon Sep 17 00:00:00 2001
From: Clyne Sullivan <tullivan99@gmail.com>
Date: Tue, 11 Oct 2016 19:02:04 -0500
Subject: more entityx, main.cpp cleanup

---
 brice.dat             |   8 +--
 include/common.hpp    |  21 ------
 include/engine.hpp    |  17 +++++
 include/events.hpp    |  13 +++-
 include/inventory.hpp |   2 +
 include/ui.hpp        |   7 --
 include/world.hpp     |  12 ++--
 main.cpp              | 182 ++++++++++++++++++++------------------------------
 src/entities.cpp      |  28 ++++----
 src/render.cpp        |   1 -
 src/ui.cpp            |  21 +-----
 src/ui_menu.cpp       |   3 +-
 src/world.cpp         |  23 +++++--
 xml/!town.xml         |   4 +-
 14 files changed, 153 insertions(+), 189 deletions(-)
 create mode 100644 include/engine.hpp

diff --git a/brice.dat b/brice.dat
index a49519b..308f5c8 100644
--- a/brice.dat
+++ b/brice.dat
@@ -1,7 +1,7 @@
 3
-canSprint
-1
-canJump
-1
 Slow
 0
+canJump
+1
+canSprint
+1
diff --git a/include/common.hpp b/include/common.hpp
index 8d74cda..15442a7 100644
--- a/include/common.hpp
+++ b/include/common.hpp
@@ -245,27 +245,6 @@ constexpr const float PI = 3.1415926535f;
 // references the variable in main.cpp, used for drawing with the player
 extern vec2 offset;
 
-// reference to the shader programs we use throughout
-extern GLuint textShader;
-extern GLint textShader_attribute_coord;
-extern GLint textShader_attribute_tex;
-extern GLint textShader_uniform_texture;
-extern GLint textShader_uniform_color;
-
-extern GLuint worldShader;
-extern GLint worldShader_attribute_coord;
-extern GLint worldShader_attribute_tex;
-extern GLint worldShader_uniform_texture;
-extern GLint worldShader_uniform_texture_normal;
-extern GLint worldShader_uniform_color;
-extern GLint worldShader_uniform_transform;
-extern GLint worldShader_uniform_ambient;
-extern GLint worldShader_uniform_light;
-extern GLint worldShader_uniform_light_color;
-extern GLint worldShader_uniform_light_impact;
-extern GLint worldShader_uniform_light_amt;
-
-extern Color ambient;
 /**
  *	Prints a formatted debug message to the console, along with the callee's file and line
  *	number.
diff --git a/include/engine.hpp b/include/engine.hpp
new file mode 100644
index 0000000..0fabdb3
--- /dev/null
+++ b/include/engine.hpp
@@ -0,0 +1,17 @@
+#ifndef ENGINE_HPP_
+#define ENGINE_HPP_
+
+#include <entityx/entityx.h>
+
+#include <events.hpp>
+
+namespace game {
+    extern entityx::EventManager events;
+    extern entityx::EntityManager entities;
+
+    inline void endGame(void) {
+        events.emit<GameEndEvent>();
+    }
+}
+
+#endif // ENGINE_HPP_
diff --git a/include/events.hpp b/include/events.hpp
index aa3ca07..1fe7d7a 100644
--- a/include/events.hpp
+++ b/include/events.hpp
@@ -8,24 +8,31 @@
 #include <SDL2/SDL.h>
 
  struct MouseScrollEvent {
- 	MouseScrollEvent(int sd)
+ 	MouseScrollEvent(int sd = 0)
  		: scrollDistance(sd) {}
 
  	int scrollDistance;
  };
 
 struct KeyDownEvent {
-    KeyDownEvent(SDL_Keycode kc)
+    KeyDownEvent(SDL_Keycode kc = 0)
         : keycode(kc) {}
 
     SDL_Keycode keycode;
 };
 
 struct KeyUpEvent {
-    KeyUpEvent(SDL_Keycode kc)
+    KeyUpEvent(SDL_Keycode kc = 0)
         : keycode(kc) {}
 
     SDL_Keycode keycode;
 };
 
+struct GameEndEvent {
+    GameEndEvent(bool r = true)
+        : really(r) {}
+
+    bool really;
+};
+
 #endif // EVENTS_HPP_
diff --git a/include/inventory.hpp b/include/inventory.hpp
index 9a42aa4..533318c 100644
--- a/include/inventory.hpp
+++ b/include/inventory.hpp
@@ -18,8 +18,10 @@ class Entity;
 class Item {
 private:
 	bool beingUsed;
+
 protected:
 	std::vector<Entity*> interact;
+	
 public:
 
 	// what we want to call each item
diff --git a/include/ui.hpp b/include/ui.hpp
index f276882..8671393 100644
--- a/include/ui.hpp
+++ b/include/ui.hpp
@@ -149,13 +149,6 @@ namespace ui {
 
 	void quitGame();
 
-
-
-	/*
-	 *	Handle keyboard/mouse events.
-	*/
-	void handleEvents(void);
-
 	/*
 	 *	Toggle the black overlay thing.
 	*/
diff --git a/include/world.hpp b/include/world.hpp
index 92a07b2..6b26452 100644
--- a/include/world.hpp
+++ b/include/world.hpp
@@ -131,7 +131,7 @@ public:
 };
 
 /**
- * The world class. 
+ * The world class.
  * This class handles entity creation, management, and deletion. Most
  * world-related operations have to be done through this class, such as
  * drawing.
@@ -262,7 +262,7 @@ protected:
 	 * This function is only called in the world destructor.
 	 */
 	void deleteEntities(void);
-	
+
 	/**
 	 * Draws background textures.
 	 */
@@ -271,7 +271,7 @@ protected:
 public:
 
 	CoolArray<Particles>    particles;
-	
+
 	/**
 	 * A vector of pointers to all entities from the other vectors.
 	 * This is used to mass-manage entities, or operate on entities
@@ -330,6 +330,10 @@ public:
 	 */
 	float getWorldStart(void) const;
 
+	inline unsigned int getEntityCount(void) const {
+		return entity.size();
+	}
+
 	/**
 	 * Gets a pointer to the most recently created light.
 	 * This is used to update properties of the light outside of the
@@ -413,7 +417,7 @@ public:
 	 * Adopts an NPC from another world, taking its ownership.
 	 */
 	void adoptNPC(NPC *e);
-	
+
 	/**
 	 * Adopts a mob from another world, taking its ownership.
 	 */
diff --git a/main.cpp b/main.cpp
index 454f4b4..83afdc1 100644
--- a/main.cpp
+++ b/main.cpp
@@ -12,6 +12,7 @@
 
 #include <window.hpp>
 #include <render.hpp>
+#include <engine.hpp>
 
 // local library includes
 #include <tinyxml2.h>
@@ -32,9 +33,6 @@ using namespace tinyxml2;
 ** Variables section
 ** --------------------------------------------------------------------------*/
 
-// main loop runs based on this variable's value
-bool gameRunning = true;
-
 // world objects for the current world and the two that are adjacent
 World *currentWorld        = NULL,
 	  *currentWorldToLeft  = NULL,
@@ -52,9 +50,6 @@ Menu *currentMenu;
 // the player object
 Player *player;
 
-// the ambient light of the current world
-Color ambient;
-
 // keeps a simple palette of colors for single-color draws
 GLuint colorIndex;
 
@@ -86,12 +81,25 @@ void mainLoop(void);
 ** MAIN ************************************************************************
 ********************************************************************************/
 
-class Engine : public entityx::EntityX {
+namespace game {
+	entityx::EventManager events;
+	entityx::EntityManager entities (events);
+}
+
+class Engine : public entityx::Receiver<Engine> {
+private:
+	bool gameRunning;
+
 public:
-	explicit Engine(void) {}
+	entityx::SystemManager systems;
+
+	explicit Engine(void)
+		: gameRunning(true), systems(game::entities, game::events) {}
 
 	void init(void) {
 		game::config::read();
+		game::events.subscribe<GameEndEvent>(*this);
+
 		systems.add<WindowSystem>();
 		systems.add<InputSystem>();
 		systems.add<InventorySystem>();
@@ -112,6 +120,18 @@ public:
 		currentWorld->update(player, dt);
 		currentWorld->detect(player);
 	}
+
+	void configure(entityx::EventManager &ev) {
+		(void)ev;
+	}
+
+	void receive(const GameEndEvent &gee) {
+		gameRunning = !(gee.really);
+	}
+
+	inline bool shouldRun(void) const {
+		return gameRunning;
+	}
 };
 
 Engine engine;
@@ -260,13 +280,9 @@ int main(int argc, char *argv[])
 	arena->setBackground(WorldBGType::Forest);
 	arena->setBGM("assets/music/embark.wav");
 
-
-	player->inv->addItem("Hunters Bow", 1);
-
-
 	// the main loop, in all of its gloriousness..
 	std::thread([&]{
-		while (gameRunning) {
+		while (engine.shouldRun()) {
 			mainLoop();
 			std::this_thread::sleep_for(std::chrono::milliseconds(1));
 		}
@@ -274,7 +290,7 @@ int main(int argc, char *argv[])
 
 	// the debug loop, gets debug screen values
 	std::thread([&]{
-		while (gameRunning) {
+		while (engine.shouldRun()) {
 			fps = 1000 / game::time::getDeltaTime();
 			debugY = player->loc.y;
 
@@ -282,10 +298,9 @@ int main(int argc, char *argv[])
 		}
 	}).detach();
 
-	while (gameRunning) {
+	while (engine.shouldRun()) {
 		engine.render(0);
 		render();
-		ui::handleEvents();
 	}
 
 	// put away the brice for later
@@ -307,14 +322,21 @@ int main(int argc, char *argv[])
     return 0; // Calls everything passed to atexit
 }
 
+extern std::vector<NPC *> aipreload;
+
 void mainLoop(void){
 	game::time::mainLoopHandler();
 
 	if (currentMenu) {
 		return;
 	} else {
-		// handle keypresses - currentWorld could change here
-		//ui::handleEvents();
+		// Flush preloaded AI functions if necessary
+		if (!ui::dialogBoxExists) {
+			while (!aipreload.empty()) {
+				aipreload.front()->addAIFunc(false);
+				aipreload.erase(std::begin(aipreload));
+			}
+		}
 
 		if (game::time::tickHasPassed())
 			logic();
@@ -358,51 +380,30 @@ void render() {
 
 	// TODO add depth
     glEnable(GL_DEPTH_TEST);
-	//glEnable(GL_CULL_FACE);
 
 	Render::textShader.use();
-	glUniformMatrix4fv(Render::textShader.uniform[WU_transform], 1, GL_FALSE, glm::value_ptr(ortho));
-    glUniform4f(Render::textShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.0);
+		glUniformMatrix4fv(Render::textShader.uniform[WU_transform], 1, GL_FALSE, glm::value_ptr(ortho));
+    	glUniform4f(Render::textShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.0);
+	Render::textShader.unuse();
     Render::worldShader.use();
-	glUniformMatrix4fv(Render::worldShader.uniform[WU_ortho], 1, GL_FALSE, glm::value_ptr(ortho));
-	glUniformMatrix4fv(Render::worldShader.uniform[WU_transform], 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f)));
-
-	glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.0);
-	glUniform4f(Render::worldShader.uniform[WU_ambient], ambient.red, ambient.green, ambient.blue, 1.0);
-	glUniform1f(Render::worldShader.uniform[WU_light_impact], 1.0);
-
-	/*static GLfloat l[]  = {460.0, 100.0, 0.0, 300.0};
-	static GLfloat lc[] = {1.0, 1.0, 1.0, 1.0};
-	glUniform4fv(worldShader_uniform_light, 1, l);
-	glUniform4fv(worldShader_uniform_light_color, 1, lc);
-	glUniform1i(worldShader_uniform_light_amt, 1);
-	*/
-	/**************************
-	**** RENDER STUFF HERE ****
-	**************************/
-
-	/**
-	 * Call the world's draw function, drawing the player, the world, the background, and entities. Also
-	 * draw the player's inventory if it exists.
-	 */
-	//player->near = true; // allow player's name to be drawn
+		glUniformMatrix4fv(Render::worldShader.uniform[WU_ortho], 1, GL_FALSE, glm::value_ptr(ortho));
+		glUniformMatrix4fv(Render::worldShader.uniform[WU_transform], 1, GL_FALSE, glm::value_ptr(glm::mat4(1.0f)));
+	Render::worldShader.unuse();
+
+	// draw the world and player
 	currentWorld->draw(player);
 
 	// draw the player's inventory
 	player->inv->draw();
 
-
 	// draw the fade overlay
 	ui::drawFade();
 
 	// draw ui elements
 	ui::draw();
 
-	/*
-	 * Draw the debug overlay if it has been enabled.
-	 */
-
-	if(ui::debug){
+	// draw the debug overlay if desired
+	if (ui::debug) {
 		ui::putText(offset.x-SCREEN_WIDTH/2, (offset.y+SCREEN_HEIGHT/2)-ui::fontSize,
 					"fps: %d\ngrounded:%d\nresolution: %ux%u\nentity cnt: %d\nloc: (%+.2f, %+.2f)\nticks: %u\nvolume: %f\nweather: %s\nxml: %s",
 					fps,
@@ -418,16 +419,12 @@ void render() {
 					currentXML.c_str()
 					);
 
-		static GLuint tracerText = Texture::genColor(Color(100,100,255));
-
-		uint es = currentWorld->entity.size();
-		GLfloat tpoint[es * 2 * 5];
-		GLfloat *tp = &tpoint[0];
-
-		Render::textShader.use();
-		glBindTexture(GL_TEXTURE_2D, tracerText);
-
+		// draw tracer lines if desired
+		static const GLuint tracerText = Texture::genColor(Color(100,100,255));
 		if (ui::posFlag) {
+			GLfloat *tpoint = new GLfloat[currentWorld->getEntityCount() * 2 * 5];
+			auto tp = tpoint;
+
 			for (auto &e : currentWorld->entity) {
 				*(tp++) = player->loc.x + player->width / 2;
 				*(tp++) = player->loc.y + player->height / 2;
@@ -443,58 +440,32 @@ void render() {
 				*(tp++) = 1.0;
 				*(tp++) = 1.0;
 			}
-		}
 
-		Render::worldShader.enable();
+			Render::textShader.use();
+				glBindTexture(GL_TEXTURE_2D, tracerText);
+				Render::textShader.enable();
+				glVertexAttribPointer(Render::worldShader.coord, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &tpoint[0]);
+				glVertexAttribPointer(Render::worldShader.tex, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &tpoint[3]);
+				glDrawArrays(GL_LINES, 0, currentWorld->getEntityCount() * 2);
+				Render::textShader.disable();
+			Render::textShader.unuse();
 
-		glVertexAttribPointer(Render::worldShader.coord, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &tpoint[0]);
-		glVertexAttribPointer(Render::worldShader.tex, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &tpoint[3]);
-		glDrawArrays(GL_LINES, 0, es * 2);
-
-		Render::worldShader.disable();
-		Render::worldShader.unuse();
+			delete[] tpoint;
+		}
 
 	}
 
-
-
+	// draw the menu
 	if (currentMenu)
 		ui::menu::draw();
 
+	// draw the mouse
 	Render::textShader.use();
-	glActiveTexture(GL_TEXTURE0);
-	glBindTexture(GL_TEXTURE_2D, mouseTex);
-	glUniform1i(Render::textShader.uniform[WU_texture], 0);
-
-	Render::textShader.enable();
-
-	glDisable(GL_DEPTH_TEST);
-
-	GLfloat mouseCoords[] = {
-		ui::mouse.x			,ui::mouse.y, 	      -9.9, //bottom left
-		ui::mouse.x+15		,ui::mouse.y, 		  -9.9, //bottom right
-		ui::mouse.x+15		,ui::mouse.y-15,	  -9.9, //top right
-
-		ui::mouse.x+15		,ui::mouse.y-15, 	  -9.9, //top right
-		ui::mouse.x 		,ui::mouse.y-15, 	  -9.9, //top left
-		ui::mouse.x			,ui::mouse.y, 		  -9.9, //bottom left
-	};
-
-	GLfloat mouseTex[] = {
-		0.0f, 0.0f, //bottom left
-		1.0f, 0.0f, //bottom right
-		1.0f, 1.0f, //top right
-
-		1.0f, 1.0f, //top right
-		0.0f, 1.0f, //top left
-		0.0f, 0.0f, //bottom left
-	};
-
-	glVertexAttribPointer(Render::textShader.coord, 3, GL_FLOAT, GL_FALSE, 0, mouseCoords);
-	glVertexAttribPointer(Render::textShader.tex, 2, GL_FLOAT, GL_FALSE, 0, mouseTex);
-	glDrawArrays(GL_TRIANGLES, 0, 6);
-
-	Render::textShader.disable();
+		glActiveTexture(GL_TEXTURE0);
+		glBindTexture(GL_TEXTURE_2D, mouseTex);
+		Render::useShader(&Render::textShader);
+		Render::drawRect(ui::mouse, ui::mouse + 15, -9.9);
+	Render::textShader.unuse();
 }
 
 void logic(){
@@ -503,7 +474,7 @@ void logic(){
 
 	// exit the game if the player falls out of the world
 	if (player->loc.y < 0)
-		gameRunning = false;
+		game::endGame();
 
 	if (player->inv->usingi) {
 		for (auto &e : currentWorld->entity) {
@@ -557,13 +528,6 @@ void logic(){
 	// calculate the world shading value
 	worldShade = 50 * sin((game::time::getTickCount() + (DAY_CYCLE / 2)) / (DAY_CYCLE / PI));
 
-	float ws = 75 * sin((game::time::getTickCount() + (DAY_CYCLE / 2)) / (DAY_CYCLE / PI));
-
-	float ambRG = std::clamp(.5f + (-ws / 100.0f), 0.01f, .9f);
-	float ambB =	std::clamp(.5f + (-ws / 80.0f), 0.03f, .9f);
-
-	ambient = Color(ambRG, ambRG, ambB, 1.0f);
-
 	// update fades
 	ui::fadeUpdate();
 
diff --git a/src/entities.cpp b/src/entities.cpp
index fbab59f..544b1c8 100644
--- a/src/entities.cpp
+++ b/src/entities.cpp
@@ -10,6 +10,7 @@
 #include <brice.hpp>
 
 #include <render.hpp>
+#include <engine.hpp>
 
 extern std::istream *names;
 
@@ -47,7 +48,6 @@ void PlayerSystem::configure(entityx::EventManager &ev)
 	ev.subscribe<KeyUpEvent>(*this);
 }
 
-extern std::array<SDL_Keycode, 6> controlMap;
 extern World  *currentWorldToLeft;
 extern World  *currentWorldToRight;
 extern bool gameRunning;
@@ -61,13 +61,13 @@ void PlayerSystem::receive(const KeyUpEvent &kue)
 	if (kc == SDLK_ESCAPE) {
 		ui::menu::toggle();
 		p->save();
-	} else if (kc == controlMap[1]) {
+	} else if (kc == getControl(1)) {
 		m_MoveLeft = false;
-	} else if (kc == controlMap[2]) {
+	} else if (kc == getControl(2)) {
 		m_MoveRight = false;
-	} else if (kc == controlMap[3] || kc == controlMap[4]) {
+	} else if (kc == getControl(3) || kc == getControl(4)) {
 		player->speed = 1;
-	} else if (kc == controlMap[5]) {
+	} else if (kc == getControl(5)) {
 		if (p->inv->invHover) {
 			p->inv->invHover = false;
 		} else {
@@ -94,10 +94,10 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 		ui::toggleBlackFast();
 		ui::waitForCover();
 		wsi.first->bgmPlay(currentWorld);
-		std::tie(currentWorld, p->loc) = wsi;
+		std::tie(currentWorld, player->loc) = wsi; // using p causes segfault
 		ui::toggleBlackFast();
 		ui::waitForUncover();
-		p->canMove = true;
+		player->canMove = true; // using p causes segfault
 	};
 
 	if ((kc == SDLK_SPACE) && (game::canJump & p->ground)) {
@@ -107,7 +107,7 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 	}
 
 	if (!ui::dialogBoxExists || ui::dialogPassive) {
-		if (kc == controlMap[0]) {
+		if (kc == getControl(0)) {
 			if (inBattle) {
 				std::thread([&](void){
 					auto thing = dynamic_cast<Arena *>(currentWorld)->exitArena(p);
@@ -121,7 +121,7 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 						worldSwitch(thing);
 				}).detach();
 			}
-		} else if (kc == controlMap[1]) {
+		} else if (kc == getControl(1)) {
 			if (!ui::fadeEnable) {
 				p->vel.x = -PLAYER_SPEED_CONSTANT;
 				if (std::stoi(game::getValue("Slow")) == 1)
@@ -136,7 +136,7 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 					}).detach();
 				}
 			}
-		} else if (kc == controlMap[2]) {
+		} else if (kc == getControl(2)) {
 			if (!ui::fadeEnable) {
 				p->vel.x = PLAYER_SPEED_CONSTANT;
 				if (std::stoi(game::getValue("Slow")) == 1)
@@ -151,12 +151,12 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 					}).detach();
 				}
 			}
-		} else if (kc == controlMap[3]) {
+		} else if (kc == getControl(3)) {
 			if (game::canSprint)
 				p->speed = 2.0f;
-		} else if (kc == controlMap[4]) {
+		} else if (kc == getControl(4)) {
 			p->speed = .5;
-		} else if (kc == controlMap[5]) {
+		} else if (kc == getControl(5)) {
 			static int heyOhLetsGo = 0;
 
 			//edown = true;
@@ -176,7 +176,7 @@ void PlayerSystem::receive(const KeyDownEvent &kde)
 			}
 		}
 	} else if (kc == SDLK_DELETE) {
-		gameRunning = false;
+		game::endGame();
 	} else if (kc == SDLK_t) {
 		game::time::tick(50);
 	}
diff --git a/src/render.cpp b/src/render.cpp
index 0c522f6..908b620 100644
--- a/src/render.cpp
+++ b/src/render.cpp
@@ -62,5 +62,4 @@ void drawRect(vec2 ll, vec2 ur, float z)
     currentShader->disable();
 }
 
-
 }
diff --git a/src/ui.cpp b/src/ui.cpp
index a7e3777..f7cc099 100644
--- a/src/ui.cpp
+++ b/src/ui.cpp
@@ -6,6 +6,7 @@
 #include <gametime.hpp>
 
 #include <render.hpp>
+#include <engine.hpp>
 #include <events.hpp>
 
 extern Menu* currentMenu;
@@ -21,12 +22,6 @@ extern World  *currentWorld;
 extern World  *currentWorldToLeft;
 extern World  *currentWorldToRight;
 
-/**
- * In the case of dialog, some NPC quests can be preloaded so that they aren't assigned until
- * the dialog box closes. Reference variables for that here.
- */
-extern std::vector<NPC *> aipreload;
-
 /**
  * Pressing ESC or closing the window will set this to false.
  */
@@ -1214,9 +1209,9 @@ namespace ui {
 	void quitGame() {
 		dialogBoxExists = false;
 		currentMenu = NULL;
-		gameRunning = false;
 		game::config::update();
 		game::config::save();
+		game::endGame();
 	}
 
 	void closeBox() {
@@ -1281,16 +1276,6 @@ EXIT:
 		}
 	}
 
-	void handleEvents(void) {
-		// Flush preloaded AI functions if necessary
-		if (!dialogBoxExists) {
-			while (!aipreload.empty()) {
-				aipreload.front()->addAIFunc(false);
-				aipreload.erase(std::begin(aipreload));
-			}
-		}
-	}
-
 	void drawFade(void) {
 		auto SCREEN_WIDTH = game::SCREEN_WIDTH;
 		auto SCREEN_HEIGHT = game::SCREEN_HEIGHT;
@@ -1474,7 +1459,7 @@ void InputSystem::update(entityx::EntityManager &en, entityx::EventManager &ev,
 
 		// escape - quit game
 		case SDL_QUIT:
-			gameRunning = false;
+			game::endGame();
 			break;
 
 		// mouse movement - update mouse vector
diff --git a/src/ui_menu.cpp b/src/ui_menu.cpp
index f0299c6..4b4b05c 100644
--- a/src/ui_menu.cpp
+++ b/src/ui_menu.cpp
@@ -1,5 +1,6 @@
 #include <ui_menu.hpp>
 
+#include <engine.hpp>
 #include <render.hpp>
 
 #include <fstream>
@@ -211,7 +212,7 @@ namespace ui {
             while(SDL_PollEvent(&e)) {
                 switch (e.type) {
                 case SDL_QUIT:
-                    gameRunning = false;
+                    game::endGame();
                     return;
                     break;
                 case SDL_MOUSEMOTION:
diff --git a/src/world.cpp b/src/world.cpp
index 3871867..320c1db 100644
--- a/src/world.cpp
+++ b/src/world.cpp
@@ -236,11 +236,8 @@ generate(int width)
 	weather = WorldWeather::None;
 }
 
-/**
- * The world draw function.
- * This function will draw the background layers, entities, and player to the
- * screen.
- */
+static Color ambient;
+
 void World::drawBackgrounds(void)
 {
     auto SCREEN_WIDTH = game::SCREEN_WIDTH;
@@ -515,6 +512,22 @@ void World::draw(Player *p)
 	uint lpIndex = 0;
 	uint lcIndex = 0;
 
+	static bool ambientUpdaterStarted = false;
+	if (!ambientUpdaterStarted) {
+		ambientUpdaterStarted = true;
+		std::thread([&](void) {
+			while (true) {
+				float v = 75 * sin((game::time::getTickCount() + (DAY_CYCLE / 2)) / (DAY_CYCLE / PI));
+				float rg = std::clamp(.5f + (-v / 100.0f), 0.01f, .9f);
+				float b  = std::clamp(.5f + (-v / 80.0f), 0.03f, .9f);
+
+				ambient = Color(rg, rg, b, 1.0f);
+
+				std::this_thread::sleep_for(std::chrono::milliseconds(1));
+			}
+		}).detach();
+	}
+
 	for (uint i = 0; i < ls; i++) {
        	auto &l = light[i];
  	   	if (l.belongsTo) {
diff --git a/xml/!town.xml b/xml/!town.xml
index b105127..2627e82 100644
--- a/xml/!town.xml
+++ b/xml/!town.xml
@@ -4,8 +4,8 @@
     <generation type="Random" width="1600"/>
     <time>6000</time>
     <spawnx>-300</spawnx>
-    <npc name="Sanc" hasDialog="true" health="1" x="406.31683" y="72.998955" dindex="9999"/>
-    <npc name="Bob" hasDialog="true" spawnx="30" health="1" x="403.52261" y="72.998955" dindex="0"/>
+    <npc name="Sanc" hasDialog="true" health="1" x="735" y="66.199104" dindex="9999"/>
+    <npc name="Bob" hasDialog="true" spawnx="30" health="1" x="460.8461" y="67.399094" dindex="0"/>
     <structure type="1" spawnx="300" alive="1"/>
     <structure inside="bobshouse.xml" type="1" spawnx="10" alive="1"/>
     <chest alive="1"/>
-- 
cgit v1.2.3