Browse Source

Initial commit

POSITIVE-MENTAL-ATTITUDE 9 years ago
parent
commit
f8363e9843
81 changed files with 3816 additions and 0 deletions
  1. 64 0
      Assets.cpp
  2. 48 0
      Assets.hpp
  3. 139 0
      Bullet.cpp
  4. 50 0
      Bullet.hpp
  5. 110 0
      Camera.cpp
  6. 50 0
      Camera.hpp
  7. 63 0
      Collidable.cpp
  8. 49 0
      Collidable.hpp
  9. 1 0
      Context.cpp
  10. 38 0
      Context.hpp
  11. 155 0
      Foreground.cpp
  12. 55 0
      Foreground.hpp
  13. 65 0
      GuiButton.cpp
  14. 29 0
      GuiButton.hpp
  15. 82 0
      GuiTriangle.cpp
  16. 35 0
      GuiTriangle.hpp
  17. 191 0
      IngameState.cpp
  18. 50 0
      IngameState.hpp
  19. 68 0
      Input.cpp
  20. 30 0
      Input.hpp
  21. 32 0
      InputMap.hpp
  22. 73 0
      InputTarget.hpp
  23. 307 0
      MenuState.cpp
  24. 56 0
      MenuState.hpp
  25. 243 0
      Player.cpp
  26. 68 0
      Player.hpp
  27. 39 0
      State.cpp
  28. 51 0
      State.hpp
  29. 88 0
      Triangle.cpp
  30. 51 0
      Triangle.hpp
  31. 382 0
      Triangles.cpp
  32. 103 0
      Triangles.hpp
  33. 151 0
      TrianglesWindow.cpp
  34. 61 0
      TrianglesWindow.hpp
  35. 80 0
      Utility.cpp
  36. 108 0
      Utility.hpp
  37. BIN
      data/audio/Bomb.ogg
  38. BIN
      data/audio/GlassHit.ogg
  39. BIN
      data/audio/GlassRekt.ogg
  40. BIN
      data/audio/Mario.ogg
  41. BIN
      data/audio/Past the Edge.ogg
  42. BIN
      data/audio/Shotgun.ogg
  43. 32 0
      data/audio/about.txt
  44. BIN
      data/background/75pxjitter03.png
  45. BIN
      data/background/Background.png
  46. BIN
      data/background/Foreground.png
  47. BIN
      data/background/Light.png
  48. BIN
      data/background/Wasted.png
  49. 7 0
      data/background/about.txt
  50. 8 0
      data/credits.txt
  51. 4 0
      data/hints.txt
  52. BIN
      data/hitbox/Bullet.png
  53. BIN
      data/hitbox/Player.png
  54. 10 0
      data/light/lightOverShapeShader.frag
  55. 5 0
      data/light/lightOverShapeShader.vert
  56. BIN
      data/light/penumbraTexture.png
  57. 12 0
      data/light/unshadowShader.frag
  58. 7 0
      data/light/unshadowShader.vert
  59. BIN
      data/menu/Back.png
  60. BIN
      data/menu/Logo.png
  61. BIN
      data/menu/Play.png
  62. BIN
      data/menu/Quit.png
  63. BIN
      data/menu/Settings.png
  64. BIN
      data/shaders/noise.jpg
  65. 67 0
      data/shaders/normalmap.fs
  66. 46 0
      data/shaders/normalmap.vs
  67. 1 0
      data/shaders/old_pi_blur/about.txt
  68. 25 0
      data/shaders/old_pi_blur/blur-h.frag
  69. 25 0
      data/shaders/old_pi_blur/blur-v.frag
  70. 19 0
      data/shaders/old_pi_blur/blur.vert
  71. 26 0
      data/shaders/uniforms
  72. 19 0
      data/shaders/water.frag
  73. 12 0
      data/shaders/water.vs
  74. BIN
      data/triangle/Texture-Reference-UNUSED.png
  75. BIN
      data/triangle/Texture.png
  76. 96 0
      data/ttf/canonical/LICENCE.txt
  77. BIN
      data/ttf/canonical/Ubuntu-L.ttf
  78. 41 0
      data/ttf/soria/SIL Open Font License.txt
  79. BIN
      data/ttf/soria/soria-font.ttf
  80. 51 0
      main.cpp
  81. 38 0
      statCounter.hpp

+ 64 - 0
Assets.cpp

@@ -0,0 +1,64 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Assets.hpp"
+#include "Utility.hpp"
+
+Assets::~Assets()
+{
+	textures.clear();
+	sounds.clear();
+	fonts.clear();
+}
+		
+sf::Texture& Assets::loadTexture(std::string file)
+{
+	if(!textures.count(file))
+	{
+		sf::Texture texture;
+		if(!texture.loadFromFile(file))
+			Echo::out(Echo::Error, "Unable to load texture file: ", file, "\nExpect a segfault!");
+		texture.setSmooth(true);
+		textures.emplace(file, texture);
+	}
+	return textures.at(file);
+}
+
+sf::SoundBuffer& Assets::loadSound(std::string file)
+{
+	if(!sounds.count(file))
+	{
+		sf::SoundBuffer sound;
+		if(!sound.loadFromFile(file))
+			Echo::out(Echo::Error, "Unable to load sound file: ", file, "\nExpect a segfault!");
+		sound.loadFromFile(file);
+		sounds.emplace(file, sound);
+	}
+	return sounds.at(file);
+}
+
+sf::Font& Assets::loadFont(std::string file)
+{
+	if(!fonts.count(file))
+	{
+		sf::Font font;
+		if(!font.loadFromFile(file))
+			Echo::out(Echo::Error, "Unable to load font file: ", file, "\nExpect a segfault!");
+		fonts.emplace(file, font);
+	}
+	return fonts.at(file);
+}

+ 48 - 0
Assets.hpp

@@ -0,0 +1,48 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+ 
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include <SFML/Audio.hpp>
+#include <unordered_map>
+
+/**
+ * @class Assets
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Assets.hpp
+ * @brief Your standard asset manager. Does not work properly and leaks memory when switching gamestates. Meh.
+ */
+class Assets
+{
+	public:
+		Assets(const Assets& copy) = delete;
+		Assets& operator=(const Assets&) = delete;
+		
+		Assets() = default;
+		~Assets();
+		
+		sf::Texture& loadTexture(std::string file);
+		sf::SoundBuffer& loadSound(std::string file);
+		sf::Font& loadFont(std::string file);
+
+	private:
+		std::unordered_map<std::string, sf::Texture> textures;
+		std::unordered_map<std::string, sf::SoundBuffer> sounds;
+		std::unordered_map<std::string, sf::Font> fonts;
+};

+ 139 - 0
Bullet.cpp

@@ -0,0 +1,139 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Bullet.hpp"
+#include "Utility.hpp"
+#include "cmath"
+
+Bullet::Bullet(float rotation, sf::Vector2f initialPosition, sf::SoundBuffer& buffer1, sf::SoundBuffer& buffer2, Foreground* foreground): _texture(nullptr), _foreground(foreground), _hit(false), _dead(false)
+{
+	setRotation(rotation);
+	setPosition(initialPosition);
+	_vertex[0].position = sf::Vector2f(-3.f, 40.f);
+	_vertex[1].position = sf::Vector2f(3.f, 40.f);
+	_vertex[2].position = sf::Vector2f(3.f, -40.f);
+	_vertex[3].position = sf::Vector2f(-3.f, -40.f);
+	_sound.setBuffer(buffer1);
+	_explosion.setBuffer(buffer2);
+	_sound.play();
+	genCollisionBox("data/hitbox/Bullet.png", sf::Vector2f(3.f, 40.f));
+}
+
+void Bullet::setTexture(sf::Texture& texture)
+{
+	_texture = &texture;
+	sf::Vector2u size = texture.getSize();
+	_vertex[0].texCoords = sf::Vector2f(0, size.y);
+	_vertex[1].texCoords = sf::Vector2f(size.x, size.y);
+	_vertex[2].texCoords = sf::Vector2f(size.x / 2.f, 0);
+	_vertex[3].texCoords = sf::Vector2f(size.x / 2.f, size.y / 2.f);
+}
+
+void Bullet::draw(sf::RenderTarget& target, sf::RenderStates states) const
+{
+	if(_hit)
+		return;
+
+	states.transform *= getTransform();
+
+	if(_texture != nullptr)
+		states.texture = (_texture);
+    
+	target.draw(_vertex, 4, sf::Quads, states);
+}
+#include "iostream"
+void Bullet::destroyPixels(int& k, int x, int y)
+{
+	int plus = 1;
+	int dir = 0;
+	int curr = 0;
+	bool q[4] = {false, false, false, false};
+	while(k <= 6400) {
+		if(_foreground->destroy(x, y))
+		{
+			q[dir] = true;
+			++k;
+		}
+		switch(dir)
+		{
+			case 0: ++x; break;
+			case 1: ++y; break;
+			case 2: --x; break;
+			case 3: --y; break;
+			default: break;
+		}
+		++curr;
+		if(curr >= plus)
+		{
+			curr = 0;
+			++dir;
+			if(dir == 4)
+			{
+				dir = 0;
+				/*
+				 * This prevents destroying remote islands of foreground.
+				 */
+				bool exit = true;
+				for(int i = 0; i < 4; ++i)
+				{
+					if(q[i])
+						exit = false;
+					q[i] = false;
+				}
+				if(exit)
+					break;
+			}
+			if(dir == 0 or dir == 2)
+				++plus;
+		}
+	}
+}
+
+void Bullet::update(sf::Time delta)
+{
+	float seconds = delta.asSeconds();
+	const double degree = 3.14159265358 / 180.;
+	sf::Vector2f movement;
+	movement.x = std::sin(getRotation() * degree) * 1080.f * seconds;
+	movement.y = -std::cos(getRotation() * degree) * 1080.f * seconds;
+	move(movement);
+	
+	Collidable::updateTransform(getTransform());
+	if(!_hit)
+	{
+		int k = pixelPerfect(*_foreground);
+		if(k != -1)
+			{
+				sf::Vector2i vec = (sf::Vector2i)getTransform().transformPoint(_cmap[k]);
+				int n = vec.x, m = vec.y;
+				int k = 0;
+				destroyPixels(k, n, m);
+				_foreground->reloadFromPixel(vec.x, vec.y, true);
+				_explosion.play();
+				_hit = true;
+			}
+	}
+	if(_hit and _sound.getStatus() == sf::Sound::Status::Stopped and _explosion.getStatus() == sf::Sound::Status::Stopped)
+	{
+		_dead = true;
+	}
+}
+
+const bool Bullet::isDead() const
+{
+	return _dead;
+}

+ 50 - 0
Bullet.hpp

@@ -0,0 +1,50 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include <SFML/Audio.hpp>
+#include "Collidable.hpp"
+
+/**
+ * @class Bullet
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Bullet.hpp
+ * @brief When created, it moves forward until it crashes with a wall.
+ */
+class Bullet: public sf::Drawable, public sf::Transformable, public Collidable
+{
+	public:
+		Bullet() = delete;
+		Bullet(float rotation, sf::Vector2f initialPosition, sf::SoundBuffer& buffer1, sf::SoundBuffer& buffer2, Foreground* foreground);
+		void setTexture(sf::Texture& texture);
+		void update(sf::Time delta);
+		const bool isDead() const;
+	private:
+		sf::Vertex _vertex[4];
+		const sf::Texture* _texture;
+		virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
+		void destroyPixels(int& k, int x, int y);
+		sf::Sound _sound;
+		sf::Sound _explosion;
+		Foreground* _foreground;
+		bool _hit;
+		bool _dead;
+};
+

+ 110 - 0
Camera.cpp

@@ -0,0 +1,110 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Camera.hpp"
+
+Camera::Camera(): _offset(0, 0), _twilightViewport(0, 0), _view()
+{
+	
+}
+
+void Camera::setTwilightViewport(const sf::Vector2f twilightViewport)
+{
+	_twilightViewport = twilightViewport;
+}
+
+void Camera::setTwilightViewport(float x, float y)
+{
+	_twilightViewport = sf::Vector2f(x, y);
+}
+
+void Camera::setSize(const sf::Vector2f size)
+{
+	_view.setSize(size);
+}
+void Camera::setSize(float x, float y)
+{
+	_view.setSize(x, y);
+}
+void Camera::setCenter(const sf::Vector2f center)
+{
+	_view.setCenter(center);
+	if(center.x - _view.getSize().x / 2.f <= 0)
+		_view.setCenter(_view.getSize().x / 2.f, _view.getCenter().y);
+	if(center.y - _view.getSize().y / 2.f <= 0)
+		_view.setCenter(_view.getCenter().x, _view.getSize().y / 2.f);
+	_offset = center - _view.getCenter();
+}
+void Camera::setCenter(float x, float y)
+{
+	setCenter(sf::Vector2f(x, y));
+}
+
+void Camera::move(const sf::Vector2f& offset)
+{	
+	bool offsetX = false, offsetY = false;
+
+	if(offset.x < 0)
+	{
+		if(offset.x + _offset.x > -_twilightViewport.x)
+			offsetX = true;
+		if(_view.getCenter().x - _view.getSize().x / 2.f + offset.x <= 0)
+			offsetX = true;
+	}
+	else
+	{
+		if(offset.x + _offset.x < _twilightViewport.x)
+			offsetX = true;
+	}
+		
+	if(offset.y < 0)
+	{
+		if(offset.y + _offset.y > -_twilightViewport.y)
+			offsetY = true;
+		if(_view.getCenter().y - _view.getSize().y / 2.f + offset.y <= 0)
+			offsetY = true;
+	}
+	else
+	{
+		if(offset.y + _offset.y < _twilightViewport.y)
+			offsetY = true;
+	}
+	
+	_offset.x += offset.x * offsetX;
+	_offset.y += offset.y * offsetY;
+	_view.move(offset.x * !offsetX, offset.y * !offsetY);
+}
+
+void Camera::move(float offsetX, float offsetY)
+{
+	move(sf::Vector2f(offsetX, offsetY));
+}
+
+const sf::Vector2f Camera::getSize() const
+{
+	return _view.getSize();
+}
+
+const sf::Vector2f Camera::getCenter() const
+{
+	return _view.getCenter();
+}
+
+const sf::View* Camera::getView() const
+{
+	return &_view;
+}

+ 50 - 0
Camera.hpp

@@ -0,0 +1,50 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+
+/**
+ * @class Camera
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Camera.hpp
+ * @brief A comfy wrapper around sf::View handling this Mario camera, or Twilight camera as I call it.
+ */
+class Camera
+{
+	public:
+		Camera();
+		void setTwilightViewport(const sf::Vector2f twilightViewport);
+		void setTwilightViewport(float x, float y);
+		void setSize(const sf::Vector2f size);
+		void setSize(float x, float y);
+		void setCenter(const sf::Vector2f center);
+		void setCenter(float x, float y);
+		void move(const sf::Vector2f& offset);
+		void move(float offsetX, float offsetY);
+		const sf::Vector2f getSize() const;
+		const sf::Vector2f getCenter() const;
+		const sf::View* getView() const;
+	
+	private:
+		sf::Vector2f _offset;
+		sf::Vector2f _twilightViewport;
+		sf::View _view;
+};
+

+ 63 - 0
Collidable.cpp

@@ -0,0 +1,63 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Collidable.hpp"
+
+Collidable::~Collidable()
+{
+	_cmap.clear();
+	_cbanned.clear();
+}
+
+void Collidable::genCollisionBox(std::string file, sf::Vector2f size)
+{
+	sf::Image img;
+	img.loadFromFile(file);
+	_cmap.reserve((img.getSize().x * img.getSize().y) / 10);
+	_cbanned.reserve((img.getSize().x * img.getSize().y) / 2);
+	for(unsigned int i = 0; i < img.getSize().x; ++i)
+		for(unsigned int j = 0; j < img.getSize().y; ++j)
+			if(img.getPixel(i, j).r == 255)
+				_cmap.push_back(sf::Vector2f(int(i - size.x), int(j - size.y)));
+}
+
+void Collidable::setPointCount(unsigned pointCount)
+{
+	_pointCount = pointCount;
+}
+
+void Collidable::updateTransform(sf::Transform transform)
+{
+	_transform = transform;
+}
+
+int Collidable::pixelPerfect(Foreground& foreground)
+{
+	unsigned tileSize = foreground.getTileSize();
+
+	for(unsigned k = 0; k < _cmap.size(); ++k)
+	{
+		sf::Vector2i vec = (sf::Vector2i)_transform.transformPoint(_cmap[k]);
+		if(vec.x < 0) vec.x = 0;
+		if(vec.y < 0) vec.y = 0;
+		if(vec.x >= (signed int)foreground.getSize().x) vec.x = foreground.getSize().x - 1;
+		if(vec.y >= (signed int)foreground.getSize().y) vec.y = foreground.getSize().y - 1;
+		if(foreground.tileAlpha(vec.x / tileSize, vec.y / tileSize, vec.x % tileSize, vec.y % tileSize) > 0)
+			return k;
+	}
+	return -1;
+}

+ 49 - 0
Collidable.hpp

@@ -0,0 +1,49 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include "Foreground.hpp"
+
+/**
+ * @class Collidable
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Collidable.hpp
+ * @brief In theory this enables pixel perfect collision with the Foreground.
+ * @see Foreground
+ */
+class Collidable
+{
+	public:
+		Collidable() = default;
+		virtual ~Collidable();
+		void updateTransform(sf::Transform transform);
+		int pixelPerfect(Foreground& foreground);
+		
+	protected:
+		void genCollisionBox(std::string file, sf::Vector2f size);
+		void setPointCount(unsigned pointCount);
+		std::vector<sf::Vector2f> _cmap; /// Current to-be-destroyed points on the edges
+		std::vector<sf::Vector2f> _cbanned; /// Already destroyed points
+		unsigned _pointCount;
+		float _durability; // Normalized value
+
+	private:
+		sf::Transform _transform;
+};

+ 1 - 0
Context.cpp

@@ -0,0 +1 @@
+#include "Context.hpp"

+ 38 - 0
Context.hpp

@@ -0,0 +1,38 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+class Triangles;
+class TrianglesWindow;
+class Assets;
+class Foreground;
+
+struct Context
+{
+	bool shaders;
+	bool fullscreen;
+	bool vsync;
+	bool needsUpdate;
+	bool running;
+	
+	Assets* assets;
+	TrianglesWindow* window;
+	Triangles* core;
+	
+	Foreground* foreground;
+};

+ 155 - 0
Foreground.cpp

@@ -0,0 +1,155 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Foreground.hpp"
+#include <iostream>
+#include <cmath>
+
+Foreground::~Foreground()
+{
+	for(unsigned i = 0; i < _sprites.size(); ++i)
+		_sprites[i].clear();
+	_sprites.clear();
+	_tiles.clear();
+}
+
+sf::Vector2u Foreground::getSize()
+{
+	return {_tileCount.x * _tileSize, _tileCount.y * _tileSize};
+}
+
+unsigned const Foreground::tileAlpha(unsigned x, unsigned y, unsigned pixelX, unsigned pixelY)
+{
+	return _tiles[y * _tileCount.x + x].first.getPixel(pixelX, pixelY).a;
+}
+ 
+void Foreground::reloadFromTile(int x, int y, bool neighbouring)
+{
+	if(x < 0 or y < 0 or x >= (int)_tileCount.x or y >= (int)_tileCount.y)
+		return;
+		
+	if(neighbouring)
+	{
+		for(int i = -1; i <= 1; ++i)
+		{
+			if(x + i < 0 or x + i >= (int)_tileCount.x) 
+				continue;
+			for(int j = -1; j <= 1; ++j)
+			{
+				if(y + j < 0 or y + j >= (int)_tileCount.y) 
+					continue;
+				unsigned id = (y + j) * _tileCount.x + (x + i);
+				_tiles[id].second.loadFromImage(_tiles[id].first);
+			}
+		}
+	}
+	else
+	{
+		unsigned id = y * _tileCount.x + x;
+		_tiles[id].second.loadFromImage(_tiles[id].first);
+	}
+}
+
+void Foreground::reloadFromPixel(int x, int y, bool neighbouring)
+{
+	reloadFromTile((x / _tileSize), (y / _tileSize), neighbouring);
+}
+
+bool Foreground::destroy(unsigned x, unsigned y)
+{
+	if((x / _tileSize) < 0 or (y / _tileSize) < 0 or (x / _tileSize) >= _tileCount.x or (y / _tileSize) >= _tileCount.y)
+		return true;
+
+	unsigned id = ((y / _tileSize) * _tileCount.x) + (x / _tileSize);
+	x %= _tileSize;
+	y %= _tileSize;
+	int a = _tiles[id].first.getPixel(x, y).a;
+	if(a > 0)
+	{
+		_tiles[id].first.setPixel(x, y, sf::Color::Transparent);
+		return true;
+	}
+	else
+		return false;
+}
+
+const unsigned Foreground::getTileSize() const
+{
+	return _tileSize;
+}
+
+void Foreground::create(sf::Image& foreground, sf::Image& map)
+{
+	_tileSize = 512;
+	_position.x = 0; _position.y = 0;
+	_tileCount.x = std::ceil(static_cast<float>(map.getSize().x) / _tileSize);
+	_tileCount.y = std::ceil(static_cast<float>(map.getSize().y) / _tileSize);
+	_tiles.clear();
+	_tiles.resize(_tileCount.x * _tileCount.y);
+	_sprites.resize(_tileCount.x);
+	for(unsigned i = 0; i < _tileCount.x; ++i)
+		_sprites[i].resize(_tileCount.y);
+	for(unsigned i = 0; i < _tiles.size(); ++i)
+		std::get<0>(_tiles[i]).create(_tileSize, _tileSize, sf::Color::Yellow);
+	for(unsigned i = 0; i < _tileCount.x * _tileSize; ++i)
+		for(unsigned j = 0; j < _tileCount.y * _tileSize; ++j)
+		{
+			sf::Color color = foreground.getPixel(i % foreground.getSize().x, j % foreground.getSize().y);
+			std::get<0>(_tiles[j / _tileSize * _tileCount.x + i / _tileSize])
+			.setPixel(i % _tileSize, j % _tileSize, sf::Color(color.r, color.g, color.b, i < map.getSize().x and j < map.getSize().y ? 255 - map.getPixel(i, j).r : 255));
+		}
+	for(unsigned i = 0; i < _tiles.size(); ++i)
+	{
+		std::get<1>(_tiles[i]).loadFromImage(std::get<0>(_tiles[i]));
+		_sprites[i % _tileCount.x][i / _tileCount.x].setTexture(std::get<1>(_tiles[i]));
+		_sprites[i % _tileCount.x][i / _tileCount.x].setPosition((i % _tileCount.x) * _tileSize, (i / _tileCount.x) * _tileSize);
+		//_sprites[i % _tileCount.x][i / _tileCount.x].setColor(sf::Color(rand() % 256,rand() % 256,rand() % 256, 255)); //Debug
+		//std::get<2>(_tiles[i]).setTexture(std::get<1>(_tiles[i]));
+		//std::get<2>(_tiles[i]).setPosition((i % _tileCount.x) * _tileSize, (i / _tileCount.x) * _tileSize);
+	}
+}
+
+void Foreground::create(std::string foreground, std::string map)
+{
+	sf::Image image1, image2;
+	image1.loadFromFile(foreground);
+	image2.loadFromFile(map);
+	create(image1, image2);
+}
+
+void Foreground::setResolution(sf::Vector2u resolution)
+{
+	sf::Vector2f temp;
+	temp.x = static_cast<float>(resolution.x) / static_cast<float>(_tileSize);
+	temp.y = static_cast<float>(resolution.y) / static_cast<float>(_tileSize);
+	_renderCount = sf::Vector2u(std::ceil(temp.x) + 1, std::ceil(temp.y) + 1);
+}
+
+void Foreground::setPosition(sf::Vector2f position)
+{
+	_position.x = position.x >= 0 ? position.x : 0;
+	_position.y = position.y >= 0 ? position.y : 0;
+}
+
+void Foreground::draw(sf::RenderTarget& target, sf::RenderStates states) const
+{
+	unsigned x = _position.x / _tileSize, y = _position.y / _tileSize;
+	for(unsigned i = 0; i < _renderCount.x; ++i)
+			for(unsigned j = 0; j < _renderCount.y; ++j)
+				if(i + x < _tileCount.x and j + y < _tileCount.y)
+					target.draw(_sprites[i + x][j + y], states);
+}

+ 55 - 0
Foreground.hpp

@@ -0,0 +1,55 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <vector>
+#include <SFML/Graphics.hpp>
+
+/**
+ * @class Foreground
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Foreground.hpp
+ * @brief The destructible foreground.
+ */
+class Foreground: public sf::Drawable
+{
+	public:
+		Foreground() = default;
+		~Foreground();
+		void create(sf::Image& foreground, sf::Image& map);
+		void create(std::string foreground, std::string map);
+		void setResolution(sf::Vector2u resolution);
+		void setPosition(sf::Vector2f position);
+		void reloadFromTile(int x, int y, bool neighbouring = false);
+		void reloadFromPixel(int x, int y, bool neighbouring = false);
+		bool destroy(unsigned x, unsigned y);
+		sf::Vector2u getSize();
+		const unsigned getTileSize() const;
+		unsigned const tileAlpha(unsigned x, unsigned y, unsigned pixelX, unsigned pixelY);
+
+	private:
+		void draw(sf::RenderTarget& target, sf::RenderStates states) const;
+		std::vector<std::pair<sf::Image, sf::Texture>> _tiles;
+		std::vector<std::vector<sf::Sprite>> _sprites;
+		sf::Vector2u _renderCount;
+		sf::Vector2u _tileCount;
+		unsigned _tileSize;
+		sf::Vector2u _position;
+};
+

+ 65 - 0
GuiButton.cpp

@@ -0,0 +1,65 @@
+#include <sstream>
+#include "GuiButton.hpp"
+
+void GuiButton::flip(bool val)
+{
+	_flip = val;
+	setRotation(_flip ? -97.5f : 97.5f);
+	setPosition(getPosition());
+} 
+
+void GuiButton::setCharacterSize(unsigned size)
+{
+	_caption.setCharacterSize(size);
+}
+
+void GuiButton::create(const sf::Font& font, std::string caption, bool* value)
+{
+	_string = caption;
+	_caption.setFont(font); 
+	_caption.setColor(sf::Color::Black);
+	_value = value; 
+	recreate(); 
+}
+
+void GuiButton::setColor(sf::Color color)
+{
+	color.a = (*_value ? 255 : 80);
+	Triangle::setColor(color);
+}
+
+void GuiButton::recreate() 
+{
+	std::ostringstream oss;
+	oss << _string << ':' << (*_value ? "on" : "off");
+	_caption.setString(oss.str());
+}
+
+void GuiButton::click(sf::Vector2i mouseXY)
+{
+	if(mouseIntersection(mouseXY))
+		(*_value) = !(*_value);
+	recreate();
+}
+
+void GuiButton::draw(sf::RenderTarget& target, sf::RenderStates states) const
+{
+	states.transform *= getTransform();
+
+	states.texture = (_texture);
+    
+	target.draw(_vertices, states);
+	target.draw(_caption);
+}
+
+void GuiButton::setPosition(sf::Vector2f position)
+{ 
+	Triangle::setPosition(position);
+	_caption.setPosition(position);
+	_caption.setOrigin(_caption.getLocalBounds().width / 2.f + (_flip ? -60.f : 60.f), _caption.getLocalBounds().height / 2.f + 10.f);
+}
+
+void GuiButton::setPosition(float x, float y)
+{
+	setPosition(sf::Vector2f(x, y));
+}

+ 29 - 0
GuiButton.hpp

@@ -0,0 +1,29 @@
+#include "GuiTriangle.hpp"
+
+/**
+ * @class GuiButton
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Triangle.hpp
+ * @brief Not too much swag in this class tbh.
+ */
+class GuiButton: public GuiTriangle
+{
+	public:
+		GuiButton(): GuiTriangle(), _flip(false) {rotate(97.5f); _value = nullptr;}
+		void create(const sf::Font& font, std::string caption, bool* value);
+		void recreate();
+		void click(sf::Vector2i mouseXY);
+		void flip(bool val);
+		void setPosition(sf::Vector2f position);
+		void setPosition(float x, float y);
+		void setCharacterSize(unsigned size);
+		void setColor(sf::Color color);
+
+	private:
+		void draw(sf::RenderTarget& target, sf::RenderStates states) const;
+		std::string _string;
+		sf::Text _caption;
+		bool* _value;
+		bool _flip;
+};

+ 82 - 0
GuiTriangle.cpp

@@ -0,0 +1,82 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+#include "GuiTriangle.hpp"
+
+GuiTriangle::GuiTriangle(): Triangle(), _scale(1.f)
+{
+	/// A default empty lambda.
+	bind([](){});
+}
+#include <iostream>
+bool GuiTriangle::mouseIntersection(sf::Vector2i mouseXY)
+{	
+	double degree = 3.14159265358 / 180.;
+	sf::Vector2f A = sf::Vector2f(std::sin((0.f + getRotation()) * degree) * getSize() * getScale().x, -std::cos((0.f + getRotation()) * degree) * getSize() * getScale().x) + getPosition(); 
+	sf::Vector2f B = sf::Vector2f(std::sin((165.f + getRotation()) * degree) * getSize() * getScale().x, -std::cos((165.f + getRotation()) * degree) * getSize() * getScale().x) + getPosition(); 
+	sf::Vector2f C = sf::Vector2f(std::sin((195.f + getRotation()) * degree) * getSize() * getScale().x, -std::cos((195.f + getRotation()) * degree) * getSize() * getScale().x) + getPosition(); 
+	bool b0 = ((mouseXY.x - B.x) * (A.y - B.y) - (A.x - B.x) * (mouseXY.y - B.y)) < 0.f;
+	bool b1 = ((mouseXY.x - C.x) * (B.y - C.y) - (B.x - C.x) * (mouseXY.y - C.y)) < 0.f;
+	bool b2 = ((mouseXY.x - A.x) * (C.y - A.y) - (C.x - A.x) * (mouseXY.y - A.y)) < 0.f;
+
+	return (b0 == b1 && b1 == b2);
+}
+
+float GuiTriangle::getWidth()
+{
+	return (std::sin((92.5 * 3.14159265358 / 180.) * getSize() * getScale().x)) - (std::sin((257.5 * 3.14159265358 / 180.)) * getSize() * getScale().x);
+}
+
+void GuiTriangle::click(sf::Vector2i mouseXY)
+{
+	if(mouseIntersection(mouseXY))
+		onClick();
+}
+
+void GuiTriangle::update(sf::Vector2i mouseXY, sf::Time delta)
+{
+	if(mouseIntersection(mouseXY))
+	{
+		if(_scale < 1.2f)
+		{
+			_scale += delta.asSeconds() * (1.25f - _scale) * 2.5f; 
+			setScale(_scale < 1.2f ? _scale : 1.2f, _scale < 1.2f ? _scale : 1.2f);
+		}
+	}
+	else
+	{
+		if(_scale > 1.f)
+		{
+			_scale -= delta.asSeconds() * (1.25f - _scale) * 1.5f;
+			setScale(_scale > 1.f ? _scale : 1.f, _scale > 1.f ? _scale : 1.f);
+		}
+	}
+}
+
+void GuiTriangle::bind(const std::function<void()>& lambda)
+{
+	onClick = lambda;
+}
+
+void GuiTriangle::redraw()
+{
+	double degree = 3.14159265358 / 180.;
+	_vertices[0].position = sf::Vector2f(0, -getSize()); 
+	_vertices[1].position = sf::Vector2f(std::sin(165.f * degree) * getSize(), -std::cos(165.f * degree) * getSize());
+	_vertices[2].position = sf::Vector2f(std::sin(195.f * degree) * getSize(), -std::cos(195.f * degree) * getSize());
+} 

+ 35 - 0
GuiTriangle.hpp

@@ -0,0 +1,35 @@
+#pragma once
+
+#include "Triangle.hpp"
+
+/**
+ * @class GuiTriangle
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 05/05/16
+ * @file Triangle.hpp
+ * @brief All these (not so) fancy main menu triangles are GUI triangles.
+ */
+class GuiTriangle: public Triangle
+{
+	public:
+		GuiTriangle();
+		/**
+		 * @brief Returns the width of the triangle when it stands on its arm (rotated by 92.5°)
+		 */
+		float getWidth();
+		/**
+		 * @brief Checks if the cursor is inside of the triangle
+		 * @return boolean
+		 */
+		bool mouseIntersection(sf::Vector2i mouseXY);
+		void click(sf::Vector2i mouseXY);
+		void update(sf::Vector2i mouseXY, sf::Time delta);
+		void bind(const std::function<void()>& lambda);
+
+	protected:
+		virtual void redraw();
+
+	private:
+		float _scale;
+		std::function<void()> onClick;
+};

+ 191 - 0
IngameState.cpp

@@ -0,0 +1,191 @@
+#include "IngameState.hpp"
+#include "Player.hpp"
+#include <thread>
+
+IngameState::IngameState(): State(), _player(), _paused(false), _blurSize(0.f), _frameAlpha(0.f)
+{
+
+}
+
+
+Rect Player::getBounds()
+{
+	Rect rect;
+	sf::Vector2f A = _vertices[0].position;
+	sf::Vector2f B = _vertices[1].position;
+	sf::Vector2f C = _vertices[2].position;
+	
+	rect.top = std::min({A.y, B.y, C.y});
+	rect.bottom = std::max({A.y, B.y, C.y});
+	rect.left = std::min({A.x, B.x, C.x});
+	rect.right = std::max({A.x, B.x, C.x});
+	
+	return rect;
+}
+
+void IngameState::init()
+{
+	_background.setTexture(_context->assets->loadTexture("data/background/Background.png"));
+	_context->assets->loadTexture("data/background/Background.png").setRepeated(true);
+
+	_player.setContext(_context);
+	_player.setTexture(_context->assets->loadTexture("data/triangle/Texture.png"));
+	_player.setPosition(400.f, 400.f);
+	
+	_foreground.create("data/background/Foreground.png", "data/background/75pxjitter03.png");
+	_context->foreground = &_foreground;
+	
+	_pauseFrame.setFillColor(sf::Color(0, 0, 0, 155));
+	_pauseFrame.setPosition(0, 0);
+	
+	/*_unshadowShader.loadFromFile("assets/unshadowShader.frag", sf::Shader::Fragment);
+    _lightOverShapeShader.loadFromFile("assets/lightOverShapeShader.frag", sf::Shader::Fragment);
+    _normalsShader.loadFromFile("assets/normalsShader.frag", sf::Shader::Fragment);
+	_penumbraTexture.loadFromFile("data/light/penumbraTexture.png");
+	_penumbraTexture.setSmooth(true);*/
+	
+	/*_light = std::make_shared<ltbl::LightPointEmission>();
+	_light->_emissionSprite.setOrigin(330.f, 871.f);
+	_light->_emissionSprite.setPosition(0.f, 0.f);
+	_light->_emissionSprite.setTexture(_context->assets->loadTexture("data/background/texture.png"));
+	_light->_emissionSprite.setColor(sf::Color::White);
+	_light->_localCastCenter = sf::Vector2f(0.0f, 0.0f); // This is where the shadows emanate from relative to the sprite*/
+	
+	_blurV.loadFromFile("data/shaders/old_pi_blur/blur.vert", "data/shaders/old_pi_blur/blur-v.frag");
+	_blurH.loadFromFile("data/shaders/old_pi_blur/blur.vert", "data/shaders/old_pi_blur/blur-h.frag");
+	
+	_marioSound.setBuffer(_context->assets->loadSound("data/audio/Mario.ogg"));
+	_wasted.setTexture(_context->assets->loadTexture("data/background/Wasted.png"));
+	
+	// Global window is not initialized yet in this method. If you need _context->window->getSize(), go to refresh() instead.
+	
+	_status = State::Ongoing;
+}
+
+void IngameState::refresh()
+{
+	sf::Vector2u res = _context->window->getSize();
+	_background.setTextureRect(sf::IntRect(0, 0, _context->window->getSize().x, _context->window->getSize().y));
+	_camera.setSize(static_cast<sf::Vector2f>(_context->window->getSize()));
+	_camera.setTwilightViewport(_context->window->getSize().x / 2.f - 240.f/* * (bool) _czyWOgóleTwilightViewportMaByć*/, _context->window->getSize().y / 2.f - 240.f);
+	_camera.setCenter(_player.getPosition());
+	_foreground.setResolution(res);
+	
+	_pauseFrame.setSize((sf::Vector2f)res);
+	
+	_wasted.setScale(_context->window->getSize().x / 1920.f,  _context->window->getSize().y / 1080.f);
+	_wasted.setPosition(0, _context->window->getSize().y);
+	
+	_backgroundH.create(_context->window->getSize().x, _context->window->getSize().y);
+	_backgroundV.create(_context->window->getSize().x, _context->window->getSize().y);
+	
+	//_lightSystem.create(sf::FloatRect{{0.f, 0.f}, {0.f, 0.f}}, {1920, 1080}, _penumbraTexture, _unshadowShader, _lightOverShapeShader, _normalsShader);
+	//_lightSystem.normalsEnabled(true);
+	//_lightSystem.addLight(_light);
+}
+
+void IngameState::coreThink(const sf::Event& event)
+{
+	if(event.type == sf::Event::Closed or (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Escape))
+		_status = State::Menu;
+	if(event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::P and !_player.isDead())
+		_paused = !_paused;
+	_player.think(event);
+}
+void IngameState::coreInput()
+{
+	if(_paused)
+		return;
+
+	_player.think();
+}
+void IngameState::coreUpdate(sf::Time delta)
+{
+	if(_player.isDead())
+	{
+		if(_blurSize < 4.f)
+			_blurSize += delta.asSeconds();
+			
+		/** 
+		 * This will dynamically fade the grey pause foreground from 0 to 100 alpha.
+		 */
+		if(_frameAlpha < 100.f)
+			_frameAlpha += (delta.asSeconds() * 25.f);
+		sf::Color color = _pauseFrame.getFillColor();
+		color.a = (int)_frameAlpha;
+		_pauseFrame.setFillColor(color);
+		
+		_blurH.setParameter("blurSize", _blurSize / _context->window->getSize().x);
+		_blurV.setParameter("blurSize", _blurSize / _context->window->getSize().y);
+		
+		if(_wasted.getPosition().y > 0.f)
+			_wasted.move(0.f, -250.f * delta.asSeconds());
+		else if(_wasted.getPosition().y < 0.f)
+			_wasted.setPosition(0.f, 0.f);
+	}
+
+	if(_paused)
+		return;
+
+	const sf::Vector2f pos = _player.getPosition();
+	_player.update(delta);
+	_camera.move(_player.getPosition() - pos);
+	//_light->_emissionSprite.setPosition(_player.getPosition());
+	//_light->_emissionSprite.setRotation(_player.getRotation());
+	_foreground.setPosition(_camera.getCenter() - _camera.getSize() / 2.f);
+	
+	if(_player.checkCollision(_foreground)) /// True if the target died.
+	{
+		_paused = true;
+		_marioSound.play();
+	}
+}
+void IngameState::coreRender(const bool shaders)
+{
+	if(_player.isDead())
+	{
+	//	_blurH.setParameter("RTScene", backgroundH.getTexture());
+	//	_blurV.setParameter("RTBlurH", backgroundV.getTexture());
+		
+		_background.setTexture(_context->assets->loadTexture("data/background/Background.png"));
+		_backgroundH.draw(_background);
+		_backgroundH.setView(*_camera.getView());
+		_backgroundH.draw(_foreground);
+		_backgroundH.draw(_player);
+		sf::Sprite sprite(_backgroundH.getTexture());
+		sprite.setScale(1.f, -1.f);
+		sprite.setOrigin(0, _context->window->getSize().y);
+		sprite.setPosition(0.f, 0.f);
+		_backgroundV.draw(sprite, &_blurH);
+		sf::Sprite sprite2(_backgroundV.getTexture());
+		sprite2.setScale(1.f, -1.f);
+		sprite2.setOrigin(0, _context->window->getSize().y);
+		sprite2.setPosition(0.f, 0.f);
+		_context->window->draw(sprite2, &_blurH);
+		_context->window->draw(_pauseFrame);
+		_context->window->draw(_wasted);
+	}
+	else
+	{
+	
+	//_lightSystem.normalsTargetClear();
+	_background.setTexture(_context->assets->loadTexture("data/background/Background.png"));
+	_context->window->draw(_background);//, &_normalBackground);
+	//_background.setTexture(_context->assets->loadTexture("data/background/normal.JPG"));
+	//_lightSystem.normalsTargetDraw(_background);
+	//_lightSystem.normalsTargetDraw(_foreground);
+	_context->window->setView(_camera.getView());
+	_context->window->draw(_foreground);
+	//_context->window->draw(_foreground);
+	_context->window->draw(_player);
+	//_lightSystem.normalsTargetDisplay();
+	//_lightSystem.render(*_camera.getView(), _unshadowShader, _lightOverShapeShader, _normalsShader);
+	//sf::Sprite sprite(_lightSystem.getLightingTexture());
+	//sf::RenderStates lightRenderStates;
+    //lightRenderStates.blendMode = sf::BlendMultiply;
+	//_context->window->draw(sprite, lightRenderStates);
+	_context->window->setView();
+	if(_paused)
+		_context->window->draw(_pauseFrame);
+	}
+}

+ 50 - 0
IngameState.hpp

@@ -0,0 +1,50 @@
+#pragma once
+
+#include "State.hpp"
+#include "Player.hpp"
+#include "Foreground.hpp"
+#include "Camera.hpp"
+#include <ltbl/lighting/LightSystem.h>
+
+class IngameState: public State
+{
+	public:
+		IngameState();
+		void init() final;
+		/**
+		* @brief Method casted whenever the game's resolution changes.
+		*/
+		void refresh() final;
+		void coreThink(const sf::Event& event) final;
+		void coreInput() final;
+		void coreUpdate(sf::Time delta) final;
+		void coreRender(const bool shaders) final;
+
+	private:
+		Player _player;
+		Foreground _foreground;
+		Camera _camera;
+		sf::Sprite _background;
+		
+		/*sf::Shader _unshadowShader;
+		sf::Shader _lightOverShapeShader;
+		sf::Shader _normalsShader;
+		sf::Texture _penumbraTexture;
+		ltbl::LightSystem _lightSystem;
+		std::shared_ptr<ltbl::LightPointEmission> _light;*/
+		
+		bool _cameraMode;
+		bool _paused;
+		sf::RectangleShape _pauseFrame;
+		sf::Sprite _wasted;
+		sf::Shader _blurV;
+		sf::Shader _blurH;
+		
+		float _blurSize;
+		float _frameAlpha;
+		
+		sf::Sound _marioSound;
+		sf::RenderTexture _backgroundH;
+		sf::RenderTexture _backgroundV;
+};
+

+ 68 - 0
Input.cpp

@@ -0,0 +1,68 @@
+#include "Input.hpp"
+
+Input::Input(const Input& other): _type(other._type)
+{
+	std::memcpy(&_event, &other._event, sizeof(sf::Event));
+}
+Input& Input::operator=(const Input& other)
+{
+	std::memcpy(&_event, &other._event, sizeof(sf::Event));
+	_type = other._type;
+	return *this;
+}
+Input::Input(const sf::Keyboard::Key& key, int type): _type(type)
+{
+	_event.type = sf::Event::EventType::KeyPressed;
+	_event.key.code = key;
+}
+Input::Input(const sf::Mouse::Button& button, int type): _type(type)
+{
+	_event.type = sf::Event::EventType::MouseButtonPressed;
+	_event.mouseButton.button = button;
+}
+bool Input::test() const
+{
+	switch(_event.type)
+	{
+		case sf::Event::EventType::KeyPressed:
+			if(_type & Type::Pressed)
+				return sf::Keyboard::isKeyPressed(_event.key.code);
+			break;
+		case sf::Event::EventType::MouseButtonPressed:
+			if(_type & Type::Pressed)
+				return sf::Mouse::isButtonPressed(_event.mouseButton.button);
+			break;
+		default:
+			break;
+	}
+	return false;
+}
+bool Input::operator==(const sf::Event& event) const
+{
+	switch(event.type)
+	{
+		case sf::Event::EventType::KeyPressed:
+			if(_type & Type::Pressed and _event.type == sf::Event::EventType::KeyPressed)
+				return event.key.code == _event.key.code;
+			break;
+		case sf::Event::EventType::KeyReleased:
+			if(_type & Type::Released and _event.type == sf::Event::EventType::KeyPressed)
+				return event.key.code == _event.key.code;
+			break;
+		case sf::Event::EventType::MouseButtonPressed:
+			if(_type & Type::Pressed and _event.type == sf::Event::EventType::MouseButtonPressed)
+				return event.mouseButton.button == _event.mouseButton.button;
+			break;
+		case sf::Event::EventType::MouseButtonReleased:
+			if(_type & Type::Released and _event.type == sf::Event::EventType::MouseButtonPressed)
+				return event.mouseButton.button == _event.mouseButton.button;
+			break;
+		default: 
+			break;
+	}
+	return false;
+}
+bool Input::operator==(const Input& other) const
+{
+	return _type == other._type and other == _event;
+}

+ 30 - 0
Input.hpp

@@ -0,0 +1,30 @@
+#pragma once
+
+#include <SFML/Window.hpp>
+#include <cstring>
+
+class Input
+{
+	public:
+		enum Type
+		{
+			RealTime = 1,
+			Pressed = 1 << 1,
+			Released = 1 << 2
+		};
+		Input() = delete;
+		Input(const Input& other);
+		Input& operator=(const Input& other);
+		Input(const sf::Keyboard::Key& key, int type=Type::RealTime|Type::Pressed);
+		Input(const sf::Mouse::Button& button, int type=Type::RealTime|Type::Pressed);
+		
+		bool test() const;
+		
+		bool operator==(const sf::Event& event) const;
+		bool operator==(const Input& other) const;
+	private:
+		template<typename> friend class InputTarget;
+		sf::Event _event;
+		int _type;
+};
+

+ 32 - 0
InputMap.hpp

@@ -0,0 +1,32 @@
+#pragma once
+
+#include <unordered_map>
+#include "Input.hpp"
+
+template<typename T = int>
+class InputMap
+{
+	public:
+		InputMap(const InputMap& copy) = delete;
+		InputMap<T>& operator=(const InputMap<T>& copy) = delete;
+		
+		InputMap() = default;
+
+		
+		void map(const T& key, const Input& input)
+		{
+			_map.emplace(key, input);
+		}
+		void duoMap(const T& key, const Input& input1, const Input& input2)
+		{
+			_map.emplace(key, input1);
+			_map.emplace(-key, input2);
+		}
+		const Input& get(const T& key) const
+		{
+			return _map.at(key);
+		}
+	private:
+		std::unordered_map<T, Input> _map;
+};
+

+ 73 - 0
InputTarget.hpp

@@ -0,0 +1,73 @@
+#pragma once
+
+#include <list>
+#include <functional>
+#include <SFML/Window.hpp>
+#include "InputMap.hpp"
+
+template<typename T = int>
+class InputTarget
+{
+	public:
+		using Func = std::function<void(const sf::Event&)>;
+		InputTarget(const InputTarget<T>&) = delete;
+		InputTarget<T>& operator=(const InputTarget<T>&) = delete;
+		
+		InputTarget(const InputMap<T>& map): _inputMap(map) {}
+		
+		bool think(const sf::Event& event) const
+		{
+			for(auto& pair: _eventsPoll)
+			{
+				if(_inputMap.get(pair.first) == event)
+				{
+					pair.second(event);
+					return true;
+				}
+			}
+			return false;
+		}
+		void think() const
+		{
+			for(auto& pair: _eventsRealTime)
+			{
+				const Input& input = _inputMap.get(pair.first);
+				if(input.test())
+					pair.second(input._event);
+			}
+		}
+		
+		void bind(const T& key, const Func& callback)
+		{
+			const Input& input = _inputMap.get(key);
+			if(input._type & Input::Type::RealTime)
+				_eventsRealTime.emplace_back(key, callback);
+			else
+				_eventsPoll.emplace_back(key, callback);
+		}
+
+		void duoBind(const T& key, const Func& callback)
+		{
+			bind(key, callback);
+			bind(-key, callback);
+		}
+
+		void unbind(const T& key)
+		{
+			auto remove_func = [&key](const std::pair<T,Func>& pair) -> bool
+			{
+				return pair.first == key;
+			};
+
+			if(_inputMap.get(key)._type & Input::Type::RealTime)
+				_eventsRealTime.remove_if(remove_func);
+			else
+				_eventsPoll.remove_if(remove_func);
+		}
+	
+	private:
+		std::list<std::pair<T, Func>> _eventsRealTime;
+		std::list<std::pair<T, Func>> _eventsPoll;
+		
+		const InputMap<T>& _inputMap;
+};

+ 307 - 0
MenuState.cpp

@@ -0,0 +1,307 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <string>
+#include <fstream>
+#include <sstream>
+#include "MenuState.hpp"
+#include "Triangles.hpp"
+
+MenuState::MenuState()
+{
+	
+}
+
+void MenuState::init()
+{
+	for(unsigned int i = 0; i < 30; ++i)
+	{
+		//_crazyTriangles[i].setColor(50, rand() % 50 + 125, rand() % 50 + 200, rand() % 150 + 105);
+		_crazyColors[i][3] = rand() % 150 + 105;
+		_crazyTriangles[i].setSize(rand() % 384 + (rand() % 20 == 0 ? 900 : 300));
+		_crazyTriangles[i].setRotation(rand() % 360);
+	}
+
+	std::ifstream ifs("data/credits.txt");
+	std::ostringstream oss;
+	oss << ifs.rdbuf();
+				
+	_credits.setString(sf::String(oss.str()));
+	_credits.setFont(_context->assets->loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
+	_credits.setColor(sf::Color::Black);
+	
+	_music.openFromFile("data/audio/Past the Edge.ogg");
+	_music.play();
+	
+	_waterSine.loadFromFile("data/shaders/water.vs", sf::Shader::Fragment);
+	_waterMap.create(1920, 1080);
+
+	_waterFrag.loadFromFile("data/shaders/water.frag", sf::Shader::Fragment);
+	_waterFrag.setParameter("heightmap", _waterMap.getTexture());
+	
+	_subStatus = Main;
+	
+	_logo.bind([this]()
+	{
+		_subStatus = Credits;
+		_quit.setTexture(_context->assets->loadTexture("data/menu/Back.png"));
+	});
+	_logo.setRotation(97.5f);
+	_logo.setTexture(_context->assets->loadTexture("data/menu/Logo.png"));
+	
+	_play.bind([this]()
+	{
+		_status = State::Ingame;
+	});
+	_play.setRotation(80.f);
+	_play.setTexture(_context->assets->loadTexture("data/menu/Play.png"));
+	
+	_settings.bind([this]()
+	{ 
+		_subStatus = Settings;
+		_quit.setTexture(_context->assets->loadTexture("data/menu/Back.png"));
+	});
+	_settings.setRotation(260.f);
+	_settings.setTexture(_context->assets->loadTexture("data/menu/Settings.png"));
+	
+	_quit.bind([this]()
+	{
+		switch(_subStatus)
+		{
+			case Main:
+				_context->running = false;
+				break;
+			case Settings:
+				_context->window->recreate();
+				// move on
+			default:
+				_quit.setTexture(_context->assets->loadTexture("data/menu/Quit.png"));
+				_subStatus = Main;
+		}
+	});
+	_quit.setRotation(100.f);
+	_quit.setTexture(_context->assets->loadTexture("data/menu/Quit.png"));
+	
+	_status = State::Ongoing;
+}
+
+void MenuState::refresh()
+{
+	_crazy.create(_context->window->getSize().x, _context->window->getSize().y);
+	
+	for(unsigned int i = 0; i < 30; ++i)
+	{
+		for(unsigned int j = 0; j < 3; ++j)
+			_crazyColors[i][j] = rand() % 50 - 25;
+		_crazyTriangles[i].setPosition(rand() % _context->window->getSize().x, rand() % _context->window->getSize().y);
+	}
+		
+	float u = 1.f; // GUI size essentially; halves when screen size drops below 1024x768.
+	if(_context->window->getSize().x < 1024)
+		u = 0.5f;
+	
+	/*
+	 * SETTINGS
+	 */
+	_shaders.create(_context->assets->loadFont("data/ttf/canonical/Ubuntu-L.ttf"), "Shaders", &_context->shaders);
+	_shaders.setSize(250.f * u);
+	_shaders.setPosition(_context->window->getSize().x / 2.f + 250.f * u, 
+						 _context->window->getSize().y / 2.f - 200.f * u);
+	_shaders.setCharacterSize(40 * u);
+	
+	_fullscreen.create(_context->assets->loadFont("data/ttf/canonical/Ubuntu-L.ttf"), "Fullscreen", &_context->fullscreen);
+	_fullscreen.flip(true);
+	_fullscreen.setSize(250.f * u);
+	_fullscreen.setPosition(_context->window->getSize().x / 2.f - 250.f * u, 
+						 _context->window->getSize().y / 2.f - 200.f * u);
+	_fullscreen.setCharacterSize(40 * u);
+	
+	/*
+	 * CREDITS
+	 */
+	_credits.setCharacterSize(32.f * u);
+	_credits.setPosition(_context->window->getSize().x / 2.f - _credits.getLocalBounds().width / 2.f, _context->window->getSize().y / 2.f - _credits.getLocalBounds().height / 2.f);
+
+	/*
+	 * MAIN MENU
+	 */
+	_logo.setSize(325.f * u);
+	_play.setSize(225.f * u);
+	_settings.setSize(225.f * u);
+	_quit.setSize(175.f * u);
+	_logo.setPosition(_context->window->getSize().x / 2.f - 110.f * u, _context->window->getSize().y / 2.f - 200.f * u);
+	_play.setPosition(_context->window->getSize().x / 2.f + 260.f * u, _context->window->getSize().y / 2.f - 60.f * u);
+	_settings.setPosition(_context->window->getSize().x / 2.f - 260.f * u, _context->window->getSize().y / 2.f + 30.f * u);
+	_quit.setPosition(_context->window->getSize().x / 2.f + 260.f * u, _context->window->getSize().y / 2.f + 260.f * u);
+}
+
+void MenuState::coreThink(const sf::Event& event)
+{
+	if(event.type == sf::Event::MouseButtonPressed and event.mouseButton.button == sf::Mouse::Left)
+	{
+		sf::Vector2i xy = _context->window->getMousePosition();
+		switch(_subStatus)
+		{
+			case Main:
+				_logo.click(xy);
+				_play.click(xy);
+				_settings.click(xy);
+				break;
+			case Settings:
+				_shaders.click(xy);
+				_fullscreen.click(xy);
+				break;
+			case Credits:
+				break;
+		}
+		
+		_quit.click(xy);
+	}
+	if(event.type == sf::Event::Closed or (event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::Escape))
+		_context->running = false;
+}
+void MenuState::coreInput()
+{
+
+}
+void MenuState::coreUpdate(sf::Time delta)
+{
+	float seconds = delta.asSeconds();
+	
+	{
+		static float r = rand() % 256, g = rand() % 256, b = rand() % 256;
+		static int rInc = 1, gInc = 1, bInc = 1;
+		r += (rand() % 40) * seconds * rInc;
+		g += (rand() % 30) * seconds * gInc;
+		b += (rand() % 20) * seconds * bInc;
+		if(r >= 225.f)
+		{
+			r = 225.f;
+			rInc = -1;
+		}
+		if(g >= 225.f)
+		{
+			g = 225.f;
+			gInc = -1;
+		}
+		if(b >= 225.f)
+		{
+			b = 225.f;
+			bInc = -1;
+		}
+		if(r <= 60)
+		{
+			r = 60;
+			rInc = 1;
+		}
+		if(g <= 60)
+		{
+			g = 60;
+			gInc = 1;
+		}
+		if(b <= 60)
+		{
+			b = 60;
+			bInc = 1;
+		}
+		_color = sf::Color(r, g, b);
+		_logo.setColor(_color);
+		_quit.setColor(_color);
+		_settings.setColor(_color);
+		_play.setColor(_color);
+		_shaders.setColor(_color);
+		_fullscreen.setColor(_color);
+		for(unsigned i = 0; i < 30; ++i)
+			_crazyTriangles[i].setColor(sf::Color(r + _crazyColors[i][0] , g + _crazyColors[i][1], b + _crazyColors[i][2], _crazyColors[i][3]));
+	}
+	
+	static bool ongoing = false;
+	static int k = -1;
+	static float d = 0;
+	static float direction = 0.f;
+	static float speed = 0.f;
+	static float r = 0;
+
+	if(!ongoing)
+	{
+		r = rand() % 2 + 1.f;
+		k = rand() % 30;
+		ongoing = true;
+		rand() % 2 == 0 ? direction = 1.f : direction = -1.f;
+		speed = 0.1f;
+	}
+	if(k != -1)
+	{
+		float x = delta.asSeconds() * 60.f * speed * direction;
+		_crazyTriangles[k].rotate(x);
+		d += delta.asSeconds();
+		2 * d < r ? speed += delta.asSeconds() * 3.5f : speed -= delta.asSeconds() * 3.5f;
+		if(d > r)
+		{
+			k = -1;
+			ongoing = false;
+			d = 0;
+		}
+	}
+	
+	static sf::Clock clock;
+	_waterSine.setParameter("time", clock.getElapsedTime().asSeconds());
+	
+	_logo.update(_context->window->getMousePosition(), delta);
+	_play.update(_context->window->getMousePosition(), delta);
+	_settings.update(_context->window->getMousePosition(), delta);
+	_quit.update(_context->window->getMousePosition(), delta);
+}
+
+void MenuState::coreRender(const bool shaders)
+{
+	//_crazy.clear(sf::Color(100, 150, 220));
+	_crazy.clear(_color);
+	for(unsigned int i = 0; i < 30; ++i)
+		_crazy.draw(_crazyTriangles[i]);
+	sf::Sprite crazy(_crazy.getTexture());
+
+	_waterMap.clear();
+	sf::RectangleShape rect(sf::Vector2f(1920.f, 1080.f));
+	if(shaders)
+		_waterMap.draw(rect, &_waterSine);
+	else
+		_waterMap.draw(rect);
+	_waterMap.display();
+
+	if(shaders)
+		_context->window->draw(crazy, &_waterFrag);
+	else
+		_context->window->draw(crazy);
+		
+	switch(_subStatus)
+	{
+		case Main:
+			_context->window->draw(_logo);
+			_context->window->draw(_play);
+			_context->window->draw(_settings);
+			break;
+		case Settings:
+			_context->window->draw(_shaders);
+			_context->window->draw(_fullscreen);
+			break;
+		case Credits:
+			_context->window->draw(_credits);
+			break;
+	}
+	_context->window->draw(_quit);
+}

+ 56 - 0
MenuState.hpp

@@ -0,0 +1,56 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <functional>
+#include "State.hpp"
+#include "Triangle.hpp"
+#include "GuiButton.hpp"
+
+class MenuState: public State
+{
+	public:
+		MenuState();
+		void init() final;
+		void refresh() final;
+		void coreThink(const sf::Event& event) final;
+		void coreInput() final;
+		void coreUpdate(sf::Time delta) final;
+		void coreRender(const bool shaders) final;
+
+	private:
+		enum {
+			Main = 0, Credits, Settings
+		};
+		int _subStatus;
+		Triangle _crazyTriangles[30];
+		int _crazyColors[30][4];
+		GuiTriangle _logo;
+		GuiTriangle _play;
+		GuiTriangle _settings;
+		GuiTriangle _quit;
+		GuiButton _shaders;
+		GuiButton _fullscreen;
+		sf::Text _credits;
+		sf::RenderTexture _crazy;
+		sf::RenderTexture _waterMap;
+		sf::Shader _waterSine;
+		sf::Shader _waterFrag;
+		sf::Music _music;
+		sf::Color _color;
+};

+ 243 - 0
Player.cpp

@@ -0,0 +1,243 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cmath>
+#include "Player.hpp"
+#include "Assets.hpp"
+
+Player::Player(): 
+	Triangle(), 
+	InputTarget(_inputMap), 
+	Collidable(),
+	_movement(0.f), 
+	_movementSpeed(4.f), 
+	_movementMomentum(0.f),
+	_movementAcceleration(10.f),
+	_movementSuppresion(5.f),
+	_rotation(0.f), 
+	_rotationSpeed(0.6f), 
+	_rotationMomentum(0.f),
+	_rotationAcceleration(38.f),
+	_rotationSuppresion(20.f),
+	_hit(false),
+	_dead(false)
+{
+	/**
+	 * Default binding
+	 */
+	_inputMap.duoMap(1, Input(sf::Keyboard::Up), Input(sf::Keyboard::W));
+	_inputMap.duoMap(2, Input(sf::Keyboard::Down), Input(sf::Keyboard::S));
+	_inputMap.duoMap(3, Input(sf::Keyboard::Left), Input(sf::Keyboard::A));
+	_inputMap.duoMap(4, Input(sf::Keyboard::Right), Input(sf::Keyboard::D));
+	_inputMap.map(5, Input(sf::Keyboard::Space));
+	
+	/**
+	 * Input lambdas
+	 */
+	duoBind(1, [this](const sf::Event&)
+	{
+		_movement += 1.f;
+	});
+	duoBind(2, [this](const sf::Event&)
+	{
+		_movement -= 0.8f;
+	});
+	duoBind(3, [this](const sf::Event&)
+	{
+		_rotation -= 1.f;
+	});
+	duoBind(4, [this](const sf::Event&)
+	{
+		_rotation += 1.f;
+	});
+	bind(5, [this](const sf::Event&)
+	{
+		if(_bulletTimer.getElapsedTime().asMilliseconds() > 1500)
+		{
+			// Does not check if _context != nullptr
+			// so please do remember to call setContext().
+			_bullets.emplace_back(getRotation(), getPosition(), 
+								_context->assets->loadSound("data/audio/Shotgun.ogg"), 
+								_context->assets->loadSound("data/audio/Bomb.ogg"),
+								_context->foreground);
+			_bullets.back().setTexture(_innerTexture);
+			_bulletTimer.restart();
+			_movementMomentum -= 3.f;
+		}
+	});
+	
+	/**
+	 * Technical
+	 */
+	
+	genCollisionBox("data/hitbox/Player.png", sf::Vector2f(71.f, 100.f));
+	
+	_bullets.reserve(10);
+	_bulletTimer.restart();
+	
+	_durability = 0.99f; 
+}
+
+void Player::setContext(Context* context)
+{
+	_context = context;
+	_glass.setBuffer(_context->assets->loadSound("data/audio/GlassHit.ogg"));
+	_deathSound.setBuffer(_context->assets->loadSound("data/audio/GlassRekt.ogg"));
+}
+
+bool Player::isDead()
+{
+	return _dead;
+}
+
+void Player::setTexture(sf::Texture& texture)
+{
+	_innerTexture = texture;
+	_image = texture.copyToImage();
+	_texture = &_innerTexture;
+	vertexFit();
+	
+	unsigned pointCount = 0;
+	for(unsigned i = 0; i < _image.getSize().x; ++i)
+		for(unsigned j = 0; j < _image.getSize().y; ++j)
+			if(_image.getPixel(i, j).a > 0)
+				++pointCount;
+	setPointCount(pointCount);
+}
+
+bool Player::checkCollision(Foreground& foreground)
+{
+	updateTransform(getTransform());
+	int k = pixelPerfect(foreground);
+	if(k == -1)
+	{
+		if(_glass.getStatus() != sf::Sound::Status::Playing)
+			_glass.setVolume(0.f);
+		_hit = false;
+		return false;
+	}
+	float i = 10.f;
+	while(k != -1)
+	{
+		sf::Vector2f pos = _cmap[k];
+		sf::Vector2f center = sf::Vector2f(_image.getSize().x / 2.f, 100.f);
+		if(center.x + pos.x >= 0 && center.x + pos.x < _image.getSize().x and center.y + pos.y >= 0 && center.y + pos.y < _image.getSize().y)
+			_image.setPixel(center.x + pos.x, center.y + pos.y, sf::Color(0, 0, 0, 0));
+		for(int i = -1; i <= 1; i++)
+			for(int j = -1; j <= 1; j++)
+			{
+				if(i == 0 && j == 0) 
+					continue;
+				if(pos.x + center.x + i < 0 || pos.y + center.y + j < 0 or pos.x + center.x + i > _image.getSize().x || pos.y + center.y + j > _image.getSize().y)
+					continue;
+				if(_image.getPixel(pos.x + center.x + i, pos.y + center.y + j).a > 0)
+				{
+					sf::Vector2f vec;
+					vec.x = pos.x + i;
+					vec.y = pos.y + j;
+					if(std::find(_cmap.begin(), _cmap.end(), vec) == _cmap.end())
+						if(std::find(_cbanned.begin(), _cbanned.end(), vec) == _cbanned.end())
+							_cmap.push_back(vec);
+				}
+			}
+		_cmap.erase(_cmap.begin() + k);
+		_cbanned.push_back(pos);
+		k = pixelPerfect(foreground);
+		if(i < 100.f) i += 0.1f;
+	}
+	if(i > _glass.getVolume())
+		_glass.setVolume(i);
+	if(_glass.getStatus() != sf::Sound::Status::Playing) _glass.play();
+	
+	_innerTexture.loadFromImage(_image);
+	_hit = true;
+	if(_pointCount * _durability < _cbanned.size())
+	{
+		_dead = true;
+		_deathSound.play();
+		return true;
+	}
+	else
+		return false;
+}
+
+void Player::update(sf::Time delta)
+{
+	float seconds = delta.asSeconds();
+	const double degree = 3.14159265358 / 180.;
+	
+	if(_hit)
+	{
+		if(_movement > 0 || _movementMomentum > 0)
+			_movementMomentum -= seconds * 15.f;
+		if(_movement < 0 || _movementMomentum < 0)
+			_movementMomentum += seconds * 15.f;
+		_movement *= 0.6f;
+	}
+
+	_movementMomentum += _movementAcceleration * seconds * _movement;
+
+	if(_movementMomentum > _movementSuppresion * seconds)
+		_movementMomentum -= _movementSuppresion * seconds;
+	else if(_movementMomentum < -_movementSuppresion * seconds)
+		_movementMomentum += _movementSuppresion * seconds;
+	else
+		_movementMomentum = 0.f;
+
+	sf::Vector2f movement;
+	movement.x = std::sin(getRotation() * degree) * 60.f * _movementSpeed * _movementMomentum * seconds;
+	movement.y = -std::cos(getRotation() * degree) * 60.f * _movementSpeed * _movementMomentum * seconds;
+	move(movement);
+	//px.move(movement);
+
+	_movement = 0.f;
+
+	_rotationMomentum += _rotationAcceleration * seconds * _rotation;
+
+	if(_rotationMomentum > _rotationSuppresion * seconds)
+		_rotationMomentum -= _rotationSuppresion * seconds;
+	else if(_rotationMomentum < -_rotationSuppresion * seconds)
+		_rotationMomentum += _rotationSuppresion * seconds;
+	else
+		_rotationMomentum = 0.f;
+
+	float rotation = 90.f * _rotationSpeed * _rotationMomentum * seconds;
+	rotate(rotation);
+	//px.rotate(rotation);
+
+	_rotation = 0.f;
+	
+	for(unsigned i = 0; i < _bullets.size(); ++i)
+	{
+		_bullets[i].update(delta);
+		if(_bullets[i].isDead())
+			_bullets.erase(_bullets.begin() + i);
+	}
+}
+
+void Player::draw(sf::RenderTarget& target, sf::RenderStates states) const
+{
+	for(unsigned i = 0; i < _bullets.size(); ++i)
+		target.draw(_bullets[i]);
+		
+	states.transform *= getTransform();
+
+	if(_texture != nullptr)
+		states.texture = (_texture);
+    
+	target.draw(_vertices, states);
+}

+ 68 - 0
Player.hpp

@@ -0,0 +1,68 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Audio.hpp>
+#include "Triangle.hpp"
+#include "InputTarget.hpp"
+#include "Utility.hpp"
+#include "Bullet.hpp"
+#include "Collidable.hpp"
+#include "Context.hpp"
+
+/**
+ * @class Player
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Player.hpp
+ * @brief TODO Player class is in fact a class for everything related to a moving triangles.
+ * This has to be moved to an Entity class or something
+ * and leave the camera and input stuff to the Player class.
+ */
+class Player: public Triangle, public InputTarget<int>, public Collidable
+{
+	public:
+		Player();
+		void update(sf::Time delta);
+		void setTexture(sf::Texture& texture) override;
+		/**
+		 * @brief 
+		 * @param foreground
+		 * @return Returns true if the target died. 
+		 */
+		bool checkCollision(Foreground& foreground);
+		bool isDead();
+		Rect getBounds();
+		sf::Image _image;
+		sf::Texture _innerTexture;
+		void setContext(Context* context);
+	private:
+		Context* _context;
+		std::vector<Bullet> _bullets;
+		sf::Clock _bulletTimer;
+		InputMap<int> _inputMap;
+		float _movement, _movementSpeed, _movementMomentum, _movementAcceleration, _movementSuppresion;
+		float _rotation, _rotationSpeed, _rotationMomentum, _rotationAcceleration, _rotationSuppresion;
+		bool _canStrafe;
+		float _strafe;
+		bool _hit;
+		bool _dead;
+		virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const final override;
+		sf::Sound _glass;
+		sf::Sound _deathSound;
+};

+ 39 - 0
State.cpp

@@ -0,0 +1,39 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "State.hpp"
+
+State::State()
+{
+	_status = State::Loading;
+	_context = nullptr;
+}
+
+State::~State()
+{
+	
+}
+
+void State::setContext(Context* context)
+{
+	_context = context;
+}
+
+const int State::status() const
+{
+	return _status;
+}

+ 51 - 0
State.hpp

@@ -0,0 +1,51 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Window.hpp>
+#include "Assets.hpp"
+#include "TrianglesWindow.hpp"
+#include "Context.hpp"
+
+/**
+ * @class State
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file State.hpp
+ * @brief This is an abstract base state class.
+ */
+class State
+{
+	public:
+		enum {
+			Ongoing = -1, Loading = 0, Menu = 1, Ingame = 2, Empty = 3
+		};
+		State();
+		virtual ~State();
+		void setContext(Context* context);
+		virtual void init() = 0;
+		virtual void refresh() = 0;
+		virtual void coreThink(const sf::Event& event) = 0;
+		virtual void coreInput() = 0;
+		virtual void coreUpdate(sf::Time delta) = 0;
+		virtual void coreRender(const bool shaders) = 0;
+		const int status() const;
+	protected:
+		Context* _context;
+		int _status;
+};

+ 88 - 0
Triangle.cpp

@@ -0,0 +1,88 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Triangle.hpp"
+#include <iostream>
+#include <cmath>
+
+Triangle::Triangle(): _texture(nullptr), _size(100.f)
+{
+	_vertices.setPrimitiveType(sf::Triangles);
+	_vertices.resize(3);
+	_vertices[0].color = sf::Color(255, 255, 255, 255);
+	_vertices[1].color = sf::Color(255, 255, 255, 255);
+	_vertices[2].color = sf::Color(255, 255, 255, 255);
+	setPosition(500.f, 300.f);
+	redraw();
+}
+
+float Triangle::getSize()
+{
+	return _size;
+}
+
+void Triangle::setSize(float size)
+{
+	_size = size;
+	redraw();
+}
+
+void Triangle::vertexFit()
+{
+	if(_texture == nullptr)
+		return;
+
+	_vertices[0].texCoords = sf::Vector2f(_texture->getSize().x / 2, 0);
+	_vertices[1].texCoords = sf::Vector2f(_texture->getSize().x, _texture->getSize().y);
+	_vertices[2].texCoords = sf::Vector2f(0, _texture->getSize().y);
+}
+
+void Triangle::setTexture(sf::Texture& texture)
+{
+	_texture = &texture; 
+	vertexFit();
+}
+
+void Triangle::setColor(sf::Color color)
+{
+	_vertices[0].color = color;
+	_vertices[1].color = color;
+	_vertices[2].color = color;
+}
+
+void Triangle::setColor(unsigned r, unsigned g, unsigned b, unsigned a)
+{
+	setColor(sf::Color(r, g, b, a));
+}
+
+void Triangle::redraw()
+{
+	double degree = 3.14159265358 / 180.;
+	_vertices[0].position = sf::Vector2f(0, -_size);
+	_vertices[1].position = sf::Vector2f(std::sin(135.f * degree) * _size, -std::cos(135.f * degree) * _size);
+	_vertices[2].position = sf::Vector2f(std::sin(225.f * degree) * _size, -std::cos(225.f * degree) * _size);
+} 
+
+void Triangle::draw(sf::RenderTarget& target, sf::RenderStates states) const
+{
+	states.transform *= getTransform();
+
+	if(_texture != nullptr)
+		states.texture = (_texture);
+
+	target.draw(_vertices, states);
+}

+ 51 - 0
Triangle.hpp

@@ -0,0 +1,51 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include <functional>
+#include "Foreground.hpp"
+
+/**
+ * @class Triangle
+ * @author POSITIVE MENTAL ATTITUDE 
+ * @date 05/03/16
+ * @file Triangle.hpp
+ * @brief A simple class for 45°/67.5°/67.5° triangles, including the player. Unlike sfml sf::Shapes, it does not support outlines.
+ * Because they are not realy useful here.
+ */
+class Triangle: public sf::Drawable, public sf::Transformable
+{
+	public:
+		Triangle(); 
+		float getSize();
+		void setSize(float size);
+		virtual void setTexture(sf::Texture& texture);
+		void setColor(sf::Color color);
+		void setColor(unsigned r, unsigned g, unsigned b, unsigned a = 255);
+
+	protected:
+		virtual void redraw();
+		void vertexFit();
+		sf::VertexArray _vertices;
+		const sf::Texture* _texture;
+
+	private:
+		virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const override;
+		float _size;
+};

+ 382 - 0
Triangles.cpp

@@ -0,0 +1,382 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <sstream>
+#include <thread>
+#include <fstream>
+#include <iomanip>
+#include "Triangles.hpp"
+#include "IngameState.hpp"
+#include "MenuState.hpp"
+#include "Utility.hpp"
+
+Triangles::Triangles(int argc, char** argv):
+	_returnCode(0),
+	_window("Triangles", sf::Vector2u(640u, 480u)),
+	_current(nullptr),
+	_initialState(State::Menu)
+{
+	_variables.fullscreen = false;
+	_variables.vsync = false;
+	_variables.running = true;
+	_variables.needsUpdate = false;
+	_variables.shaders = true;
+	_variables.window = &_window; 
+	_variables.assets = &_assets;
+	_variables.core = this;
+	_variables.foreground = nullptr;
+	
+	passArguments(argc, argv);
+	
+	if(_variables.running)
+	{
+		init();
+		refresh();
+		load(_initialState);
+	}
+}
+
+void Triangles::init()
+{
+	_context = _variables;
+	_window.setContext(&_context);
+	_window.recreate();
+
+	_fps.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
+	_fps.setCharacterSize(12);
+	_fps.setColor(sf::Color::Black);
+	_fps.setPosition(10, 10);
+	_fps.setString("TRIANGLES");
+	_fpsRect.setPosition(0, 0);
+	_fpsRect.setSize(sf::Vector2f(375.f, 60.f));
+	_fpsRect.setFillColor(sf::Color(255, 255, 255, 120));
+	
+	_loadingText.setString("Loading");
+	_loadingHint.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
+	_loadingText.setFont(_assets.loadFont("data/ttf/canonical/Ubuntu-L.ttf"));
+	_loadingHint.setColor(sf::Color::White);
+	_loadingText.setColor(sf::Color::White);
+	_loadingTriangle.setColor(sf::Color::White);
+	
+	std::ifstream ifs;
+	ifs.open("data/hints.txt");
+	while(ifs.good())
+	{
+		std::string s;
+		std::getline(ifs, s);
+		if(s.size() > 1)
+			_loadingHints.push_back(sf::String(s));
+	}
+	ifs.close();
+		
+	if(_loadingHints.empty())
+		_loadingHints.push_back(sf::String(""));
+}
+
+void Triangles::passArguments(int argc, char** argv)
+{
+	std::string argvStr[argc];
+	for(int i = 0; i < argc; ++i)
+	{
+		argvStr[i] = argv[i];
+		if(argvStr[i].size() < 2)
+			argvStr[i] = "--kaczka";
+	}
+	searchArgument(argc, argvStr, 'f', "fullscreen", [this]()
+	{
+		_variables.fullscreen = true;
+	});
+	searchArgument(argc, argvStr, 'w', "window", [this]()
+	{
+		_variables.fullscreen = false;
+	});
+	searchArgument(argc, argvStr, 'v', "vsync", [this]()
+	{
+		_variables.vsync = true;
+	});
+	searchArgument(argc, argvStr, 's', "skip", [this]()
+	{
+		_initialState = State::Ingame;
+	});
+	searchArgument(argc, argvStr, 'r', "resolution", [this](std::string param)
+	{
+		if(param == "640x480")
+		{
+			std::cerr << "What year is it?\n";
+			_returnCode = 1;
+			_variables.running = false;
+		}
+		std::size_t found = param.rfind('x');
+		if(found == std::string::npos)
+			return;
+		
+		std::string sub = param;
+		std::string sup = param;
+
+		sub.erase(sub.begin() + found, sub.end());
+		sup.erase(sup.begin(), sup.begin() + found + 1);
+		
+		unsigned w, h;
+		
+		std::stringstream ss;
+		ss << sub;
+		ss >> w;
+		ss.str(std::string());
+		ss.clear();
+		ss << sup;
+		ss >> h;
+		
+		_window.setSize(w, h);
+	});
+	searchArgument(argc, argvStr, 'h', "help", [this]()
+	{
+		std::cout << "triangles: triangles [OPTION]..." << std::endl;
+		std::cout << "A pseudogame not meant to be run by sane people." << std::endl << std::endl;
+		std::cout << "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE" << std::endl;
+		std::cout << "This program comes with ABSOLUTELY NO WARRANTY;" << std::endl;
+		std::cout << "This is free software, and you are welcome to redistribute it" << std::endl;
+		std::cout << "under certain conditions; see LICENSE.md" << std::endl << std::endl;
+		std::cout << std::setw(8) << "-f" << " or " << std::setw(16) << "--fullscreen" << "    prevents from playing in windowed mode; try F11" << std::endl;
+		std::cout << std::setw(8) << "-w" << " or " << std::setw(16) << "--window" << "    prevents from playing in fullscreen mode; try F11" << std::endl;
+		std::cout << std::setw(8) << "-v" << " or " << std::setw(16) << "--vsync" << "    prevents from playing without vertical synchronization; try F10" << std::endl;
+		std::cout << std::setw(8) << "-s" << " or " << std::setw(16) << "--skip" << "    prevents from going to the main menu; try Escape" << std::endl;
+		std::cout << std::setw(8) << "-h" << " or " << std::setw(16) << "--help" << "    prevents from running the actual game" << std::endl;
+		std::cout << std::setw(8) << "-r WxH" << " or " << std::setw(16) << "--resolution=WxH" << "    prevents from running at 640x480" << std::endl;
+		_variables.running = false;
+	});
+}
+
+void Triangles::searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function<void()> lambda)
+{
+	for(int i = 1; i < argc; ++i)
+	{
+		if((argvStr[i][0] == '-' and argvStr[i][1] == callShort) or argvStr[i] == "--" + callLong)
+		{
+			lambda();
+			break;
+		}
+	}
+}
+void Triangles::searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function<void(std::string param)> lambda)
+{
+	for(int i = 1; i < argc; ++i)
+	{
+		if(argvStr[i][0] == '-' and argvStr[i][1] == callShort and i < argc - 1)
+		{
+			lambda(argvStr[i + 1]);
+			break;
+		}
+		
+		std::size_t found = argvStr[i].rfind('=');
+		if(found == std::string::npos)
+			continue;
+		
+		std::string sub = argvStr[i];
+		std::string sup = argvStr[i];
+
+		sub.erase(sub.begin() + found, sub.end());
+		sup.erase(sup.begin(), sup.begin() + found + 1);
+		
+		if(sub == "--" + callLong)
+		{
+			lambda(sup);
+			break;
+		}
+	}
+}
+
+void Triangles::refresh()
+{
+	float u = _window.getSize().x >= 1024 ? 1.f : 0.75f;
+	_loadingHint.setCharacterSize(20 * u);
+	_loadingText.setCharacterSize(20 * u);
+	_loadingTriangle.setSize(12.f * u);
+	_loadingTriangle.setPosition(_window.getSize().x - 140.f, _window.getSize().y - (u == 1.f ? 45.f : 50.f));
+	_loadingText.setPosition(_window.getSize().x - 120.f, _window.getSize().y - 60.f);
+}
+
+void Triangles::load(int stateType)
+{
+	if(_current)
+		delete _current;
+	
+	switch(stateType)
+	{
+		case State::Menu:
+			_current = new MenuState;
+			break;
+		case State::Ingame:
+			_current = new IngameState;
+			break;
+		default:
+			return;
+	}
+	
+	_current->setContext(&_context);
+	
+	_window.setActive(false); // Weird behaviour without this.
+	_window.lockFramerate(true);
+	
+	_loadingHint.setString(_loadingHints[rand() % _loadingHints.size()]);
+	_loadingHint.setPosition(_window.getSize().x / 2.f - _loadingHint.getLocalBounds().width / 2.f, _window.getSize().y - 60.f);
+
+	std::thread t1(&Triangles::loadingRender, this);
+	std::thread t2(&State::init, _current);
+	t1.join();
+	t2.join();
+	
+	_window.lockFramerate(false);
+	
+	_current->refresh();
+}
+
+int Triangles::run(unsigned count)
+{
+	sf::Clock clock;
+	sf::Time delta, epsilon = sf::seconds(1.f / 60.f);
+	
+	if(_context.running)
+	{
+		Echo::out(Echo::Empty, "Triangles version -1.0.0.0");
+		Echo::out(Echo::Empty, "Copyright (C) 2016 POSITIVE MENTAL ATTITUDE");
+		Echo::out(Echo::Empty, "This program comes with ABSOLUTELY NO WARRANTY;");
+		Echo::out(Echo::Empty, "This is free software, and you are welcome to redistribute it");
+		Echo::out(Echo::Empty, "under certain conditions; see LICENSE.md");
+		Echo::out(Echo::Info, "This is #", count, " Triangles run on this install.", count >= 666 ? " Thanks for being addicted!" : " I like cookies.");
+	}
+	
+	/**
+	 *  Main loop
+	 */
+	while(_context.running)
+	{
+		if(_context.needsUpdate)
+		{
+			refresh();
+			_current->refresh();
+			_context.needsUpdate = false;
+			_variables = _context;
+		}
+		if(_current->status() != State::Ongoing)
+		{
+			load(_current->status());
+		}
+		
+		coreThink();
+		coreInput();
+		delta = clock.restart();
+		coreFPScalc(delta);
+		while(delta > epsilon)
+		{
+			delta -= epsilon;
+			coreUpdate(epsilon);
+		}
+		coreUpdate(delta);
+		coreRender();
+	}
+	
+	return _returnCode;
+}
+
+void Triangles::coreThink()
+{
+	sf::Event event;
+	while(_window.pollEvent(event))
+	{
+		if(_current->status())
+			_current->coreThink(event);
+		_window.think(event);
+		if(event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::F11)
+			_context.fullscreen = !_context.fullscreen, _variables.fullscreen = _context.fullscreen, _window.recreate();
+		if(event.type == sf::Event::KeyPressed and event.key.code == sf::Keyboard::F10)
+			_context.vsync = !_context.vsync, _variables.vsync = _context.vsync, _window.recreate();
+	}
+}
+
+void Triangles::coreInput()
+{
+	_current->coreInput();
+	_window.think();
+}
+
+void Triangles::coreFPScalc(sf::Time delta)
+{
+	static sf::Clock clock;
+	static float avg = 0.f, avgL = 0.f;
+	static float min = 10000.f, max = 0.f;
+	static float minL = 0.f, maxL = 0.f;
+	static unsigned avgC = 0;
+
+	float curr = 1.f / delta.asSeconds();
+	if(avgC == 0)
+	{
+		avg = curr, avgC = 1;
+		min = curr;
+		max = curr;
+	}
+	else
+	{
+		avg = (avg * avgC + curr) / (avgC + 1);
+		++avgC; // Called explicitly in order to avoid undefined behaviour.
+		min = std::min(min, curr);
+		max = std::max(max, curr);
+	}
+		
+	if(clock.getElapsedTime() >= sf::seconds(1.f))
+	{
+		minL = min;
+		maxL = max;
+		avgL = avg;
+		avgC = 0;
+		clock.restart();
+	}
+		
+	std::ostringstream oss;
+	oss << "Build time: " << __DATE__ << ' ' << __TIME__ << '\n';
+	oss << "Framerate: (min/max/avg/curr): " << minL << '/' << maxL << '/' << avgL << '/' << curr << '\n';
+	oss << "Settings: " << (_variables.vsync ? "vsync " : "") << (_variables.fullscreen ? "fullscreen " : "") << (_variables.shaders ? "shaders " : "") << '\n';
+	_fps.setString(oss.str());
+}
+
+void Triangles::coreUpdate(sf::Time delta)
+{
+	_current->coreUpdate(delta);
+}
+
+void Triangles::loadingRender()
+{
+	_window.setActive(true);
+	while(!_current->status() and _context.running)
+	{
+		_loadingTriangle.rotate(8.f);
+		coreThink();
+		_window.clear();
+		_window.draw(_loadingHint);
+		_window.draw(_loadingText);
+		_window.draw(_loadingTriangle);
+		_window.render();
+	}
+}
+
+void Triangles::coreRender()
+{
+	_window.clear();
+	_current->coreRender(_variables.shaders);
+	_window.draw(_fpsRect);
+	_window.draw(_fps);
+	_window.render();
+}

+ 103 - 0
Triangles.hpp

@@ -0,0 +1,103 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include <vector>
+#include <functional>
+#include "TrianglesWindow.hpp"
+#include "State.hpp"
+#include "Assets.hpp"
+#include "Triangle.hpp"
+
+class Triangles 
+{
+	public:
+		Triangles() = delete;
+		Triangles(int argc, char** argv);
+		int run(unsigned count);
+
+	private:
+		/**
+		 * @brief This speaks for itself.
+		 */
+		void init();
+		/**
+		 * @brief This will redo some of constructor's and init's job, reading the program arguments.
+		 * @param argc Argument count + 1
+		 * @param argv Argument vector
+		 */
+		void passArguments(int argc, char** argv);
+		void searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function<void()> lambda);
+		void searchArgument(int argc, std::string argvStr[], char callShort, std::string callLong, std::function<void(std::string param)> lambda);
+		/**
+		 * @brief Changes gamestate.
+		 * @param stateType The type of state that is about to be loaded (takes flags from the enum of State class).
+		 */
+		void load(int stateType);
+		/**
+		 * @brief Called whenever the screen resolution changes.
+		 */
+		void refresh();
+		/**
+		 * @brief Game's event loop
+		 */
+		void coreThink();
+		/**
+		 * @brief Input outside of the event loop.
+		 */
+		void coreInput();
+		/**
+		 * @brief Handles the top-left fps counter, if it is enabled.
+		 * @param delta Delta time since last update.
+		 */
+		void coreFPScalc(sf::Time delta);
+		/**
+		 * @brief Game logic
+		 * @param delta Delta time since last update.
+		 */
+		void coreUpdate(sf::Time delta);
+		/**
+		 * @brief Renders the stuff of the current gamestate.
+		 */
+		void coreRender();
+		/**
+		 * @brief A temporal loop that renders the loading screen in a seperate thread. Calls coreThink() and renders stuff.
+		 */
+		void loadingRender();
+		
+		sf::Text _loadingText;
+		sf::Text _loadingHint;
+		Triangle _loadingTriangle;
+		std::vector<sf::String> _loadingHints;
+		
+		int _returnCode;
+		
+		TrianglesWindow _window;
+		
+		sf::Text _fps;
+		sf::RectangleShape _fpsRect;
+		
+		State* _current;
+		int _initialState;
+		
+		Context _context; // Intended to be a pointer for States.
+		Context _variables; // Used internally by Triangles class.
+		
+		Assets _assets;
+};

+ 151 - 0
TrianglesWindow.cpp

@@ -0,0 +1,151 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "TrianglesWindow.hpp"
+#include "InputTarget.hpp"
+
+TrianglesWindow::TrianglesWindow(const std::string& title, sf::Vector2u size):
+	InputTarget(_inputMap),
+	_size(size),
+	_title(title)
+{
+	_context = nullptr;
+	_inputMap.map(0, Input(sf::Keyboard::R, Input::Type::Pressed));
+	bind(0, [this](const sf::Event&)
+	{
+		if(_size == sf::Vector2u(640u, 480u))
+			_size = sf::Vector2u(1366u, 768u);
+		else
+			_size = sf::Vector2u(640u, 480u);
+		close();
+		open();
+	});
+}
+
+TrianglesWindow::~TrianglesWindow()
+{
+	close();
+}
+
+void TrianglesWindow::setContext(Context* context)
+{
+	_context = context;
+}
+
+void TrianglesWindow::clear()
+{
+	_window.clear(sf::Color::Black);
+}
+
+void TrianglesWindow::clear(sf::Color color)
+{
+	_window.clear(color);
+}
+
+void TrianglesWindow::render()
+{
+	_window.display();
+}
+
+void TrianglesWindow::recreate()
+{
+	close();
+	open();
+}
+
+void TrianglesWindow::lockFramerate(bool locked)
+{
+	if(_context->vsync)
+		return;
+	if(locked)
+		_window.setFramerateLimit(30);
+	else
+		_window.setFramerateLimit(240);
+}
+
+bool TrianglesWindow::setActive(bool active)
+{
+	return _window.setActive(active);
+}
+
+void TrianglesWindow::setSize(sf::Vector2u& size)
+{
+	_size = size;
+}
+
+void TrianglesWindow::setSize(unsigned width, unsigned height)
+{
+	_size = sf::Vector2u(width, height);
+}
+
+sf::Vector2u TrianglesWindow::getSize()
+{
+	return _window.getSize();
+}
+
+void TrianglesWindow::setView(const sf::View* view)
+{
+	_window.setView(*view);
+}
+
+void TrianglesWindow::setView()
+{
+	_window.setView(_window.getDefaultView());
+}
+
+void TrianglesWindow::draw(sf::Drawable& drawable)
+{
+	_window.draw(drawable);
+}
+void TrianglesWindow::draw(sf::Drawable& drawable, sf::RenderStates states)
+{
+	_window.draw(drawable, states);
+}
+void TrianglesWindow::draw(sf::Drawable& drawable, sf::Shader* shader)
+{
+	_window.draw(drawable, shader);
+}
+
+bool TrianglesWindow::pollEvent(sf::Event& event)
+{
+	return _window.pollEvent(event);
+}
+
+sf::Vector2i TrianglesWindow::getMousePosition()
+{
+	return sf::Mouse::getPosition(_window);
+}
+
+void TrianglesWindow::open()
+{
+	sf::ContextSettings settings;
+	settings.majorVersion = 3;
+	settings.minorVersion = 0;
+	settings.antialiasingLevel = 8;
+	_window.create(sf::VideoMode(_size.x, _size.y), _title, (_context->fullscreen ? sf::Style::Fullscreen : (sf::Style::Titlebar | sf::Style::Close)), settings);
+	_window.setKeyRepeatEnabled(false);
+	if(_context->vsync)
+		_window.setVerticalSyncEnabled(true);
+	if(_context)
+		_context->needsUpdate = true;
+}
+
+void TrianglesWindow::close()
+{
+	if(_window.isOpen())
+		_window.close();
+}

+ 61 - 0
TrianglesWindow.hpp

@@ -0,0 +1,61 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <SFML/Graphics.hpp>
+#include <iostream>
+#include "InputTarget.hpp"
+#include "Context.hpp"
+
+class TrianglesWindow: public InputTarget<int>
+{
+	public:
+		TrianglesWindow() = delete;
+		TrianglesWindow(const std::string& title, sf::Vector2u size);
+		~TrianglesWindow();
+		
+		void clear();
+		void clear(sf::Color color);
+		void render();
+		void recreate();
+		void lockFramerate(bool locked);
+		void setContext(Context* context);
+		bool setActive(bool active);
+		void setSize(sf::Vector2u& size);
+		void setSize(unsigned width, unsigned height);
+		sf::Vector2u getSize();
+		void setView(const sf::View* view);
+		void setView();
+		void draw(sf::Drawable& drawable);
+		void draw(sf::Drawable& drawable, sf::RenderStates states);
+		void draw(sf::Drawable& drawable, sf::Shader* shader);
+		bool pollEvent(sf::Event& event);
+		sf::Vector2i getMousePosition();
+
+	private:
+		void open();
+		void close();
+		
+		Context* _context;
+		InputMap<int> _inputMap;
+		sf::RenderWindow _window;
+		sf::Vector2u _size;
+		std::string _title;
+
+};
+

+ 80 - 0
Utility.cpp

@@ -0,0 +1,80 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <cstdio>
+#include "Utility.hpp"
+
+int Echo::_loglevel = 4;
+
+bool Echo::printType(int order)
+{
+	switch(order)
+	{
+		case Error: 
+			if(_loglevel >= 1)
+			{
+				std::cerr << "Error: ";
+				return true;
+			}
+			return false;
+		case Info: 
+			if(_loglevel >= 2)
+			{
+				std::cout << "PSA: ";
+				return true;
+			}
+			return false;
+		case Load: 
+			if(_loglevel >= 3)
+			{
+				std::cout << "Loaded ";
+				return true;
+			}
+			return false;
+		case Debug:
+			if(_loglevel >= 4)
+			{
+				std::cout << "Debug: ";
+				return true;
+			}
+			return false;
+		case Empty: default: 
+			if(_loglevel >= 1)
+				return true;
+			return false;
+	}
+}
+
+void Echo::helper(bool err, std::wstring out)
+{
+	if(err)
+		std::wcerr << out;
+	else
+		std::wcout << out;
+}
+
+void Echo::out(std::string debug)
+{
+	if(!printType(Debug))
+		std::printf("%s", &debug[0]);
+}
+
+void Echo::out(float debug)
+{
+	if(printType(Debug))
+		std::printf("%f\n", debug);
+}

+ 108 - 0
Utility.hpp

@@ -0,0 +1,108 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#pragma once
+
+#include <iostream>
+
+/**
+ * @class Rect
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Utility.hpp
+ * @brief Helper class for box collision.
+ */
+struct Rect
+{
+	float left, right, top, bottom;
+};
+
+/**
+ * @class Echo
+ * @author POSITIVE MENTAL ATTITUDE
+ * @date 06/09/16
+ * @file Utility.hpp
+ * @brief Helper class for stdout/stderr output.
+ */
+class Echo
+{
+	public:
+		enum
+		{
+			Empty, Info, Load, Debug, Error
+		};
+		
+		Echo() = default;
+		
+		template<typename Type, typename... Args>
+		static void out(int prefix, Type out, Args... args);
+
+		static void out(std::string debug);
+		static void out(float debug);
+		static void setLogLevel(int loglevel);
+
+	private:
+		static void helper(bool err, std::wstring out);
+		
+		template <typename T>
+		static void helper(bool err, T out);
+
+		template<typename Type, typename... Args>
+		static void helper(bool err, const Type out, Args... args);
+
+		static bool printType(int order);
+		
+		static int _loglevel;
+};
+
+template <typename Type>
+void Echo::helper(bool err, Type out)
+{
+	if(err)
+		std::cerr << out;
+	else
+		std::cout << out;
+}
+
+template<typename Type, typename... Args>
+void Echo::helper(bool err, const Type out, Args... args)
+{
+	if(err)
+		std::cerr << out;
+	else
+		std::cout << out;
+
+    helper(err, args...);
+}
+
+template<typename Type, typename... Args>
+void Echo::out(int order, Type out, Args... args)
+{
+	if(!printType(order))
+		return;
+
+	if(order == Error)
+	{
+		helper(true, out, args...);
+		std::cerr << std::endl;
+	}
+	else
+	{
+		helper(false, out, args...);
+		std::cout << std::endl;
+	}
+}

BIN
data/audio/Bomb.ogg


BIN
data/audio/GlassHit.ogg


BIN
data/audio/GlassRekt.ogg


BIN
data/audio/Mario.ogg


BIN
data/audio/Past the Edge.ogg


BIN
data/audio/Shotgun.ogg


+ 32 - 0
data/audio/about.txt

@@ -0,0 +1,32 @@
+Bomb.ogg:
+Bomb Explosion 1 by Mike Koenig
+Licensed under Creative Commons: Attribution 3.0 License
+http://creativecommons.org/licenses/by/3.0/
+Received from soundbible.com/107-Bomb-Explosion-1.html
+
+GlassHit.ogg:
+Glass, Smash, Bottle, G sound by InspectorJ
+Licensed under Creative Commons: Attribution 3.0 License
+http://creativecommons.org/licenses/by/3.0/
+Received from freesound.org/people/InspectorJ/sounds/344270/
+
+GlassRekt.ogg:
+Glass Breaking sound by Mike Koenig
+Licensed under Creative Commons: Attribution 3.0 License
+http://creativecommons.org/licenses/by/3.0/
+Received from soundbible.com/1761-Glass-Breaking.html
+
+Mario.ogg:
+nintendo plz dont murder me
+
+PastTheEdge.ogg:
+Past the Edge by Kevin MacLeod (incompetech.com)
+Licensed under Creative Commons: Attribution 3.0 License
+http://creativecommons.org/licenses/by/3.0/
+Received from incompetech.com
+
+Shotgun.ogg:
+Shotgun Blast sound by Jim Rogers
+Licensed under Creative Commons: Attribution 3.0 License
+http://creativecommons.org/licenses/by/3.0/
+Received from soundbible.com/1919-Shotgun-Blast.html

BIN
data/background/75pxjitter03.png


BIN
data/background/Background.png


BIN
data/background/Foreground.png


BIN
data/background/Light.png


BIN
data/background/Wasted.png


+ 7 - 0
data/background/about.txt

@@ -0,0 +1,7 @@
+Wasted.png:
+GTA V "Wasted" screen by LunicAura106: http://lunicaura106.deviantart.com/ http://fav.me/d7t2en9 
+Used without permission, I hope no one will kill me.
+
+The rest is created by POSITIVE MENTAL ATTITUDE (https://github.com/POSITIVE-MENTAL-ATTITUDE)
+and licensed under Creative Commons: Attribution-ShareAlike 4.0 International License
+http://creativecommons.org/licenses/by-sa/4.0/

+ 8 - 0
data/credits.txt

@@ -0,0 +1,8 @@
+Triangles
+Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 3 of the License.
+
+Allan please add details

+ 4 - 0
data/hints.txt

@@ -0,0 +1,4 @@
+This is a hint. A placeholder.
+Each triangle has 45 degrees between its arms.
+Triangles do not have speed caps.
+[](){}

BIN
data/hitbox/Bullet.png


BIN
data/hitbox/Player.png


+ 10 - 0
data/light/lightOverShapeShader.frag

@@ -0,0 +1,10 @@
+uniform sampler2D emissionTexture;
+
+uniform vec2 targetSizeInv;
+
+void main() {
+	vec2 targetCoords = gl_FragCoord.xy * targetSizeInv;
+	vec4 emissionColor = texture2D(emissionTexture, targetCoords);
+	
+    gl_FragColor = vec4(emissionColor.rgb, 1.0);
+}

+ 5 - 0
data/light/lightOverShapeShader.vert

@@ -0,0 +1,5 @@
+void main() {
+    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+    gl_TexCoord[0] = gl_MultiTexCoord0;
+}

BIN
data/light/penumbraTexture.png


+ 12 - 0
data/light/unshadowShader.frag

@@ -0,0 +1,12 @@
+uniform sampler2D penumbraTexture;
+
+uniform float lightBrightness;
+uniform float darkBrightness;
+
+void main() {
+    float penumbra = texture2D(penumbraTexture, gl_TexCoord[0].xy).x;
+	
+	float shadow = (lightBrightness - darkBrightness) * penumbra + darkBrightness;
+
+    gl_FragColor = vec4(vec3(1.0 - shadow), 1.0);
+}

+ 7 - 0
data/light/unshadowShader.vert

@@ -0,0 +1,7 @@
+void main() {
+    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
+
+    gl_TexCoord[0] = gl_MultiTexCoord0;
+
+    gl_FrontColor = gl_Color;
+}

BIN
data/menu/Back.png


BIN
data/menu/Logo.png


BIN
data/menu/Play.png


BIN
data/menu/Quit.png


BIN
data/menu/Settings.png


BIN
data/shaders/noise.jpg


+ 67 - 0
data/shaders/normalmap.fs

@@ -0,0 +1,67 @@
+#version 120
+//input textures
+uniform sampler2D colourMap;
+uniform sampler2D normalMap;
+uniform sampler2D reflectMap;
+uniform sampler2D specularMap;
+//light properties
+uniform vec3 lightPosition = vec3(0.0);
+uniform vec3 lightColour = vec3(0.6, 0.6, 0.6);
+uniform float lightPower = 0.6;
+uniform vec4 lightSpecColour = vec4(0.6, 0.6, 0.6, 1.0);
+uniform float lightSpecPower = 0.08; //smaller is more glossy
+
+uniform vec2 reflectOffset; //offset the reflection map by sprite coords
+			    //so that the reflection appears static when the sprite moves
+uniform vec2 resolution = vec2(60.0, 102.0); //set this to size of the sprite to which this shader is applied
+uniform bool invertBumpY = true; //some normal maps require inverted Y channel
+
+void main()
+{
+	//sample our diffuse and normal maps
+	vec2 coord = gl_TexCoord[0].xy;
+	vec4 diffuseColour = texture2D(colourMap, coord);
+	vec3 normalColour = texture2D(normalMap, coord).rgb;
+	
+	//get normal value from sample
+	normalColour.g = invertBumpY ? 1.0 - normalColour.g : normalColour.g;
+	vec3 normal = normalize(normalColour * 2.0 - 1.0);
+	
+	//mix reflection map with colour using normal map alpha
+	float blendVal = texture2D(normalMap, coord).a;
+	coord = mod(reflectOffset, resolution) / resolution; //add offset to coord
+	coord.y = 1.0 - coord.y;
+	vec3 reflectColour = texture2D(reflectMap, coord + (normal.rg * 2.0)).rgb; //adding normal distorts reflection
+	diffuseColour.rgb = mix(diffuseColour.rgb, reflectColour, blendVal);
+	
+	//calculate the light vector
+	vec3 lightDir = vec3((lightPosition.xy - gl_FragCoord.xy) / resolution, lightPosition.z);
+	
+	//calculate the colour intensity based on normal and add specular to pixels facing light
+	float colourIntensity = max(dot(normal, normalize(lightDir)), 0.0);
+	vec4 specular = vec4(0.0);
+	vec4 diffuse = vec4(0.0);
+	if(colourIntensity > 0.0)
+	{
+		//vector half way between light and view direction
+		vec3 halfVec = normalize(lightDir + vec3(0.5, 0.5, 0.5)); //fixed 2D view, so view is centred
+		//get specular value from map
+		vec4 specColour = vec4(texture2D(specularMap, gl_TexCoord[0].xy).rgb, 1.0);
+		float specModifier = max(dot(normal, halfVec), 0.0);
+		specular = pow(specModifier, lightSpecPower) * specColour * lightSpecColour;
+		specular.a *= diffuseColour.a;
+		diffuse = lightSpecColour * diffuseColour * colourIntensity;		
+	}
+	
+	//add light colour
+	diffuseColour.rgb += ((lightColour * lightPower) * colourIntensity);
+	diffuseColour.rgb *= diffuseColour.rgb;
+	
+	//output sum
+	gl_FragColor = clamp(specular + diffuse + diffuseColour, 0.0, 1.0);
+}
+
+
+
+
+

+ 46 - 0
data/shaders/normalmap.vs

@@ -0,0 +1,46 @@
+#version 400
+
+layout (location = 0) in vec3 VertexPosition;
+layout (location = 1) in vec3 VertexNormal;
+layout (location = 2) in vec2 VertexTexCoord;
+layout (location = 3) in vec4 VertexTangent;
+
+struct LightInfo {
+  vec4 Position;  // Light position in eye coords.
+  vec3 Intensity; // A,D,S intensity
+};
+uniform LightInfo Light;
+
+out vec3 LightDir;
+out vec2 TexCoord;
+out vec3 ViewDir;
+
+uniform mat4 ModelViewMatrix;
+uniform mat3 NormalMatrix;
+uniform mat4 ProjectionMatrix;
+uniform mat4 MVP;
+
+void main()
+{
+    // Transform normal and tangent to eye space
+    vec3 norm = normalize( NormalMatrix * VertexNormal );
+    vec3 tang = normalize( NormalMatrix * vec3(VertexTangent) );
+    // Compute the binormal
+    vec3 binormal = normalize( cross( norm, tang ) ) * VertexTangent.w;
+
+    // Matrix for transformation to tangent space
+    mat3 toObjectLocal = mat3(
+        tang.x, binormal.x, norm.x,
+        tang.y, binormal.y, norm.y,
+        tang.z, binormal.z, norm.z ) ;
+
+    // Transform light direction and view direction to tangent space
+    vec3 pos = vec3( ModelViewMatrix * vec4(VertexPosition,1.0) );
+    LightDir = normalize( toObjectLocal * (Light.Position.xyz - pos) );
+
+    ViewDir = toObjectLocal * normalize(-pos);
+
+    TexCoord = VertexTexCoord;
+
+    gl_Position = MVP * vec4(VertexPosition,1.0);
+}

+ 1 - 0
data/shaders/old_pi_blur/about.txt

@@ -0,0 +1 @@
+These are gaussian blurs I found in my 2014 project named Pi (Patapon 4 Untitled). To be updated.

+ 25 - 0
data/shaders/old_pi_blur/blur-h.frag

@@ -0,0 +1,25 @@
+#version 130
+
+uniform sampler2D RTScene; // the texture with the scene you want to blur
+varying vec2 vTexCoord;
+
+uniform float blurSize = 1.0 / 400.0; // 1000 bez tekstury
+
+void main(void)
+{
+   vec4 sum = vec4(0.0);
+
+   // blur in y (vertical)
+   // take nine samples, with the distance blurSize between them
+   sum += texture2D(RTScene, vec2(vTexCoord.x - 4.0*blurSize, vTexCoord.y)) * 0.05;
+   sum += texture2D(RTScene, vec2(vTexCoord.x - 3.0*blurSize, vTexCoord.y)) * 0.09;
+   sum += texture2D(RTScene, vec2(vTexCoord.x - 2.0*blurSize, vTexCoord.y)) * 0.12;
+   sum += texture2D(RTScene, vec2(vTexCoord.x - blurSize, vTexCoord.y)) * 0.15;
+   sum += texture2D(RTScene, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
+   sum += texture2D(RTScene, vec2(vTexCoord.x + blurSize, vTexCoord.y)) * 0.15;
+   sum += texture2D(RTScene, vec2(vTexCoord.x + 2.0*blurSize, vTexCoord.y)) * 0.12;
+   sum += texture2D(RTScene, vec2(vTexCoord.x + 3.0*blurSize, vTexCoord.y)) * 0.09;
+   sum += texture2D(RTScene, vec2(vTexCoord.x + 4.0*blurSize, vTexCoord.y)) * 0.05;
+
+   gl_FragColor = sum;
+}

+ 25 - 0
data/shaders/old_pi_blur/blur-v.frag

@@ -0,0 +1,25 @@
+#version 130
+
+uniform sampler2D RTBlurH; // this should hold the texture rendered by the horizontal blur pass
+varying vec2 vTexCoord;
+
+uniform float blurSize = 1.0 / 400.0;
+
+void main(void)
+{
+   vec4 sum = vec4(0.0);
+
+   // blur in y (vertical)
+   // take nine samples, with the distance blurSize between them
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 4.0*blurSize)) * 0.05;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 3.0*blurSize)) * 0.09;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - 2.0*blurSize)) * 0.12;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y - blurSize)) * 0.15;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y)) * 0.16;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + blurSize)) * 0.15;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 2.0*blurSize)) * 0.12;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 3.0*blurSize)) * 0.09;
+   sum += texture2D(RTBlurH, vec2(vTexCoord.x, vTexCoord.y + 4.0*blurSize)) * 0.05;
+
+   gl_FragColor = sum;
+}

+ 19 - 0
data/shaders/old_pi_blur/blur.vert

@@ -0,0 +1,19 @@
+#version 130
+
+varying vec2 vTexCoord;
+
+// remember that you should draw a screen aligned quad
+void main(void)
+{
+   //gl_Position = ftransform();;
+
+   // Clean up inaccuracies
+   vec2 Pos = sign(gl_Vertex.xy) * 2.0;
+
+   Pos.x -= 1.0;
+   Pos.y -= 1.0;
+
+   gl_Position = vec4(Pos, 0.0, 1.0);
+   // Image-space
+   vTexCoord = vec2(Pos.x * 0.5 + 0.5, Pos.y * 0.5 + 0.5);
+}

+ 26 - 0
data/shaders/uniforms

@@ -0,0 +1,26 @@
+uniform sampler2D ColorTex;
+uniform sampler2D NormalMapTex;
+
+struct LightInfo {
+  vec4 Position;  // Light position in eye coords.
+  vec3 Intensity; // A,D,S intensity
+};
+uniform LightInfo Light;
+
+struct MaterialInfo {
+  vec3 Ka;            // Ambient reflectivity
+  vec3 Ks;            // Specular reflectivity
+  float Shininess;    // Specular shininess factor
+};
+uniform MaterialInfo Material;
+
+struct LightInfo {
+  vec4 Position;  // Light position in eye coords.
+  vec3 Intensity; // A,D,S intensity
+};
+uniform LightInfo Light;
+
+uniform mat4 ModelViewMatrix;
+uniform mat3 NormalMatrix;
+uniform mat4 ProjectionMatrix;
+uniform mat4 MVP;

+ 19 - 0
data/shaders/water.frag

@@ -0,0 +1,19 @@
+#version 130
+
+uniform sampler2D texture;
+uniform sampler2D heightmap;
+const vec2 size = vec2(2.0, 0.0);
+const ivec3 offset = ivec3(-1, 0, 1);
+
+void main(void)
+{
+	vec2 coord = gl_TexCoord[0].xy;
+	float s01 = textureOffset(heightmap, coord, offset.xy).x;
+	float s21 = textureOffset(heightmap, coord, offset.zy).x;
+	float s10 = textureOffset(heightmap, coord, offset.yx).x;
+	float s12 = textureOffset(heightmap, coord, offset.yz).x;
+	vec3 va = normalize(vec3(size.xy, s21 - s01));
+	vec3 vb = normalize(vec3(size.yx, s12 - s10));
+	vec2 dudv = cross(va, vb).rg;
+	gl_FragColor = texture2D(texture, coord + dudv * 0.2);
+}

+ 12 - 0
data/shaders/water.vs

@@ -0,0 +1,12 @@
+#version 120
+
+uniform float time = 1.0;
+
+void main(void)
+{
+	const float sinWidth = 0.04;
+	const float sinHeight = 0.02;
+	const float timeMulti = 3.0;
+	float val = (sin(gl_FragCoord.x * sinWidth + time * timeMulti) + sin(gl_FragCoord.y * sinHeight + time * timeMulti)) * 0.3 + 0.5;
+	gl_FragColor = vec4(vec3(val), 1.0);
+}

BIN
data/triangle/Texture-Reference-UNUSED.png


BIN
data/triangle/Texture.png


+ 96 - 0
data/ttf/canonical/LICENCE.txt

@@ -0,0 +1,96 @@
+-------------------------------
+UBUNTU FONT LICENCE Version 1.0
+-------------------------------
+
+PREAMBLE
+This licence allows the licensed fonts to be used, studied, modified and
+redistributed freely. The fonts, including any derivative works, can be
+bundled, embedded, and redistributed provided the terms of this licence
+are met. The fonts and derivatives, however, cannot be released under
+any other licence. The requirement for fonts to remain under this
+licence does not require any document created using the fonts or their
+derivatives to be published under this licence, as long as the primary
+purpose of the document is not to be a vehicle for the distribution of
+the fonts.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this licence and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Original Version" refers to the collection of Font Software components
+as received under this licence.
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to
+a new environment.
+
+"Copyright Holder(s)" refers to all individuals and companies who have a
+copyright ownership of the Font Software.
+
+"Substantially Changed" refers to Modified Versions which can be easily
+identified as dissimilar to the Font Software by users of the Font
+Software comparing the Original Version with the Modified Version.
+
+To "Propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy. Propagation includes copying,
+distribution (with or without modification and with or without charging
+a redistribution fee), making available to the public, and in some
+countries other activities as well.
+
+PERMISSION & CONDITIONS
+This licence does not grant any rights under trademark law and all such
+rights are reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of the Font Software, to propagate the Font Software, subject to
+the below conditions:
+
+1) Each copy of the Font Software must contain the above copyright
+notice and this licence. These can be included either as stand-alone
+text files, human-readable headers or in the appropriate machine-
+readable metadata fields within text or binary files as long as those
+fields can be easily viewed by the user.
+
+2) The font name complies with the following:
+(a) The Original Version must retain its name, unmodified.
+(b) Modified Versions which are Substantially Changed must be renamed to
+avoid use of the name of the Original Version or similar names entirely.
+(c) Modified Versions which are not Substantially Changed must be
+renamed to both (i) retain the name of the Original Version and (ii) add
+additional naming elements to distinguish the Modified Version from the
+Original Version. The name of such Modified Versions must be the name of
+the Original Version, with "derivative X" where X represents the name of
+the new work, appended to that name.
+
+3) The name(s) of the Copyright Holder(s) and any contributor to the
+Font Software shall not be used to promote, endorse or advertise any
+Modified Version, except (i) as required by this licence, (ii) to
+acknowledge the contribution(s) of the Copyright Holder(s) or (iii) with
+their explicit written permission.
+
+4) The Font Software, modified or unmodified, in part or in whole, must
+be distributed entirely under this licence, and must not be distributed
+under any other licence. The requirement for fonts to remain under this
+licence does not affect any document created using the Font Software,
+except any version of the Font Software extracted from a document
+created using the Font Software may only be distributed under this
+licence.
+
+TERMINATION
+This licence becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF
+COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER
+DEALINGS IN THE FONT SOFTWARE.

BIN
data/ttf/canonical/Ubuntu-L.ttf


File diff suppressed because it is too large
+ 41 - 0
data/ttf/soria/SIL Open Font License.txt


BIN
data/ttf/soria/soria-font.ttf


+ 51 - 0
main.cpp

@@ -0,0 +1,51 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "Triangles.hpp"
+#include "statCounter.hpp"
+#include <X11/Xlib.h>
+
+/**
+ * @brief The Triangles class holds everything.
+ * Do not mind the empty lambda separators. They look sweet.
+ * @param argc Argument count
+ * @param argv Argument vector
+ * @return Triangles::run() will return the error code.
+ * @see Triangles
+ */
+int main(int argc, char** argv)
+{
+	/**
+	 * Loading screen should not use the window with two threads, yet there are issues when this is not called.
+	 * Maybe I am a noob and have to create a mutex somewhere.
+	 */
+	#ifdef __linux__
+	XInitThreads();
+	#endif
+	
+	[](){}(); [](){}(); [](){}(); [](){}();
+	
+	std::srand(time(NULL));
+	
+	[](){}(); [](){}(); [](){}(); [](){}();
+	
+	Triangles game(argc, argv);
+	
+	[](){}(); [](){}(); [](){}(); [](){}();
+	
+	return game.run(statCounter());
+}

+ 38 - 0
statCounter.hpp

@@ -0,0 +1,38 @@
+/**
+ *  Triangles
+ *  Copyright (C) 2016 POSITIVE MENTAL ATTITUDE
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 3 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <fstream>
+
+/**
+ * @brief This will open a file, increase its content (a number) by one, close the file and print the number.
+ * AKA how many times was the F9 pressed without errors.
+ * @return The current count. 
+ */
+unsigned statCounter()
+{
+	std::ifstream ifs;
+	std::ofstream ofs;
+	ifs.open("data/counter");
+	unsigned count;
+	ifs >> count;
+	ifs.close();
+	++count;
+	ofs.open("data/counter");
+	ofs << count;
+	ofs.close();
+	return count;
+}