diff --git a/Modding.txt b/Modding.txt
new file mode 100644
index 0000000..a53b484
--- /dev/null
+++ b/Modding.txt
@@ -0,0 +1,171 @@
+Clonepoint loads maps from .tmx files generated by the Tiled Editor. You
+can obtain the editor here.
+
+http://www.mapeditor.org/
+
+Notes on making maps for Clonepoint:
+
+- If you want to test your maps quickly, compile Clonepoint with the
+-DDEBUG flag to allow you to teleport to the mouse's position with
+the middle mouse button.
+
+- Clonepoint considers all files in the data/ directory with the .tmx
+extension as potential maps to load.
+
+- It is highly recommended that you copy one of the existing maps and
+use them as a template to quickly make new ones. template.tmx contains
+all entities you need to copy into other maps as well.
+
+- Don't make a map called template.tmx - Clonepoint discards this as
+a potential map to load.
+
+- Clonepoint only loads entities and collision volumes if they are in
+the correct Object Group.
+
+- There are no "collision" tiles, but collision volumes. To use them,
+create rectangles in the "Collision" object group.
+
+ * To make glass volumes, make a collision volume with the type field as
+ "Glass". Glass volumes can be of any dimension, but the standard I use
+ is 4 pixels (or 0.25 units) for width or height.
+
+ * To make invisible volumes that only block enemies (used to keep them
+ from leaving a building and patrolling the end of the map), create a
+ collision volume with the type field as "GuardBlock".
+
+- To add other entities with graphics, you can use Tile's "Insert Tile (T)"
+feature to add a graphical representation of them in the editor. This is
+for your convenience. Clonepoint's level loading function doesn't care
+about what graphics you choose, but rather what "type" the object is in
+the editor. If an entity you added in the editor is not appearing in the
+game, make sure you wrote in the type for the entity and put it in the
+right object group.
+
+- To add guards, circuit boxes, and stairs, add them in the "Entity" object
+group.
+
+ * Guards have type "Guard". Enforcers have type "Enforcer". Professionals
+ have type "Professional".
+
+ * By default, created enemies face left. To make them face right, add a
+ custom property called "direction" with the value "right".
+
+ * By default, created enemies stand still. To make them patrol, add a
+ custom property called "patrolling" with the value "yes".
+
+- To add stairs, add entities with the type "Stairs" in the "Entity"
+object group.
+
+ * To create stairwells, just create stairs with the same x position.
+
+- To create circuit boxes that unlock a particlar circuit in the map, create
+an entity in the "Entity" object group with the following names in the type
+property.
+
+ * CircuitBoxG - Green
+ * CircuitBoxV - Violet
+ * CircuitBoxY - Yellow
+ * CircuitBoxB - Blue
+
+- To create a spawn point for the player, add an entity with the type "Player"
+in the "Player" object group. Creating multiple spawn points will cause the
+player to be spawned in the last point.
+
+- To add props in the map that serve no gameplay purpose, create them in the
+"Props" object group. There are a few names you can write in the type property
+to get a valid prop.
+
+ * Watercooler
+ * Shelf
+ * Trashcan
+ * Couch
+ * Plant1
+ * Plant2
+ * LightPanel
+
+- To create objectives in the map, add entities with the type "Terminal" in the
+"Objectives" object group.
+
+ * If no Terminals are found, the player can simply go to the exit to complete
+ the map.
+
+- To create the subway entrance where the player can exit the map, create an entity
+with the type "Subway" in the "Objectives" object group.
+
+ * Creating multiple subways will simply spawn one subway in the last location added.
+
+ * If no subways are found, the player may exit the map by going to the right
+ boundary of the map.
+
+- To create linkable objects, create entities in the following object groups
+
+ * RedLinkable - for objects in the Red circuit.
+ * BlueLinkable - for objects in the Blue circuit.
+ * VioletLinkable - for objects in the Violet circuit.
+ * YellowLinkable - for objects in the Yellow circuit.
+ * GreenLinkable - for objects in the Green circuit.
+
+- The following linkable objects may be added by writing in the correct name
+in the "type" property and adding them in the previous object groups.
+
+ * Alarm
+ * SoundDetector
+ * TrapDoor
+ * Elevator
+ + To create elevator shafts, just create Elevators with the same x position.
+ * Switch
+ + To create handscanners, create a switch with a custom property "handscanner" as
+ "yes".
+ * VaultDoor
+ * Socket
+ * Scanner
+ * Door
+ + By default, Doors spawn closed. To make them spawn opened, create a custom property
+ "open" with the value "yes".
+ * SecurityCamera
+ + By default, SecurityCameras spawn facing left. To make them spawn facing right, create
+ a custom property "direction" with the value "right".
+ * LightFixture
+
+- To make LightFixtures control other lights, perform the following actions.
+
+ * Create a entity with the "type" property "Light" in the "Lights" object group.
+
+ * Create a "polyline" (press L) object that goes from a LightFixture to the Light
+ in the "LightLinks" object group. Remember to right click after the link is made to
+ disable polyline creation.
+
+- To make lights that are not controlled by a LightFixture, simply create a light in the
+"Lights" object group without a LightFixture linking to it.
+
+ * By default, Lights emit light from all angles. To limit the number of angles, create
+ the custom property "numangles" with the value in degrees.
+
+- To make linkable objects link to others when the map starts, perform the following action.
+
+ * Create a "polyline" (press L) object that goes from a linkable object to another in
+ the "Links" object group.
+
+ * Only objects in the same circuit will actually be linked when the map starts.
+
+* To make a tilemap of graphics in the map, select the "Tile Layer 1" layer.
+
+ * The data encoding for the tile layer MUST be in CSV format.
+
+ * The "firstgid" property of this tileset MUST be 1. Multiple tilesets for a map
+ are not supported.
+
+* There are other map properties you can set in the "Map Properties" area. They are:
+
+ * startingupgrades - The number of upgrades you want the player to start with when
+ upgrading jump power and time to charge jump.
+
+ * startingenergy - The starting energy the player has to perform certain actions.
+ Currently, the only energy-spending action available is linking between enemy guns.
+ If not set, starting energy is zero.
+
+ * startingammo - The starting ammo the player has for their weapon. If not set, starting
+ ammo is zero.
+
+ * timetosniper - The number of seconds the player has after firing their weapon before
+ the police sniper arrives. If not set, time to sniper is 1 second.
\ No newline at end of file
diff --git a/VeraMono.txt b/VeraMono.txt
new file mode 100644
index 0000000..e651be1
--- /dev/null
+++ b/VeraMono.txt
@@ -0,0 +1,124 @@
+Bitstream Vera Fonts Copyright
+
+The fonts have a generous copyright, allowing derivative works (as
+long as "Bitstream" or "Vera" are not in the names), and full
+redistribution (so long as they are not *sold* by themselves). They
+can be be bundled, redistributed and sold with any software.
+
+The fonts are distributed under the following copyright:
+
+Copyright
+=========
+
+Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream
+Vera is a trademark of Bitstream, Inc.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the fonts accompanying this license ("Fonts") and associated
+documentation files (the "Font Software"), to reproduce and distribute
+the Font Software, including without limitation the rights to use,
+copy, merge, publish, distribute, and/or sell copies of the Font
+Software, and to permit persons to whom the Font Software is furnished
+to do so, subject to the following conditions:
+
+The above copyright and trademark notices and this permission notice
+shall be included in all copies of one or more of the Font Software
+typefaces.
+
+The Font Software may be modified, altered, or added to, and in
+particular the designs of glyphs or characters in the Fonts may be
+modified and additional glyphs or characters may be added to the
+Fonts, only if the fonts are renamed to names not containing either
+the words "Bitstream" or the word "Vera".
+
+This License becomes null and void to the extent applicable to Fonts
+or Font Software that has been modified and is distributed under the
+"Bitstream Vera" names.
+
+The Font Software may be sold as part of a larger software package but
+no copy of one or more of the Font Software typefaces may be sold by
+itself.
+
+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
+BITSTREAM OR THE GNOME FOUNDATION 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.
+
+Except as contained in this notice, the names of Gnome, the Gnome
+Foundation, and Bitstream Inc., shall not be used in advertising or
+otherwise to promote the sale, use or other dealings in this Font
+Software without prior written authorization from the Gnome Foundation
+or Bitstream Inc., respectively. For further information, contact:
+fonts at gnome dot org.
+
+Copyright FAQ
+=============
+
+ 1. I don't understand the resale restriction... What gives?
+
+ Bitstream is giving away these fonts, but wishes to ensure its
+ competitors can't just drop the fonts as is into a font sale system
+ and sell them as is. It seems fair that if Bitstream can't make money
+ from the Bitstream Vera fonts, their competitors should not be able to
+ do so either. You can sell the fonts as part of any software package,
+ however.
+
+ 2. I want to package these fonts separately for distribution and
+ sale as part of a larger software package or system. Can I do so?
+
+ Yes. A RPM or Debian package is a "larger software package" to begin
+ with, and you aren't selling them independently by themselves.
+ See 1. above.
+
+ 3. Are derivative works allowed?
+ Yes!
+
+ 4. Can I change or add to the font(s)?
+ Yes, but you must change the name(s) of the font(s).
+
+ 5. Under what terms are derivative works allowed?
+
+ You must change the name(s) of the fonts. This is to ensure the
+ quality of the fonts, both to protect Bitstream and Gnome. We want to
+ ensure that if an application has opened a font specifically of these
+ names, it gets what it expects (though of course, using fontconfig,
+ substitutions could still could have occurred during font
+ opening). You must include the Bitstream copyright. Additional
+ copyrights can be added, as per copyright law. Happy Font Hacking!
+
+ 6. If I have improvements for Bitstream Vera, is it possible they might get
+ adopted in future versions?
+
+ Yes. The contract between the Gnome Foundation and Bitstream has
+ provisions for working with Bitstream to ensure quality additions to
+ the Bitstream Vera font family. Please contact us if you have such
+ additions. Note, that in general, we will want such additions for the
+ entire family, not just a single font, and that you'll have to keep
+ both Gnome and Jim Lyles, Vera's designer, happy! To make sense to add
+ glyphs to the font, they must be stylistically in keeping with Vera's
+ design. Vera cannot become a "ransom note" font. Jim Lyles will be
+ providing a document describing the design elements used in Vera, as a
+ guide and aid for people interested in contributing to Vera.
+
+ 7. I want to sell a software package that uses these fonts: Can I do so?
+
+ Sure. Bundle the fonts with your software and sell your software
+ with the fonts. That is the intent of the copyright.
+
+ 8. If applications have built the names "Bitstream Vera" into them,
+ can I override this somehow to use fonts of my choosing?
+
+ This depends on exact details of the software. Most open source
+ systems and software (e.g., Gnome, KDE, etc.) are now converting to
+ use fontconfig (see www.fontconfig.org) to handle font configuration,
+ selection and substitution; it has provisions for overriding font
+ names and subsituting alternatives. An example is provided by the
+ supplied local.conf file, which chooses the family Bitstream Vera for
+ "sans", "serif" and "monospace". Other software (e.g., the XFree86
+ core server) has other mechanisms for font substitution.
+
diff --git a/animations.cpp b/animations.cpp
new file mode 100644
index 0000000..947e3b2
--- /dev/null
+++ b/animations.cpp
@@ -0,0 +1,117 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "animations.h"
+
+AnimationManager::AnimationManager()
+{
+ _sequences[ANIM_PLAYER_RUNNING].reset(new AnimationSequence(true));
+ _sequences[ANIM_PLAYER_RUNNING]->readFromFile("./data/animations/player_run.anim");
+
+ _sequences[ANIM_PLAYER_WALKING_AIMING].reset(new AnimationSequence(true));
+ _sequences[ANIM_PLAYER_WALKING_AIMING]->readFromFile("./data/animations/player_walk_aiming.anim");
+
+ _sequences[ANIM_PLAYER_CLIMBING_UP].reset(new AnimationSequence(true));
+ _sequences[ANIM_PLAYER_CLIMBING_UP]->readFromFile("./data/animations/player_climb_up.anim");
+
+ _sequences[ANIM_PLAYER_CEILING].reset(new AnimationSequence(true));
+ _sequences[ANIM_PLAYER_CEILING]->readFromFile("./data/animations/player_ceiling.anim");
+
+ _sequences[ANIM_PLAYER_HACKING].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_HACKING]->readFromFile("./data/animations/player_hack_terminal.anim");
+
+ _sequences[ANIM_PLAYER_ATTACH_DOWN].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_ATTACH_DOWN]->readFromFile("./data/animations/player_attach_down.anim");
+
+ _sequences[ANIM_PLAYER_REACH_ROOF].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_REACH_ROOF]->readFromFile("./data/animations/player_reach_roof.anim");
+
+ _sequences[ANIM_PLAYER_ATTACH_TO_CEILING].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_ATTACH_TO_CEILING]->readFromFile("./data/animations/player_attach_to_ceiling.anim");
+
+ _sequences[ANIM_PLAYER_ATTACH_FROM_CEILING].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_ATTACH_FROM_CEILING]->readFromFile("./data/animations/player_attach_from_ceiling.anim");
+
+ _sequences[ANIM_PLAYER_ENTER_STAIRS].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_ENTER_STAIRS]->readFromFile("./data/animations/player_enter_stairs.anim");
+
+ _sequences[ANIM_PLAYER_EXIT_STAIRS].reset(new AnimationSequence(false));
+ _sequences[ANIM_PLAYER_EXIT_STAIRS]->readFromFile("./data/animations/player_exit_stairs.anim");
+
+ _sequences[ANIM_ENEMY_PATROLLING].reset(new AnimationSequence(true));
+ _sequences[ANIM_ENEMY_PATROLLING]->readFromFile("./data/animations/enemy_patrolling.anim");
+
+ _sequences[ANIM_ENEMY_PLAYER_STRUGGLE].reset(new AnimationSequence(true));
+ _sequences[ANIM_ENEMY_PLAYER_STRUGGLE]->readFromFile("./data/animations/enemy_player_struggle.anim");
+
+ _sequences[ANIM_ENEMY_KNOCK_OUT].reset(new AnimationSequence(false));
+ _sequences[ANIM_ENEMY_KNOCK_OUT]->readFromFile("./data/animations/enemy_punch_out.anim");
+
+ _sequences[ANIM_ENEMY_ENTER_STAIRS].reset(new AnimationSequence(false));
+ _sequences[ANIM_ENEMY_ENTER_STAIRS]->readFromFile("./data/animations/enemy_enter_stairs.anim");
+
+ _sequences[ANIM_ENEMY_EXIT_STAIRS].reset(new AnimationSequence(false));
+ _sequences[ANIM_ENEMY_EXIT_STAIRS]->readFromFile("./data/animations/enemy_exit_stairs.anim");
+
+ _sequences[ANIM_VAULT_OPEN].reset(new AnimationSequence(false));
+ _sequences[ANIM_VAULT_OPEN]->readFromFile("./data/animations/vault_open.anim");
+
+ _sequences[ANIM_VAULT_CLOSE].reset(new AnimationSequence(false));
+ _sequences[ANIM_VAULT_CLOSE]->readFromFile("./data/animations/vault_close.anim");
+
+ _sequences[ANIM_ALARM_ACTIVE].reset(new AnimationSequence(true));
+ _sequences[ANIM_ALARM_ACTIVE]->readFromFile("./data/animations/alarm_active.anim");
+
+ _sequences[ANIM_ELEVATOR_CLOSE].reset(new AnimationSequence(false));
+ _sequences[ANIM_ELEVATOR_CLOSE]->readFromFile("./data/animations/elevator_close.anim");
+
+ _sequences[ANIM_ELEVATOR_OPEN].reset(new AnimationSequence(false));
+ _sequences[ANIM_ELEVATOR_OPEN]->readFromFile("./data/animations/elevator_open.anim");
+}
+
+AnimationManager::~AnimationManager()
+{
+}
+
+AnimationSequence* AnimationManager::getSequence(eAnimations index)
+{
+ return _sequences[index].get();
+}
+
+unsigned int AnimationManager::getNextSprite(AnimationSequence* sequence, unsigned int* index, float* timeLeft, unsigned int dT, bool* finished)
+{
+ *finished = false;
+ *timeLeft -= dT;
+
+ if (*timeLeft <= 0.0f)
+ {
+ *timeLeft = sequence->getMsPerFrame();
+ *index = *index + 1;
+ if (*index >= sequence->getNumSprites())
+ {
+ *index = 0;
+ if (!sequence->isLooping())
+ {
+ *finished = true;
+ }
+ }
+ }
+
+ return sequence->getSpriteAt(*index);
+}
\ No newline at end of file
diff --git a/animations.h b/animations.h
new file mode 100644
index 0000000..f8c5962
--- /dev/null
+++ b/animations.h
@@ -0,0 +1,64 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef ANIMATIONS_H
+#define ANIMATIONS_H
+
+#include
+
+#include "sprite.h"
+
+enum eAnimations
+{
+ ANIM_PLAYER_RUNNING = 0,
+ ANIM_PLAYER_WALKING_AIMING,
+ ANIM_PLAYER_CLIMBING_UP,
+ ANIM_PLAYER_CEILING,
+ ANIM_PLAYER_HACKING,
+ ANIM_PLAYER_ATTACH_DOWN,
+ ANIM_PLAYER_REACH_ROOF,
+ ANIM_PLAYER_ATTACH_TO_CEILING,
+ ANIM_PLAYER_ATTACH_FROM_CEILING,
+ ANIM_PLAYER_ENTER_STAIRS,
+ ANIM_PLAYER_EXIT_STAIRS,
+ ANIM_ENEMY_PATROLLING,
+ ANIM_ENEMY_PLAYER_STRUGGLE,
+ ANIM_ENEMY_KNOCK_OUT,
+ ANIM_ENEMY_ENTER_STAIRS,
+ ANIM_ENEMY_EXIT_STAIRS,
+ ANIM_VAULT_OPEN,
+ ANIM_VAULT_CLOSE,
+ ANIM_ALARM_ACTIVE,
+ ANIM_ELEVATOR_CLOSE,
+ ANIM_ELEVATOR_OPEN,
+ NUM_ANIMATIONS
+};
+
+class AnimationManager
+{
+public:
+ AnimationManager();
+ ~AnimationManager();
+ unsigned int getNextSprite(AnimationSequence* sequence, unsigned int* index, float* timeLeft, unsigned int dT, bool* finished);
+ AnimationSequence* getSequence(eAnimations index);
+private:
+ std::unique_ptr _sequences[NUM_ANIMATIONS];
+};
+
+#endif
\ No newline at end of file
diff --git a/audio.cpp b/audio.cpp
new file mode 100644
index 0000000..9f7dfbc
--- /dev/null
+++ b/audio.cpp
@@ -0,0 +1,183 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "audio.h"
+#include "global.h"
+
+AudioManager::AudioManager()
+{
+ _device = alcOpenDevice(nullptr);
+ _context = alcCreateContext(_device, nullptr);
+ alcMakeContextCurrent(_context);
+
+ ALCint maxmono = 0, maxstereo = 0;
+ alcGetIntegerv(_device, ALC_MONO_SOURCES, 1, &maxmono);
+ alcGetIntegerv(_device, ALC_STEREO_SOURCES, 1, &maxstereo);
+
+ ALuint src;
+ for (unsigned int i = 0; i < MAX_SOURCES; i++)
+ {
+ src = 0;
+ alGenSources(1, &src);
+ LOGF((stdout, "Pushing source %i into buffer.\n", src));
+ _loadedSources.push_back(src);
+ }
+
+ loadWAV("./data/sounds/alarm.wav");
+ loadWAV("./data/sounds/door_open.wav");
+ loadWAV("./data/sounds/door_close.wav");
+ loadWAV("./data/sounds/circuitbox.wav");
+ loadWAV("./data/sounds/pistol.wav");
+ loadWAV("./data/sounds/pistol_ready.wav");
+ loadWAV("./data/sounds/menu_click.wav");
+ loadWAV("./data/sounds/switch.wav");
+ loadWAV("./data/sounds/link.wav");
+ loadWAV("./data/sounds/jump.wav");
+ loadWAV("./data/sounds/enter_crosslink.wav");
+ loadWAV("./data/sounds/exit_crosslink.wav");
+ loadWAV("./data/sounds/punch1.wav");
+ loadWAV("./data/sounds/punch2.wav");
+ loadWAV("./data/sounds/glass_break.wav");
+ loadWAV("./data/sounds/elevator_arrive.wav");
+ loadWAV("./data/sounds/elevator_leave.wav");
+ loadWAV("./data/sounds/elevator_decelerate.wav");
+}
+
+AudioManager::~AudioManager()
+{
+ for(auto& elem : _loadedSources)
+ alDeleteSources(1, &elem);
+
+ _loadedSources.clear();
+
+ std::map::iterator it;
+
+ for (it = _loadedBuffers.begin(); it != _loadedBuffers.end(); it++)
+ {
+ ALuint buf = it->second;
+ LOGF((stdout, "Deleting OpenAL buffer %i.\n", buf));
+ alDeleteBuffers(1, &buf);
+ }
+
+ alcMakeContextCurrent(nullptr);
+ if (_context) alcDestroyContext(_context);
+ if (_device) alcCloseDevice(_device);
+}
+
+void AudioManager::playSound(std::string filename)
+{
+ ALuint src = getNextAvailableSource();
+ ALuint buf = getBuffer(filename);
+
+ alSourcei(src, AL_BUFFER, buf);
+ alSource3f(src, AL_POSITION, 0, 0, 0);
+ alSourcePlay(src);
+}
+
+void AudioManager::playSound3D(const char* filename, float x, float y, float z)
+{
+ ALuint src = getNextAvailableSource();
+ ALuint buf = getBuffer(filename);
+
+ alSourcei(src, AL_BUFFER, buf);
+ alSource3f(src, AL_POSITION, x, y, z);
+ alSourcePlay(src);
+}
+
+void AudioManager::loadWAV(std::string filename)
+{
+ SDL_AudioSpec wav_spec;
+ Uint32 wav_length = 0;
+ Uint8* wav_buffer;
+
+ if (SDL_LoadWAV(filename.c_str(), &wav_spec, &wav_buffer, &wav_length) != nullptr)
+ {
+ ALenum format;
+ switch(wav_spec.format)
+ {
+ case AUDIO_U8:
+ case AUDIO_S8:
+ format = wav_spec.channels == 2 ? AL_FORMAT_STEREO8 : AL_FORMAT_MONO8;
+ break;
+ case AUDIO_U16:
+ case AUDIO_S16:
+ format = wav_spec.channels == 2 ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
+ break;
+ default:
+ LOGF((stderr, "Wave file %s is of unknown format!\n", filename.c_str()));
+ SDL_FreeWAV(wav_buffer);
+ return;
+ break;
+ }
+
+ ALuint buf;
+
+ alGenBuffers(1, &buf);
+ alBufferData(buf, format, wav_buffer, wav_length, wav_spec.freq);
+ SDL_FreeWAV(wav_buffer);
+
+ int slash = filename.find_last_of("/");
+ int dot = filename.find_last_of(".");
+
+ std::string bufferString = filename.substr(slash + 1, dot - slash - 1);
+ LOGF((stdout, "Storing %s into buffer %i.\n", bufferString.c_str(), buf));
+ _loadedBuffers.insert(std::pair(bufferString, buf));
+ }
+ else
+ {
+ LOGF((stderr, "Couldn't load wave file %s\n", filename.c_str()));
+ LOGF((stderr, "%s\n", SDL_GetError()));
+ }
+}
+
+ALuint AudioManager::getBuffer(std::string filename)
+{
+ auto it = _loadedBuffers.find(filename);
+ if (it == _loadedBuffers.end())
+ {
+ LOGF((stderr, "Failed to find buffer for %s\n", filename.c_str()));
+ return 0;
+ }
+
+ return it->second;
+}
+
+ALuint AudioManager::getNextAvailableSource()
+{
+ int status;
+ for (auto& elem : _loadedSources)
+ {
+ alGetSourcei(elem, AL_SOURCE_STATE, &status);
+ if (status != AL_PLAYING)
+ {
+ return elem;
+ }
+ }
+
+ //all sounds are currently playing!
+ return 0;
+}
+
+void AudioManager::setVolume(float volume)
+{
+ for (auto& elem : _loadedSources)
+ {
+ alSourcef(elem, AL_GAIN, volume);
+ }
+}
\ No newline at end of file
diff --git a/audio.h b/audio.h
new file mode 100644
index 0000000..c737357
--- /dev/null
+++ b/audio.h
@@ -0,0 +1,50 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef AUDIO_H
+#define AUDIO_H
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#define MAX_SOURCES 10
+
+class AudioManager
+{
+public:
+ AudioManager();
+ ~AudioManager();
+ void playSound(std::string filename);
+ void playSound3D(const char* filename, float x, float y, float z);
+ void loadWAV(std::string filename);
+ ALuint getBuffer(std::string filename);
+ ALuint getNextAvailableSource();
+ void setVolume(float volume);
+private:
+ std::map _loadedBuffers;
+ std::vector _loadedSources;
+ ALCdevice* _device;
+ ALCcontext* _context;
+};
+
+#endif
diff --git a/bindings.cpp b/bindings.cpp
new file mode 100644
index 0000000..d48f953
--- /dev/null
+++ b/bindings.cpp
@@ -0,0 +1,289 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "bindings.h"
+#include "global.h"
+
+BindingsManager::BindingsManager()
+{
+
+}
+
+BindingsManager::~BindingsManager()
+{
+}
+
+eBinding BindingsManager::checkToken(std::string token)
+{
+ if (token == "Bind_MoveRight") return Bind_MoveRight;
+ if (token == "Bind_MoveLeft") return Bind_MoveLeft;
+ if (token == "Bind_MoveUp") return Bind_MoveUp;
+ if (token == "Bind_MoveDown") return Bind_MoveDown;
+ if (token == "Bind_ToggleCrosslink") return Bind_ToggleCrosslink;
+ return Bind_Nothing;
+}
+
+std::string BindingsManager::bindingToString(eBinding binding)
+{
+ switch (binding)
+ {
+ case Bind_MoveRight:
+ return "Bind_MoveRight";
+ case Bind_MoveLeft:
+ return "Bind_MoveLeft";
+ case Bind_MoveUp:
+ return "Bind_MoveUp";
+ case Bind_MoveDown:
+ return "Bind_MoveDown";
+ case Bind_ToggleCrosslink:
+ return "Bind_ToggleCrosslink";
+ default:
+ return "";
+ }
+}
+
+SDL_Keycode BindingsManager::getKeyFromToken(char* token)
+{
+ if (!strcmp(token, "A")) return SDLK_a;
+ if (!strcmp(token, "B")) return SDLK_b;
+ if (!strcmp(token, "C")) return SDLK_c;
+ if (!strcmp(token, "D")) return SDLK_d;
+ if (!strcmp(token, "E")) return SDLK_e;
+ if (!strcmp(token, "F")) return SDLK_f;
+ if (!strcmp(token, "G")) return SDLK_g;
+ if (!strcmp(token, "H")) return SDLK_h;
+ if (!strcmp(token, "I")) return SDLK_i;
+ if (!strcmp(token, "J")) return SDLK_j;
+ if (!strcmp(token, "K")) return SDLK_k;
+ if (!strcmp(token, "L")) return SDLK_l;
+ if (!strcmp(token, "M")) return SDLK_m;
+ if (!strcmp(token, "N")) return SDLK_n;
+ if (!strcmp(token, "O")) return SDLK_o;
+ if (!strcmp(token, "P")) return SDLK_p;
+ if (!strcmp(token, "Q")) return SDLK_q;
+ if (!strcmp(token, "R")) return SDLK_r;
+ if (!strcmp(token, "S")) return SDLK_s;
+ if (!strcmp(token, "T")) return SDLK_t;
+ if (!strcmp(token, "U")) return SDLK_u;
+ if (!strcmp(token, "V")) return SDLK_v;
+ if (!strcmp(token, "W")) return SDLK_w;
+ if (!strcmp(token, "X")) return SDLK_x;
+ if (!strcmp(token, "Y")) return SDLK_y;
+ if (!strcmp(token, "Z")) return SDLK_z;
+ if (!strcmp(token, "Left")) return SDLK_LEFT;
+ if (!strcmp(token, "Right")) return SDLK_RIGHT;
+ if (!strcmp(token, "Up")) return SDLK_UP;
+ if (!strcmp(token, "Down")) return SDLK_DOWN;
+ if (!strcmp(token, "RCtrl")) return SDLK_RCTRL;
+ if (!strcmp(token, "RAlt")) return SDLK_RALT;
+ if (!strcmp(token, "LCtrl")) return SDLK_LCTRL;
+ if (!strcmp(token, "LAlt")) return SDLK_LALT;
+
+ return SDLK_CLEAR;
+}
+
+std::string BindingsManager::getKeyAsString(SDL_Keycode key)
+{
+ switch(key)
+ {
+ case SDLK_a:
+ return "A";
+ case SDLK_b:
+ return "B";
+ case SDLK_c:
+ return "C";
+ case SDLK_d:
+ return "D";
+ case SDLK_e:
+ return "E";
+ case SDLK_f:
+ return "F";
+ case SDLK_g:
+ return "G";
+ case SDLK_h:
+ return "H";
+ case SDLK_i:
+ return "I";
+ case SDLK_j:
+ return "J";
+ case SDLK_k:
+ return "K";
+ case SDLK_l:
+ return "L";
+ case SDLK_m:
+ return "M";
+ case SDLK_n:
+ return "N";
+ case SDLK_o:
+ return "O";
+ case SDLK_p:
+ return "P";
+ case SDLK_q:
+ return "Q";
+ case SDLK_r:
+ return "R";
+ case SDLK_s:
+ return "S";
+ case SDLK_t:
+ return "T";
+ case SDLK_u:
+ return "U";
+ case SDLK_v:
+ return "V";
+ case SDLK_w:
+ return "W";
+ case SDLK_x:
+ return "X";
+ case SDLK_y:
+ return "Y";
+ case SDLK_z:
+ return "Z";
+ case SDLK_LEFT:
+ return "Left";
+ case SDLK_RIGHT:
+ return "Right";
+ case SDLK_UP:
+ return "Up";
+ case SDLK_DOWN:
+ return "Down";
+ case SDLK_RCTRL:
+ return "RCtrl";
+ case SDLK_RALT:
+ return "RAlt";
+ case SDLK_LCTRL:
+ return "LCtrl";
+ case SDLK_LALT:
+ return "LAlt";
+ default:
+ return "NONE";
+ }
+}
+
+eBinding BindingsManager::getBindingFromKey(SDL_Keycode key)
+{
+ auto it = _bindings.find(key);
+
+ if (it != _bindings.end())
+ {
+ return it->second;
+ }
+
+ return Bind_Nothing;
+}
+
+void BindingsManager::loadBindingsFromConfig(const char* filename)
+{
+ char* text = file_read(filename);
+ char* delim = (char*)" =\t\n\r";
+ char* token = strtok(text, delim);
+ eBinding binding;
+
+ while (token)
+ {
+ binding = checkToken(token);
+ token = strtok(nullptr, delim);
+ if (binding != Bind_Nothing)
+ {
+ SDL_Keycode key = getKeyFromToken(token);
+ addBinding(key, binding);
+ }
+
+ if (token)
+ token = strtok(nullptr, delim);
+ }
+
+ delete [] text;
+ delete [] token;
+ text = nullptr;
+ token = nullptr;
+ delim = nullptr;
+}
+
+std::vector BindingsManager::getBindingsToSave()
+{
+ std::vector bindings;
+ std::string line;
+ std::map::iterator it;
+
+ for (it = _bindings.begin(); it != _bindings.end(); it++)
+ {
+ line = bindingToString(it->second) + " = " + getKeyAsString(it->first) + "\n";
+ bindings.push_back(line);
+ }
+
+ return bindings;
+}
+
+void BindingsManager::clearBinding(eBinding binding)
+{
+ std::map::iterator it;
+
+ for (it = _bindings.begin(); it != _bindings.end(); it++)
+ {
+ if (it->second == binding)
+ {
+ _bindings.erase(it);
+ }
+ }
+}
+
+std::string BindingsManager::getKeysBound(eBinding binding)
+{
+ std::string keysBound = "";
+ std::map::iterator it;
+
+ for (it = _bindings.begin(); it != _bindings.end(); it++)
+ {
+ if (it->second == binding)
+ {
+ if (keysBound != "")
+ {
+ keysBound += ";";
+ }
+ keysBound += getKeyAsString(it->first);
+ }
+ }
+ if (keysBound == "")
+ {
+ keysBound = "NONE";
+ }
+
+ return keysBound;
+}
+
+void BindingsManager::addBinding(SDL_Keycode key, eBinding binding)
+{
+ //erase previous keys for this binding first.
+ std::map::iterator it;
+ std::string keyAsString = getKeyAsString(key);
+
+ if (keyAsString == "NONE")
+ {
+ return;
+ }
+ for (it = _bindings.begin(); it != _bindings.end(); it++)
+ {
+ if (it->first == key)
+ {
+ _bindings.erase(it);
+ }
+ }
+
+ _bindings.insert(std::pair(key, binding));
+}
\ No newline at end of file
diff --git a/bindings.h b/bindings.h
new file mode 100644
index 0000000..44de49e
--- /dev/null
+++ b/bindings.h
@@ -0,0 +1,60 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef BINDINGS_H
+#define BINDINGS_H
+#include
+#include
+#include
+#include
+#include
+#include
+#include "file.h"
+#include "global.h"
+
+enum eBinding
+{
+ Bind_Nothing = 0,
+ Bind_MoveLeft,
+ Bind_MoveRight,
+ Bind_MoveUp,
+ Bind_MoveDown,
+ Bind_ToggleCrosslink
+};
+
+class BindingsManager
+{
+public:
+ BindingsManager();
+ ~BindingsManager();
+ void loadBindingsFromConfig(const char* filename);
+ eBinding checkToken(std::string token);
+ SDL_Keycode getKeyFromToken(char* token);
+ std::string getKeyAsString(SDL_Keycode key);
+ eBinding getBindingFromKey(SDL_Keycode key);
+ std::string bindingToString(eBinding binding);
+ std::vector getBindingsToSave();
+ void clearBinding(eBinding binding);
+ std::string getKeysBound(eBinding binding);
+ void addBinding(SDL_Keycode key, eBinding binding);
+private:
+ std::map _bindings;
+};
+
+#endif
\ No newline at end of file
diff --git a/button.cpp b/button.cpp
new file mode 100644
index 0000000..a8d42b1
--- /dev/null
+++ b/button.cpp
@@ -0,0 +1,331 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "button.h"
+
+TextLabel::TextLabel(int x, int y, std::string text, float r, float g, float b)
+{
+ _text = text;
+ _x = x;
+ _y = y;
+ _red = r;
+ _green = g;
+ _blue = b;
+ _alpha = 1.0f;
+ _visible = true;
+}
+
+TextLabel::~TextLabel()
+{
+
+}
+
+std::string& TextLabel::getText()
+{
+ return _text;
+}
+
+void TextLabel::getPosition(int* x, int* y)
+{
+ *x = _x;
+ *y = _y;
+}
+
+void TextLabel::getColors(float* r, float* g, float* b, float* a)
+{
+ *r = _red;
+ *g = _green;
+ *b = _blue;
+ *a = _alpha;
+}
+
+void TextLabel::setText(std::string text)
+{
+ _text = text;
+}
+
+void TextLabel::setPosition(int x, int y)
+{
+ _x = x;
+ _y = y;
+}
+
+void TextLabel::setPositionWithOffset(int x, int y, int xOff, int yOff)
+{
+ setPosition(x, y);
+ _x += xOff;
+ _y += yOff;
+}
+
+void TextLabel::update(unsigned int dT) {}
+
+bool TextLabel::isVisible()
+{
+ return _visible;
+}
+
+void TextLabel::setVisible(bool b)
+{
+ _visible = b;
+}
+
+FloatingMessage::FloatingMessage(int x, int y, std::string text, float r, float g, float b) : TextLabel(x, y, text, r, g, b)
+{
+ _timeRemaining = 0;
+ _setTime = 0;
+
+}
+
+FloatingMessage::~FloatingMessage()
+{
+}
+
+void FloatingMessage::update(unsigned int dT)
+{
+ if (_visible)
+ {
+ _timeRemaining -= dT;
+
+ int diff = _setTime - _timeRemaining;
+
+ if (diff < 250)
+ {
+ _alpha = ((float)(diff) / 250);
+ }
+
+ if (_timeRemaining < 500)
+ {
+ _alpha = (float)(_timeRemaining) / 500;
+ }
+ if (_timeRemaining <= 0)
+ {
+ setVisible(false);
+ _timeRemaining = 0;
+ _setTime = 0;
+ }
+ }
+}
+
+void FloatingMessage::init(int x, int y, std::string text, unsigned int timeRemaining)
+{
+ setPosition(x, y);
+ setText(text);
+ setTimeRemaining(timeRemaining);
+ _alpha = 0.0f;
+}
+
+void FloatingMessage::setTimeRemaining(unsigned int timeRemaining)
+{
+ _setTime = timeRemaining;
+ _timeRemaining = timeRemaining;
+ setVisible(_timeRemaining > 0);
+}
+
+Button::Button(int x, int y, int w, int h)
+{
+ _x = x;
+ _y = y;
+ _w = w;
+ _h = h;
+
+ _highlighted = false;
+ _clickable = true;
+}
+
+Button::Button(int x, int y, int w, int h, bool clickable)
+{
+ _x = x;
+ _y = y;
+ _w = w;
+ _h = h;
+
+ _highlighted = false;
+ _clickable = clickable;
+}
+
+Button::~Button()
+{
+}
+
+bool Button::isMouseIntersecting(int mx, int my)
+{
+ return (mx >= _x && mx <= _x + _w && my >= _y && my <= _y + _h);
+}
+
+bool Button::isHighlighted()
+{
+ return _highlighted;
+}
+
+void Button::setHighlighted(bool b)
+{
+ _highlighted = b;
+}
+
+int Button::getX()
+{
+ return _x;
+}
+
+int Button::getY()
+{
+ return _y;
+}
+
+int Button::getW()
+{
+ return _w;
+}
+
+int Button::getH()
+{
+ return _h;
+}
+
+void Button::setW(int w)
+{
+ _w = w;
+}
+
+void Button::setPosition(int x, int y)
+{
+ _x = x;
+ _y = y;
+}
+
+void Button::setPositionWithOffset(int x, int y, int xOff, int yOff)
+{
+ setPosition(x, y);
+ _x += xOff;
+ _y += yOff;
+}
+
+bool Button::isClickable()
+{
+ return _clickable;
+}
+
+//Text button functions
+
+TextButton::TextButton(int x, int y, int w, int h, std::string text) : Button(x, y, w, h)
+{
+ _text = text;
+ _red = 1.0f;
+ _green = 1.0f;
+ _blue = 1.0f;
+}
+
+TextButton::TextButton(int x, int y, int w, int h, std::string text, float r, float g, float b) : Button(x, y, w, h)
+{
+ _text = text;
+
+ _red = r;
+ _green = g;
+ _blue = b;
+}
+
+TextButton::~TextButton()
+{
+}
+
+void TextButton::changeColor(float r, float g, float b)
+{
+ _red = r;
+ _green = g;
+ _blue = b;
+}
+
+const char* TextButton::getText()
+{
+ return _text.c_str();
+}
+
+void TextButton::setText(std::string text)
+{
+ _text = text;
+ _w = (text.length() + 2) * 16;
+}
+
+void TextButton::getColors(float* r, float* g, float* b)
+{
+ *r = _red;
+ *g = _green;
+ *b = _blue;
+}
+
+void TextButton::handleMouseIntersection(int mx, int my)
+{
+ if (isMouseIntersecting(mx, my))
+ {
+ changeColor(0, 1, 0);
+ }
+ else
+ {
+ changeColor(1, 1, 1);
+ }
+}
+
+ImageButton::ImageButton(int x, int y, int w, int h, unsigned int index) : Button(x, y, w, h)
+{
+ _defaultIndex = index;
+ _selectedIndex = index;
+ _currIndex = index;
+}
+
+ImageButton::ImageButton(int x, int y, int w, int h, bool clickable, unsigned int index) : Button(x, y, w, h, clickable)
+{
+ _defaultIndex = index;
+ _selectedIndex = index;
+ _currIndex = index;
+}
+
+ImageButton::ImageButton(int x, int y, int w, int h, unsigned int index, unsigned int selectedIndex) : Button(x, y, w, h)
+{
+ _defaultIndex = index;
+ _selectedIndex = selectedIndex;
+ _currIndex = index;
+}
+
+ImageButton::~ImageButton()
+{
+
+}
+
+unsigned int ImageButton::getSpriteIndex()
+{
+ return _currIndex;
+}
+
+void ImageButton::handleMouseIntersection(int mx, int my)
+{
+ if (isMouseIntersecting(mx, my))
+ {
+ _currIndex = _selectedIndex;
+ }
+ else
+ {
+ _currIndex = _defaultIndex;
+ }
+}
+
+void ImageButton::changeSprites(unsigned int index, unsigned int selectedIndex)
+{
+ _currIndex = _defaultIndex = index;
+ _selectedIndex = selectedIndex;
+}
\ No newline at end of file
diff --git a/button.h b/button.h
new file mode 100644
index 0000000..60aabc7
--- /dev/null
+++ b/button.h
@@ -0,0 +1,117 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef BUTTON_H
+#define BUTTON_H
+#include
+#include
+
+class TextLabel
+{
+public:
+ TextLabel(int x, int y, std::string text, float r, float g, float b);
+ ~TextLabel();
+ std::string& getText();
+ virtual void update(unsigned int dT);
+ bool isMouseIntersecting(int mx, int my);
+ void setText(std::string text);
+ void getPosition(int* x, int* y);
+ void getColors(float* r, float* g, float* b, float* a);
+ void setPosition(int x, int y);
+ void setPositionWithOffset(int x, int y, int xOff, int yOff);
+ bool isVisible();
+ void setVisible(bool b);
+protected:
+ std::string _text;
+ float _red, _green, _blue, _alpha;
+ int _x, _y;
+ bool _visible;
+};
+
+class FloatingMessage : public TextLabel
+{
+public:
+ FloatingMessage(int x, int y, std::string text, float r, float g, float b);
+ ~FloatingMessage();
+ void update(unsigned int dT);
+ void init(int x, int y, std::string text, unsigned int timeRemaining);
+ void setTimeRemaining(unsigned int timeRemaining);
+private:
+ int _timeRemaining;
+ int _setTime;
+};
+
+class Button
+{
+public:
+ Button(int x, int y, int w, int h);
+ Button(int x, int y, int w, int h, bool clickable);
+ virtual ~Button();
+ bool isMouseIntersecting(int mx, int my);
+ bool isHighlighted();
+ void setHighlighted(bool b);
+ virtual void handleMouseIntersection(int mx, int my) = 0;
+ int getX();
+ int getY();
+ int getW();
+ int getH();
+ void setW(int w);
+ void setPosition(int x, int y);
+ void setPositionWithOffset(int x, int y, int xOff, int yOff);
+ bool isClickable();
+protected:
+ int _x, _y;
+ int _w, _h;
+ bool _highlighted;
+ bool _clickable;
+};
+
+class TextButton : public Button
+{
+public:
+ TextButton(int x, int y, int w, int h, std::string text);
+ TextButton(int x, int y, int w, int h, std::string text, float r, float g, float b);
+ ~TextButton();
+ void changeColor(float r, float g, float b);
+ const char* getText();
+ void setText(std::string text);
+ void getColors(float* r, float* g, float* b);
+ void handleMouseIntersection(int mx, int my);
+private:
+ std::string _text;
+ float _red, _green, _blue;
+};
+
+class ImageButton : public Button
+{
+public:
+ ImageButton(int x, int y, int w, int h, unsigned int index);
+ ImageButton(int x, int y, int w, int h, bool clickable, unsigned int index);
+ ImageButton(int x, int y, int w, int h, unsigned int index, unsigned int selectedIndex);
+ ~ImageButton();
+ unsigned int getSpriteIndex();
+ void handleMouseIntersection(int mx, int my);
+ void changeSprites(unsigned int index, unsigned int selectedIndex);
+private:
+ unsigned int _currIndex;
+ unsigned int _defaultIndex;
+ unsigned int _selectedIndex;
+};
+
+#endif
\ No newline at end of file
diff --git a/config.cfg b/config.cfg
new file mode 100644
index 0000000..9e8d9a5
--- /dev/null
+++ b/config.cfg
@@ -0,0 +1,16 @@
+entered_light_flash = 0
+fullscreen = 0
+screenshot_index = 0
+volume = 1
+window_x = 1280
+window_y = 720
+Bind_MoveLeft = A
+Bind_MoveRight = D
+Bind_MoveDown = S
+Bind_MoveUp = W
+Bind_MoveRight = Right
+Bind_MoveLeft = Left
+Bind_MoveDown = Down
+Bind_MoveUp = Up
+Bind_ToggleCrosslink = LAlt
+Bind_ToggleCrosslink = RAlt
diff --git a/config.cpp b/config.cpp
new file mode 100644
index 0000000..bb7272f
--- /dev/null
+++ b/config.cpp
@@ -0,0 +1,120 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include "config.h"
+
+ConfigManager::ConfigManager()
+{
+
+}
+
+ConfigManager::~ConfigManager()
+{
+
+}
+
+//bindings are loaded in BindingsManager.
+bool ConfigManager::isKeyValid(char* key)
+{
+ if (!strcmp(key, "fullscreen")) return true;
+ if (!strcmp(key, "window_x")) return true;
+ if (!strcmp(key, "window_y")) return true;
+ if (!strcmp(key, "screenshot_index")) return true;
+ if (!strcmp(key, "entered_light_flash")) return true;
+ if (!strcmp(key, "volume")) return true;
+
+ return false;
+}
+
+std::string ConfigManager::getValue(std::string key)
+{
+ auto it = _settings.find(key);
+
+ return it != _settings.end() ? it->second : "";
+}
+
+void ConfigManager::setValue(std::string key, std::string value)
+{
+ if (getValue(key) != "")
+ {
+ _settings[key] = value;
+ }
+ else
+ {
+ if (isKeyValid((char*)key.c_str()))
+ {
+ _settings.insert(std::pair(key, value));
+ }
+ }
+}
+
+void ConfigManager::loadConfig(const char* filename)
+{
+ char* text = file_read(filename);
+ char* delim = (char*)" =\t\n\r";
+ char* token = strtok(text, delim);
+ char* key;
+
+ while (token)
+ {
+ key = token;
+ token = strtok(nullptr, delim);
+
+ if (isKeyValid(key))
+ {
+ _settings.insert(std::pair(std::string(key), std::string(token)));
+ }
+
+ if (token)
+ token = strtok(nullptr, delim);
+ }
+ delete [] text;
+ delete [] token;
+ text = nullptr;
+ token = nullptr;
+ delim = nullptr;
+}
+
+void ConfigManager::saveConfig(const char* filename, std::vector bindings)
+{
+ std::ofstream output(filename);
+ size_t i;
+ std::map::iterator it;
+
+ if (!output)
+ {
+ return;
+ }
+
+ for (it = _settings.begin(); it != _settings.end(); it++)
+ {
+ output << it->first << " = " << it->second << "\n";
+ }
+
+ for (i = 0; i < bindings.size(); i++)
+ {
+ output << bindings[i];
+ }
+}
+
+std::map* ConfigManager::getSettings()
+{
+ return &_settings;
+}
\ No newline at end of file
diff --git a/config.h b/config.h
new file mode 100644
index 0000000..ef022cd
--- /dev/null
+++ b/config.h
@@ -0,0 +1,42 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef CONFIG_H
+#define CONFIG_H
+#include
+#include
+#include
+#include
+#include "file.h"
+
+class ConfigManager
+{
+public:
+ ConfigManager();
+ ~ConfigManager();
+ void loadConfig(const char* filename);
+ void saveConfig(const char* filename, std::vector bindings);
+ bool isKeyValid(char* key);
+ std::string getValue(std::string key);
+ void setValue(std::string key, std::string value);
+ std::map* getSettings();
+private:
+ std::map _settings;
+};
+#endif
\ No newline at end of file
diff --git a/creditsstate.cpp b/creditsstate.cpp
new file mode 100644
index 0000000..5513997
--- /dev/null
+++ b/creditsstate.cpp
@@ -0,0 +1,74 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "creditsstate.h"
+#include "statemanager.h"
+
+CreditsState::CreditsState(StateManager* sm) : MenuState(sm)
+{
+ _cancelButton.reset(new TextButton(128, 600, (strlen("Go Back") + 2) * 16, 32, "Go Back"));
+ _titleLabel.reset(new TextLabel(0, 0, "Credits", 1, 1, 1));
+ _credit1.reset(new TextLabel(0, 0,
+ "Clonepoint created by Rohit Nirmal\n\
+ Gunpoint created by Tom Francis (www.pentadact.com)\n\
+ \n\n\
+ Libraries Used:\n\
+ stb_image and stb_ttf by Sean Barrett (www.nothings.org)\n\
+ TinyXML (www.grinninglizard.com/tinyxml/)\n\
+ SDL2 (www.libsdl.org)\n\
+ OpenAL Soft (kcat.strangesoft.net/openal.html)\n\
+ \n\n\
+ Special Thanks:\n\
+ /agdg/ <3\
+ ", 1, 1, 1));
+ _buttons.push_back(_cancelButton);
+ _labels.push_back(_titleLabel);
+ _labels.push_back(_credit1);
+}
+
+CreditsState::~CreditsState()
+{
+}
+
+void CreditsState::resetPositions(int w, int h)
+{
+ _titleLabel->setPosition(w * 0.45f, h * 0.1f);
+ _cancelButton->setPosition(w * 0.45f, h * 0.85f);
+ _credit1->setPosition(w * 0.1f, h * 0.2f);
+}
+
+void CreditsState::handleButton(Button* button)
+{
+ if (button == _cancelButton.get())
+ {
+ _manager->switchToState(MAINMENU_SCREEN);
+ }
+}
+
+void CreditsState::handleKeyUp(SDL_Keycode key)
+{
+ switch(key)
+ {
+ case SDLK_PRINTSCREEN:
+ _takeScreenshot();
+ break;
+ default:
+ break;
+ }
+}
\ No newline at end of file
diff --git a/creditsstate.h b/creditsstate.h
new file mode 100644
index 0000000..4847e6b
--- /dev/null
+++ b/creditsstate.h
@@ -0,0 +1,38 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef CREDITSTATE_H
+#define CREDITSTATE_H
+#include "menustate.h"
+
+class CreditsState : public MenuState
+{
+public:
+ CreditsState(StateManager* sm);
+ ~CreditsState();
+ void handleButton(Button* button);
+ void resetPositions(int w, int h);
+ void handleKeyUp(SDL_Keycode key);
+private:
+ std::shared_ptr _cancelButton;
+ std::shared_ptr _titleLabel;
+ std::shared_ptr _credit1;
+};
+
+#endif
\ No newline at end of file
diff --git a/draw.cpp b/draw.cpp
new file mode 100644
index 0000000..3bf2069
--- /dev/null
+++ b/draw.cpp
@@ -0,0 +1,1550 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include "draw.h"
+#include "locator.h"
+
+PFNGLATTACHSHADERPROC glAttachShader;
+PFNGLCOMPILESHADERPROC glCompileShader;
+PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObject;
+PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObject;
+PFNGLDELETEOBJECTARBPROC glDeleteObject;
+PFNGLGETINFOLOGARBPROC glGetInfoLog;
+PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameteriv;
+PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation;
+PFNGLLINKPROGRAMARBPROC glLinkProgram;
+PFNGLSHADERSOURCEARBPROC glShaderSource;
+PFNGLUNIFORM1IARBPROC glUniform1i;
+PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObject;
+PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+PFNGLBINDBUFFERPROC glBindBuffer;
+PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+PFNGLGENBUFFERSPROC glGenBuffers;
+PFNGLUNIFORM3FPROC glUniform3f;
+PFNGLUNIFORM2FPROC glUniform2f;
+PFNGLUNIFORM1FPROC glUniform1f;
+PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+PFNGLBUFFERDATAPROC glBufferData;
+PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+PFNGLVALIDATEPROGRAMPROC glValidateProgram;
+#if _WIN32
+PFNGLACTIVETEXTUREARBPROC glActiveTexture;
+#endif
+PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData;
+
+struct point
+{
+ GLfloat x;
+ GLfloat y;
+ GLfloat s;
+ GLfloat t;
+};
+
+struct SpriteVertex
+{
+ float position[2];
+ float texcoord[2];
+};
+
+Renderer::Renderer()
+{
+
+}
+
+Renderer::~Renderer()
+{
+ LOGF((stdout, "Running Renderer destructor.\n"));
+ if (pgmText)
+ glDeleteObject(pgmText);
+ if (pgmMap)
+ glDeleteObject(pgmMap);
+ if (pgmButton)
+ glDeleteObject(pgmButton);
+ if (pgmColoredSprite)
+ glDeleteObject(pgmColoredSprite);
+ if (text_vbo)
+ glDeleteBuffers(1, &text_vbo);
+ if (entRectVBO)
+ glDeleteBuffers(1, &entRectVBO);
+ if (pointVBO)
+ glDeleteBuffers(1, &pointVBO);
+ if (_screenVBO)
+ glDeleteBuffers(1, &_screenVBO);
+}
+
+void Renderer::toggleWireframe()
+{
+ if (!wireframe)
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ }
+ else
+ {
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+ }
+ wireframe = !wireframe;
+}
+
+void Renderer::init(int x, int y)
+{
+ _screenVBO = 0;
+
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+ //glClearColor(0.3960784313725490196078431372549, 0.61176470588235294117647058823529, 0.93725490196078431372549019607843, 1);
+
+ setResolution(x, y);
+
+ pgmMap = compileShaders("shaders/main.vert", "shaders/main.frag");
+ pgmText = compileShaders("shaders/text.vert", "shaders/text.frag");
+ pgmButton = compileShaders("shaders/button.vert", "shaders/button.frag");
+ pgmColoredSprite = compileShaders("shaders/sprite_colored.vert", "shaders/sprite_colored.frag");
+ pgmLight = compileShaders("shaders/main.vert", "shaders/light.frag");
+ pgmCamera = compileShaders("shaders/main.vert", "shaders/camera.frag");
+
+ //initialize map shader variables
+
+ glUseProgramObject(pgmMap);
+
+ glLinkProgram(pgmMap); /*Must link program before uniforms and after attributes?*/
+ gWorldLocation = glGetUniformLocation(pgmMap, "gWorld");
+
+ //initialize text shader variables
+ /*Not sure if needed...
+ glUseProgram(pgmText);*/
+ glBindAttribLocation(pgmText, 0, "coord");
+ glBindAttribLocation(pgmText, 1, "texpos");
+ uniform_color = glGetUniformLocation(pgmText, "color");
+ uniform_alpha_scale = glGetUniformLocation(pgmText, "alpha_mod");
+ glGenBuffers(1, &text_vbo);
+
+ glBindAttribLocation(pgmButton, 0, "coord");
+ glBindAttribLocation(pgmButton, 1, "texpos");
+
+ screenshotIndex = 1;
+ wireframe = false;
+ _enteredLightFlash = false;
+
+ linkProgress = 0.0f;
+
+ SDL_ShowCursor(SDL_DISABLE);
+
+ bufferQuads();
+
+ font1.reset(new Font("./data/fonts/VeraMono.ttf", 32.0f));
+ font2.reset(new Font("./data/fonts/VeraMono.ttf", 16.0f));
+
+ resPlayer.reset(new SpriteSheet("./data/sprites/player.png", ENTDIM, false));
+ generateSheetBuffers(resPlayer.get(), ENTDIM);
+
+ resPlayerLeft.reset(new SpriteSheet("./data/sprites/player.png", ENTDIM, true));
+ generateSheetBuffers(resPlayerLeft.get(), ENTDIM);
+
+ resGuardRight.reset(new SpriteSheet("./data/sprites/guard.png", ENTDIM, false));
+ generateSheetBuffers(resGuardRight.get(), ENTDIM);
+
+ resGuardLeft.reset(new SpriteSheet("./data/sprites/guard.png", ENTDIM, true));
+ generateSheetBuffers(resGuardLeft.get(), ENTDIM);
+
+ resEnforcerLeft.reset(new SpriteSheet("./data/sprites/enforcer.png", ENTDIM, true));
+ generateSheetBuffers(resEnforcerLeft.get(), ENTDIM);
+
+ resEnforcerRight.reset(new SpriteSheet("./data/sprites/enforcer.png", ENTDIM, false));
+ generateSheetBuffers(resEnforcerRight.get(), ENTDIM);
+
+ resProfessionalLeft.reset(new SpriteSheet("./data/sprites/professional.png", ENTDIM, true));
+ generateSheetBuffers(resProfessionalLeft.get(), ENTDIM);
+
+ resProfessionalRight.reset(new SpriteSheet("./data/sprites/professional.png", ENTDIM, false));
+ generateSheetBuffers(resProfessionalRight.get(), ENTDIM);
+
+ resSniperLeft.reset(new SpriteSheet("./data/sprites/sniper.png", ENTDIM, true));
+ generateSheetBuffers(resSniperLeft.get(), ENTDIM);
+
+ resSniperRight.reset(new SpriteSheet("./data/sprites/sniper.png", ENTDIM, false));
+ generateSheetBuffers(resSniperRight.get(), ENTDIM);
+
+ resObjects.reset(new SpriteSheet("./data/sprites/objects.png", ENTDIM, false));
+ generateSheetBuffers(resObjects.get(), ENTDIM);
+
+ resLinkables.reset(new SpriteSheet("./data/sprites/linkable.png", ENTDIM, false));
+ generateSheetBuffers(resLinkables.get(), ENTDIM);
+
+ resInterface.reset(new SpriteSheet("./data/sprites/interface.png", 32, false));
+ generateSheetBuffers(resInterface.get(), 32);
+
+ resGlass.reset(new SpriteSheet("./data/sprites/glass.png", 8, false));
+ generateSheetBuffers(resGlass.get(), 8);
+
+ mouseOverStrings[MO_Nothing] = "";
+ mouseOverStrings[MO_CircuitBox] = "A circuit box. Use it to unlock its circuit.";
+ mouseOverStrings[MO_LightFixture] = "A light fixture. Toggles its light when activated.";
+ mouseOverStrings[MO_MainComputer] = "A computer terminal. Hack these to complete mission objectives.";
+ mouseOverStrings[MO_HandScanner] = "A hand scanner. Only guards can use these.";
+ mouseOverStrings[MO_Elevator] = "An elevator switch. Makes nearby enemies look when an elevator arrives.";
+ mouseOverStrings[MO_MotionScanner] = "A motion detector. Activates when any living entity passes through.";
+ mouseOverStrings[MO_Switch] = "A light switch.";
+ mouseOverStrings[MO_Door] = "A door. Opens or closes when activated.";
+ mouseOverStrings[MO_TrapDoor] = "A trap door. Opens when activated, then closes after a time.";
+ mouseOverStrings[MO_VaultDoor] = "A vault door. Opens when activated, then closes after a time.";
+ mouseOverStrings[MO_SoundDetector] = "A sound detector. Activated by loud sounds nearby.";
+ mouseOverStrings[MO_Alarm] = "An alarm. When activated, alerts nearby guard to turn it off.";
+ mouseOverStrings[MO_PowerSocket] = "A power socket. Knocks out guards nearby when activated.";
+ mouseOverStrings[MO_SecurityCamera] = "A security camera. Activates whenever you enter its field of vision.";
+ mouseOverStrings[MO_Guard] = "A Guard. Shoots on sight.";
+ mouseOverStrings[MO_Enforcer] = "An Enforcer. Cannot be pounced.";
+ mouseOverStrings[MO_Professional] = "A Professional. Has faster reflexes, can see in the dark, shoots when held at gunpoint.";
+ mouseOverStrings[MO_Sniper] = "A Sniper. You probably won't make it out.";
+
+ objectivesNotCompleted = "Objectives not completed.";
+
+ handleSettingsChange();
+}
+
+void Renderer::setResolution(int x, int y)
+{
+ winX = x;
+ winY = y;
+ orthographic = mat4f_orthographic(0.0f, winX, winY, 0.0f, -5.0f, 5.0f);
+ glViewport(0, 0, winX, winY);
+ bufferScreenVBO((float)winX, (float)winY);
+}
+
+bool CheckGLExtension(const char* ext, const char* list)
+{
+ const char *str = strstr(list, ext);
+ if (!str)
+ {
+ return false;
+ }
+ const char ch = str[strlen(ext)];
+ return ((ch == ' ') || (ch == '\t') || (ch == '\0'));
+}
+
+bool Renderer::initShaders()
+{
+ char* extensions = (char*)glGetString(GL_EXTENSIONS);
+
+ bool shaders_supported = false;
+ if (CheckGLExtension("GL_ARB_shader_objects", extensions) &&
+ CheckGLExtension("GL_ARB_shading_language_100", extensions) &&
+ CheckGLExtension("GL_ARB_vertex_shader", extensions) &&
+ CheckGLExtension("GL_ARB_fragment_shader", extensions))
+ {
+ glAttachShader = (PFNGLATTACHSHADERPROC)SDL_GL_GetProcAddress("glAttachShader");
+ glCompileShader = (PFNGLCOMPILESHADERPROC)SDL_GL_GetProcAddress("glCompileShader");
+ glCreateProgramObject = (PFNGLCREATEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glCreateProgramObjectARB");
+ glCreateShaderObject = (PFNGLCREATESHADEROBJECTARBPROC)SDL_GL_GetProcAddress("glCreateShaderObjectARB");
+ glDeleteObject = (PFNGLDELETEOBJECTARBPROC)SDL_GL_GetProcAddress("glDeleteObjectARB");
+ glGetInfoLog = (PFNGLGETINFOLOGARBPROC)SDL_GL_GetProcAddress("glGetInfoLogARB");
+ glGetObjectParameteriv = (PFNGLGETOBJECTPARAMETERIVARBPROC)SDL_GL_GetProcAddress("glGetObjectParameterivARB");
+ glGetUniformLocation = (PFNGLGETUNIFORMLOCATIONARBPROC)SDL_GL_GetProcAddress("glGetUniformLocationARB");
+ glLinkProgram = (PFNGLLINKPROGRAMARBPROC)SDL_GL_GetProcAddress("glLinkProgramARB");
+ glShaderSource = (PFNGLSHADERSOURCEARBPROC)SDL_GL_GetProcAddress("glShaderSourceARB");
+ glUniform1i = (PFNGLUNIFORM1IARBPROC)SDL_GL_GetProcAddress("glUniform1iARB");
+ glUseProgramObject = (PFNGLUSEPROGRAMOBJECTARBPROC)SDL_GL_GetProcAddress("glUseProgramObjectARB");
+ glEnableVertexAttribArray = (PFNGLENABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glEnableVertexAttribArray");
+ glDisableVertexAttribArray = (PFNGLDISABLEVERTEXATTRIBARRAYPROC)SDL_GL_GetProcAddress("glDisableVertexAttribArray");
+ glBindBuffer = (PFNGLBINDBUFFERPROC)SDL_GL_GetProcAddress("glBindBuffer");
+ glUniformMatrix4fv = (PFNGLUNIFORMMATRIX4FVPROC)SDL_GL_GetProcAddress("glUniformMatrix4fv");
+ glGenBuffers = (PFNGLGENBUFFERSPROC)SDL_GL_GetProcAddress("glGenBuffers");
+ glUniform3f = (PFNGLUNIFORM3FPROC)SDL_GL_GetProcAddress("glUniform3f");
+ glUniform2f = (PFNGLUNIFORM2FPROC)SDL_GL_GetProcAddress("glUniform2f");
+ glUniform1f = (PFNGLUNIFORM1FPROC)SDL_GL_GetProcAddress("glUniform1f");
+ glDeleteBuffers = (PFNGLDELETEBUFFERSPROC)SDL_GL_GetProcAddress("glDeleteBuffers");
+ glBindAttribLocation = (PFNGLBINDATTRIBLOCATIONPROC)SDL_GL_GetProcAddress("glBindAttribLocation");
+ glBufferData = (PFNGLBUFFERDATAPROC)SDL_GL_GetProcAddress("glBufferData");
+ glVertexAttribPointer = (PFNGLVERTEXATTRIBPOINTERPROC)SDL_GL_GetProcAddress("glVertexAttribPointer");
+ glGetAttribLocation = (PFNGLGETATTRIBLOCATIONPROC)SDL_GL_GetProcAddress("glGetAttribLocation");
+ glValidateProgram = (PFNGLVALIDATEPROGRAMPROC)SDL_GL_GetProcAddress("glValidateProgram");
+#if _WIN32
+ glActiveTexture = (PFNGLACTIVETEXTUREARBPROC)SDL_GL_GetProcAddress("glActiveTexture");
+#endif
+ glGetBufferSubData = (PFNGLGETBUFFERSUBDATAARBPROC)SDL_GL_GetProcAddress("glGetBufferSubData");
+
+ if (glAttachShader &&
+ glCompileShader &&
+ glCreateProgramObject &&
+ glCreateShaderObject &&
+ glDeleteObject &&
+ glGetInfoLog &&
+ glGetObjectParameteriv &&
+ glGetUniformLocation &&
+ glLinkProgram &&
+ glShaderSource &&
+ glUniform1i &&
+ glUseProgramObject &&
+ glEnableVertexAttribArray &&
+ glDisableVertexAttribArray &&
+ glBindBuffer &&
+ glUniformMatrix4fv &&
+ glGenBuffers &&
+ glUniform3f &&
+ glUniform2f &&
+ glUniform1f &&
+ glDeleteBuffers &&
+ glBindAttribLocation &&
+ glBufferData &&
+ glVertexAttribPointer &&
+ glGetAttribLocation &&
+ glValidateProgram
+#if _WIN32
+ && glActiveTexture
+#endif
+ )
+ {
+ shaders_supported = true;
+ }
+ else
+ {
+ LOGF((stderr, "Error getting OpenGL function pointers!\n"));
+ }
+ }
+ return shaders_supported;
+}
+
+void Renderer::takeScreenshot()
+{
+ sprintf(screenshot_filename, "screenshot%i.bmp", screenshotIndex);
+ image = SDL_CreateRGBSurface(SDL_SWSURFACE, winX, winY, 24, 0x000000FF, 0x0000FF00, 0x00FF0000, 0);
+ glReadBuffer(GL_FRONT);
+ glReadPixels(0, 0, winX, winY, GL_RGB, GL_UNSIGNED_BYTE, image->pixels);
+ image = flipSurface(image, Flip_Vertical);
+ SDL_SaveBMP(image, screenshot_filename);
+ SDL_FreeSurface(image);
+ screenshotIndex++;
+}
+
+void Renderer::drawState(std::shared_ptr state)
+{
+ size_t i;
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+ if (std::dynamic_pointer_cast(state))
+ {
+ for (i = 0; i < std::static_pointer_cast(state)->getButtonCount(); i++)
+ {
+ drawButton(std::static_pointer_cast(state)->getButtonAt(i));
+ }
+ }
+ else
+ {
+ drawScene(std::static_pointer_cast(state)->getScene());
+ }
+
+ for (i = 0; i < std::static_pointer_cast(state)->getLabelCount(); i++)
+ {
+ drawTextLabel(state->getLabelAt(i));
+ }
+ drawMouseCursor(state);
+}
+
+void Renderer::drawMouseCursor(std::shared_ptr state)
+{
+ int x, y;
+ state->getMousePosition(&x, &y);
+ bool safe = true;
+
+ if (std::dynamic_pointer_cast(state) && std::static_pointer_cast(state)->isMouseCursorSeen())
+ {
+ safe = false;
+ }
+
+ drawSpriteBind(x, y, 4.0f, 0, resInterface.get(), Locator::getSpriteManager()->getIndex("./data/sprites/interface.sprites",
+ safe ? "pointer_safe" : "pointer_unsafe"), false, 1, 1, 1);
+}
+
+void Renderer::drawText(float x, float y, const char* text, float red, float green, float blue, float alpha_scale, std::shared_ptr font)
+{
+ float xCurr = x;
+ glBindBuffer(GL_ARRAY_BUFFER, text_vbo);
+ glUseProgramObject(pgmText);
+ glActiveTexture(GL_TEXTURE0);
+
+ glBindTexture(GL_TEXTURE_2D, font->getTexture());
+ glUniformMatrix4fv(glGetUniformLocation(pgmText, "proj_mat"), 1, GL_TRUE, &orthographic.m[0][0]);
+ glUniform3f(uniform_color, red, green, blue);
+ glUniform1f(uniform_alpha_scale, alpha_scale);
+ glEnableVertexAttribArray(0);
+
+ glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, nullptr);
+ std::vector coords;
+ point p;
+ point top_left;
+ point bottom_right;
+ stbtt_aligned_quad q;
+ int c = 0;
+
+ while (*text)
+ {
+ if ((*text >= 32 && *text < 127) || *text == '\n')
+ {
+ if (*text == '\n')
+ {
+ y += font->getSize();
+ xCurr = x;
+ ++text;
+ continue;
+ }
+ stbtt_GetBakedQuad(font->data(), 512, 512, *text - 32, &xCurr, &y, &q, 1);
+
+ p.x = q.x0;
+ p.y = q.y0;
+ p.s = q.s0;
+ p.t = q.t0;
+ coords.push_back(p);
+
+ top_left.x = q.x0;
+ top_left.y = q.y1;
+ top_left.s = q.s0;
+ top_left.t = q.t1;
+
+ bottom_right.x = q.x1;
+ bottom_right.y = q.y0;
+ bottom_right.s = q.s1;
+ bottom_right.t = q.t0;
+
+ coords.push_back(top_left);
+ coords.push_back(bottom_right);
+ coords.push_back(top_left);
+ coords.push_back(bottom_right);
+
+ p.x = q.x1;
+ p.y = q.y1;
+ p.s = q.s1;
+ p.t = q.t1;
+ coords.push_back(p);
+
+ c += 6;
+ }
+ ++text;
+ }
+
+ glBufferData(GL_ARRAY_BUFFER, sizeof(point) * coords.size(), coords.data(), GL_DYNAMIC_DRAW);
+
+ glEnable(GL_BLEND);
+ glDisable(GL_DEPTH_TEST);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDrawArrays(GL_TRIANGLES, 0, c);
+ glDisable(GL_BLEND);
+ glEnable(GL_DEPTH_TEST);
+ glDisableVertexAttribArray(0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Renderer::drawTextButton(std::shared_ptr tb)
+{
+ float r, g, b;
+
+ r = g = b = 0;
+ tb->getColors(&r, &g, &b);
+ drawText(tb->getX(), tb->getY() + 32, tb->getText(), r, g, b, 1.0f, font1);
+}
+
+void Renderer::drawImageButton(std::shared_ptr ib)
+{
+ drawSprite(ib->getX(), ib->getY(), 2, 0, resInterface.get(), ib->getSpriteIndex(), false, 1, 1, 1);
+}
+
+void Renderer::drawButton(std::shared_ptr button)
+{
+ glBindTexture(GL_TEXTURE_2D, resInterface->getTexId());
+ if (std::dynamic_pointer_cast(button))
+ {
+ drawTextButton(std::static_pointer_cast(button));
+ }
+
+ if (std::dynamic_pointer_cast(button))
+ {
+ drawImageButton(std::static_pointer_cast(button));
+ }
+}
+
+void Renderer::drawTextLabel(std::shared_ptr tl)
+{
+ if (!tl->isVisible())
+ {
+ return;
+ }
+
+ int x, y;
+ float r, g, b, a;
+
+ x = y = r = g = b = 0;
+
+ tl->getPosition(&x, &y);
+ tl->getColors(&r, &g, &b, &a);
+
+ drawText(x, y, tl->getText().c_str(), r, g, b, a, font1);
+}
+
+void Renderer::bufferQuads()
+{
+ //2 triangles.
+ float verts[] = { 0.0, 0.0,
+ 0.0, ENTDIM,
+ ENTDIM, 0.0f,
+ 0.0, ENTDIM,
+ ENTDIM, 0.0f,
+ ENTDIM, ENTDIM
+ };
+
+ glGenBuffers(1, &entRectVBO);
+ glBindBuffer(GL_ARRAY_BUFFER, entRectVBO);
+ glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), &verts[0], GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ float vertsp[] = { 0.0, 0.0,
+ 0.0, 4.0f,
+ 4.0f, 0.0f,
+ 0.0, 4.0f,
+ 4.0, 0.0f,
+ 4.0f, 4.0f
+ };
+
+ glGenBuffers(1, &pointVBO);
+ glBindBuffer(GL_ARRAY_BUFFER, pointVBO);
+ glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), &vertsp[0], GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Renderer::bufferScreenVBO(float x, float y)
+{
+ if (_screenVBO)
+ glDeleteBuffers(1, &_screenVBO);
+
+ float verts[] = { 0.0, 0.0,
+ 0.0, y,
+ x, 0.0f,
+ 0.0, y,
+ x, 0.0f,
+ x, y
+ };
+
+ glGenBuffers(1, &_screenVBO);
+ glBindBuffer(GL_ARRAY_BUFFER, _screenVBO);
+ glBufferData(GL_ARRAY_BUFFER, 12 * sizeof(float), &verts[0], GL_STATIC_DRAW);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+void Renderer::drawRect(float x, float y, float z, GLuint vbo, float red, float green, float blue, float alpha, bool fill)
+{
+ glUseProgramObject(pgmMap);
+ glUniform3f(glGetUniformLocation(pgmMap, "color"), red, green, blue);
+ glUniform1f(glGetUniformLocation(pgmMap, "alpha"), alpha);
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(x, y, z));
+ glUniformMatrix4fv(glGetUniformLocation(pgmMap, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glEnableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glVertexAttribPointer(glGetAttribLocation(pgmMap, "Position"), 2, GL_FLOAT, GL_FALSE, 0, nullptr);
+
+ if (!fill)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+ glDisable(GL_BLEND);
+ if (!fill)
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+ glUseProgramObject(0);
+}
+
+void Renderer::drawSpriteBind(float x, float y, float z, float rotation, SpriteSheet* resource, unsigned int index, bool colorOverride, float red, float green, float blue)
+{
+ glBindTexture(GL_TEXTURE_2D, resource->getTexId());
+ drawSprite(x, y, z, rotation, resource, index, colorOverride, red, green, blue);
+}
+
+void Renderer::drawSprite(float x, float y, float z, float rotation, SpriteSheet* resource, unsigned int index, bool colorOverride, float red, float green, float blue)
+{
+ float xOff, yOff;
+ GLuint pgm;
+ resource->getClipPosition(index, &xOff, &yOff);
+
+ if (colorOverride)
+ {
+ pgm = pgmColoredSprite;
+ glUseProgramObject(pgm);
+ glUniform3f(glGetUniformLocation(pgm, "color"), red, green, blue);
+ }
+ else
+ {
+ pgm = pgmButton;
+ glUseProgramObject(pgm);
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, resource->getVertexBuffer());
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, resource->getIndexBuffers()[index]);
+ glActiveTexture(GL_TEXTURE0);
+
+ //Cast position to int to prevent "shimmering" texture.
+ quadTransform = mat4f_mult(orthographic, mat4f_translate((int)x - xOff, (int)y - yOff, z));
+ quadTransform = mat4f_mult(quadTransform, mat4f_translate(xOff + (resource->getTileDim() / 2) , yOff + (resource->getTileDim() / 2), 0));
+ quadTransform = mat4f_mult(quadTransform, mat4f_rotate(0, 0, rotation));
+ quadTransform = mat4f_mult(quadTransform, mat4f_translate(-xOff - (resource->getTileDim() / 2), -yOff - (resource->getTileDim() / 2) , 0));
+
+ glUniformMatrix4fv(glGetUniformLocation(pgm, "proj_mat"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glEnableVertexAttribArray(glGetAttribLocation(pgm, "Position"));
+ glEnableVertexAttribArray(glGetAttribLocation(pgm, "TexCoord"));
+
+ SpriteVertex* vert = nullptr;
+ glVertexAttribPointer(glGetAttribLocation(pgm, "Position"), 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), vert->position);
+ glVertexAttribPointer(glGetAttribLocation(pgm, "TexCoord"), 2, GL_FLOAT, GL_FALSE, sizeof(SpriteVertex), vert->texcoord);
+
+ glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgm, "Position"));
+ glDisableVertexAttribArray(glGetAttribLocation(pgm, "TexCoord"));
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+
+ glUseProgramObject(0);
+}
+
+void Renderer::drawFieldOfView(Scene* scene, FieldOfView* fov, GLuint program)
+{
+ if (!fov->isActive())
+ {
+ return;
+ }
+
+ float r, g, b;
+ fov->getColors(&r, &g, &b);
+
+ float* verts = fov->getVertData();
+
+ glUseProgramObject(program);
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(-scene->getCamera().x, -scene->getCamera().y, 0));
+ glUniformMatrix4fv(glGetUniformLocation(program, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glUniform3f(glGetUniformLocation(program, "Color"), r, g, b);
+ glUniform2f(glGetUniformLocation(program, "lightCenter"), fov->getPosition().x - scene->getCamera().x , winY - fov->getPosition().y + scene->getCamera().y);
+
+ glEnableVertexAttribArray(glGetAttribLocation(program, "Position"));
+ glVertexAttribPointer(glGetAttribLocation(program, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+
+ glDrawArrays(GL_TRIANGLE_FAN, 0, fov->getNumberOfVerts());
+
+ glDisableVertexAttribArray(glGetAttribLocation(program, "Position"));
+}
+
+void Renderer::generateSheetBuffers(SpriteSheet* sheet, int tileDim)
+{
+ unsigned int spriteCount = sheet->getNumberOfSprites();
+
+ unsigned int i, j;
+ float clipX, clipY;
+ float sheetWidth = sheet->getWidth();
+ float sheetHeight = sheet->getHeight();
+ std::vector verts(spriteCount * 6);
+ GLuint* ibos = sheet->getIndexBuffers();
+ GLuint indices[6] = {0, 0, 0, 0};
+ glGenBuffers(spriteCount, ibos);
+
+ for (i = 0; i < spriteCount; i++)
+ {
+ for (j = 0; j < 6; j++)
+ {
+ indices[j] = i * 6 + j;
+ }
+
+ sheet->getClipPosition(i, &clipX, &clipY);
+
+ verts[indices[0]].position[0] = clipX;
+ verts[indices[0]].position[1] = clipY;
+ verts[indices[0]].texcoord[0] = clipX / sheetWidth;
+ verts[indices[0]].texcoord[1] = clipY / sheetHeight;
+
+ verts[indices[1]].position[0] = clipX;
+ verts[indices[1]].position[1] = clipY + tileDim;
+ verts[indices[1]].texcoord[0] = clipX / sheetWidth;
+ verts[indices[1]].texcoord[1] = (clipY + tileDim) / sheetHeight;
+
+ verts[indices[2]].position[0] = clipX + tileDim;
+ verts[indices[2]].position[1] = clipY;
+ verts[indices[2]].texcoord[0] = (clipX + tileDim) / sheetWidth;
+ verts[indices[2]].texcoord[1] = clipY / sheetHeight;
+
+ verts[indices[3]] = verts[indices[1]];
+ verts[indices[4]] = verts[indices[2]];
+
+ verts[indices[5]].position[0] = clipX + tileDim;
+ verts[indices[5]].position[1] = clipY + tileDim;
+ verts[indices[5]].texcoord[0] = (clipX + tileDim) / sheetWidth;
+ verts[indices[5]].texcoord[1] = (clipY + tileDim) / sheetHeight;
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibos[i]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLuint), indices, GL_STATIC_DRAW);
+ LOGF((stdout, "Created Element Buffer Object %i.\n", ibos[i]));
+ }
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
+
+ GLuint vbo = sheet->getVertexBuffer();
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, spriteCount * 6 * sizeof(SpriteVertex), verts.data(), GL_STATIC_DRAW);
+ LOGF((stdout, "Created Vertex Buffer Object %i.\n", vbo));
+ sheet->setVertexBuffer(vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, 0);
+}
+
+//Draw a Rect structure of arbitrary size - no VBO.
+void Renderer::drawRect2(Rect rect, float red, float green, float blue, int z)
+{
+ glUseProgramObject(pgmMap);
+ glUniform3f(glGetUniformLocation(pgmMap, "color"), red, green, blue);
+
+ float verts[] = { 0.0f, 0.0f,
+ 0.0f, rect.h,
+ 0.0f, 0.0f,
+ rect.w, 0.0f,
+ 0.0f, rect.h,
+ rect.w, rect.h,
+ rect.w, 0.0f,
+ rect.w, rect.h
+ };
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(rect.x, rect.y, z));
+ glUniformMatrix4fv(glGetUniformLocation(pgmMap, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glEnableVertexAttribArray(glGetAttribLocation(pgmMap, "Position")); /*vertex coords*/
+ glVertexAttribPointer(glGetAttribLocation(pgmMap, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+
+ glDrawArrays(GL_LINES, 0, 8);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+ glUseProgramObject(0);
+}
+
+void Renderer::drawRect2Fill(Rect rect, float red, float green, float blue, int z)
+{
+ glUseProgramObject(pgmMap);
+ glUniform3f(glGetUniformLocation(pgmMap, "color"), red, green, blue);
+
+ float verts[] = { 0.0f, 0.0f,
+ 0.0f, rect.h,
+ rect.w, 0.0f,
+ 0.0f, rect.h,
+ rect.w, 0.0f,
+ rect.w, rect.h
+ };
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(rect.x, rect.y, z));
+ glUniformMatrix4fv(glGetUniformLocation(pgmMap, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glEnableVertexAttribArray(glGetAttribLocation(pgmMap, "Position")); /*vertex coords*/
+ glVertexAttribPointer(glGetAttribLocation(pgmMap, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+ glUseProgramObject(0);
+}
+
+void Renderer::drawCollisionVols(Scene* scene)
+{
+ std::shared_ptr map = scene->getMap();
+
+ int numVols = map->getNumberOfCollideVols();
+ Rect rect;
+ for (int i = 0; i < numVols; i++)
+ {
+ if (map->getCollideVolAt(i).active)
+ {
+ rect = map->getCollideVolAt(i).rect;
+ rect.x -= scene->getCamera().x;
+ rect.y -= scene->getCamera().y;
+
+ if (map->getCollideVolAt(i).glass)
+ drawRect2(rect, 0, 1, 1, 1);
+ #ifdef DEBUG
+ else
+ {
+ drawRect2(rect, 0.40625f, 0.71875f, 0.91796875f, 1);
+ // drawRect2(rect, 0, 0, 0, scene->inCrosslinkMode(), 1);
+ }
+ #endif
+ }
+ }
+}
+
+void Renderer::drawEntities(Scene* scene)
+{
+ std::shared_ptr map = scene->getMap();
+ Rect cam = scene->getCamera();
+
+ size_t numEnts = map->getNumberOfEnts();
+ size_t numParticles = scene->getNumberOfParticles();
+ Entity* ent;
+ Particle* particle;
+ vec2f position;
+ size_t i;
+ Rect vol;
+ glBindTexture(GL_TEXTURE_2D, resObjects->getTexId());
+ for (i = 0; i < numEnts; i++)
+ {
+ ent = map->getEntAt(i);
+ position = ent->getPosition();
+ vol = ent->getCollisionRect();
+ vol.x -= scene->getCamera().x;
+ vol.y -= scene->getCamera().y;
+
+ if (ent->isHighlighted())
+ {
+ drawRect2(vol, 1, 1, 1, 1);
+ }
+
+ if (dynamic_cast(ent) || dynamic_cast(ent))
+ {
+
+ }
+ else if (dynamic_cast(ent))
+ {
+ ElevatorDoor* ed = static_cast(ent);
+ if (ed->isOpening() || ed->isClosing())
+ {
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ 1,
+ ent->getRotation(),
+ resObjects.get(),
+ Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatoropen"),
+ false, 1, 0, 1);
+ }
+
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ (ed->isOpening() || ed->isClosing()) ? 2.75f : 1.5f,
+ ent->getRotation(),
+ resObjects.get(),
+ ent->getCurrentSprite(),
+ false, 1, 0, 1);
+ }
+ else
+ {
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ 1,
+ ent->getRotation(),
+ resObjects.get(),
+ ent->getCurrentSprite(),
+ false, 1, 0, 1);
+ }
+ }
+
+ drawLinkableEntities(scene);
+ glBindTexture(GL_TEXTURE_2D, resObjects->getTexId());
+ if (map->subwayFound())
+ {
+ drawSprite( map->getSubwayPosition().x - cam.x,
+ map->getSubwayPosition().y - cam.y - ENTDIM,
+ 3,
+ 0,
+ resObjects.get(),
+ Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "subwayexit"),
+ false, 1, 0, 1);
+ }
+
+ for (i = 0; i < map->getNumberOfStairs(); i++)
+ {
+ position = map->getStairsAt(i)->getPosition();
+
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ 1,
+ 0,
+ resObjects.get(),
+ Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "stairs"), false, 0, 0, 0);
+ }
+
+ for (i = 0; i < map->getNumberOfShafts(); i++)
+ {
+ if (map->getShaftAt(i)->isMoving())
+ {
+ vol = map->getShaftAt(i)->getRect();
+ vol.x -= scene->getCamera().x;
+ vol.y -= scene->getCamera().y;
+ drawRect2(vol, 0, 1, 1, 1);
+ }
+ }
+
+ for (i = 0; i < numEnts; i++)
+ {
+ ent = map->getEntAt(i);
+ position = ent->getPosition();
+ vol = ent->getCollisionRect();
+ Rect rect = {vol.x + 1 - scene->getCamera().x, vol.y + 4 - scene->getCamera().y, 4, 4};
+ if (dynamic_cast(ent))
+ {
+ CircuitBox* cb = static_cast(ent);
+
+ if (cb->isHacked())
+ {
+ drawRect2Fill(rect, 0, 0, 0, 2);
+ }
+ }
+ }
+
+ glBindTexture(GL_TEXTURE_2D, resGlass->getTexId());
+ for (i = 0; i < numParticles; i++)
+ {
+ particle = scene->getParticleAt(i);
+ position = particle->getPosition();
+ vol = particle->getCollisionRect();
+ vol.x -= scene->getCamera().x;
+ vol.y -= scene->getCamera().y;
+ if (particle->isAlive())
+ {
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ 1,
+ 0,
+ resGlass.get(),
+ particle->getCurrentSprite(),
+ false, 1, 0, 1);
+ }
+ }
+
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ for (i = 0; i < numEnts; i++)
+ {
+ ent = map->getEntAt(i);
+ position = ent->getPosition();
+ if (dynamic_cast(ent))
+ {
+ SecurityCamera* camera = static_cast(ent);
+ if (scene->inCrosslinkMode() && !scene->isCircuitUnlocked(camera->getCircuitType()) && camera->getCircuitType() != RED)
+ {
+ glBlendFunc(GL_DST_COLOR, GL_ZERO);
+ }
+ drawFieldOfView(scene, ((SecurityCamera*)ent)->getFOV(), pgmCamera);
+ }
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+}
+
+void Renderer::drawEnemies(Scene* scene, bool crosslink)
+{
+ std::shared_ptr map = scene->getMap();
+ Rect cam = scene->getCamera();
+
+ size_t numEnemies = map->getNumberOfEnemies();
+ Enemy* enemy;
+ SpriteSheet* sheet;
+ vec2f position;
+ size_t i;
+ for (i = 0; i < numEnemies; i++)
+ {
+ enemy = map->getEnemyAt(i);
+ position = enemy->getPosition();
+ switch (enemy->getType())
+ {
+ case Enemy_Sniper:
+ sheet = enemy->getDirection() == Right ? resSniperRight.get() : resSniperLeft.get();
+ break;
+ case Enemy_Enforcer:
+ sheet = enemy->getDirection() == Right ? resEnforcerRight.get() : resEnforcerLeft.get();
+ break;
+ case Enemy_Professional:
+ sheet = enemy->getDirection() == Right ? resProfessionalRight.get() : resProfessionalLeft.get();
+ break;
+ case Enemy_Guard:
+ default:
+ sheet = enemy->getDirection() == Right ? resGuardRight.get() : resGuardLeft.get();
+ break;
+ }
+ glBindTexture(GL_TEXTURE_2D, sheet->getTexId());
+ drawSprite( position.x - cam.x,
+ position.y - cam.y,
+ 2.5f,
+ enemy->getRotation(),
+ sheet,
+ enemy->getCurrentSprite(),
+ crosslink, 0, 0, 0);
+#ifdef DEBUG
+
+ sprintf(print, "%i", enemy->getAlertType());
+ drawText(position.x - cam.x, position.y - cam.y, print, RGB_WHITE, 1.0f, font1);
+
+ if (enemy->getTargetType() != TARGET_NONE)
+ {
+ drawLine(scene, 1, 0, 1, 1, enemy->getCollisionRectPosition(), enemy->getTarget(), 1);
+
+ }
+ if (enemy->getSecondaryTarget() != NULL)
+ {
+ drawLine(scene, 1, 1, 0, 1, enemy->getCollisionRectPosition(), enemy->getSecondaryTarget()->getCollisionRectPosition(), 1);
+ }
+
+ if (enemy->getStrongestLight() != NULL)
+ {
+ drawLine(scene, 1, 1, 1, 1, enemy->getCollisionRectCenterPosition(), enemy->getStrongestLight()->getPosition(), 1);
+ }
+
+ if (enemy->getLightToActivate() != NULL)
+ {
+ drawLine(scene, 0, 1, 0, 1, enemy->getCollisionRectCenterPosition(), enemy->getLightToActivate()->getPosition(), 1);
+ }
+
+ if (enemy->getState() != KNOCKED_OUT && enemy->getState() != FALLING /*&& scene->inCrosslinkMode()*/)
+ drawEnemyFOV(scene, enemy);
+
+#endif
+ }
+}
+
+void Renderer::drawLinkableEntities(Scene* scene)
+{
+ std::shared_ptr map = scene->getMap();
+ std::vector >::iterator linkBegin;
+ std::vector >::iterator linkEnd;
+ std::vector >::iterator linkIter;
+ LinkableEntity* ent;
+ map->getLinkableIters(&linkBegin, &linkEnd);
+ float r, g, b;
+ Rect vol;
+ vec2f position;
+ Rect cam = scene->getCamera();
+ glBindTexture(GL_TEXTURE_2D, resLinkables->getTexId());
+ for (linkIter = linkBegin; linkIter != linkEnd; ++linkIter)
+ {
+ ent = (*linkIter).get();
+ vol = ent->getCollisionRect();
+ vol.x -= scene->getCamera().x;
+ vol.y -= scene->getCamera().y;
+ position = ent->getPosition();
+ if (!scene->inCrosslinkMode())
+ {
+ r = 1.0f;
+ g = 0.0f;
+ b = 1.0f;
+ }
+ else
+ {
+ if (!scene->isCircuitUnlocked(ent->getCircuitType()) && ent->getCircuitType() != RED)
+ {
+ r = g = b = 0;
+ }
+ else
+ {
+ scene->getCircuitColor(ent->getCircuitType(), r, g, b);
+ }
+ }
+ if (dynamic_cast(ent) || dynamic_cast(ent) || dynamic_cast(ent) || dynamic_cast(ent) || dynamic_cast(ent) || dynamic_cast(ent)
+ || dynamic_cast(ent) || dynamic_cast(ent) || dynamic_cast(ent))
+ {
+ drawSprite(position.x - cam.x, position.y - cam.y, 1.5f, 0, resLinkables.get(), ent->getCurrentSprite(), scene->inCrosslinkMode(), r, g, b);
+ }
+ else if (dynamic_cast(ent))
+ {
+ if (scene->inCrosslinkMode())
+ drawRect2(vol, 1, 0, 1, 2);
+ }
+ else
+ {
+ drawRect2(vol, 1, 0, 1, 1);
+ }
+ }
+}
+
+void Renderer::drawLights(Scene* scene)
+{
+ size_t numLights = scene->getMap()->getNumberOfLights();
+ size_t i;
+
+ glDisable(GL_DEPTH_TEST);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_ONE, GL_ONE);
+
+ for (i = 0; i < numLights; i++)
+ {
+ if (scene->getMap()->getLightAt(i)->getType() == FOV_LIGHT)
+ {
+ drawFieldOfView(scene, scene->getMap()->getLightAt(i), pgmLight);
+ }
+ }
+
+ glEnable(GL_DEPTH_TEST);
+ glDisable(GL_BLEND);
+}
+
+void Renderer::drawJumpTrajectory(Scene* scene)
+{
+ Rect cam = scene->getCamera();
+ size_t numPoints = scene->getNumberOfTrajPoints();
+ vec2f point;
+ for (size_t i = 0; i < numPoints; i++)
+ {
+ point = scene->getTrajPointAt(i);
+ drawRect(point.x - cam.x, point.y - cam.y, 2, pointVBO, 1, 1, 1, 1, true);
+ }
+}
+
+void Renderer::drawLink(Scene* scene, Circuit c, vec2f pA, vec2f pB)
+{
+ float r, g, b;
+ r = g = b = 0;
+
+ if (scene->isCircuitUnlocked(c))
+ {
+ scene->getCircuitColor(c, r, g, b);
+ }
+
+ drawLine(scene, r, g, b, 1, pA, pB, 1.5);
+}
+
+void Renderer::drawLine(Scene* scene, float r, float g, float b, float alpha, vec2f pA, vec2f pB, float z)
+{
+ float verts[] = {pB.x - pA.x, pB.y - pA.y, 0.0f, 0.0f};
+ glUseProgramObject(pgmMap);
+ glUniform3f(glGetUniformLocation(pgmMap, "color"), r, g, b);
+ glUniform1f(glGetUniformLocation(pgmMap, "alpha"), alpha);
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(pA.x - scene->getCamera().x, pA.y - scene->getCamera().y, z));
+ glUniformMatrix4fv(glGetUniformLocation(pgmMap, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+
+ glEnableVertexAttribArray(glGetAttribLocation(pgmMap, "Position")); /*vertex coords*/
+ glVertexAttribPointer(glGetAttribLocation(pgmMap, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glDrawArrays(GL_LINES, 0, 2);
+ glDisable(GL_BLEND);
+ glDisableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+}
+
+void Renderer::drawCrossLink(Scene* scene)
+{
+ if (!scene->inCrosslinkMode())
+ return;
+
+ std::shared_ptr map = scene->getMap();
+ Rect cam = scene->getCamera();
+ Circuit c;
+ std::vector >::iterator linkBegin;
+ std::vector >::iterator linkEnd;
+ std::vector >::iterator linkIter;
+ LinkableEntity* le;
+ map->getLinkableIters(&linkBegin, &linkEnd);
+ for (linkIter = linkBegin; linkIter != linkEnd; ++linkIter)
+ {
+ le = (*linkIter).get();
+ c = le->getCircuitType();
+
+ if (le->getTarget())
+ {
+ vec2f v1 = le->getCollisionRectCenterPosition();
+ vec2f v2 = le->getTarget()->getCollisionRectCenterPosition();
+
+ distance = vec2f(v2.x - v1.x, v2.y - v1.y);
+ progress = vec2f_normalize(distance);
+ progress = progress + v1;
+ distance = distance * linkProgress;
+ progress = progress + distance;
+
+ if (scene->isCircuitUnlocked(c))
+ {
+ drawRect(progress.x - cam.x, progress.y - cam.y, 1.5, pointVBO, 1, 1, 1, 1, true);
+ }
+ else
+ {
+ drawRect(progress.x - cam.x, progress.y - cam.y, 1.5, pointVBO, 0, 0, 0, 1, true);
+ }
+ drawLink(scene, c, v1, v2);
+ }
+ else
+ {
+ if (le == scene->getLinker() && scene->isPlayerSelecting())
+ {
+ drawLink(scene, c, le->getCollisionRectCenterPosition(), scene->getMouseDragPosition());
+ }
+ }
+ }
+ glUseProgramObject(0);
+}
+
+void Renderer::drawEnemyFOV(Scene* scene, Enemy* enemy)
+{
+ vec2f position = enemy->getCollisionRectCenterPosition();
+ int negMod;
+ int radius = enemy->getType() == Enemy_Sniper ? ENEMY_FOV_RADIUS_SNIPER : scene->isPlayerInLight() ? ENEMY_FOV_RADIUS_LIT : ENEMY_FOV_RADIUS_DARK;
+ float angle = enemy->getType() == Enemy_Sniper ? ENEMY_FOV_HALFANGLE_SEEN : enemy->canSeePlayer() ? ENEMY_FOV_HALFANGLE_SEEN : ENEMY_FOV_HALFANGLE;
+
+ if (enemy->getDirection() == Right)
+ {
+ negMod = 1;
+ }
+ else
+ {
+ negMod = -1;
+ }
+
+ float verts[] = { position.x, position.y,
+ radius * sinf(ToRadian(-angle + 90)) * negMod + position.x, radius * cosf(ToRadian(-angle + 90)) + position.y,
+ radius * sinf(ToRadian( 90)) * negMod + position.x, radius * cosf(ToRadian( 90)) + position.y,
+ radius * sinf(ToRadian(angle + 90)) * negMod + position.x, radius * cosf(ToRadian(angle + 90)) + position.y
+ };
+
+ glUseProgramObject(pgmMap);
+ glUniform3f(glGetUniformLocation(pgmMap, "color"), 0, 0, 1);
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(-scene->getCamera().x, -scene->getCamera().y, 1.5));
+
+ glUniformMatrix4fv(glGetUniformLocation(pgmMap, "gWorld"), 1, GL_TRUE, &quadTransform.m[0][0]);
+
+ glEnableVertexAttribArray(glGetAttribLocation(pgmMap, "Position")); /*vertex coords*/
+ glVertexAttribPointer(glGetAttribLocation(pgmMap, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+
+ glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
+ glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
+ glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgmMap, "Position"));
+}
+
+void Renderer::drawTileLayer(Scene* scene, int z)
+{
+ glUseProgramObject(pgmButton);
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, scene->inCrosslinkMode() ? scene->getMap()->getCrosslinkTexture() : scene->getMap()->getMapTexture());
+
+ float verts[] = { 0.0f, 0.0f,
+ 0.0f, (float)scene->getMap()->getMapHeight(),
+ (float)scene->getMap()->getMapWidth(), 0.0f,
+ 0.0f, (float)scene->getMap()->getMapHeight(),
+ (float)scene->getMap()->getMapWidth(), 0.0f,
+ (float)scene->getMap()->getMapWidth(), (float)scene->getMap()->getMapHeight(),
+ };
+
+ float texes[] = { 0.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 0.0f, 1.0f,
+ 1.0f, 0.0f,
+ 1.0f, 1.0f,
+ };
+
+ quadTransform = mat4f_mult(orthographic, mat4f_translate(0 - scene->getCamera().x, 0 - scene->getCamera().y, z));
+ glUniformMatrix4fv(glGetUniformLocation(pgmButton, "proj_mat"), 1, GL_TRUE, &quadTransform.m[0][0]);
+ glEnableVertexAttribArray(glGetAttribLocation(pgmButton, "Position")); /*vertex coords*/
+ glEnableVertexAttribArray(glGetAttribLocation(pgmButton, "TexCoord")); /*texture coords*/
+ glVertexAttribPointer(glGetAttribLocation(pgmButton, "Position"), 2, GL_FLOAT, GL_FALSE, 0, verts);
+ glVertexAttribPointer(glGetAttribLocation(pgmButton, "TexCoord"), 2, GL_FLOAT, GL_FALSE, 0, texes);
+
+ glDrawArrays(GL_TRIANGLES, 0, 6);
+
+ glDisableVertexAttribArray(glGetAttribLocation(pgmButton, "Position"));
+ glDisableVertexAttribArray(glGetAttribLocation(pgmButton, "TexCoord"));
+
+ glBindTexture(GL_TEXTURE_2D, 0);
+ glUseProgramObject(0);
+}
+
+void Renderer::drawScene(Scene* scene)
+{
+ Rect cam = scene->getCamera();
+ Player* player = scene->getPlayer();
+ drawTileLayer(scene, 0);
+ if (!scene->inCrosslinkMode())
+ {
+ if ((!player->isInElevator() || !player->getElevatorDoor()->getShaft()->isMoving()) && !player->isPinning())
+ {
+ glBindTexture(GL_TEXTURE_2D, player->getDirection() == Right ? resPlayer->getTexId() : resPlayerLeft->getTexId());
+ drawSprite( scene->getPlayerPosition().x - cam.x,
+ scene->getPlayerPosition().y - cam.y,
+ player->isInElevator() ? 2.5f : 2.8f,
+ player->getRotation(),
+ player->getDirection() == Right ? resPlayer.get() : resPlayerLeft.get(),
+ player->getCurrentSprite(),
+ false, 0, 0, 0);
+ if (player->isAimingGun())
+ {
+ unsigned int spr = Locator::getSpriteManager()->getIndex("./data/sprites/player.sprites", "player_arm_gun");
+ drawSprite( scene->getPlayerPosition().x - cam.x,
+ scene->getPlayerPosition().y - cam.y - 4,
+ 2.8f,
+ player->getArmRotation(),
+ player->getDirection() == Right ? resPlayer.get() : resPlayerLeft.get(),
+ spr,
+ false, 0, 0, 0);
+ drawLine(scene, 1, 0, 0, 0.5f, player->getCollisionRectCenterPosition(), scene->getLaserEnd(), 2.5f);
+ }
+ }
+ drawEnemies(scene, false);
+ }
+
+ drawCollisionVols(scene);
+
+ if (scene->inCrosslinkMode())
+ {
+ drawLights(scene);
+ drawEntities(scene);
+ }
+ else
+ {
+ drawEntities(scene);
+ drawLights(scene);
+ }
+
+ drawCrossLink(scene);
+ drawInterface(scene);
+
+ if (scene->inCrosslinkMode())
+ {
+ //if in crosslink mode, draw black sprites of enemies and player last so that color is not affected by lights.
+ if ((!player->isInElevator() || !player->getElevatorDoor()->getShaft()->isMoving()) && !player->isPinning())
+ {
+ drawSpriteBind( scene->getPlayerPosition().x - cam.x,
+ scene->getPlayerPosition().y - cam.y,
+ 2.5f,
+ player->getRotation(),
+ player->getDirection() == Right ? resPlayer.get() : resPlayerLeft.get(),
+ player->getCurrentSprite(),
+ true, 0, 0, 0);
+ }
+ drawEnemies(scene, true);
+ sprintf(print, "Energy: %i", scene->getPlayerEnergy());
+ drawText(32, 160, print, RGB_WHITE, 1.0f, font1);
+ }
+
+ drawJumpTrajectory(scene);
+
+ MouseOverObject moo = scene->getObjectMousedOver();
+ float xOff = (mouseOverStrings[moo].length() * font2->getSize()) / 4.0f;
+ drawText((winX / 2) - xOff, winY - 32, mouseOverStrings[moo].c_str(), RGB_WHITE, 1.0f, font2);
+
+ if (scene->getObjectivesIncompleteTime() >= 0)
+ {
+ float xOff = (objectivesNotCompleted.length() * font2->getSize()) / 4.0f;
+ sprintf(print, "%s", objectivesNotCompleted.c_str());
+ drawText((winX / 2) - xOff, winY - 64, print, RGB_WHITE, 1.0f, font2);
+ }
+
+ if (scene->hasPlayerFiredShot() || player->isAimingGun())
+ {
+ sprintf(print, "%i", scene->getTimeToSniper());
+ if (scene->hasPlayerFiredShot())
+ {
+ drawText(winX - 64, winY - 32, print, RGB_RED, 1.0f, font1);
+ }
+ else
+ {
+ drawText(winX - 64, winY - 32, print, RGB_WHITE, 1.0f, font1);
+ }
+ if (player->isAimingGun())
+ {
+ sprintf(print, "Ammo: %i", scene->getNumPlayerBullets());
+ drawText(32, 128, print, RGB_WHITE, 1.0f, font1);
+ }
+ }
+
+ if (_enteredLightFlash)
+ {
+ drawRect(0, 0, 1.5f, _screenVBO, 1, 1, 1, scene->getLightEnteredAlpha(), true);
+ }
+#ifdef DEBUG
+ drawDebugSceneText(scene);
+#endif
+}
+
+void Renderer::updateLinkProgress(unsigned int dT)
+{
+ linkProgress += ((float)dT / 1000.0f);
+ if (linkProgress > 1.0f)
+ linkProgress = 0.0f;
+}
+
+void Renderer::drawDebugSceneText(Scene* scene)
+{
+ px = scene->getPlayer()->getCollisionRectPosition().x;
+ py = scene->getPlayer()->getCollisionRectPosition().y;
+
+ sprintf(print, "(%f, %f)", px, py);
+ px = scene->getPlayerPosition().x;
+ py = scene->getPlayerPosition().y;
+ drawText(px - scene->getCamera().x, py - scene->getCamera().y, print, RGB_WHITE, 1.0f, font1);
+ drawText(32, 32, (char*)"On Ground: ", RGB_WHITE, 1.0f, font1);
+ if (scene->getPlayer()->isOnGround())
+ drawText(192, 32, (char*)"Yes", 0.0f, 1.0f, 0.0f, 1.0f, font1);
+ else
+ drawText(192, 32, (char*)"No", RGB_RED, 1.0f, font1);
+
+ sprintf(print, "Attach Type: %i", scene->getPlayer()->getAttachType());
+ drawText(32, 64, print, RGB_WHITE, 1.0f, font1);
+ sprintf(print, "Light: %i", scene->getPlayer()->getLightVisibility());
+ drawText(32, 96, print, RGB_WHITE, 1.0f, font1);
+}
+
+void Renderer::drawInterface(Scene* scene)
+{
+ Rect gem = {32, 32, 32, 32};
+ if (scene->isPlayerInLight())
+ {
+ drawRect2Fill(gem, 1, 1, 1, 2);
+ }
+ else
+ {
+ drawRect2(gem, 1, 1, 1, 2);
+ }
+
+ if (scene->isLoadMenuVisible())
+ {
+ drawLoadMenu(/*scene*/);
+ }
+}
+
+void Renderer::drawLoadMenu(/*Scene* scene*/)
+{
+ int incr = 32;
+ // int index = 1;
+ // unsigned int i;
+
+ // drawText(winX / 2, winY / 2, (char*)"Select a save to load.", RGB_WHITE, 1.0f, 1.0f, atlas);
+
+ // for (i = 0; i < MAX_SAVES; i++)
+ // {
+ // if (scene->getSaveTimeAt(i) >= 0)
+ // {
+ // sprintf(print, (char*)"%i: Autosave %i seconds ago", index, scene->getSecondsSince(i));
+ // drawText(winX / 2, winY / 2 + incr, print, RGB_WHITE, 1.0f, 1.0f, atlas);
+ // index++;
+ // incr += 32;
+ // }
+ // }
+
+ drawText(winX / 2, winY / 2 + incr, (char*)"R: Restart", RGB_WHITE, 1.0f, font2);
+ incr += 32;
+ drawText(winX / 2, winY / 2 + incr, (char*)"F9: Load quick save", RGB_WHITE, 1.0f, font2);
+ incr += 32;
+ drawText(winX / 2, winY / 2 + incr, (char*)"ESC: Abort", RGB_WHITE, 1.0f, font2);
+}
+
+unsigned int Renderer::getScreenshotIndex()
+{
+ return screenshotIndex;
+}
+
+void Renderer::setScreenshotIndex(unsigned int value)
+{
+ screenshotIndex = value;
+}
+
+std::function Renderer::getScreenshotFunc()
+{
+ return [this] {takeScreenshot();};
+}
+
+void Renderer::handleSettingsChange()
+{
+ _enteredLightFlash = (Locator::getConfigManager()->getValue("entered_light_flash") == "1");
+}
+
+void Renderer::addShader(GLuint shader_program, const char* shader_text, GLuint shader_id)
+{
+ if (shader_id == 0)
+ {
+ LOGF((stderr, "Error creating shader!\n"));
+ exit(0);
+ }
+
+ const GLchar* p[1];
+ p[0] = shader_text;
+ GLint Lengths[1];
+ Lengths[0]= strlen(shader_text);
+ glShaderSource(shader_id, 1, p, Lengths);
+ glCompileShader(shader_id);
+ GLint success;
+ glGetObjectParameteriv(shader_id, GL_COMPILE_STATUS, &success);
+ if (!success)
+ {
+ GLchar InfoLog[1024];
+ glGetInfoLog(shader_id, 1024, nullptr, InfoLog);
+ LOGF((stderr, "Error compiling shader type: '%s'\n", InfoLog));
+ exit(1);
+ }
+ else
+ {
+ LOGF((stdout, "Compiled shader type!\n"));
+ }
+
+ glAttachShader(shader_program, shader_id);
+}
+
+GLuint Renderer::compileShaders(const char* vert_filename, const char* frag_filename)
+{
+ GLuint program = glCreateProgramObject();
+
+ if (!program)
+ {
+ LOGF((stderr, "Error creating shader program from %s and %s!\n", vert_filename, frag_filename));
+ exit(1);
+ }
+
+ GLuint vert_shader = glCreateShaderObject(GL_VERTEX_SHADER);
+ GLuint frag_shader = glCreateShaderObject(GL_FRAGMENT_SHADER);
+
+ char* vertString = file_read(vert_filename);
+ char* fragString = file_read(frag_filename);
+
+ addShader(program, vertString, vert_shader);
+ addShader(program, fragString, frag_shader);
+
+ GLint link_successful = 0;
+ GLchar error[1024] = {0};
+
+ glLinkProgram(program);
+ glGetObjectParameteriv(program, GL_LINK_STATUS, &link_successful);
+ if (!link_successful)
+ {
+ glGetInfoLog(program, sizeof(error), nullptr, error);
+ LOGF((stderr, "Error linking shader program: '%s'\n", error));
+ Assert(false);
+ }
+
+ glValidateProgram(program);
+ glGetObjectParameteriv(program, GL_VALIDATE_STATUS, &link_successful);
+
+ if (!link_successful)
+ {
+ glGetInfoLog(program, sizeof(error), nullptr, error);
+ LOGF((stderr, "Invalid shader program: '%s'\n", error));
+ Assert(false);
+ }
+
+ delete [] vertString;
+ delete [] fragString;
+
+ glDeleteObject(vert_shader);
+ glDeleteObject(frag_shader);
+
+ return program;
+}
\ No newline at end of file
diff --git a/draw.h b/draw.h
new file mode 100644
index 0000000..36b039d
--- /dev/null
+++ b/draw.h
@@ -0,0 +1,213 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef DRAW_H
+#define DRAW_H
+#include
+#include
+#include "file.h"
+#include "sprite.h"
+#include "scene.h"
+#include "gamestate.h"
+#include "menustate.h"
+#include "font.h"
+#include "stb_truetype.h"
+#include "button.h"
+#include "matrix.h"
+
+#define RGB_WHITE 1.0f, 1.0f, 1.0f
+#define RGB_RED 1.0f, 0.0f, 0.0f
+
+extern PFNGLATTACHSHADERPROC glAttachShader;
+extern PFNGLCOMPILESHADERPROC glCompileShader;
+extern PFNGLCREATEPROGRAMOBJECTARBPROC glCreateProgramObject;
+extern PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObject;
+extern PFNGLDELETEOBJECTARBPROC glDeleteObject;
+extern PFNGLGETINFOLOGARBPROC glGetInfoLog;
+extern PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameteriv;
+extern PFNGLGETUNIFORMLOCATIONARBPROC glGetUniformLocation;
+extern PFNGLLINKPROGRAMARBPROC glLinkProgram;
+extern PFNGLSHADERSOURCEARBPROC glShaderSource;
+extern PFNGLUNIFORM1IARBPROC glUniform1i;
+extern PFNGLUSEPROGRAMOBJECTARBPROC glUseProgramObject;
+extern PFNGLENABLEVERTEXATTRIBARRAYPROC glEnableVertexAttribArray;
+extern PFNGLDISABLEVERTEXATTRIBARRAYPROC glDisableVertexAttribArray;
+extern PFNGLBINDBUFFERPROC glBindBuffer;
+extern PFNGLUNIFORMMATRIX4FVPROC glUniformMatrix4fv;
+extern PFNGLGENBUFFERSPROC glGenBuffers;
+extern PFNGLUNIFORM3FPROC glUniform3f;
+extern PFNGLUNIFORM2FPROC glUniform2f;
+extern PFNGLUNIFORM1FPROC glUniform1f;
+extern PFNGLDELETEBUFFERSPROC glDeleteBuffers;
+extern PFNGLBINDATTRIBLOCATIONPROC glBindAttribLocation;
+extern PFNGLBUFFERDATAPROC glBufferData;
+extern PFNGLVERTEXATTRIBPOINTERPROC glVertexAttribPointer;
+extern PFNGLGETATTRIBLOCATIONPROC glGetAttribLocation;
+extern PFNGLVALIDATEPROGRAMPROC glValidateProgram;
+#if _WIN32
+extern PFNGLACTIVETEXTUREARBPROC glActiveTexture;
+#endif
+extern PFNGLGETBUFFERSUBDATAARBPROC glGetBufferSubData;
+
+class Renderer
+{
+public:
+ Renderer();
+ ~Renderer();
+ void init(int x, int y);
+ void setResolution(int x, int y);
+ bool initShaders();
+ void drawText(float x, float y, const char* text, float red, float green, float blue, float alpha_scale, std::shared_ptr font);
+ void drawTextLabel(std::shared_ptr tl);
+ void drawTextButton(std::shared_ptr tb);
+ void drawImageButton(std::shared_ptr ib);
+ void drawButton(std::shared_ptr button);
+ void drawState(std::shared_ptr state);
+ void drawMouseCursor(std::shared_ptr state);
+ void drawScene(Scene* scene);
+ void drawDebugSceneText(Scene* scene);
+ void toggleWireframe();
+ vec3f getCameraPosition();
+ void setCameraPosition(vec3f newPos);
+ void takeScreenshot();
+ void bufferQuads();
+ void bufferScreenVBO(float x, float y);
+ void drawRect(float x, float y, float z, GLuint vbo, float red, float green, float blue, float alpha, bool fill);
+ void drawRect2(Rect rect, float red, float green, float blue, int z);
+ void drawRect2Fill(Rect rect, float red, float green, float blue, int z);
+ void drawSprite(float x, float y, float z, float rotation, SpriteSheet* resource, unsigned int index, bool colorOverride, float red, float green, float blue);
+ void drawSpriteBind(float x, float y, float z, float rotation, SpriteSheet* resource, unsigned int index, bool colorOverride, float red, float green, float blue);
+ void drawFieldOfView(Scene* scene, FieldOfView* fov, GLuint program);
+ void drawTileLayer(Scene* scene, int z);
+ void updateLinkProgress(unsigned int dT);
+
+ void generateSheetBuffers(SpriteSheet* sheet, int tileDim);
+
+ void drawCollisionVols(Scene* scene);
+ void drawEntities(Scene* scene);
+ void drawEnemies(Scene* scene, bool crosslink);
+ void drawLinkableEntities(Scene* scene);
+ void drawLights(Scene* scene);
+ void drawJumpTrajectory(Scene* scene);
+ void drawCrossLink(Scene* scene);
+ void drawLink(Scene* scene, Circuit c, vec2f pA, vec2f pB);
+ void drawLine(Scene* scene, float r, float g, float b, float a, vec2f pA, vec2f pB, float z);
+ void drawInterface(Scene* scene);
+ void drawLoadMenu(/*Scene* scene*/);
+
+ //test functions.
+ void drawEnemyFOV(Scene* scene, Enemy* enemy);
+
+ //shaders
+ void addShader(GLuint shader_program, const char* shader_text, GLuint shader_id);
+ GLuint compileShaders(const char* vert_filename, const char* frag_filename);
+
+ unsigned int getScreenshotIndex();
+ void setScreenshotIndex(unsigned int value);
+
+ void handleSettingsChange();
+
+ std::function getScreenshotFunc();
+
+private:
+ GLuint entRectVBO; //vbo for storing tile-sized quads.
+ GLuint pointVBO; //vbo for storing quad for jump trajectory.
+ GLuint _screenVBO; //vbo for quad with dimensions of window.
+
+ bool wireframe;
+ GLuint attribute_coord;
+ GLuint uniform_color;
+ GLuint uniform_alpha_scale;
+ GLuint text_vbo;
+ GLuint gWorldLocation;
+
+ //textures
+
+ GLuint buttonTex;
+ //programs
+ GLuint pgmText;
+ GLuint pgmButton;
+ GLuint pgmColoredSprite; //for use with crosslink.
+ GLuint pgmMap;
+ GLuint pgmLight;
+ GLuint pgmCamera;
+
+ //transformations
+ mat4f orthographic;
+ mat4f transformation;
+ mat4f camera_rotation;
+ mat4f quadTransform;
+
+ //for taking screenshots
+ SDL_Surface* image;
+
+ char screenshot_filename[32];
+ unsigned int screenshotIndex;
+ int winX;
+ int winY;
+
+ GLuint notex;
+
+ bool _enteredLightFlash;
+
+ //for animating crosslink lines
+ float linkProgress;
+ vec2f progress;
+ vec2f distance;
+
+ float px, py;
+ char print[256];
+
+ struct SpriteSheetDeleter
+ {
+ void operator()(SpriteSheet* sheet) const
+ {
+ GLuint vbo = sheet->getVertexBuffer();
+ glDeleteBuffers(sheet->getNumberOfSprites(), sheet->getIndexBuffers());
+ glDeleteBuffers(1, &vbo);
+ delete sheet;
+ }
+ };
+
+ //Fonts
+ std::shared_ptr font1;
+ std::shared_ptr font2;
+
+ //Sprite Resources
+ std::unique_ptr resPlayer;
+ std::unique_ptr resPlayerLeft;
+ std::unique_ptr resGuardRight;
+ std::unique_ptr resGuardLeft;
+ std::unique_ptr resEnforcerRight;
+ std::unique_ptr resEnforcerLeft;
+ std::unique_ptr resProfessionalRight;
+ std::unique_ptr resProfessionalLeft;
+ std::unique_ptr resSniperRight;
+ std::unique_ptr resSniperLeft;
+ std::unique_ptr resObjects;
+ std::unique_ptr resLinkables;
+ std::unique_ptr resInterface;
+ std::unique_ptr resGlass;
+
+ //Gameplay strings
+ std::string mouseOverStrings[NUMBER_OF_MOUSEOVER_OBJECTS];
+ std::string objectivesNotCompleted;
+};
+
+#endif
diff --git a/elevators.cpp b/elevators.cpp
new file mode 100644
index 0000000..6dbdeaf
--- /dev/null
+++ b/elevators.cpp
@@ -0,0 +1,346 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+
+ElevatorDoor::ElevatorDoor(float x, float y) : Entity(x, y)
+{
+ setCollisionRectDims(32, 48, ENTDIM);
+ _open = false;
+ _switch = nullptr;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatorclosed");
+}
+
+void ElevatorDoor::open(bool animate)
+{
+ _open = true;
+ _switch->activateTarget();
+ _switch->changeSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "elev_switch_open"));
+ if (!isClosing())
+ {
+ if (animate)
+ {
+ Locator::getAudio()->playSound("elevator_arrive");
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ELEVATOR_OPEN));
+ }
+ else
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatoropen"));
+ }
+ }
+ else
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatorclosed"));
+ }
+
+}
+
+void ElevatorDoor::close(bool animate)
+{
+ _open = false;
+ _switch->changeSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "elev_switch_off"));
+ if (!animate)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatorclosed"));
+ }
+ else
+ {
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ELEVATOR_CLOSE));
+ }
+}
+
+bool ElevatorDoor::isOpen()
+{
+ return _open;
+}
+
+void ElevatorDoor::registerShaft(ElevatorShaft* shaft)
+{
+ _shaft = shaft;
+}
+
+void ElevatorDoor::registerSwitch(ElevatorSwitch* eSwitch)
+{
+ _switch = eSwitch;
+}
+
+ElevatorShaft* ElevatorDoor::getShaft()
+{
+ return _shaft;
+}
+
+void ElevatorDoor::update(unsigned int dT)
+{
+ Entity::update(dT);
+ if (isOpening() && _currentAnimFinished)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatoropen"));
+ }
+
+ if (isClosing() && _currentAnimFinished)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", "elevatorclosed"));
+ }
+}
+
+bool ElevatorDoor::isOpening()
+{
+ return _activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ELEVATOR_OPEN);
+}
+
+bool ElevatorDoor::isClosing()
+{
+ return _activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ELEVATOR_CLOSE);
+}
+
+ElevatorSwitch* ElevatorDoor::getSwitch()
+{
+ return _switch;
+}
+
+ElevatorShaft::ElevatorShaft(int x)
+{
+ _moving = false;
+ _waitingForClose = false;
+ _target = nullptr;
+ _x = x;
+ _openDoor = nullptr;
+ _elevatorPosition = vec2f(_x, 0);
+ _yVel = 0;
+
+ _rect.x = _x + 48;
+ _rect.y = 0;
+ _rect.w = 32;
+ _rect.h = 48;
+
+ _acceleration.accelerating = false;
+ _acceleration.accel = 0.0f;
+ _acceleration.target = 0.0f;
+}
+
+ElevatorShaft::~ElevatorShaft()
+{
+ _doors.clear();
+}
+
+Rect ElevatorShaft::getRect()
+{
+ return _rect;
+}
+
+vec2f ElevatorShaft::getElevatorPosition()
+{
+ return _elevatorPosition;
+}
+
+void ElevatorShaft::update()
+{
+ if (_moving)
+ {
+ if (!_acceleration.accelerating && fabs(_elevatorPosition.y - _target->getCollisionRectPosition().y) < fabs(_yVel) * 32.0f)
+ {
+ //start slowing down to destination.
+ setAccel(&_acceleration, true, _yVel < 0.0f ? 0.02f : -0.02f, _yVel < 0.0f ? -0.1f : 0.1f);
+ Locator::getAudio()->playSound("elevator_decelerate");
+ }
+ if (_acceleration.accelerating)
+ {
+ if (fabs(_yVel - _acceleration.target) < fabs(_acceleration.accel))
+ {
+ _acceleration.accelerating = false;
+ _acceleration.accel = 0.0f;
+ _yVel = _acceleration.target;
+ }
+ _yVel += _acceleration.accel;
+ }
+ _elevatorPosition.y += _yVel;
+ _rect.y = _elevatorPosition.y;
+ }
+
+ if (_waitingForClose && !_openDoor->isClosing() && _target != nullptr)
+ {
+ _yVel = _target->getCollisionRectPosition().y - _elevatorPosition.y;
+ _yVel /= fabs(_yVel);
+ _yVel *= 0.1f;
+ setAccel(&_acceleration, true, _yVel < 0.0f ? -0.01f : 0.01f, _yVel < 0.0f ? -1.5f : 1.5f);
+ setMoving(true);
+ _waitingForClose = false;
+ Locator::getAudio()->playSound("elevator_leave");
+ }
+}
+
+int ElevatorShaft::getX()
+{
+ return _x;
+}
+
+void ElevatorShaft::setOpenDoor(ElevatorDoor* door, bool animate)
+{
+ int index = containsDoor(door);
+ if (index < 0)
+ {
+ return;
+ }
+
+ _yVel = 0;
+ _openDoor = door;
+ _openDoor->open(animate);
+ _moving = false;
+
+ for (ElevatorDoor* door : _doors)
+ {
+ if (door != _openDoor)
+ {
+ door->close(false);
+ }
+ }
+
+ _elevatorPosition.y = door->getCollisionRectPosition().y;
+ _rect.y = _elevatorPosition.y;
+}
+
+void ElevatorShaft::setOpenDoorFirst()
+{
+ setOpenDoor(_doors[0], false);
+}
+
+void ElevatorShaft::setTarget(ElevatorDoor* target)
+{
+ int index = containsDoor(target);
+ if (index < 0)
+ {
+ return;
+ }
+
+ _openDoor->close(true);
+ _waitingForClose = true;
+ _target = target;
+ _target->getSwitch()->changeSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "elev_switch_wait"));
+}
+
+void ElevatorShaft::addDoor(ElevatorDoor* ed)
+{
+ _doors.push_back(ed);
+}
+
+bool ElevatorShaft::isMoving()
+{
+ return _moving;
+}
+
+int ElevatorShaft::containsDoor(ElevatorDoor* door)
+{
+ unsigned int i;
+ for (i = 0; i < _doors.size(); i++)
+ {
+ if (_doors[i] == door)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+void ElevatorShaft::setMoving(bool b)
+{
+ _moving = b;
+}
+
+ElevatorDoor* ElevatorShaft::getOpenDoor()
+{
+ return _openDoor;
+}
+
+ElevatorDoor* ElevatorShaft::getTarget()
+{
+ return _target;
+}
+
+void ElevatorShaft::calculateDoorOrders(int yMax)
+{
+ size_t i;
+ size_t prevDoor = -1;
+ float yMin = (float)yMax;
+ int y;
+ for (i = 0; i < _doors.size(); i++)
+ {
+ if (_doors[i]->getPosition().y < yMin)
+ {
+ yMin = _doors[i]->getPosition().y;
+ prevDoor = i;
+ }
+ }
+
+ _order.push_back(prevDoor);
+
+ //door positions are unordered in vector, so scan down and get the real order!
+
+ for (y = (int)yMin; y < yMax; y++)
+ {
+ for (i = 0; i < _doors.size(); i++)
+ {
+ if (_doors[i]->getPosition().y == y && _doors[prevDoor] != _doors[i])
+ {
+ prevDoor = i;
+ _order.push_back(prevDoor);
+ break;
+ }
+ }
+ }
+}
+
+ElevatorDoor* ElevatorShaft::getDoorAbove(ElevatorDoor* door)
+{
+ int index = getDoorIndexOrdered(containsDoor(door));
+ if (index <= 0)
+ {
+ return nullptr; //door is already the highest or does not exist in this shaft
+ }
+
+ return _doors[_order[index - 1]];
+}
+
+ElevatorDoor* ElevatorShaft::getDoorBelow(ElevatorDoor* door)
+{
+ int index = getDoorIndexOrdered(containsDoor(door));
+ if (index == -1 || index + 1 >= (int)_order.size())
+ {
+ return nullptr; //door is already the lowest or does not exist in this shaft
+ }
+
+ return _doors[_order[index + 1]];
+}
+
+int ElevatorShaft::getDoorIndexOrdered(int index)
+{
+ unsigned int i;
+ for (i = 0; i < _order.size(); i++)
+ {
+ if (_order[i] == index)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+float ElevatorShaft::getVelocity()
+{
+ return _yVel;
+}
\ No newline at end of file
diff --git a/enemy.cpp b/enemy.cpp
new file mode 100644
index 0000000..1ff010f
--- /dev/null
+++ b/enemy.cpp
@@ -0,0 +1,839 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+
+#define GUNPOINT_MIN_MOVE_DISTANCE_SQUARED 15000
+#define GUNPOINT_MAX_MOVE_DISTANCE_SQUARED 17000
+#define PATROLSPEED 0.06
+#define INVESTIGATESPEED 0.25
+#define TIMETOPATROL 4000 //Time for enemy to switch from IDLE_CAUTION to PATROLLING.
+#define TIMETOTURN 500
+#define GUARDREACTIONTIME 500
+#define PROFESSIONALREACTIONTIME 250
+#define TIME_TO_RESET_REACTION_TIME 20000
+#define ENEMYALERTTIME 500
+#define GUARD_SWITCH_TIME 500
+#define GUARD_DISTANCE_TO_USE_SWITCH 16.0f
+#define ENEMY_TIME_GUN_UP 1000
+
+Enemy::Enemy(float x, float y, Direction startingDir, bool startPatrol, EnemyType type) : LivingEntity(x, y, startingDir)
+{
+ setCollisionRectDims(16, 40, ENTDIM);
+ _waitingForAlert = false;
+ _type = type;
+ _gun = nullptr;
+ if (startPatrol)
+ {
+ _state = PATROLLING;
+ _velocity.x = (startingDir == Right) ? PATROLSPEED : -PATROLSPEED;
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_PATROLLING));
+ }
+ else
+ {
+ _state = IDLE;
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_GROUND));
+ }
+ _playerInSight = false;
+ _hasSeenPlayer = false;
+ _target = vec2f(0, 0);
+ _shootTarget = vec2f(0, 0);
+ _desiredStairDirection = NotMoving;
+ _timeToPatrol = TIMETOPATROL;
+ _timeToTurn1 = TIMETOTURN;
+ _timeToTurn2 = TIMETOTURN;
+ _timeToHitSwitch = GUARD_SWITCH_TIME;
+ setResolve(0, Shot_None);
+ _secondaryTarget = nullptr;
+ _numSwitchAttempts = 0;
+ _lightToActivate = nullptr;
+ _strongestLight = nullptr;
+ _targetSwitch = nullptr;
+ _targetType = TARGET_NONE;
+ _alertType = ALERT_NONE;
+ _fullyPunched = false;
+ _ignoreFall = true;
+ _readyToShoot = false;
+ _heldAtGunpoint = false;
+ _timeToAlert = ENEMYALERTTIME;
+ _timeToResetReactionTime = TIME_TO_RESET_REACTION_TIME;
+
+ resetReactionTime();
+
+ if (_type == Enemy_Sniper)
+ {
+ _readyToShoot = true;
+ _state = IDLE;
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_GROUND));
+ }
+}
+
+void Enemy::setPosition(float x, float y)
+{
+ Entity::setPosition(x, y);
+ _ignoreFall = true;
+}
+
+void Enemy::landOnGround()
+{
+ LivingEntity::landOnGround();
+ _ignoreFall = false;
+ if (_state == FALLING)
+ {
+ _velocity.x = 0;
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_FALLEN));
+ }
+}
+
+void Enemy::update(unsigned int dT)
+{
+ _fixDirection = _heldAtGunpoint || (_state == PINNED);
+ switch (_state)
+ {
+ case AIMING:
+ if (_reactionTime <= 0 && !_heldAtGunpoint && _playerInSight)
+ {
+ _gun->fire(Shot_FromEnemyVoluntary);
+ if (_reactionTime <= 0)
+ {
+ resetReactionTime();
+ }
+ else
+ {
+ _gun->unlink();
+ }
+ }
+ case PATROLLING:
+ case INVESTIGATING:
+ if (_secondaryTarget != nullptr && _overlappingStairs != nullptr && _overlappingStairs == _secondaryTarget)
+ {
+ //time to move through stairs.
+ setStairMovement(_desiredStairDirection);
+ }
+ //If enemy is close enough to their target switch...
+ else if (_secondaryTarget != nullptr && dynamic_cast(_secondaryTarget) && vec2f_distance(getCollisionRectPosition(), _secondaryTarget->getCollisionRectPosition()) < GUARD_DISTANCE_TO_USE_SWITCH)
+ {
+ changeState(USING_SWITCH);
+ }
+ else if (_targetSwitch != nullptr && (_targetType == TARGET_LIGHTSWITCH || _targetType == TARGET_ALARM) && vec2f_distance(getCollisionRectPosition(), _target) < GUARD_DISTANCE_TO_USE_SWITCH)
+ {
+ changeState(USING_SWITCH);
+ }
+ case IDLE:
+ LivingEntity::update(dT);
+ updateCollisionRectPosition();
+ break;
+ case IDLE_CAUTION:
+ LivingEntity::update(dT);
+ updateCollisionRectPosition();
+
+ _timeToTurn1 -= dT;
+
+ if (_timeToTurn1 <= 0 && _timeToTurn2 == TIMETOTURN)
+ {
+ reverseDirection();
+ _timeToTurn2 -= dT;
+ }
+ else if (_timeToTurn1 <= 0 && _timeToTurn2 > 0)
+ {
+ _timeToTurn2 -= dT;
+ }
+ else if (_timeToTurn1 <= 0 && _timeToTurn2 <= 0 && _timeToPatrol == TIMETOPATROL)
+ {
+ reverseDirection();
+ _timeToPatrol -= dT;
+ }
+
+ if (_timeToPatrol < TIMETOPATROL)
+ {
+ _timeToPatrol -= dT;
+ }
+
+ if (_timeToPatrol <= 0)
+ {
+ _timeToPatrol = TIMETOPATROL;
+ _timeToTurn1 = TIMETOTURN;
+ _timeToTurn2 = TIMETOTURN;
+ changeState(PATROLLING);
+ }
+
+ break;
+ case USING_SWITCH:
+ LivingEntity::update(dT);
+ updateCollisionRectPosition();
+ _timeToHitSwitch -= dT;
+ if (_timeToHitSwitch <= 0)
+ {
+ _timeToHitSwitch = GUARD_SWITCH_TIME;
+ if (_secondaryTarget != nullptr && dynamic_cast(_secondaryTarget))
+ {
+ if (_targetType == TARGET_LIGHTSWITCH)
+ {
+ changeState(PATROLLING);
+ }
+ else if (_targetType != TARGET_NONE)
+ {
+ changeState(INVESTIGATING);
+ }
+
+ ((LightSwitch*)_secondaryTarget)->activate();
+ if (_targetSwitch == _secondaryTarget)
+ {
+ _targetSwitch = nullptr;
+ }
+ _secondaryTarget = nullptr;
+ _numSwitchAttempts++;
+ }
+ else if (_targetSwitch != nullptr)
+ {
+ if (_targetType == TARGET_LIGHTSWITCH)
+ {
+ _targetSwitch->activate();
+ _numSwitchAttempts++;
+ if ((_lightToActivate != nullptr && _lightToActivate->isActive()) || _numSwitchAttempts == 3) //activate the light, or give up.
+ {
+ _targetSwitch = nullptr;
+ _targetType = TARGET_NONE;
+ changeState(PATROLLING);
+ _lightToActivate = nullptr;
+ _numSwitchAttempts = 0;
+ }
+ }
+
+ if (_targetType == TARGET_ALARM)
+ {
+ (static_cast(_targetSwitch))->deactivate();
+ changeState(IDLE_CAUTION);
+ }
+ }
+ }
+ break;
+ case PINNED:
+ case KNOCKED_OUT:
+ case FALLING:
+ LivingEntity::update(dT);
+ updateCollisionRectPosition();
+ break;
+ }
+
+ if (_playerInSight)
+ {
+ _shootTarget = _target;
+ if (_heldAtGunpoint)
+ {
+ vec2f sub = getCollisionRectCenterPosition() - _shootTarget;
+ float len = sub.length_squared();
+ if (len < GUNPOINT_MIN_MOVE_DISTANCE_SQUARED)
+ {
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_PATROLLING));
+ _velocity.x = vec2f_normalize(sub).x / 48.0f;
+ }
+ else if (len > GUNPOINT_MAX_MOVE_DISTANCE_SQUARED)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_CAUTION));
+ _velocity.x = 0;
+ }
+ }
+ }
+ else
+ {
+ _shootTarget.x = getCollisionRectCenterPosition().x + (_dir == Right ? 64 : -64);
+ _shootTarget.y = getCollisionRectCenterPosition().y;
+ }
+
+ if (_readyToShoot)
+ {
+ _reactionTime -= dT;
+ _timeToResetReactionTime -= dT;
+ if (_reactionTime <= 0)
+ {
+ _reactionTime = 0;
+ }
+
+ if (_timeToResetReactionTime <= 0)
+ {
+ setReadyToShoot(false);
+ }
+ }
+
+ if (_resolve.timeSinceShot > 0)
+ {
+ _resolve.timeSinceShot += dT;
+ if (_resolve.timeSinceShot < ENEMY_TIME_GUN_UP)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_CAUTION));
+ }
+ else
+ {
+ if (_resolve.shotType == Shot_FromEnemyVoluntary)
+ {
+ //if player is dead, this will remain.
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_GROUND));
+ }
+
+ if (_resolve.shotType == Shot_FromEnemyInvoluntary)
+ {
+ //why did my gun fire by itself?
+ changeState(IDLE_CAUTION);
+ }
+ setResolve(0, Shot_None);
+ }
+ }
+
+ if (_waitingForAlert)
+ {
+ _timeToAlert -= dT;
+ if (_timeToAlert <= 0)
+ {
+ _waitingForAlert = false;
+ _timeToAlert = ENEMYALERTTIME;
+ if (isPositionBehind(_target.x) && _desiredStairDirection == NotMoving)
+ {
+ reverseDirection();
+ setVelX(-_velocity.x);
+ }
+ switch(_alertType)
+ {
+ case ALERT_WALK:
+ changeState(PATROLLING);
+ break;
+ case ALERT_RUN:
+ changeState(INVESTIGATING);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ if (_gun != nullptr)
+ _gun->update(dT);
+
+ int r;
+ if (_activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ENEMY_KNOCK_OUT))
+ {
+ if (_currentAnimFinished)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_FALLEN_PLAYER));
+ _fullyPunched = false;
+ }
+ else if (_sprite == Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_PUNCHED) && !_fullyPunched)
+ {
+ r = ceil(((double)rand() / (double)(RAND_MAX)) * 10);
+ if (r > 5)
+ {
+ Locator::getAudio()->playSound("punch1");
+ }
+ else
+ {
+ Locator::getAudio()->playSound("punch2");
+ }
+
+ _fullyPunched = true;
+ }
+ }
+
+ if (_activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ENEMY_ENTER_STAIRS) && _currentAnimFinished)
+ {
+ changeToStaticSprite(7);
+ }
+
+ if (_activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ENEMY_EXIT_STAIRS) && _currentAnimFinished)
+ {
+ _secondaryTarget = nullptr;
+ _desiredStairDirection = NotMoving;
+ setSprite();
+ if (_state == PATROLLING)
+ {
+ _velocity.x = (_dir == Right) ? PATROLSPEED : -PATROLSPEED;
+ }
+ if (_state == INVESTIGATING)
+ {
+ _velocity.x = (_dir == Right) ? INVESTIGATESPEED : -INVESTIGATESPEED;
+ if (isPositionBehind(_target.x))
+ {
+ setVelX(-_velocity.x);
+ }
+ }
+ }
+}
+
+void Enemy::seePlayer(float x, float y)
+{
+ _target = vec2f(x, y);
+ _playerInSight = true;
+ _hasSeenPlayer = true;
+
+ if (_state != AIMING)
+ changeState(AIMING);
+}
+
+void Enemy::alertToPosition(float x, float y, AlertType aType, TargetType tType)
+{
+ if (_state == FALLING || _state == KNOCKED_OUT || _state == PINNED)
+ {
+ return;
+ }
+
+ if (aType == ALERT_LOOK)
+ {
+ if (_state == IDLE && isPositionBehind(x))
+ {
+ reverseDirection();
+ }
+ return;
+ }
+
+ if (!_waitingForAlert)
+ {
+ _waitingForAlert = true;
+ if (tType == TARGET_PLAYER)
+ {
+ _timeToAlert = 1;
+ }
+ }
+
+ _target.x = x;
+ _target.y = y;
+ _targetType = tType;
+ _alertType = aType;
+ //if target requires running (like noise), turn immediately. Otherwise, turn
+ //only when alert time runs out (like when light goes out and switch is behind.)
+ if (aType == ALERT_RUN && _desiredStairDirection == NotMoving && isPositionBehind(x))
+ {
+ reverseDirection();
+ setVelX(-_velocity.x);
+ }
+}
+
+bool Enemy::hasSeenPlayer()
+{
+ return _hasSeenPlayer;
+}
+
+void Enemy::loseSightOfPlayer(bool wentInElevator, vec2f newTarget)
+{
+ _playerInSight = false;
+ if (_type == Enemy_Sniper)
+ {
+ return;
+ }
+ _velocity.x = 0; //done in case guard is maintaining distance from aiming player
+ if (!wentInElevator) //otherwise, wait to strike...
+ {
+ alertToPosition(newTarget.x, newTarget.y, ALERT_RUN, TARGET_PLAYER);
+ }
+ else
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_CAUTION));
+ }
+}
+
+bool Enemy::canSeePlayer()
+{
+ return _playerInSight;
+}
+
+vec2f Enemy::getTarget()
+{
+ return _target;
+}
+
+TargetType Enemy::getTargetType()
+{
+ return _targetType;
+}
+
+void Enemy::forgetTarget()
+{
+ _targetType = TARGET_NONE;
+ _alertType = ALERT_NONE;
+}
+
+GuardState Enemy::getState()
+{
+ return _state;
+}
+
+AlertType Enemy::getAlertType()
+{
+ return _alertType;
+}
+
+void Enemy::changeState(GuardState state)
+{
+ if (_state == state)
+ return;
+
+ if ((_state == KNOCKED_OUT || _state == FALLING) && !(state == KNOCKED_OUT || state == FALLING))
+ {
+ setCollisionRectDims(16, 40, ENTDIM);
+ }
+
+ if (_state == IDLE_CAUTION)
+ {
+ //reset some variables for when enemy returns to IDLE_CAUTION.
+ _timeToPatrol = TIMETOPATROL;
+ _timeToTurn1 = TIMETOTURN;
+ _timeToTurn2 = TIMETOTURN;
+ }
+
+ _state = state;
+
+ switch (state)
+ {
+ case IDLE:
+ case USING_SWITCH:
+ _velocity.x = 0;
+ break;
+ case IDLE_CAUTION:
+ if (!_acceleration.accelerating)
+ {
+ _velocity.x = 0;
+ }
+ forgetTarget();
+ break;
+ case PATROLLING:
+ _velocity.x = (_dir == Right) ? PATROLSPEED : -PATROLSPEED;
+ break;
+ case INVESTIGATING:
+ _velocity.x = (_dir == Right) ? INVESTIGATESPEED : -INVESTIGATESPEED;
+ break;
+ case AIMING:
+ _velocity.x = 0;
+ break;
+ case PINNED:
+ setCollisionRectDims(40, 16, ENTDIM);
+ setCollisionRectPosition(_collisionRect.x, _collisionRect.y + 12);
+ _targetType = TARGET_NONE;
+ _alertType = ALERT_LOOK;
+ _waitingForAlert = false;
+ break;
+ case KNOCKED_OUT:
+ case FALLING:
+ _velocity.x = 0.0f;
+ setCollisionRectDims(40, 16, ENTDIM);
+ _playerInSight = false;
+ _onGround = false;
+ _targetType = TARGET_NONE;
+ _alertType = ALERT_LOOK;
+ break;
+ }
+
+ setSprite();
+}
+
+void Enemy::setSprite()
+{
+ switch (_state)
+ {
+ case IDLE:
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_GROUND));
+ break;
+ case IDLE_CAUTION:
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_CAUTION));
+ break;
+ case PATROLLING:
+ case INVESTIGATING:
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_PATROLLING));
+ break;
+ case AIMING:
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_IDLE_CAUTION));
+ break;
+ case PINNED:
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_PLAYER_STRUGGLE));
+ break;
+ case KNOCKED_OUT:
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_FALLEN));
+ break;
+ case FALLING:
+ changeToStaticSprite(Locator::getSpriteManager()->getGuardSpriteIndex(GUARD_FALLING));
+ break;
+ case USING_SWITCH:
+ break;
+ }
+}
+
+bool Enemy::goingForStairs()
+{
+ return _secondaryTarget != nullptr && dynamic_cast(_secondaryTarget);
+}
+
+void Enemy::setDesiredStairsAndDirection(Stairs* sw, StairTraversal st)
+{
+ if (isPositionBehind(sw->getCollisionRect().x))
+ {
+ reverseDirection();
+ setVelX(-_velocity.x);
+ }
+ setSecondaryTarget(sw);
+ _desiredStairDirection = st;
+}
+
+void Enemy::setSecondaryTarget(Entity* ent)
+{
+ if (ent != nullptr)
+ {
+ if (isPositionBehind(ent->getCollisionRectPosition().x))
+ {
+ setVelX(-_velocity.x);
+ }
+ }
+ _secondaryTarget = ent;
+}
+
+Entity* Enemy::getSecondaryTarget()
+{
+ return _secondaryTarget;
+}
+
+unsigned int Enemy::getNumSwitchAttempts()
+{
+ return _numSwitchAttempts;
+}
+
+void Enemy::resetSwitchAttempts()
+{
+ _numSwitchAttempts = 0;
+}
+
+void Enemy::setStrongestLight(FieldOfView* light)
+{
+ if (_lightToActivate != nullptr && light != nullptr && _state == IDLE)
+ {
+ changeState(PATROLLING);
+ }
+
+ if ((light == _lightToActivate && light != nullptr) || (light != nullptr && _lightToActivate != nullptr && light->getLightFixture() == _lightToActivate->getLightFixture()))
+ {
+ //Nothing to activate, since it's now on.
+ _lightToActivate = nullptr;
+ _targetType = TARGET_NONE;
+ //_alertType = ALERT_LOOK;
+ }
+ _strongestLight = light;
+}
+
+void Enemy::setLightToActivate(FieldOfView* toActivate)
+{
+ if (_state == FALLING || _state == KNOCKED_OUT)
+ {
+ return;
+ }
+ _lightToActivate = toActivate;
+}
+
+void Enemy::loseStrongestLight()
+{
+ _lightToActivate = _strongestLight;
+}
+
+FieldOfView* Enemy::getStrongestLight()
+{
+ return _strongestLight;
+}
+
+FieldOfView* Enemy::getLightToActivate()
+{
+ return _lightToActivate;
+}
+
+void Enemy::setLinkableTarget(LinkableEntity* ls)
+{
+ if (_state == FALLING || _state == KNOCKED_OUT)
+ {
+ return;
+ }
+ _targetSwitch = ls;
+}
+
+bool Enemy::ignoringFall()
+{
+ return _type == Enemy_Sniper ? true : _ignoreFall;
+}
+
+void Enemy::setIgnoreFall(bool b)
+{
+ _ignoreFall = b;
+}
+
+LinkableEntity* Enemy::getTargetSwitch()
+{
+ return _targetSwitch;
+}
+
+void Enemy::setReactionTime(int time)
+{
+ _reactionTime = time;
+}
+
+int Enemy::getReactionTime()
+{
+ return _reactionTime;
+}
+
+void Enemy::setReadyToShoot(bool b)
+{
+ if (_readyToShoot && b)
+ {
+ return;
+ }
+
+ _readyToShoot = b;
+
+ if (_readyToShoot)
+ {
+ Locator::getAudio()->playSound("pistol_ready");
+ }
+ else
+ {
+ if (_type != Enemy_Sniper)
+ {
+ _timeToResetReactionTime = TIME_TO_RESET_REACTION_TIME;
+ resetReactionTime();
+ }
+ else
+ {
+ _timeToResetReactionTime = 0;
+ _readyToShoot = true;
+ }
+ }
+}
+
+bool Enemy::isWaitingForAlert()
+{
+ return _waitingForAlert;
+}
+
+void Enemy::setWaitingForAlert(bool b)
+{
+ _waitingForAlert = b;
+}
+
+void Enemy::setFireFunction(std::function func)
+{
+ fireWeapon = func;
+}
+
+EnemyType Enemy::getType()
+{
+ return _type;
+}
+
+void Enemy::resetReactionTime()
+{
+ switch (_type)
+ {
+ case Enemy_Guard:
+ case Enemy_Enforcer:
+ _reactionTime = GUARDREACTIONTIME;
+ break;
+ case Enemy_Professional:
+ _reactionTime = PROFESSIONALREACTIONTIME;
+ break;
+ case Enemy_Sniper:
+ _reactionTime = 0;
+ break;
+ }
+}
+
+void Enemy::setHeldAtGunpoint(bool b)
+{
+ if (_heldAtGunpoint && !b)
+ {
+ _reactionTime = 500;
+ }
+ _heldAtGunpoint = b;
+ if (_type == Enemy_Professional)
+ {
+ _heldAtGunpoint = false;
+ }
+}
+
+bool Enemy::isHeldAtGunpoint()
+{
+ return _heldAtGunpoint;
+}
+
+void Enemy::setCanSeePlayer(bool b)
+{
+ _playerInSight = b;
+}
+
+void Enemy::_fireWeapon(GunShotTraceType gstt)
+{
+ fireWeapon(this, _shootTarget, gstt);
+ setResolve(1, gstt);
+}
+
+void Enemy::setGun(EnemyGun* gun)
+{
+ _gun = gun;
+}
+
+EnemyGun* Enemy::getGun()
+{
+ return _gun;
+}
+
+void Enemy::setResolve(int timeToReact, GunShotTraceType gstt)
+{
+ _resolve.timeSinceShot = timeToReact;
+ _resolve.shotType = gstt;
+ if (gstt != Shot_None)
+ {
+ _velocity.x = 0;
+ }
+}
+
+EnemyShotResolve Enemy::getResolve()
+{
+ return _resolve;
+}
+
+void Enemy::arriveAtStairs(Stairs* st)
+{
+ LivingEntity::arriveAtStairs(st);
+ Rect stairsRect = st->getCollisionRect();
+ setCollisionRectPosition(stairsRect.x + 8, stairsRect.y + stairsRect.h - _collisionRect.h - 1);
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_EXIT_STAIRS));
+}
+
+void Enemy::setStairMovement(StairTraversal st)
+{
+ if (isAnimatingThroughStairs() || isMovingThroughStairs())
+ {
+ return;
+ }
+ LivingEntity::setStairMovement(st);
+ _waitingForAlert = false;
+
+ if (isMovingThroughStairs())
+ {
+ Rect stairsRect = _overlappingStairs->getCollisionRect();
+ setCollisionRectPosition(stairsRect.x + 8, stairsRect.y + stairsRect.h - _collisionRect.h - 1);
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ENEMY_ENTER_STAIRS));
+ }
+}
+
+bool Enemy::isAnimatingThroughStairs()
+{
+ return (_activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ENEMY_ENTER_STAIRS)) ||
+ (_activeSequence == Locator::getAnimationManager()->getSequence(ANIM_ENEMY_EXIT_STAIRS));
+}
\ No newline at end of file
diff --git a/entity.cpp b/entity.cpp
new file mode 100644
index 0000000..b312d75
--- /dev/null
+++ b/entity.cpp
@@ -0,0 +1,318 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+
+Entity::Entity(float x, float y)
+{
+ _position.x = x;
+ _position.y = y;
+
+ _velocity.x = 0;
+ _velocity.y = 0;
+
+ _offset.x = 0;
+ _offset.y = 0;
+
+ _rotation = 0.0f;
+
+ _activeSequence = nullptr;
+
+ _sprite = 0;
+
+ _animDT = 0;
+ _currentSequenceIndex = 0;
+ _currentAnimFinished = true;
+ _highlighted = false;
+}
+
+Entity::Entity(float x, float y, unsigned int sprite)
+{
+ //code duplicated from above to avoid g++ delegation warning.
+ _position.x = x;
+ _position.y = y;
+
+ _velocity.x = 0;
+ _velocity.y = 0;
+
+ _offset.x = 0;
+ _offset.y = 0;
+
+ _rotation = 0.0f;
+
+ _activeSequence = nullptr;
+
+ _sprite = sprite;
+
+ _animDT = 0;
+ _currentSequenceIndex = 0;
+ _currentAnimFinished = false;
+}
+
+Entity::~Entity()
+{
+}
+
+vec2f Entity::getPosition()
+{
+ return _position;
+}
+
+void Entity::setPosition(float x, float y)
+{
+ _position.x = x;
+ _position.y = y;
+
+ _collisionRect.x = x + _offset.x;
+ _collisionRect.y = y + _offset.y;
+}
+
+void Entity::setCollisionRectPosition(float x, float y)
+{
+ _collisionRect.x = x;
+ _collisionRect.y = y;
+
+ _position.x = x - _offset.x;
+ _position.y = y - _offset.y;
+}
+
+void Entity::setPosition(vec2f newPos)
+{
+ _position = newPos;
+}
+
+void Entity::setVelocity(float x, float y)
+{
+ _velocity.x = x;
+ _velocity.y = y;
+}
+
+void Entity::setPosX(float x)
+{
+ _position.x = x;
+}
+
+void Entity::setPosY(float y)
+{
+ _position.y = y;
+}
+
+void Entity::setVelX(float x)
+{
+ _velocity.x = x;
+}
+
+void Entity::setVelY(float y)
+{
+ _velocity.y = y;
+}
+
+vec2f Entity::getVelocity()
+{
+ return _velocity;
+}
+
+float Entity::getRotation()
+{
+ return _rotation;
+}
+
+void Entity::update(unsigned int dT)
+{
+ _position.x += _velocity.x * dT;
+ _position.y += _velocity.y * dT;
+
+ if (_activeSequence)
+ {
+ _sprite = Locator::getAnimationManager()->getNextSprite(_activeSequence, &_currentSequenceIndex, &_animDT, dT, &_currentAnimFinished);
+ }
+}
+
+Rect Entity::getCollisionRect()
+{
+ return _collisionRect;
+}
+
+vec2f Entity::getCollisionRectPosition()
+{
+ return vec2f(_collisionRect.x, _collisionRect.y);
+}
+
+vec2f Entity::getCollisionRectCenterPosition()
+{
+ return vec2f(_collisionRect.x + _collisionRect.w / 2, _collisionRect.y + + _collisionRect.h / 2);
+}
+
+void Entity::setCollisionRectDims(float w, float h, int entDim)
+{
+ _collisionRect.w = w;
+ _collisionRect.h = h;
+
+ _offset.x = (entDim - w) / 2;
+ _offset.y = (entDim - h) / 2;
+
+ updateCollisionRectPosition();
+}
+
+void Entity::setCollisionRectDimsAndPosition(float x, float y, float w, float h, int entDim)
+{
+ setCollisionRectDims(w, h, entDim);
+ setCollisionRectPosition(x, y);
+}
+
+void Entity::updateCollisionRectPosition()
+{
+ _collisionRect.x = _position.x + _offset.x;
+ _collisionRect.y = _position.y + _offset.y;
+}
+
+unsigned int Entity::getCurrentSprite()
+{
+ return _sprite;
+}
+
+void Entity::changeToStaticSprite(unsigned int sprite)
+{
+ _sprite = sprite;
+ _activeSequence = nullptr;
+ _currentAnimFinished = true;
+}
+
+void Entity::changeAnimationSequence(AnimationSequence* sequence)
+{
+ if (_activeSequence != sequence)
+ {
+ _activeSequence = sequence;
+ if (_activeSequence)
+ {
+ _sprite = _activeSequence->getSpriteAt(0);
+ _animDT = _activeSequence->getMsPerFrame();
+ _currentSequenceIndex = 0;
+ _currentAnimFinished = false;
+ }
+ }
+}
+
+bool Entity::isHighlighted()
+{
+ return _highlighted;
+}
+
+void Entity::setHighlighted(bool b)
+{
+ _highlighted = b;
+}
+
+Particle::Particle(float x, float y, unsigned int sprite) : Entity(x, y, sprite)
+{
+ setCollisionRectDims(8, 8, 8);
+ _alive = false;
+}
+
+void Particle::update(unsigned int dT)
+{
+ Entity::update(dT);
+ _velocity.y += GRAVITY;
+ updateCollisionRectPosition();
+}
+
+bool Particle::isAlive()
+{
+ return _alive;
+}
+void Particle::setAlive(bool b)
+{
+ _alive = b;
+}
+
+MainComputer::MainComputer(float x, float y, bool active) : Entity(x, y)
+{
+ setCollisionRectDims(64, 32, ENTDIM);
+ _active = active;
+
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", _active ? "terminalon" : "terminaloff");
+}
+
+bool MainComputer::isActive()
+{
+ return _active;
+}
+
+void MainComputer::setActive(bool b)
+{
+ _active = b;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", _active ? "terminalon" : "terminaloff");
+}
+
+CircuitBox::CircuitBox(float x, float y, Circuit circuit) : Entity(x, y)
+{
+ setCollisionRectDims(6, 34, ENTDIM);
+ _hacked = false;
+ _circuit = circuit;
+ std::string sprite;
+ switch (_circuit)
+ {
+ case BLUE:
+ sprite = "circuitboxblue";
+ break;
+ case GREEN:
+ sprite = "circuitboxgreen";
+ break;
+ case VIOLET:
+ sprite = "circuitboxviolet";
+ break;
+ case YELLOW:
+ sprite = "circuitboxyellow";
+ break;
+ default:
+ sprite = "";
+ break;
+ }
+ _sprite = sprite != "" ? Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", sprite) : 0;
+}
+
+bool CircuitBox::isHacked()
+{
+ return _hacked;
+}
+
+void CircuitBox::setHacked(bool hacked, bool playSound)
+{
+ if (_hacked && hacked)
+ {
+ return;
+ }
+ _hacked = hacked;
+ if (_hacked && playSound)
+ {
+ Locator::getAudio()->playSound("circuitbox");
+ }
+}
+
+Circuit CircuitBox::getCircuit()
+{
+ return _circuit;
+}
+
+void setAccel(Acceleration* accel, bool accelerating, float start, float target)
+{
+ accel->accelerating = accelerating;
+ accel->accel = start;
+ accel->target = target;
+}
\ No newline at end of file
diff --git a/entity.h b/entity.h
new file mode 100644
index 0000000..ad957e0
--- /dev/null
+++ b/entity.h
@@ -0,0 +1,703 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef ENTITY_H
+#define ENTITY_H
+#include //for AudioManager::playSound
+#include "vec.h"
+#include "global.h"
+#include "sprite.h"
+#include "locator.h"
+
+#define ENEMY_FOV_RADIUS_SNIPER 1024
+#define ENEMY_FOV_RADIUS_LIT 512
+#define ENEMY_FOV_RADIUS_DARK 64
+#define ENEMY_FOV_HALFANGLE 45
+#define ENEMY_FOV_HALFANGLE_SEEN 80
+
+#define GRAVITY 0.025
+
+enum StairTraversal
+{
+ NotMoving = 0,
+ MovingUp,
+ MovingDown
+};
+
+enum GuardState
+{
+ IDLE = 0,
+ IDLE_CAUTION, //just ended investigation - wait a few seconds to switch to patrolling.
+ PATROLLING, //normal speed.
+ INVESTIGATING, //running toward point of interest - either noise or player's last position
+ AIMING,
+ USING_SWITCH,
+ PINNED,
+ FALLING,
+ KNOCKED_OUT
+};
+
+enum AttachType
+{
+ NotAttached = 0,
+ LeftSide,
+ RightSide,
+ Ceiling
+};
+
+enum AlertType
+{
+ ALERT_NONE = 0,
+ ALERT_LOOK,
+ ALERT_WALK,
+ ALERT_RUN
+};
+
+enum TargetType
+{
+ TARGET_NONE = 0,
+ TARGET_NOISE,
+ TARGET_PLAYER,
+ TARGET_LIGHTSWITCH,
+ TARGET_ALARM
+};
+
+enum FOVType
+{
+ FOV_LIGHT = 0,
+ FOV_CAMERA
+};
+
+enum DoorType
+{
+ Door_Normal = 0,
+ Door_Trap,
+ Door_Vault
+};
+
+enum EnemyType
+{
+ Enemy_Guard = 0,
+ Enemy_Enforcer,
+ Enemy_Professional,
+ Enemy_Sniper
+};
+
+struct Acceleration
+{
+ bool accelerating;
+ float target;
+ float accel;
+};
+
+enum GunShotTraceType
+{
+ Shot_None = 0, //used only for EnemyShotResolve.
+ Shot_FromPlayer,
+ Shot_FromEnemyVoluntary,
+ Shot_FromEnemyInvoluntary
+};
+
+struct EnemyShotResolve
+{
+ int timeSinceShot;
+ GunShotTraceType shotType;
+};
+
+void setAccel(Acceleration* accel, bool accelerating, float start, float target);
+
+class LightFixture;
+
+class FieldOfView
+{
+public:
+ FieldOfView(float x, float y, float radius, int direction, int halfSize, bool active, FOVType type);
+ ~FieldOfView();
+ void setActive(bool b);
+ bool isActive();
+ vec2f getPosition();
+ float getRadius();
+ int getDirection();
+ int getSize();
+ void addVertex(float x, float y);
+ void changeVertex(unsigned int i, float x, float y); //for calculating light when door opens or closes.
+ float* getVertData();
+ int getNumberOfVerts();
+ void rotate(int deg);
+ void moveTo(float x, float y);
+ void clearVerts();
+ void getColors(float* r, float* g, float* b);
+ void setColors(float r, float g, float b);
+ Rect getCollisionRect();
+ LightFixture* getLightFixture();
+ void registerLightFixture(LightFixture* fixture);
+ FOVType getType();
+private:
+ vec2f _position;
+ float _radius;
+ float _red;
+ float _green;
+ float _blue;
+ int _direction;
+ int _halfSize;
+ std::vector _verts;
+ bool _active;
+ LightFixture* _fixture;
+ FOVType _type;
+};
+
+struct LightAndAngles
+{
+ FieldOfView* fov;
+ int angle1;
+ int angle2;
+};
+
+class Entity
+{
+public:
+ Entity(float x, float y);
+ Entity(float x, float y, unsigned int sprite);
+ virtual ~Entity();
+ vec2f getPosition();
+ vec2f getVelocity();
+ float getRotation();
+ void setPosition(float x, float y);
+ void setCollisionRectPosition(float x, float y);
+ void setCollisionRectDims(float w, float h, int entDim);
+ void setCollisionRectDimsAndPosition(float x, float y, float w, float h, int entDim);
+ void setPosX(float x);
+ void setPosY(float y);
+ void setPosition(vec2f newPos);
+ void setVelocity(float x, float y);
+ void setVelX(float x);
+ void setVelY(float y);
+ virtual void update(unsigned int dT);
+ Rect getCollisionRect();
+ vec2f getCollisionRectPosition();
+ vec2f getCollisionRectCenterPosition();
+ void updateCollisionRectPosition();
+ unsigned int getCurrentSprite();
+ void changeToStaticSprite(unsigned int sprite);
+ void changeAnimationSequence(AnimationSequence* sequence);
+ bool isHighlighted();
+ void setHighlighted(bool b);
+protected:
+ vec2f _position;
+ vec2f _offset;
+ vec2f _velocity;
+ float _rotation;
+ Rect _collisionRect;
+ unsigned int _sprite;
+ AnimationSequence* _activeSequence; //what animation is this entity currently running?
+ float _animDT;
+ unsigned int _currentSequenceIndex;
+ bool _currentAnimFinished;
+ bool _highlighted;
+};
+
+class Particle : public Entity //probably used only for glass shards.
+{
+public:
+ Particle(float x, float y, unsigned int sprite);
+ void update(unsigned int dT);
+ bool isAlive();
+ void setAlive(bool b);
+private:
+ bool _alive;
+};
+
+class Stairwell;
+
+class Stairs : public Entity
+{
+public:
+ Stairs(float x, float y);
+ void registerStairwell(Stairwell* well);
+ Stairs* getUpstairs();
+ Stairs* getDownstairs();
+ void setUpstairs(Stairs* target);
+ void setDownstairs(Stairs* target);
+private:
+ Stairs* _upstairs;
+ Stairs* _downstairs;
+ Stairwell* _well;
+};
+
+class Stairwell
+{
+public:
+ Stairwell(int x);
+ ~Stairwell();
+ void addStairs(Stairs* stairs);
+ int getX();
+ void setDirections(unsigned int yMax);
+private:
+ std::vector _stairs;
+ int _x;
+};
+
+class ElevatorShaft;
+class ElevatorSwitch;
+
+class ElevatorDoor : public Entity
+{
+public:
+ ElevatorDoor(float x, float y);
+ bool isOpen();
+ void open(bool animate);
+ void close( bool animate);
+ void registerShaft(ElevatorShaft* shaft);
+ void registerSwitch(ElevatorSwitch* eSwitch);
+ ElevatorShaft* getShaft();
+ void update(unsigned int dT);
+ bool isOpening();
+ bool isClosing();
+ ElevatorSwitch* getSwitch();
+private:
+ bool _open;
+ ElevatorShaft* _shaft;
+ ElevatorSwitch* _switch;
+};
+
+class ElevatorShaft
+{
+public:
+ ElevatorShaft(int x);
+ ~ElevatorShaft();
+ void addDoor(ElevatorDoor* ed);
+ void update();
+ bool isMoving();
+ int containsDoor(ElevatorDoor* door);
+ void setMoving(bool b);
+ int getX();
+ void setOpenDoor(ElevatorDoor* door, bool animate);
+ void setOpenDoorFirst();
+ void setTarget(ElevatorDoor* target);
+ vec2f getElevatorPosition();
+ Rect getRect();
+ ElevatorDoor* getOpenDoor();
+ ElevatorDoor* getTarget();
+ void calculateDoorOrders(int yMax);
+ ElevatorDoor* getDoorAbove(ElevatorDoor* door);
+ ElevatorDoor* getDoorBelow(ElevatorDoor* door);
+ int getDoorIndexOrdered(int index);
+ float getVelocity();
+private:
+ std::vector _doors;
+ std::vector _order;
+ Rect _rect;
+ vec2f _elevatorPosition; //where bounding box is drawn.
+ ElevatorDoor* _target;
+ ElevatorDoor* _openDoor;
+ bool _moving;
+ int _x;
+ float _yVel;
+ bool _waitingForClose;
+ Acceleration _acceleration;
+};
+
+class MainComputer : public Entity
+{
+public:
+ MainComputer(float x, float y, bool active);
+ bool isActive();
+ void setActive(bool b);
+private:
+ bool _active;
+};
+
+class CircuitBox : public Entity
+{
+public:
+ CircuitBox(float x, float y, Circuit circuit);
+ bool isHacked();
+ void setHacked(bool hacked, bool playSound);
+ Circuit getCircuit();
+private:
+ Circuit _circuit;
+ bool _hacked;
+};
+
+class LivingEntity : public Entity
+{
+public:
+ LivingEntity(float x, float y, Direction startingDir);
+ void update(unsigned int dT);
+ void setAlive(bool b);
+ bool isAlive();
+ bool isOnGround();
+ virtual void landOnGround();
+ void setOnGround(bool b);
+ void setStairMovement(StairTraversal st);
+ StairTraversal getStairTraversal();
+ virtual void arriveAtStairs(Stairs* st);
+ void setOverlappingStairs(Stairs* sw);
+ bool isOverlappingStairs();
+ Direction getDirection();
+ void reverseDirection();
+ bool isMovingThroughStairs();
+ int getStairTimer();
+ void setDirection(Direction dir);
+ float getAcceleration();
+ bool isAccelerating();
+ Stairs* getStairsEntered();
+ virtual bool isAnimatingThroughStairs() = 0;
+ Acceleration* getAccelerationStruct();
+ bool isPositionBehind(float x);
+protected:
+ bool _onGround;
+ bool _alive;
+ bool _fixDirection;
+ bool _affectedByGravity;
+ Direction _dir;
+ int _stairTimer;
+ StairTraversal _traversal;
+ Stairs* _overlappingStairs;
+ Stairs* _stairsEntered;
+ Acceleration _acceleration;
+private:
+};
+
+class LinkableEntity : public Entity
+{
+public:
+ LinkableEntity(float x, float y, Circuit c);
+ Circuit getCircuitType();
+ LinkableEntity* getTarget();
+ void link(LinkableEntity* target, bool playSound);
+ virtual void unlink();
+ virtual void activate();
+ bool hasCycle();
+protected:
+ Circuit _circuit;
+ LinkableEntity* _other;
+private:
+};
+
+class LightSwitch : public LinkableEntity
+{
+public:
+ LightSwitch(float x, float y, Circuit c, bool hs); //hs = isHandScanner
+ void activate();
+ bool isHandScanner();
+private:
+ bool _isHandScanner;
+};
+
+class ElevatorSwitch : public LinkableEntity
+{
+public:
+ ElevatorSwitch(float x, float y, Circuit c);
+ void activate();
+ void registerDoor(ElevatorDoor* door);
+ ElevatorDoor* getElevatorDoor();
+ void activateTarget();
+ void changeSprite(unsigned int sprite);
+private:
+ ElevatorDoor* _door;
+};
+
+class Door : public LinkableEntity
+{
+public:
+ Door(float x, float y, Circuit c, bool open, DoorType type);
+ CollisionVolume* getCollisionVolume();
+ CollisionVolume* getCollisionVolume2();
+ void update(unsigned int dT);
+ void updateCollisionVolume();
+ void activate();
+ bool isOpened();
+ void open();
+ void close();
+ void openSound();
+ void closeSound();
+ void addOverlappingLight(FieldOfView* fov, int angle1, int angle2);
+ size_t getNumberOfOverlappingLights();
+ FieldOfView* getLightAndAnglesAt(int i, int* angle1, int* angle2);
+ bool isDirty();
+ void setDirty(bool b);
+ DoorType getType();
+ int getTimeToClose(); //for save games.
+private:
+ DoorType _type;
+ int _timeToClose; //for trap and vault doors only.
+ bool _opened;
+ CollisionVolume* _cvol;
+ CollisionVolume* _cvol2;
+ std::vector _overlappingLights; //all lights here update when opened or closed.
+ bool _dirty;
+};
+
+class MotionScanner : public LinkableEntity
+{
+public:
+ MotionScanner(float x, float y, Circuit c);
+ bool isTrespassed();
+ Entity* getTrespasser();
+ void setTrespassed(bool b);
+ void setTrespasser(Entity* ent);
+ void resetTrespasser();
+private:
+ bool _trespassed; //set to true when an entity (probably living) has overlapped collisionRect. Do not activate() while true, to prevent multiple activations.
+ Entity* _trespasser;
+};
+
+class SecurityCamera : public LinkableEntity
+{
+public:
+ SecurityCamera(float x, float y, Circuit c, Direction dir, FieldOfView* fov);
+ void activate();
+ void setTrespassed(bool b);
+ bool isTrespassed();
+ FieldOfView* getFOV();
+private:
+ bool _trespassed; //set to true when an entity (probably living) has overlapped collisionRect. Do not activate() while true, to prevent multiple activations.
+ Direction _direction;
+ FieldOfView* _fov;
+};
+
+class LightFixture : public LinkableEntity
+{
+public:
+ LightFixture(float x, float y, Circuit c, bool switchedOn);
+ ~LightFixture();
+ void activate();
+ void toggleAllFOVs();
+ void setSwitchedOn(bool sw);
+ bool isSwitchedOn();
+ void addFOV(FieldOfView* fov);
+private:
+ bool _switchedOn;
+ std::vector _lights;
+};
+
+class PowerSocket : public LinkableEntity
+{
+public:
+ PowerSocket(float x, float y, Circuit c);
+ void activate();
+ void deactivate();
+ bool isLive();
+private:
+ bool _live;
+};
+
+class SoundDetector : public LinkableEntity
+{
+public:
+ SoundDetector(float x, float y, Circuit c);
+ void unlink();
+ void activate();
+private:
+ bool _soundedAlarm;
+};
+
+class Alarm : public LinkableEntity
+{
+public:
+ Alarm(float x, float y, Circuit c);
+ void activate();
+ void deactivate();
+ void setSounded(bool b);
+ bool isSounded();
+ void setAnimating(bool b);
+ bool isActivated();
+private:
+ bool _sounded;
+};
+
+class Enemy;
+
+class EnemyGun : public LinkableEntity
+{
+public:
+ EnemyGun(float x, float y, Circuit c);
+ void activate();
+ void fire(GunShotTraceType gstt);
+ void update(unsigned int dT);
+ void setEnemy(Enemy* enemy);
+ Enemy* getEnemy();
+private:
+ Enemy* _enemy;
+};
+
+class Enemy : public LivingEntity
+{
+public:
+ Enemy(float x, float y, Direction startingDir, bool startPatrol, EnemyType type);
+ void update(unsigned int dT);
+ void setPosition(float x, float y);
+ void landOnGround();
+ void seePlayer(float x, float y);
+ void alertToPosition(float x, float y, AlertType aType, TargetType tType);
+ bool hasSeenPlayer();
+ void loseSightOfPlayer(bool wentInElevator, vec2f newTarget);
+ bool canSeePlayer();
+ void changeState(GuardState state);
+ GuardState getState();
+ AlertType getAlertType();
+ vec2f getTarget();
+ void setDesiredStairsAndDirection(Stairs* sw, StairTraversal st);
+ bool goingForStairs();
+ void setSecondaryTarget(Entity* ent);
+ Entity* getSecondaryTarget();
+ unsigned int getNumSwitchAttempts();
+ void resetSwitchAttempts();
+ void setStrongestLight(FieldOfView* fov);
+ void setLightToActivate(FieldOfView* toActivate); //used for loading saved games only.
+ void loseStrongestLight(); //called only when strongestLight is suddenly set to inactive.
+ FieldOfView* getStrongestLight();
+ FieldOfView* getLightToActivate();
+ TargetType getTargetType();
+ void forgetTarget();
+ void setLinkableTarget(LinkableEntity* ls);
+ LinkableEntity* getTargetSwitch();
+
+ bool ignoringFall();
+ void setIgnoreFall(bool b);
+ void setReadyToShoot(bool b);
+ void resetReactionTime();
+ void setReactionTime(int time);
+ int getReactionTime();
+ EnemyType getType();
+ void setHeldAtGunpoint(bool b);
+ bool isHeldAtGunpoint();
+ void setResolve(int timeToReact, GunShotTraceType gstt);
+ void setSprite();
+ void arriveAtStairs(Stairs* st);
+ bool isAnimatingThroughStairs();
+ void setStairMovement(StairTraversal st);
+
+ //save game related only.
+ bool isWaitingForAlert();
+ void setWaitingForAlert(bool b);
+ void setCanSeePlayer(bool b);
+
+ void setFireFunction(std::function func);
+ std::function fireWeapon;
+ void _fireWeapon(GunShotTraceType gstt); //ugh.
+ EnemyGun* getGun();
+ void setGun(EnemyGun* gun);
+ EnemyShotResolve getResolve();
+
+private:
+ int _reactionTime; //time between seeing player and shooting.
+ int _timeToResetReactionTime;
+ EnemyShotResolve _resolve;
+ bool _playerInSight;
+ bool _hasSeenPlayer; //used to detect how many guards in a level has seen the player for statistical purposes.
+ vec2f _target; //try to move here when INVESTIGATING.
+ vec2f _shootTarget; //usually set to _target, unless gun is activated by something else...
+ GuardState _state;
+ bool _fullyPunched; //used to play punch sound once in punching animation.
+ StairTraversal _desiredStairDirection;
+ int _timeToPatrol;
+ int _timeToTurn1;
+ int _timeToTurn2;
+ int _timeToHitSwitch;
+ int _timeToAlert;
+ bool _waitingForAlert;
+ LinkableEntity* _targetSwitch;
+ Entity* _secondaryTarget; //a handscanner or stairs.
+ unsigned int _numSwitchAttempts;
+ FieldOfView* _lightToActivate; //former strongest light that was just turned off. Try to "activate" again.
+ FieldOfView* _strongestLight;
+ TargetType _targetType;
+ AlertType _alertType;
+ bool _ignoreFall;
+ bool _readyToShoot;
+ bool _heldAtGunpoint;
+ EnemyType _type;
+ EnemyGun* _gun;
+};
+
+class Player : public LivingEntity
+{
+public:
+ Player(float x, float y, Direction startingDir);
+ ~Player();
+ void update(unsigned int dT);
+ void setVelX(float x);
+ void setVelY(float y);
+ void die();
+ void landOnGround();
+ void jump(float x, float y);
+ bool isJumping();
+ bool isPinning();
+ bool isHacking();
+ bool isAttachingDown();
+ bool isGoingUpRoof();
+ void pinEnemy(Enemy* enemy);
+ void hackTerminal(MainComputer* computer);
+ void punchPinnedEnemy();
+ void setNumTerminalsHacked(unsigned int num); //only used while loading saved games.
+ unsigned int getNumPunches();
+ void releasePin();
+ Enemy* getPinnedEnemy();
+ AttachType getAttachType();
+ AttachType getLastAttachType();
+ void attachToVolume(CollisionVolume* volume, AttachType at);
+ void detach();
+ CollisionVolume* getAttachedVolume();
+ bool isInElevator();
+ void enterElevator(ElevatorDoor* door);
+ void switchElevator(ElevatorDoor* door);
+ void leaveElevator();
+ ElevatorDoor* getElevatorDoor();
+ void setLightVisibility(unsigned int lightVisibility);
+ unsigned int getLightVisibility();
+ unsigned int getNumHackedTerminals();
+ void setStairMovement(StairTraversal st);
+ void arriveAtStairs(Stairs* st);
+ bool isAnimatingThroughStairs();
+ int getArmRotation();
+ void setArmRotation(int rotation);
+ void setAimingGun(bool b);
+ bool isAimingGun();
+private:
+ bool _jumping;
+ bool _pinning;
+ unsigned int _numPunches;
+ unsigned int _numHackedTerminals;
+ bool _inElevator;
+ AttachType _attach;
+ AttachType _lastAttach;
+ unsigned int _lightVisibility; //between 0 and 100.
+ ElevatorDoor* _door;
+ Enemy* _pinnedEnemy;
+ MainComputer* _hackedTerminal;
+ CollisionVolume* _attachedVolume; //The Collision Volume the player is attached to.
+ int _armRotation;
+ bool _aimingGun;
+
+ //deleted by AnimationManager.
+ AnimationSequence* _runningSequence;
+ AnimationSequence* _climbingUpSequence;
+ AnimationSequence* _ceilingSequence;
+ AnimationSequence* _hackingSequence;
+ AnimationSequence* _attachDownSequence;
+ AnimationSequence* _reachRoofSequence;
+ AnimationSequence* _attachToCeiling;
+ AnimationSequence* _attachFromCeiling;
+ AnimationSequence* _enterStairs;
+ AnimationSequence* _exitStairs;
+ AnimationSequence* _walkingSequence;
+};
+#endif
\ No newline at end of file
diff --git a/fieldofview.cpp b/fieldofview.cpp
new file mode 100644
index 0000000..b16d646
--- /dev/null
+++ b/fieldofview.cpp
@@ -0,0 +1,158 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+
+FieldOfView::FieldOfView(float x, float y, float radius, int direction, int halfSize, bool active, FOVType type)
+{
+ _position.x = x;
+ _position.y = y;
+ _radius = radius;
+ _direction = direction;
+ _halfSize = halfSize;
+ _active = active;
+ clearVerts();
+
+ _red = 1.0f;
+ _green = 1.0f;
+ _blue = 1.0f;
+
+ _fixture = nullptr;
+
+ _type = type;
+}
+
+FieldOfView::~FieldOfView()
+{
+ _verts.clear();
+}
+
+vec2f FieldOfView::getPosition()
+{
+ return _position;
+}
+
+float FieldOfView::getRadius()
+{
+ return _radius;
+}
+
+int FieldOfView::getDirection()
+{
+ return _direction;
+}
+
+int FieldOfView::getSize()
+{
+ return _halfSize;
+}
+
+bool FieldOfView::isActive()
+{
+ return _active;
+}
+
+void FieldOfView::addVertex(float x, float y)
+{
+ _verts.push_back(x);
+ _verts.push_back(y);
+}
+
+void FieldOfView::changeVertex(unsigned int i, float x, float y)
+{
+ if (i >= _verts.size())
+ {
+ return;
+ }
+ _verts[i] = x;
+ _verts[i + 1] = y;
+}
+
+float* FieldOfView::getVertData()
+{
+ return _verts.data();
+}
+
+int FieldOfView::getNumberOfVerts()
+{
+ return _verts.size() / 2;
+}
+
+void FieldOfView::rotate(int deg)
+{
+ _direction += deg;
+ clearVerts();
+}
+
+void FieldOfView::moveTo(float x, float y)
+{
+ _position.x = x;
+ _position.y = y;
+ clearVerts();
+}
+
+void FieldOfView::clearVerts()
+{
+ _verts.clear();
+ addVertex(_position.x, _position.y);
+}
+
+void FieldOfView::setActive(bool b)
+{
+ _active = b;
+}
+
+void FieldOfView::getColors(float* r, float* g, float* b)
+{
+ *r = _red;
+ *g = _green;
+ *b = _blue;
+}
+
+void FieldOfView::setColors(float r, float g, float b)
+{
+ _red = r;
+ _green = g;
+ _blue = b;
+}
+
+Rect FieldOfView::getCollisionRect()
+{
+ Rect rect;
+ rect.x = _position.x - 16;
+ rect.y = _position.y - 16;
+ rect.w = 32;
+ rect.h = 32;
+ return rect;
+}
+
+LightFixture* FieldOfView::getLightFixture()
+{
+ return _fixture;
+}
+
+void FieldOfView::registerLightFixture(LightFixture* fixture)
+{
+ _fixture = fixture;
+}
+
+FOVType FieldOfView::getType()
+{
+ return _type;
+}
\ No newline at end of file
diff --git a/file.cpp b/file.cpp
new file mode 100644
index 0000000..f12ba2b
--- /dev/null
+++ b/file.cpp
@@ -0,0 +1,29 @@
+//This code is public domain, taken from https://gitorious.org/wikibooks-opengl
+
+#include "file.h"
+
+char* file_read(const char* filename)
+{
+ FILE* in = fopen(filename, "rb");
+ if (in == NULL)
+ return NULL;
+ int res_size = BUFSIZ;
+ char* res = new char[res_size];
+ int nb_read_total = 0;
+ while (!feof(in) && !ferror(in))
+ {
+ if (nb_read_total + BUFSIZ > res_size)
+ {
+ if (res_size > 10*1024*1024)
+ break;
+ res_size = res_size * 2;
+ res = (char*)realloc(res, res_size);
+ }
+ char* p_res = res + nb_read_total;
+ nb_read_total += fread(p_res, 1, BUFSIZ, in);
+ }
+ fclose(in);
+ res = (char*)realloc(res, nb_read_total + 1);
+ res[nb_read_total] = '\0';
+ return res;
+}
\ No newline at end of file
diff --git a/file.h b/file.h
new file mode 100644
index 0000000..e9632cd
--- /dev/null
+++ b/file.h
@@ -0,0 +1,5 @@
+#include
+#include
+
+//This function is public domain, taken from https://gitorious.org/wikibooks-opengl
+char* file_read(const char* filename);
diff --git a/font.cpp b/font.cpp
new file mode 100644
index 0000000..b1348cb
--- /dev/null
+++ b/font.cpp
@@ -0,0 +1,62 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include "font.h"
+
+Font::Font(std::string filename, float size)
+{
+ _atlas = 0;
+ _size = size;
+ unsigned char temp_bitmap[512 * 512];
+ unsigned char ttf_buffer[1 << 20];
+ std::ifstream input(filename.c_str());
+ if (input)
+ {
+ input.read((char*)ttf_buffer, 1<<20);
+ stbtt_BakeFontBitmap(ttf_buffer, 0, size, temp_bitmap, 512, 512, 32, 96, _cdata);
+ glGenTextures(1, &_atlas);
+ glBindTexture(GL_TEXTURE_2D, _atlas);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512,512, 0, GL_ALPHA, GL_UNSIGNED_BYTE, temp_bitmap);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ }
+}
+
+GLuint Font::getTexture()
+{
+ return _atlas;
+}
+
+stbtt_bakedchar* Font::data()
+{
+ return _cdata;
+}
+
+float Font::getSize()
+{
+ return _size;
+}
+
+Font::~Font()
+{
+ glDeleteTextures(1, &_atlas);
+}
\ No newline at end of file
diff --git a/font.h b/font.h
new file mode 100644
index 0000000..49a0f4f
--- /dev/null
+++ b/font.h
@@ -0,0 +1,39 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef FONT_H
+#define FONT_H
+#include
+#include
+#include "stb_truetype.h"
+
+class Font
+{
+public:
+ Font(std::string filename, float size);
+ ~Font();
+ stbtt_bakedchar* data();
+ GLuint getTexture();
+ float getSize();
+private:
+ GLuint _atlas;
+ stbtt_bakedchar _cdata[96]; //used to help draw text
+ float _size;
+};
+#endif
\ No newline at end of file
diff --git a/gamestate.cpp b/gamestate.cpp
new file mode 100644
index 0000000..39932cc
--- /dev/null
+++ b/gamestate.cpp
@@ -0,0 +1,287 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "gamestate.h"
+#include "statemanager.h"
+
+GameState::GameState(StateManager* sm) : BaseState(sm)
+{
+ _playerMovingLeft = _playerMovingRight = _playerMovingDown = _playerMovingUp = false;
+ _scene.reset();
+ _movementLocked = false;
+ _LMBHeldDown = false;
+ _RMBHeldDown = false;
+ _saveMessage.reset(new FloatingMessage(0, 0, "", 0, 1, 0));
+ _labels.push_back(_saveMessage);
+}
+
+GameState::~GameState()
+{
+}
+
+void GameState::deleteScene()
+{
+ _scene.reset();
+}
+
+void GameState::initSceneAndMap(const char* filename)
+{
+ _scene.reset(new Scene());
+ _scene->loadMap(filename, false);
+ _saveMessage->setText("");
+}
+
+void GameState::update(unsigned int dT)
+{
+ Player* player = _scene->getPlayer();
+ if (_LMBHeldDown && player->isAlive() && !player->isPinning() && !player->isAimingGun() && !player->isHacking() && !player->isMovingThroughStairs() && !player->isAnimatingThroughStairs())
+ {
+ _scene->handleClick(_mouseX, _mouseY, dT);
+ _movementLocked = true;
+ }
+ else
+ {
+ _movementLocked = false;
+ }
+
+ if (!_LMBHeldDown)
+ {
+ player->setAimingGun(_RMBHeldDown);
+ }
+
+ _scene->crosslinkPan(_mouseX, _mouseY);
+ _scene->handleMouse(_mouseX, _mouseY);
+
+ if (player->isAlive())
+ {
+ if (_movementLocked ||
+ (player->isPinning() && player->getNumPunches() == 0) ||
+ player->isHacking() ||
+ player->isGoingUpRoof() ||
+ player->isAnimatingThroughStairs() ||
+ player->isMovingThroughStairs() ||
+ (player->isInElevator() && player->getElevatorDoor()->getShaft()->isMoving()) ||
+ _scene->isLoadMenuVisible())
+ {
+ _playerMovingLeft = _playerMovingRight = _playerMovingDown = _playerMovingUp = false;
+ }
+ else
+ {
+ if ((_playerMovingLeft || _playerMovingRight) && player->isInElevator() && !player->getElevatorDoor()->getShaft()->isMoving())
+ {
+ player->leaveElevator();
+ }
+ }
+ _scene->movePlayer(_playerMovingLeft, _playerMovingRight, _playerMovingDown, _playerMovingUp);
+ }
+
+ _scene->update(dT);
+
+ if (_scene->isLevelOver())
+ {
+ _playerMovingLeft = _playerMovingRight = _playerMovingDown = _playerMovingUp = false;
+ _manager->switchToState(LEVELEND_SCREEN);
+ }
+
+ _saveMessage->update(dT);
+}
+
+Scene* GameState::getScene()
+{
+ return _scene.get();
+}
+
+void GameState::handleMouseWheel(int dir)
+{
+ if (dir != 0)
+ _scene->toggleCrosslinkMode();
+}
+
+void GameState::handleKeyDown(SDL_Keycode key)
+{
+ eBinding binding = Bind_Nothing;
+ binding = Locator::getBindingsManager()->getBindingFromKey(key);
+ switch (binding)
+ {
+ case Bind_MoveLeft:
+ _playerMovingLeft = true;
+ break;
+ case Bind_MoveRight:
+ _playerMovingRight = true;
+ break;
+ case Bind_MoveUp:
+ _playerMovingUp = true;
+ _scene->handlePlayerFrob(true);
+ break;
+ case Bind_MoveDown:
+ _playerMovingDown = true;
+ _scene->handlePlayerFrob(false);
+ break;
+ default:
+ break;
+ }
+}
+
+void GameState::handleKeyUp(SDL_Keycode key)
+{
+ eBinding binding = Bind_Nothing;
+ switch(key)
+ {
+ case SDLK_ESCAPE:
+ if (!_scene->isLoadMenuVisible())
+ {
+ _manager->switchToState(PAUSE_SCREEN);
+ }
+ break;
+#ifdef DEBUG
+ case SDLK_n:
+ _scene->addNoise(_mouseX, _mouseY, 512, true, ALERT_RUN, NULL);
+ break;
+#endif
+ case SDLK_PRINTSCREEN:
+ _takeScreenshot();
+ break;
+ case SDLK_F5:
+ _scene->saveGame("quick.sav");
+ _saveMessage->init(_manager->getWindowWidth() - 128, 64, "Saved.", 3000);
+ break;
+ case SDLK_F9:
+ _scene->loadGame("quick.sav");
+ _saveMessage->setText("");
+ break;
+ case SDLK_F8:
+ _scene->showLoadMenu(true);
+ break;
+ default:
+ break;
+ }
+
+ binding = Locator::getBindingsManager()->getBindingFromKey(key);
+
+ switch (binding)
+ {
+ case Bind_MoveLeft:
+ _playerMovingLeft = false;
+ break;
+ case Bind_MoveRight:
+ _playerMovingRight = false;
+ break;
+ case Bind_MoveUp:
+ _playerMovingUp = false;
+ break;
+ case Bind_MoveDown:
+ _playerMovingDown = false;
+ break;
+ case Bind_ToggleCrosslink:
+ _scene->toggleCrosslinkMode();
+ break;
+ default:
+ break;
+ }
+
+ if (_scene->isLoadMenuVisible())
+ {
+ switch(_event.key.keysym.sym)
+ {
+ case SDLK_1:
+ _scene->loadAutosave(0);
+ break;
+ case SDLK_2:
+ _scene->loadAutosave(1);
+ break;
+ case SDLK_3:
+ _scene->loadAutosave(2);
+ break;
+ case SDLK_ESCAPE:
+ _scene->showLoadMenu(false);
+ break;
+ case SDLK_r:
+ _scene->reloadMap();
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (_scene->isDetachmentDelayed() || _scene->isElevatorLocked())
+ {
+ switch (binding)
+ {
+ case Bind_MoveLeft:
+ case Bind_MoveRight:
+ case Bind_MoveUp:
+ case Bind_MoveDown:
+ _scene->resetDelayDetachment();
+ _scene->resetElevatorLock();
+ default:
+ break;
+ }
+ }
+}
+
+void GameState::handleMouseDown(SDL_MouseButtonEvent event)
+{
+ if (event.button == SDL_BUTTON_LEFT)
+ {
+ _LMBHeldDown = true;
+ if (_scene->getPlayer()->isAlive() && _scene->getPlayer()->isPinning())
+ {
+ _scene->getPlayer()->punchPinnedEnemy();
+ }
+ if (_scene->getPlayer()->isAlive() && _scene->getPlayer()->isAimingGun())
+ {
+ _scene->traceBullet(_scene->getPlayer(), vec2f(_mouseX + _scene->getCamera().x, _mouseY + _scene->getCamera().y), Shot_FromPlayer, true);
+ }
+ }
+ else if (event.button == SDL_BUTTON_RIGHT)
+ {
+ _RMBHeldDown = true;
+ }
+}
+
+void GameState::handleMouseUp(SDL_MouseButtonEvent event)
+{
+ if (event.button == SDL_BUTTON_LEFT)
+ {
+ _LMBHeldDown = false;
+ if (_scene->getPlayer()->isAlive())
+ {
+ _scene->handleLeftClickRelease(_event.button.x, _event.button.y);
+ }
+ }
+ else if (event.button == SDL_BUTTON_MIDDLE)
+ {
+#ifdef DEBUG
+ _scene->warpPlayerTo(event.x, event.y);
+#endif
+ }
+ else if (event.button == SDL_BUTTON_RIGHT)
+ {
+ _RMBHeldDown = false;
+ }
+}
+
+bool GameState::isMouseCursorSeen()
+{
+ if (_scene)
+ {
+ return _scene->isMouseCursorSeen(_mouseX, _mouseY);
+ }
+ return false;
+}
\ No newline at end of file
diff --git a/gamestate.h b/gamestate.h
new file mode 100644
index 0000000..a97498b
--- /dev/null
+++ b/gamestate.h
@@ -0,0 +1,50 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef GAMESTATE_H
+#define GAMESTATE_H
+#include "state.h"
+#include "scene.h"
+#include "global.h"
+#include "bindings.h"
+
+class GameState : public BaseState
+{
+public:
+ GameState(StateManager* sm);
+ ~GameState();
+ void update(unsigned int dT);
+ void initSceneAndMap(const char* filename);
+ void deleteScene();
+ Scene* getScene();
+ void handleMouseWheel(int dir);
+ void handleKeyDown(SDL_Keycode);
+ void handleKeyUp(SDL_Keycode);
+ void handleMouseDown(SDL_MouseButtonEvent event);
+ void handleMouseUp(SDL_MouseButtonEvent event);
+ bool isMouseCursorSeen();
+private:
+ bool _playerMovingLeft, _playerMovingRight, _playerMovingDown, _playerMovingUp;
+ bool _movementLocked;
+ bool _LMBHeldDown, _RMBHeldDown;
+ std::unique_ptr _scene;
+ std::shared_ptr _saveMessage;
+};
+
+#endif
\ No newline at end of file
diff --git a/global.h b/global.h
new file mode 100644
index 0000000..3401327
--- /dev/null
+++ b/global.h
@@ -0,0 +1,97 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef GLOBAL_H
+#define GLOBAL_H
+
+#if 0
+#define LOGF(a) fprintf a
+#else
+#define LOGF(a) (void)0
+#endif
+
+# define MY_PI 3.14159265358979323846
+
+#define ToRadian(x) ((x) * MY_PI / 180.0f)
+#define ToDegree(x) ((x) * 180.0f / MY_PI)
+
+#if DEBUG
+#define Assert(x) if (!x) { printf("Assertion failed!\n"); *(int*)0 = 0; }
+#else
+#define Assert(x)
+#endif
+#define ENTDIM 128
+
+struct Rect
+{
+ float x;
+ float y;
+ float w;
+ float h;
+};
+
+struct CollisionVolume
+{
+ Rect rect;
+ bool active;
+ bool glass;
+ bool door;
+ bool guardblock; //invisible volume that makes guards turn around.
+};
+
+enum Circuit
+{
+ BLUE = 0,
+ GREEN,
+ YELLOW,
+ VIOLET,
+ RED
+};
+
+enum Direction
+{
+ Left = 0,
+ Right
+};
+
+enum MouseOverObject
+{
+ MO_Nothing = 0,
+ MO_CircuitBox,
+ MO_MainComputer,
+ MO_LightFixture,
+ MO_Switch,
+ MO_HandScanner,
+ MO_Elevator,
+ MO_MotionScanner,
+ MO_Door,
+ MO_TrapDoor,
+ MO_VaultDoor,
+ MO_SoundDetector,
+ MO_Alarm,
+ MO_PowerSocket,
+ MO_SecurityCamera,
+ MO_Guard,
+ MO_Enforcer,
+ MO_Professional,
+ MO_Sniper,
+ NUMBER_OF_MOUSEOVER_OBJECTS
+};
+
+#endif
\ No newline at end of file
diff --git a/intersect.cpp b/intersect.cpp
new file mode 100644
index 0000000..d81de51
--- /dev/null
+++ b/intersect.cpp
@@ -0,0 +1,134 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "intersect.h"
+
+bool vec2InRect(vec2f v, Rect r)
+{
+ return (v.x >= r.x && v.x <= r.x + r.w &&
+ v.y >= r.y && v.y <= r.y + r.h);
+}
+
+bool vec2InRect2(vec2f v, Rect r)
+{
+ return (v.x > r.x && v.x < r.x + r.w &&
+ v.y > r.y && v.y < r.y + r.h);
+}
+
+bool check_collision(Rect A, Rect B)
+{
+ //The sides of the rectangles
+ float leftA, leftB;
+ float rightA, rightB;
+ float topA, topB;
+ float bottomA, bottomB;
+
+ //Calculate the sides of rect A
+ leftA = A.x;
+ rightA = A.x + A.w;
+ topA = A.y;
+ bottomA = A.y + A.h;
+
+ //Calculate the sides of rect B
+ leftB = B.x;
+ rightB = B.x + B.w;
+ topB = B.y;
+ bottomB = B.y + B.h;
+
+ //If any of the sides from A are outside of B
+ if( bottomA <= topB )
+ {
+ return false;
+ }
+
+ if( topA >= bottomB )
+ {
+ return false;
+ }
+
+ if( rightA <= leftB )
+ {
+ return false;
+ }
+
+ if( leftA >= rightB )
+ {
+ return false;
+ }
+
+ //If none of the sides from A are outside B
+ return true;
+}
+
+bool check_collision2(Rect A, Rect B)
+{
+ //The sides of the rectangles
+ float leftA, leftB;
+ float rightA, rightB;
+ float topA, topB;
+ float bottomA, bottomB;
+
+ //Calculate the sides of rect A
+ leftA = A.x;
+ rightA = A.x + A.w;
+ topA = A.y;
+ bottomA = A.y + A.h;
+
+ //Calculate the sides of rect B
+ leftB = B.x;
+ rightB = B.x + B.w;
+ topB = B.y;
+ bottomB = B.y + B.h;
+
+ //If any of the sides from A are outside of B
+ if( bottomA < topB )
+ {
+ return false;
+ }
+
+ if( topA > bottomB )
+ {
+ return false;
+ }
+
+ if( rightA < leftB )
+ {
+ return false;
+ }
+
+ if( leftA > rightB )
+ {
+ return false;
+ }
+
+ //If none of the sides from A are outside B
+ return true;
+}
+
+bool rectInCircle(Rect r, vec2f center, float radius)
+{
+ if (vec2f_distance(center, vec2f(r.x, r.y)) <= radius ||
+ vec2f_distance(center, vec2f(r.x + r.w, r.y)) <= radius ||
+ vec2f_distance(center, vec2f(r.x, r.y + r.h)) <= radius ||
+ vec2f_distance(center, vec2f(r.x + r.w, r.y + r.h)) <= radius)
+ {
+ return true;
+ }
+ else return false;
+}
\ No newline at end of file
diff --git a/intersect.h b/intersect.h
new file mode 100644
index 0000000..e4008b6
--- /dev/null
+++ b/intersect.h
@@ -0,0 +1,8 @@
+#include "global.h"
+#include "vec.h"
+
+bool vec2InRect(vec2f v, Rect r);
+bool vec2InRect2(vec2f v, Rect r);
+bool check_collision(Rect A, Rect B);
+bool check_collision2(Rect A, Rect B);
+bool rectInCircle(Rect r, vec2f center, float radius);
\ No newline at end of file
diff --git a/levelendstate.cpp b/levelendstate.cpp
new file mode 100644
index 0000000..eb8fc78
--- /dev/null
+++ b/levelendstate.cpp
@@ -0,0 +1,55 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "levelendstate.h"
+#include "statemanager.h"
+#include "global.h"
+
+LevelEndState::LevelEndState(StateManager* sm) : MenuState(sm)
+{
+ _lvlCompleteLabel.reset(new TextLabel(640, 64, "Level Complete!", 1, 1, 1));
+ _OKButton.reset(new TextButton(320, 320, (strlen("OK") + 2) * 16, 32, "OK"));
+ _buttons.push_back(_OKButton);
+ _labels.push_back(_lvlCompleteLabel);
+}
+
+LevelEndState::~LevelEndState()
+{
+ LOGF((stdout, "running Level End destructor!\n"));
+}
+
+void LevelEndState::resetPositions(int w, int h)
+{
+ _lvlCompleteLabel->setPosition(w * 0.48f, h * 0.1f);
+ _OKButton->setPosition(w * 0.48f, h * 0.8f);
+}
+
+void LevelEndState::update(unsigned int dT)
+{
+ MenuState::update(dT);
+}
+
+void LevelEndState::handleButton(Button* button)
+{
+ if (button == _OKButton.get())
+ {
+ _manager->switchToState(MAINMENU_SCREEN);
+ _manager->destroyScene();
+ }
+}
\ No newline at end of file
diff --git a/levelendstate.h b/levelendstate.h
new file mode 100644
index 0000000..93814f4
--- /dev/null
+++ b/levelendstate.h
@@ -0,0 +1,37 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef LEVELENDSTATE_H
+#define LEVELENDSTATE_H
+#include "menustate.h"
+
+class LevelEndState : public MenuState
+{
+public:
+ LevelEndState(StateManager* sm);
+ ~LevelEndState();
+ void update(unsigned int dT);
+ void handleButton(Button* button);
+ void resetPositions(int w, int h);
+private:
+ std::shared_ptr _lvlCompleteLabel;
+ std::shared_ptr _OKButton;
+};
+
+#endif
\ No newline at end of file
diff --git a/linkableentity.cpp b/linkableentity.cpp
new file mode 100644
index 0000000..a085bb1
--- /dev/null
+++ b/linkableentity.cpp
@@ -0,0 +1,651 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+
+#define TRAPDOOR_TIMEMS 2000
+#define VAULTDOOR_TIMEMS 3000
+
+LinkableEntity::LinkableEntity(float x, float y, Circuit c) : Entity(x, y)
+{
+ _circuit = c;
+ _other = nullptr;
+}
+
+LinkableEntity* LinkableEntity::getTarget()
+{
+ return _other;
+}
+
+Circuit LinkableEntity::getCircuitType()
+{
+ return _circuit;
+}
+
+void LinkableEntity::link(LinkableEntity* target, bool playSound)
+{
+ if (this == target || _circuit != target->getCircuitType())
+ {
+ return;
+ }
+
+ if (playSound)
+ {
+ Locator::getAudio()->playSound("link");
+ }
+ _other = target;
+}
+
+void LinkableEntity::unlink()
+{
+ _other = nullptr;
+}
+
+void LinkableEntity::activate()
+{
+ if (_other != nullptr)
+ {
+ //TODO: Find some other way of avoiding a segfault.
+ //I would rather allow for entities to activate each other at least for a time.
+ if (!hasCycle())
+ {
+ _other->activate();
+ }
+
+ if (dynamic_cast(_other))
+ {
+ unlink();
+ }
+ }
+}
+
+//Thank you, Robert Floyd.
+bool LinkableEntity::hasCycle()
+{
+ if(_other == nullptr)
+ {
+ return false;
+ }
+ LinkableEntity* slow;
+ LinkableEntity* fast;
+
+ slow = fast = this;
+
+ while(true)
+ {
+ slow = slow->getTarget();
+
+ if(fast->getTarget() != nullptr)
+ fast = fast->getTarget()->getTarget();
+ else
+ return false;
+
+ if(slow == nullptr || fast == nullptr)
+ return false;
+
+ if(slow == fast)
+ return true;
+ }
+}
+
+LightSwitch::LightSwitch(float x, float y, Circuit c, bool hs) : LinkableEntity(x, y, c)
+{
+ _isHandScanner = hs;
+ std::string sprite;
+ if (!_isHandScanner)
+ {
+ setCollisionRectDims(20, 16, ENTDIM);
+ sprite = "lightswitch";
+ }
+ else
+ {
+ setCollisionRectDims(6, 10, ENTDIM);
+ sprite = "handscanner";
+ }
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", sprite);
+}
+
+void LightSwitch::activate()
+{
+ if (!_isHandScanner)
+ {
+ Locator::getAudio()->playSound("switch");
+ }
+ LinkableEntity::activate();
+}
+
+bool LightSwitch::isHandScanner()
+{
+ return _isHandScanner;
+}
+
+ElevatorSwitch::ElevatorSwitch(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(8, 16, ENTDIM);
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "elev_switch_off");
+}
+
+void ElevatorSwitch::registerDoor(ElevatorDoor* door)
+{
+ _door = door;
+}
+
+ElevatorDoor* ElevatorSwitch::getElevatorDoor()
+{
+ return _door;
+}
+
+void ElevatorSwitch::activate()
+{
+ if (_door->getShaft()->getOpenDoor() != _door)
+ {
+ _door->getShaft()->setTarget(_door);
+ }
+}
+
+void ElevatorSwitch::activateTarget()
+{
+ LinkableEntity::activate();
+}
+
+void ElevatorSwitch::changeSprite(unsigned int sprite)
+{
+ _sprite = sprite;
+}
+
+Door::Door(float x, float y, Circuit c, bool open, DoorType type) : LinkableEntity(x, y, c)
+{
+ _type = type;
+ _cvol = new CollisionVolume();
+ _cvol2 = nullptr;
+
+ switch(type)
+ {
+ case Door_Trap:
+ setCollisionRectDims(96, 12, ENTDIM);
+ _opened = false;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "trapdoorclosed");
+ break;
+ case Door_Vault:
+ setCollisionRectDims(16, 128, ENTDIM);
+ _opened = false;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "vault_closed");
+ _cvol2 = new CollisionVolume();
+ break;
+ default:
+ setCollisionRectDims(8, 48, ENTDIM);
+ _opened = open;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", open ? "dooropen" : "doorclosed");
+ break;
+ }
+
+ if (type != Door_Vault)
+ {
+ _cvol->rect.x = _collisionRect.x;
+ _cvol->rect.y = _collisionRect.y;
+ _cvol->rect.w = _collisionRect.w;
+ _cvol->rect.h = _collisionRect.h;
+ }
+ else
+ {
+ _cvol->rect.x = _collisionRect.x + 4;
+ _cvol->rect.y = _collisionRect.y + 76;
+ _cvol->rect.w = _collisionRect.w - 8;
+ _cvol->rect.h = _collisionRect.h - 76;
+
+ _cvol2->rect.x = _collisionRect.x;
+ _cvol2->rect.y = _collisionRect.y;
+ _cvol2->rect.w = _collisionRect.w;
+ _cvol2->rect.h = 76;
+ _cvol2->active = true;
+ }
+
+ _cvol->active = !open;
+ _cvol->glass = false;
+ _cvol->door = true;
+ _cvol->guardblock = false;
+ _timeToClose = _type == Door_Trap ? TRAPDOOR_TIMEMS : VAULTDOOR_TIMEMS;
+ _dirty = false;
+}
+
+CollisionVolume* Door::getCollisionVolume()
+{
+ return _cvol;
+}
+
+CollisionVolume* Door::getCollisionVolume2()
+{
+ return _cvol2;
+}
+
+void Door::activate()
+{
+ if (_opened)
+ closeSound();
+ else
+ openSound();
+
+ updateCollisionVolume();
+ LinkableEntity::activate();
+}
+
+bool Door::isOpened()
+{
+ return _opened;
+}
+
+void Door::open()
+{
+ std::string name;
+
+ switch(_type)
+ {
+ case Door_Trap:
+ name = "trapdooropen";
+ break;
+ case Door_Vault:
+ name = "vault_open";
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_VAULT_OPEN));
+ break;
+ default:
+ name = "dooropen";
+ break;
+ }
+
+ _opened = true;
+ _dirty = true;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", name);
+}
+
+void Door::close()
+{
+ std::string name;
+
+ switch(_type)
+ {
+ case Door_Trap:
+ name = "trapdoorclosed";
+ break;
+ case Door_Vault:
+ name = "vault_closed";
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_VAULT_CLOSE));
+ break;
+ default:
+ name = "doorclosed";
+ break;
+ }
+
+ _opened = false;
+ _dirty = true;
+ _timeToClose = _type == Door_Trap ? TRAPDOOR_TIMEMS : VAULTDOOR_TIMEMS;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", name);
+}
+
+void Door::openSound()
+{
+ Locator::getAudio()->playSound("door_open");
+ open();
+}
+
+void Door::closeSound()
+{
+ Locator::getAudio()->playSound("door_close");
+ close();
+}
+
+void Door::update(unsigned int dT)
+{
+ Entity::update(dT);
+ if (_type != Door_Normal)
+ {
+ if (_type == Door_Vault && _activeSequence)
+ {
+ if (_currentAnimFinished)
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", _opened ? "vault_open" : "vault_closed"));
+ }
+ }
+ if (_opened)
+ {
+ _timeToClose -= dT;
+ if (_timeToClose <= 0)
+ {
+ closeSound();
+ updateCollisionVolume();
+ }
+ }
+ }
+}
+
+void Door::updateCollisionVolume()
+{
+ _cvol->active = !_opened;
+}
+
+size_t Door::getNumberOfOverlappingLights()
+{
+ return _overlappingLights.size();
+}
+
+FieldOfView* Door::getLightAndAnglesAt(int i, int* angle1, int* angle2)
+{
+ *angle1 = _overlappingLights[i].angle1;
+ *angle2 = _overlappingLights[i].angle2;
+ return _overlappingLights[i].fov;
+}
+
+void Door::addOverlappingLight(FieldOfView* fov, int angle1, int angle2)
+{
+ LightAndAngles laa;
+ laa.fov = fov;
+ laa.angle1 = angle1;
+ laa.angle2 = angle2;
+ _overlappingLights.push_back(laa);
+}
+
+bool Door::isDirty()
+{
+ return _dirty;
+}
+
+void Door::setDirty(bool b)
+{
+ _dirty = b;
+}
+
+DoorType Door::getType()
+{
+ return _type;
+}
+
+int Door::getTimeToClose()
+{
+ return _timeToClose;
+}
+
+MotionScanner::MotionScanner(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(14, 48, ENTDIM);
+ _trespassed = false;
+ _trespasser = nullptr;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "motionscanner");
+}
+
+bool MotionScanner::isTrespassed()
+{
+ return _trespassed;
+}
+
+Entity* MotionScanner::getTrespasser()
+{
+ return _trespasser;
+}
+
+void MotionScanner::setTrespassed(bool b)
+{
+ _trespassed = b;
+}
+
+void MotionScanner::setTrespasser(Entity* ent)
+{
+ _trespasser = ent;
+ _trespassed = true;
+}
+
+void MotionScanner::resetTrespasser()
+{
+ _trespassed = false;
+ _trespasser = nullptr;
+}
+
+SecurityCamera::SecurityCamera(float x, float y, Circuit c, Direction dir, FieldOfView* fov) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(22, 20, ENTDIM);
+ _trespassed = false;
+ _direction = dir;
+ _fov = fov;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", dir == Right ? "camera_right" : "camera_left");
+}
+
+void SecurityCamera::activate()
+{
+ if (_trespassed)
+ {
+ return;
+ }
+ LinkableEntity::activate();
+}
+
+bool SecurityCamera::isTrespassed()
+{
+ return _trespassed;
+}
+
+void SecurityCamera::setTrespassed(bool b)
+{
+ _trespassed = b;
+}
+
+FieldOfView* SecurityCamera::getFOV()
+{
+ return _fov;
+}
+
+LightFixture::LightFixture(float x, float y, Circuit c, bool switchedOn) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(79, 6, ENTDIM);
+ _switchedOn = switchedOn;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "lightfixture");
+}
+
+LightFixture::~LightFixture()
+{
+ _lights.clear();
+}
+
+void LightFixture::activate()
+{
+ setSwitchedOn(!_switchedOn);
+ LinkableEntity::activate();
+}
+
+void LightFixture::setSwitchedOn(bool sw)
+{
+ _switchedOn = sw;
+ toggleAllFOVs();
+}
+
+bool LightFixture::isSwitchedOn()
+{
+ return _switchedOn;
+}
+
+void LightFixture::addFOV(FieldOfView* fov)
+{
+ fov->registerLightFixture(this);
+ _lights.push_back(fov);
+}
+
+void LightFixture::toggleAllFOVs()
+{
+ for(auto& elem : _lights)
+ elem->setActive(_switchedOn);
+}
+
+PowerSocket::PowerSocket(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(9, 5, ENTDIM);
+ _live = false;
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "powersocket");
+}
+
+void PowerSocket::activate()
+{
+ LinkableEntity::activate();
+ //TODO: Play sound here.
+ _live = true;
+}
+
+void PowerSocket::deactivate()
+{
+ _live = false;
+}
+
+bool PowerSocket::isLive()
+{
+ return _live;
+}
+
+SoundDetector::SoundDetector(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(24, 26, ENTDIM);
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "sounddetector");
+ _soundedAlarm = false;
+}
+
+void SoundDetector::unlink()
+{
+ LinkableEntity::unlink();
+ _soundedAlarm = false;
+}
+
+void SoundDetector::activate()
+{
+ bool hasAlarm = false;
+ if (!hasCycle())
+ {
+ LinkableEntity* ent = this;
+ while (ent != nullptr)
+ {
+ if (dynamic_cast(ent))
+ {
+ hasAlarm = true;
+ break;
+ }
+ ent = ent->getTarget();
+ }
+ }
+ else
+ {
+ return;
+ }
+
+ if (hasAlarm)
+ {
+ if (!_soundedAlarm)
+ {
+ _soundedAlarm = true;
+ }
+ else
+ {
+ return;
+ }
+ }
+
+ LinkableEntity::activate();
+}
+
+Alarm::Alarm(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(10, 32, ENTDIM);
+ _sprite = Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "alarm_off");
+ _sounded = false;
+}
+
+void Alarm::activate()
+{
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ALARM_ACTIVE));
+ setSounded(true);
+ Locator::getAudio()->playSound("alarm");
+}
+
+void Alarm::deactivate()
+{
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "alarm_off"));
+}
+
+void Alarm::setSounded(bool b)
+{
+ _sounded = b;
+}
+
+bool Alarm::isSounded()
+{
+ return _sounded;
+}
+
+void Alarm::setAnimating(bool b)
+{
+ if (b)
+ {
+ changeAnimationSequence(Locator::getAnimationManager()->getSequence(ANIM_ALARM_ACTIVE));
+ }
+ else
+ {
+ changeToStaticSprite(Locator::getSpriteManager()->getIndex("./data/sprites/linkable.sprites", "alarm_off"));
+ }
+}
+
+bool Alarm::isActivated()
+{
+ return !_currentAnimFinished;
+}
+
+EnemyGun::EnemyGun(float x, float y, Circuit c) : LinkableEntity(x, y, c)
+{
+ setCollisionRectDims(8, 8, ENTDIM);
+ _sprite = 0;
+ _enemy = nullptr;
+}
+
+void EnemyGun::activate()
+{
+ LinkableEntity::activate();
+ _enemy->_fireWeapon(Shot_FromEnemyInvoluntary);
+}
+
+void EnemyGun::fire(GunShotTraceType gstt)
+{
+ LinkableEntity* other = _other; //if this gun is linked to another, _other may be made null when activating.
+ LinkableEntity::activate();
+ if (other == nullptr)
+ {
+ _enemy->_fireWeapon(gstt);
+ }
+ else
+ {
+ _enemy->setReactionTime(1000);
+ }
+}
+
+void EnemyGun::update(unsigned int dT)
+{
+ _position = _enemy->getPosition();
+ updateCollisionRectPosition();
+}
+
+void EnemyGun::setEnemy(Enemy* enemy)
+{
+ _enemy = enemy;
+}
+
+Enemy* EnemyGun::getEnemy()
+{
+ return _enemy;
+}
\ No newline at end of file
diff --git a/livingentity.cpp b/livingentity.cpp
new file mode 100644
index 0000000..0ea1887
--- /dev/null
+++ b/livingentity.cpp
@@ -0,0 +1,216 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "entity.h"
+#define STAIRTRAVERSETIME 1000 //Time in milliseconds to fully traverse stairs.
+
+LivingEntity::LivingEntity(float x, float y, Direction startingDir) : Entity(x, y)
+{
+ _onGround = false;
+ _alive = true;
+ _fixDirection = false;
+ _affectedByGravity = true;
+ _dir = startingDir;
+ _stairsEntered = nullptr;
+
+ //stair related
+ _traversal = NotMoving;
+ _stairTimer = STAIRTRAVERSETIME;
+ _overlappingStairs = nullptr;
+
+ _acceleration.accelerating = false;
+ _acceleration.accel = 0.0f;
+ _acceleration.target = 0.0f;
+}
+
+void LivingEntity::setAlive(bool b)
+{
+ _alive = b;
+}
+
+bool LivingEntity::isAlive()
+{
+ return _alive;
+}
+
+void LivingEntity::update(unsigned int dT)
+{
+ Entity::update(dT);
+
+ if (!_onGround && _affectedByGravity)
+ {
+ _velocity.y += GRAVITY;
+ }
+
+ if (_alive)
+ {
+ if (!_fixDirection)
+ {
+ if (_velocity.x > 0 && _dir == Left)
+ _dir = Right;
+
+ if (_velocity.x < 0 && _dir == Right)
+ _dir = Left;
+ }
+ }
+
+ if (isMovingThroughStairs())
+ {
+ if (_stairTimer > 0)
+ {
+ _stairTimer -= dT;
+ }
+ else
+ {
+ _stairTimer = STAIRTRAVERSETIME;
+ if (_overlappingStairs != nullptr) //could be null if save game is loaded while moving through stairs.
+ {
+ Stairs* target = (_traversal == MovingUp) ? _overlappingStairs->getUpstairs() : _overlappingStairs->getDownstairs();
+
+ if (target != nullptr)
+ {
+ arriveAtStairs(target);
+ }
+ }
+ _traversal = NotMoving;
+ }
+ }
+
+ if (_acceleration.accelerating)
+ {
+ if (fabs(_velocity.x - _acceleration.target) < fabs(_acceleration.accel))
+ {
+ _acceleration.accelerating = false;
+ _acceleration.accel = 0.0f;
+ _velocity.x = _acceleration.target;
+ }
+ _velocity.x += _acceleration.accel;
+ }
+}
+
+bool LivingEntity::isOnGround()
+{
+ return _onGround;
+}
+
+void LivingEntity::landOnGround()
+{
+ _velocity.y = 0.0f;
+ _onGround = true;
+
+ if (!_alive)
+ {
+ _velocity.x = 0.0f;
+ }
+}
+
+void LivingEntity::setOnGround(bool b)
+{
+ _onGround = b;
+}
+
+Direction LivingEntity::getDirection()
+{
+ return _dir;
+}
+
+void LivingEntity::reverseDirection()
+{
+ if (_dir == Left)
+ _dir = Right;
+ else _dir = Left;
+}
+
+void LivingEntity::setStairMovement(StairTraversal st)
+{
+ //lock bounding box to stairs only if path through stairs exists.
+ Stairs* target = (st == MovingUp) ? _overlappingStairs->getUpstairs() : _overlappingStairs->getDownstairs();
+
+ if (target != nullptr)
+ {
+ _stairsEntered = _overlappingStairs;
+ _traversal = st;
+ setCollisionRectPosition(_overlappingStairs->getCollisionRectPosition().x, _overlappingStairs->getCollisionRectPosition().y);
+ setDirection(Right);
+ _velocity.x = 0;
+ _velocity.y = 0;
+ }
+}
+
+StairTraversal LivingEntity::getStairTraversal()
+{
+ return _traversal;
+}
+
+void LivingEntity::setOverlappingStairs(Stairs* sw)
+{
+ _overlappingStairs = sw;
+}
+
+bool LivingEntity::isOverlappingStairs()
+{
+ return _overlappingStairs != nullptr;
+}
+
+void LivingEntity::arriveAtStairs(Stairs* st)
+{
+ setCollisionRectPosition(st->getCollisionRectPosition().x, st->getCollisionRectPosition().y);
+ _stairsEntered = nullptr;
+}
+
+bool LivingEntity::isMovingThroughStairs()
+{
+ return _traversal != NotMoving;
+}
+
+int LivingEntity::getStairTimer()
+{
+ return _stairTimer;
+}
+
+void LivingEntity::setDirection(Direction dir)
+{
+ _dir = dir;
+}
+
+Acceleration* LivingEntity::getAccelerationStruct()
+{
+ return &_acceleration;
+}
+
+float LivingEntity::getAcceleration()
+{
+ return _acceleration.accel;
+}
+
+bool LivingEntity::isAccelerating()
+{
+ return _acceleration.accelerating;
+}
+
+Stairs* LivingEntity::getStairsEntered()
+{
+ return _stairsEntered;
+}
+
+bool LivingEntity::isPositionBehind(float x)
+{
+ return ((x > getCollisionRectPosition().x && _dir == Left) ||
+ (x < getCollisionRectPosition().x && _dir == Right));
+}
\ No newline at end of file
diff --git a/loadmapstate.cpp b/loadmapstate.cpp
new file mode 100644
index 0000000..6fa1c2a
--- /dev/null
+++ b/loadmapstate.cpp
@@ -0,0 +1,177 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include
+#include "loadmapstate.h"
+#include "statemanager.h"
+#include "global.h"
+#define MAX_ITEMS_PER_PAGE 8
+
+LoadMapState::LoadMapState(StateManager* sm) : MenuState(sm)
+{
+ _currPageIndex = 0;
+
+ std::vector > currPage;
+
+ _chooseLabel.reset(new TextLabel(0, 0, "Select a map to load.", 1, 1, 1));
+ _pageLabel.reset(new TextLabel(0, 0, "1/1", 1, 1, 1));
+ _labels.push_back(_chooseLabel);
+ _labels.push_back(_pageLabel);
+
+ _cancelButton.reset(new TextButton(0, 0, (strlen("Go Back") + 2) * 16, 32, "Go Back"));
+
+ _prevButton.reset(new ImageButton(0, 0, 32, 32, Locator::getSpriteManager()->getIndex("./data/sprites/interface.sprites", "arrowleft"),
+ Locator::getSpriteManager()->getIndex("./data/sprites/interface.sprites", "arrowleft_selected")));
+ _nextButton.reset(new ImageButton(0, 0, 32, 32, Locator::getSpriteManager()->getIndex("./data/sprites/interface.sprites", "arrowright"),
+ Locator::getSpriteManager()->getIndex("./data/sprites/interface.sprites", "arrowright_selected")));
+
+ DIR *dir = opendir("./data");
+ struct dirent *ent;
+
+ if (dir != nullptr)
+ {
+ while ((ent = readdir (dir)) != nullptr)
+ {
+ if (strlen(ent->d_name) > 4 &&
+ strcmp(ent->d_name, "template.tmx") &&
+ ent->d_name[strlen(ent->d_name) - 1] == 'x' &&
+ ent->d_name[strlen(ent->d_name) - 2] == 'm' &&
+ ent->d_name[strlen(ent->d_name) - 3] == 't' &&
+ ent->d_name[strlen(ent->d_name) - 4] == '.')
+ {
+ currPage.push_back(std::shared_ptr(new TextButton(0, 0, (strlen(ent->d_name) + 2) * 16, 32, std::string(ent->d_name))));
+ if (currPage.size() == MAX_ITEMS_PER_PAGE)
+ {
+ currPage.push_back(_cancelButton);
+ currPage.push_back(_prevButton);
+ currPage.push_back(_nextButton);
+ _pages.push_back(currPage);
+ currPage.clear();
+ }
+ }
+ }
+ closedir (dir);
+
+ if (!currPage.empty())
+ {
+ currPage.push_back(_cancelButton);
+ currPage.push_back(_prevButton);
+ currPage.push_back(_nextButton);
+ _pages.push_back(currPage);
+ currPage.clear();
+ }
+ }
+ else
+ {
+ LOGF((stderr, "ERROR: Could not load directory ./data."));
+ }
+
+ handlePageChange();
+}
+
+LoadMapState::~LoadMapState()
+{
+}
+
+void LoadMapState::resetPositions(int w, int h)
+{
+ size_t i, j;
+ int iy = 102;
+ Button* button;
+ _chooseLabel->setPosition((w * 0.5) - 168, h * 0.05f);
+ _cancelButton->setPosition(w * 0.45f, h * 0.85f);
+ _prevButton->setPosition(w * 0.4f, h * 0.07f);
+ _pageLabel->setPositionWithOffset(w * 0.5, h * 0.07f, 0, 24);
+ _nextButton->setPosition(w * 0.6f, h * 0.07f);
+
+ for (i = 0; i < _pages.size(); i++)
+ {
+ for (j = 0; j < _pages[i].size(); j++)
+ {
+ button = _pages[i][j].get();
+ if (button != _cancelButton.get() && button != _prevButton.get() && button != _nextButton.get())
+ {
+ button->setPositionWithOffset(w * 0.45f, 0, 0, iy);
+ iy += 32;
+ }
+ }
+ iy = 102;
+ }
+}
+
+void LoadMapState::handleButton(Button* button)
+{
+ if (button == _cancelButton.get())
+ {
+ _manager->switchToState(MAINMENU_SCREEN);
+ }
+ else if (button == _prevButton.get())
+ {
+ prevPage();
+ }
+ else if (button == _nextButton.get())
+ {
+ nextPage();
+ }
+ else
+ {
+ std::string fullpath = std::string("data/") + std::string((static_cast(button))->getText());
+ _manager->setActiveMapFilename(fullpath);
+ _manager->switchToState(UPGRADES_SCREEN);
+ }
+}
+
+void LoadMapState::handleMouseWheel(int dir)
+{
+ if (dir < 0)
+ nextPage();
+ else
+ prevPage();
+}
+
+void LoadMapState::nextPage()
+{
+ if (_currPageIndex < _pages.size() - 1)
+ {
+ _currPageIndex++;
+ }
+ handlePageChange();
+}
+
+void LoadMapState::prevPage()
+{
+ if (_currPageIndex > 0)
+ {
+ _currPageIndex--;
+ }
+ handlePageChange();
+}
+
+void LoadMapState::handlePageChange()
+{
+ _buttons = _pages[_currPageIndex];
+ char str[16];
+ #ifdef _WIN32
+ sprintf(str, "%i/%i", _currPageIndex + 1, _pages.size());
+ #else
+ sprintf(str, "%lu/%lu", _currPageIndex + 1, _pages.size());
+ #endif
+ _pageLabel->setText(std::string(str));
+}
\ No newline at end of file
diff --git a/loadmapstate.h b/loadmapstate.h
new file mode 100644
index 0000000..f8e9f7b
--- /dev/null
+++ b/loadmapstate.h
@@ -0,0 +1,45 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef LOADMAPSTATE_H
+#define LOADMAPSTATE_H
+#include "menustate.h"
+
+class LoadMapState : public MenuState
+{
+public:
+ LoadMapState(StateManager* sm);
+ ~LoadMapState();
+ void handleButton(Button* button);
+ void handleMouseWheel(int dir);
+ void nextPage();
+ void prevPage();
+ void resetPositions(int w, int h);
+private:
+ std::shared_ptr _chooseLabel;
+ std::shared_ptr _pageLabel;
+ std::shared_ptr _cancelButton;
+ std::shared_ptr _prevButton;
+ std::shared_ptr _nextButton;
+ std::vector > > _pages;
+ size_t _currPageIndex;
+ void handlePageChange();
+};
+
+#endif
\ No newline at end of file
diff --git a/locator.cpp b/locator.cpp
new file mode 100644
index 0000000..d665209
--- /dev/null
+++ b/locator.cpp
@@ -0,0 +1,76 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "locator.h"
+
+std::unique_ptr Locator::_audiomanager;
+std::unique_ptr Locator::_animmanager;
+std::unique_ptr Locator::_bindingsmanager;
+std::unique_ptr Locator::_configmanager;
+std::unique_ptr Locator::_spritemanager;
+
+AudioManager* Locator::getAudio()
+{
+ return _audiomanager.get();
+}
+
+void Locator::provide(std::unique_ptr service)
+{
+ _audiomanager = std::move(service);
+}
+
+BindingsManager* Locator::getBindingsManager()
+{
+ return _bindingsmanager.get();
+}
+
+void Locator::provide(std::unique_ptr service)
+{
+ _bindingsmanager = std::move(service);
+}
+
+StaticSpriteManager* Locator::getSpriteManager()
+{
+ return _spritemanager.get();
+}
+
+void Locator::provide(std::unique_ptr service)
+{
+ _spritemanager = std::move(service);
+}
+
+AnimationManager* Locator::getAnimationManager()
+{
+ return _animmanager.get();
+}
+
+void Locator::provide(std::unique_ptr service)
+{
+ _animmanager = std::move(service);
+}
+
+ConfigManager* Locator::getConfigManager()
+{
+ return _configmanager.get();
+}
+
+void Locator::provide(std::unique_ptr service)
+{
+ _configmanager = std::move(service);
+}
\ No newline at end of file
diff --git a/locator.h b/locator.h
new file mode 100644
index 0000000..3197248
--- /dev/null
+++ b/locator.h
@@ -0,0 +1,53 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef LOCATOR_H
+#define LOCATOR_H
+#include "audio.h"
+#include "animations.h"
+#include "bindings.h"
+#include "config.h"
+#include "static_sprites.h"
+
+class Locator
+{
+public:
+ static AudioManager* getAudio();
+ static void provide(std::unique_ptr service);
+
+ static BindingsManager* getBindingsManager();
+ static void provide(std::unique_ptr service);
+
+ static StaticSpriteManager* getSpriteManager();
+ static void provide(std::unique_ptr service);
+
+ static AnimationManager* getAnimationManager();
+ static void provide(std::unique_ptr service);
+
+ static ConfigManager* getConfigManager();
+ static void provide(std::unique_ptr service);
+private:
+ static std::unique_ptr _audiomanager;
+ static std::unique_ptr _animmanager;
+ static std::unique_ptr _bindingsmanager;
+ static std::unique_ptr _configmanager;
+ static std::unique_ptr _spritemanager;
+};
+
+#endif
\ No newline at end of file
diff --git a/main.cpp b/main.cpp
new file mode 100644
index 0000000..b4be644
--- /dev/null
+++ b/main.cpp
@@ -0,0 +1,286 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include
+
+#include
+
+#include "file.h"
+#include "draw.h"
+#include "matrix.h"
+#define STB_TRUETYPE_IMPLEMENTATION
+#include "stb_truetype.h"
+#include "scene.h"
+#include "statemanager.h"
+#include "optionsstate.h"
+#include "locator.h"
+#include "config.h"
+
+SDL_Window* screen;
+SDL_GLContext context;
+Renderer renderer;
+
+bool fullscreen;
+int winX, winY;
+
+std::string fontFilename;
+std::unique_ptr sm;
+
+void init(void)
+{
+ winX = 800;
+ winY = 600;
+ fullscreen = false;
+
+ std::unique_ptr manager(new AudioManager());
+ Locator::provide(std::move(manager));
+
+ std::unique_ptr spriteManager(new StaticSpriteManager());
+ Locator::provide(std::move(spriteManager));
+
+ std::unique_ptr animationManager(new AnimationManager());
+ Locator::provide(std::move(animationManager));
+
+ std::unique_ptr configManager(new ConfigManager());
+ Locator::provide(std::move(configManager));
+
+ std::unique_ptr bindingsManager(new BindingsManager());
+ Locator::provide(std::move(bindingsManager));
+
+ srand(time(nullptr));
+}
+
+void draw_frame(void)
+{
+ renderer.drawState(sm->getActiveState());
+ SDL_GL_SwapWindow(screen);
+}
+
+void getSettings(void)
+{
+ ConfigManager* cm = Locator::getConfigManager();
+ BindingsManager* bm = Locator::getBindingsManager();
+ cm->loadConfig("config.cfg");
+ bm->loadBindingsFromConfig("config.cfg");
+
+ std::string val;
+ val = cm->getValue("window_x");
+ if (val != "")
+ {
+ winX = atoi(val.c_str());
+ }
+ val = cm->getValue("window_y");
+ if (val != "")
+ {
+ winY = atoi(val.c_str());
+ }
+ val = cm->getValue("fullscreen");
+ if (val != "")
+ {
+ fullscreen = atoi(val.c_str());
+ }
+ val = cm->getValue("font");
+ if (val != "")
+ {
+ fontFilename = val;
+ }
+}
+
+bool initSDL(void)
+{
+ bool success = true;
+ int flags = SDL_WINDOW_OPENGL;
+ if (SDL_Init(SDL_INIT_EVERYTHING))
+ {
+ success = false;
+ }
+ else
+ {
+ if (fullscreen)
+ {
+ flags |= SDL_WINDOW_FULLSCREEN;
+ }
+ screen = SDL_CreateWindow("Clonepoint", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, winX, winY, flags);
+ if (!screen) success = false;
+ }
+
+ context = SDL_GL_CreateContext(screen);
+ if (!context)
+ {
+ success = false;
+ }
+
+ if (!success)
+ {
+ LOGF((stderr, "%s\n", SDL_GetError()));
+ }
+ else
+ {
+ SDL_GL_MakeCurrent(screen, context);
+ }
+
+ return success;
+}
+
+void quit()
+{
+ char scrstr[4]; //hope you don't have more than 9999 screenshots :/
+ sprintf(scrstr, "%i", renderer.getScreenshotIndex());
+ Locator::getConfigManager()->setValue("screenshot_index", std::string(scrstr));
+ Locator::getConfigManager()->saveConfig("config.cfg", Locator::getBindingsManager()->getBindingsToSave());
+ SDL_GL_DeleteContext(context);
+ SDL_DestroyWindow(screen);
+ SDL_Quit();
+}
+
+void handleSettingsChange()
+{
+ renderer.handleSettingsChange();
+ bool change = false;
+ int newX = atoi(Locator::getConfigManager()->getValue("window_x").c_str());
+ int newY = atoi(Locator::getConfigManager()->getValue("window_y").c_str());
+
+ if (winX != newX)
+ {
+ winX = newX;
+ change = true;
+ }
+
+ if (winY != newY)
+ {
+ winY = newY;
+ change = true;
+ }
+
+ if (change)
+ {
+ SDL_DestroyWindow(screen);
+ screen = SDL_CreateWindow("Clonepoint", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, winX, winY, SDL_WINDOW_OPENGL);
+ SDL_GL_MakeCurrent(screen, context);
+ renderer.setResolution(winX, winY);
+ sm->setWindowDims(winX, winY);
+ }
+ SDL_SetWindowFullscreen(screen, Locator::getConfigManager()->getValue("fullscreen") == "1");
+}
+
+void loop1()
+{
+ /*
+ Uint32 fps_lasttime = SDL_GetTicks(); //the last recorded time.
+ Uint32 fps_current; //the current FPS.
+ Uint32 fps_frames = 0; //frames passed since the last recorded fps.
+ */
+
+ Uint32 old_time;
+ Uint32 curr_time;
+ old_time = SDL_GetTicks();
+ float delta = 0.0f;
+ while (!sm->getActiveState()->isQuitting())
+ {
+ sm->getActiveState()->handleInput();
+
+ curr_time = SDL_GetTicks();
+
+ // while ((curr_time - old_time) < 15)
+ // {
+ // curr_time = SDL_GetTicks();
+ // SDL_Delay(curr_time - old_time);
+ // }
+
+ delta += (curr_time - old_time);
+ old_time = curr_time;
+
+ while (delta > 15)
+ {
+ sm->update(15);
+ renderer.updateLinkProgress(15);
+ delta -= 15;
+ }
+
+ draw_frame();
+ /*
+ fps_frames++;
+ if (fps_lasttime < SDL_GetTicks() - 1000)
+ {
+ fps_lasttime = SDL_GetTicks();
+ fps_current = fps_frames;
+ LOGF((stdout, "FPS = %i\n", fps_current));
+ fps_frames = 0;
+ }
+ */
+ }
+}
+
+void loop2()
+{
+ Uint32 old_time;
+
+ old_time = SDL_GetTicks();
+
+ unsigned int dT;
+ unsigned int accumulator = 0;
+
+ while (!sm->getActiveState()->isQuitting())
+ {
+ sm->getActiveState()->handleInput();
+ dT = SDL_GetTicks() - old_time;
+ accumulator += dT;
+ if (accumulator >= 15)
+ {
+ sm->update(accumulator);
+ accumulator = 0;
+ draw_frame();
+ }
+
+ old_time = SDL_GetTicks();
+ }
+}
+
+int main(int argc, char **argv)
+{
+ init();
+ getSettings();
+
+ if (!initSDL())
+ {
+ return 1;
+ }
+
+ if (!renderer.initShaders())
+ {
+ LOGF((stderr, "Shaders not supported.\n"));
+ return 1;
+ }
+
+ renderer.init(winX, winY);
+
+ renderer.setScreenshotIndex(atoi(Locator::getConfigManager()->getValue("screenshot_index").c_str()));
+
+ sm.reset(new StateManager());
+ sm->setWindowDims(winX, winY);
+ Locator::getAudio()->setVolume((float)atoi(Locator::getConfigManager()->getValue("volume").c_str()) / 10);
+ sm->registerScreenshotFunctions(renderer.getScreenshotFunc());
+ sm->registerSettingsChange(handleSettingsChange);
+
+ loop1();
+
+ quit();
+ return 0;
+}
\ No newline at end of file
diff --git a/mainmenustate.cpp b/mainmenustate.cpp
new file mode 100644
index 0000000..a21e4bf
--- /dev/null
+++ b/mainmenustate.cpp
@@ -0,0 +1,76 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include "mainmenustate.h"
+#include "statemanager.h"
+#include "global.h"
+
+MainMenuState::MainMenuState(StateManager* sm) : MenuState(sm)
+{
+ _titleLabel.reset(new TextLabel(0, 0, "Clonepoint", 1, 1, 1));
+ _quitButton.reset(new TextButton(0, 0, (strlen("Quit Game") + 2) * 16, 32, "Quit Game"));
+ _creditsButton.reset(new TextButton(640, 576, (strlen("View Credits") + 2) * 16, 32, "View Credits"));
+ _loadMapButton.reset(new TextButton(0, 0, (strlen("Load a Map") + 2) * 16, 32, "Load a Map"));
+ _optionsButton.reset(new TextButton(0, 0, (strlen("Options") + 2) * 16, 32, "Options"));
+
+ _labels.push_back(_titleLabel);
+ _buttons.push_back(_quitButton);
+ _buttons.push_back(_loadMapButton);
+ _buttons.push_back(_optionsButton);
+ _buttons.push_back(_creditsButton);
+}
+
+MainMenuState::~MainMenuState()
+{
+}
+
+void MainMenuState::resetPositions(int w, int h)
+{
+ int mid = w * 0.48;
+ _titleLabel->setPosition(mid, h * 0.1f);
+ _loadMapButton->setPosition(mid, h * 0.3f);
+ _optionsButton->setPosition(mid, h * 0.35f);
+ _creditsButton->setPosition(mid, h * 0.4f);
+ _quitButton->setPosition(mid, h * 0.45f);
+}
+
+void MainMenuState::update(unsigned int dT)
+{
+ MenuState::update(dT);
+}
+
+void MainMenuState::handleButton(Button* button)
+{
+ if (button == _quitButton.get())
+ {
+ _quitting = true;
+ }
+ else if (button == _creditsButton.get())
+ {
+ _manager->switchToState(CREDITS_SCREEN);
+ }
+ else if (button == _loadMapButton.get())
+ {
+ _manager->switchToState(LOADMAP_SCREEN);
+ }
+ else if (button == _optionsButton.get())
+ {
+ _manager->switchToState(OPTIONS_SCREEN);
+ }
+}
\ No newline at end of file
diff --git a/mainmenustate.h b/mainmenustate.h
new file mode 100644
index 0000000..7e28d95
--- /dev/null
+++ b/mainmenustate.h
@@ -0,0 +1,40 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef MAINMENUSTATE_H
+#define MAINMENUSTATE_H
+#include "menustate.h"
+
+class MainMenuState : public MenuState
+{
+public:
+ MainMenuState(StateManager* sm);
+ ~MainMenuState();
+ void update(unsigned int dT);
+ void handleButton(Button* button);
+ void resetPositions(int w, int h);
+private:
+ std::shared_ptr _titleLabel;
+ std::shared_ptr _quitButton;
+ std::shared_ptr _creditsButton;
+ std::shared_ptr _loadMapButton;
+ std::shared_ptr _optionsButton;
+};
+
+#endif
\ No newline at end of file
diff --git a/makefile b/makefile
new file mode 100644
index 0000000..2c655db
--- /dev/null
+++ b/makefile
@@ -0,0 +1,149 @@
+SYS := $(shell gcc -dumpmachine)
+
+CXX=g++
+CFLAGS= -Wall -pedantic -std=c++11
+OBJECTS = statemanager.o audio.o locator.o draw.o static_sprites.o animations.o vec.o matrix.o map.o texture.o button.o scene.o scene_guards.o scene_trace.o bindings.o \
+scene_physics.o scene_saved_game.o intersect.o state.o file.o entity.o enemy.o linkableentity.o player.o elevators.o fieldofview.o config.o font.o \
+livingentity.o stairs.o menustate.o mainmenustate.o creditsstate.o gamestate.o levelendstate.o pausestate.o loadmapstate.o optionsstate.o upgradesstate.o sprite.o \
+tinyxml/tinyxml.o tinyxml/tinystr.o tinyxml/tinyxmlparser.o tinyxml/tinyxmlerror.o
+
+ifneq (, $(findstring mingw, $(SYS)))
+LIBS = -lmingw32 -lSDL2main -lSDL2 -lopengl32 -lopenal32
+DELCMD = del *.o clonepoint.exe && cd tinyxml/ && del *.o
+MAKECMD = mingw32-make
+else
+LIBS = `sdl2-config --libs` -lGL -lopenal
+DELCMD = rm -f *.o clonepoint && cd tinyxml/ && make clean
+MAKECMD = make
+endif
+
+all: tinyxmldir game
+
+tinyxmldir:
+ cd tinyxml/ && $(MAKECMD)
+
+game: main.cpp $(OBJECTS)
+ $(CXX) main.cpp $(OBJECTS) $(LIBS) $(CFLAGS) -o clonepoint
+
+vec.o: vec.cpp vec.h
+ $(CXX) vec.cpp $(CFLAGS) -c
+
+matrix.o: matrix.cpp matrix.h vec.o
+ $(CXX) matrix.cpp $(CFLAGS) -c
+
+file.o: file.cpp file.h
+ $(CXX) file.cpp $(CFLAGS) -c
+
+audio.o: audio.cpp audio.h
+ $(CXX) audio.cpp $(CFLAGS) -c
+
+locator.o: locator.cpp locator.h audio.o
+ $(CXX) locator.cpp $(CFLAGS) -c
+
+button.o: button.cpp button.h
+ $(CXX) button.cpp $(CFLAGS) -c
+
+state.o: state.cpp state.h locator.o
+ $(CXX) state.cpp $(CFLAGS) -c
+
+statemanager.o: statemanager.cpp statemanager.h gamestate.o mainmenustate.o creditsstate.o levelendstate.o pausestate.o loadmapstate.o upgradesstate.o
+ $(CXX) statemanager.cpp $(CFLAGS) -c
+
+entity.o: entity.cpp entity.h vec.o sprite.o
+ $(CXX) entity.cpp $(CFLAGS) -c
+
+enemy.o: enemy.cpp entity.o livingentity.o
+ $(CXX) enemy.cpp $(CFLAGS) -c
+
+linkableentity.o: linkableentity.cpp entity.o
+ $(CXX) linkableentity.cpp $(CFLAGS) -c
+
+player.o: player.cpp entity.o livingentity.o
+ $(CXX) player.cpp $(CFLAGS) -c
+
+elevators.o: elevators.cpp entity.o
+ $(CXX) elevators.cpp $(CFLAGS) -c
+
+fieldofview.o: fieldofview.cpp entity.o
+ $(CXX) fieldofview.cpp $(CFLAGS) -c
+
+stairs.o: stairs.cpp entity.o
+ $(CXX) stairs.cpp $(CFLAGS) -c
+
+livingentity.o: livingentity.cpp entity.o
+ $(CXX) livingentity.cpp $(CFLAGS) -c
+
+scene.o: scene.cpp scene.h locator.o entity.o map.o
+ $(CXX) scene.cpp $(CFLAGS) -c
+
+scene_guards.o: scene_guards.cpp scene.o
+ $(CXX) scene_guards.cpp $(CFLAGS) -c
+
+scene_physics.o: scene_physics.cpp scene.o
+ $(CXX) scene_physics.cpp $(CFLAGS) -c
+
+scene_saved_game.o: scene_saved_game.cpp scene.o
+ $(CXX) scene_saved_game.cpp $(CFLAGS) -c
+
+scene_trace.o: scene_trace.cpp scene.o
+ $(CXX) scene_trace.cpp $(CFLAGS) -c
+
+intersect.o: intersect.cpp intersect.h vec.o
+ $(CXX) intersect.cpp $(CFLAGS) -c
+
+gamestate.o: gamestate.cpp gamestate.h state.o scene.o bindings.o
+ $(CXX) gamestate.cpp $(CFLAGS) -c
+
+menustate.o: menustate.cpp menustate.h state.o button.o
+ $(CXX) menustate.cpp $(CFLAGS) -c
+
+mainmenustate.o: mainmenustate.cpp mainmenustate.h menustate.o
+ $(CXX) mainmenustate.cpp $(CFLAGS) -c
+
+levelendstate.o: levelendstate.cpp levelendstate.h menustate.o
+ $(CXX) levelendstate.cpp $(CFLAGS) -c
+
+pausestate.o: pausestate.cpp pausestate.h menustate.o
+ $(CXX) pausestate.cpp $(CFLAGS) -c
+
+creditsstate.o: creditsstate.cpp creditsstate.h menustate.o
+ $(CXX) creditsstate.cpp $(CFLAGS) -c
+
+loadmapstate.o: loadmapstate.cpp loadmapstate.h menustate.o
+ $(CXX) loadmapstate.cpp $(CFLAGS) -c
+
+optionsstate.o: optionsstate.cpp optionsstate.h menustate.o
+ $(CXX) optionsstate.cpp $(CFLAGS) -c
+
+upgradesstate.o: upgradesstate.cpp upgradesstate.h menustate.o statemanager.h
+ $(CXX) upgradesstate.cpp $(CFLAGS) -c
+
+texture.o: texture.cpp texture.h
+ $(CXX) texture.cpp $(CFLAGS) -c
+
+draw.o: draw.cpp draw.h texture.o gamestate.o menustate.o
+ $(CXX) draw.cpp $(CFLAGS) -c
+
+static_sprites.o: static_sprites.cpp static_sprites.h
+ $(CXX) static_sprites.cpp $(CFLAGS) -c
+
+map.o: map.cpp map.h entity.o
+ $(CXX) map.cpp $(CFLAGS) -c
+
+sprite.o: sprite.cpp sprite.h texture.o
+ $(CXX) sprite.cpp $(CFLAGS) -c
+
+animations.o: animations.cpp animations.h
+ $(CXX) animations.cpp $(CFLAGS) -c
+
+bindings.o: bindings.cpp bindings.h
+ $(CXX) bindings.cpp $(CFLAGS) -c
+
+config.o: config.cpp config.h
+ $(CXX) config.cpp $(CFLAGS) -c
+
+font.o: font.cpp font.h
+ $(CXX) font.cpp $(CFLAGS) -c
+
+clean:
+ $(DELCMD)
\ No newline at end of file
diff --git a/map.cpp b/map.cpp
new file mode 100644
index 0000000..156f94e
--- /dev/null
+++ b/map.cpp
@@ -0,0 +1,1217 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#include
+#include
+#include "tinyxml/tinyxml.h"
+
+#include "entity.h"
+#include "global.h"
+#include "map.h"
+
+#define LIGHTRADIUS 768
+#define TILEDIM 16
+
+Map::Map()
+{
+ _mapTex = 0;
+}
+
+Map::~Map()
+{
+ LOGF((stdout, "Running map destructor!\n"));
+
+ _collideVols.clear();
+ _entities.clear();
+ _linkableEnts.clear();
+ _stairDoors.clear();
+ _stairwells.clear();
+ _lights.clear();
+ _shafts.clear();
+ _enemyIndices.clear();
+ glDeleteTextures(1, &_mapTex);
+}
+
+bool Map::loadFromFile(const char* filename, bool savegame)
+{
+ int i = 0;
+ char* encoding;
+ TiXmlDocument doc(filename);
+ _playerStartPos = vec2f(0, 0);
+ _subwayPos = vec2f(0, 0);
+ _subwayFound = false;
+
+ if (!doc.LoadFile())
+ {
+ LOGF((stderr, "Failed to load map file %s.\n", filename));
+ return false;
+ }
+
+ TiXmlHandle hDoc(&doc);
+ TiXmlElement *root, *lvl1, *lvl2, *lvl3;
+ root = doc.FirstChildElement("map");
+
+ if(!root)
+ {
+ LOGF((stderr, "Failed to parse map file %s.\n", filename));
+ return false;
+ }
+
+ _tilesWide = atoi(root->Attribute("width"));
+ _tilesHigh = atoi(root->Attribute("height"));
+
+ _mapWidth = _tilesWide * TILEDIM;
+ _mapHeight = _tilesHigh * TILEDIM;
+
+ lvl1 = root->FirstChildElement("tileset");
+ while(lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("image");
+ while (lvl2)
+ {
+ LOGF((stdout, "lvl2 filename = %s\n", lvl2->Attribute("source")));
+ if (atoi(lvl1->Attribute("firstgid")) == 1)
+ {
+ std::string fullSource = std::string("data/") + std::string(lvl2->Attribute("source"));
+ _tilesetImage = loadSurfaceFromImage(fullSource.c_str());
+ }
+ lvl2 = lvl1->NextSiblingElement("image");
+ }
+
+ lvl1 = lvl1->NextSiblingElement("tileset");
+ i++;
+ }
+
+ i = 0;
+
+ lvl1 = root->FirstChildElement("layer");
+ while (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("data");
+
+ if (lvl2)
+ {
+ encoding = (char*)lvl2->Attribute("encoding");
+
+ if (!strcmp(encoding, "csv"))
+ {
+ parseTileLayer((char*)lvl2->GetText());
+ }
+ }
+ else
+ {
+ LOGF((stderr, "Error. Layer has no data.\n"));
+ }
+
+ lvl1 = lvl1->NextSiblingElement("layer");
+ i++;
+ }
+
+ i = 0;
+
+ lvl1 = root->FirstChildElement("objectgroup");
+ while (lvl1)
+ {
+ if (!strcmp(lvl1->Attribute("name"), "Collision"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseCollisionVolume(lvl2);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "RedLinkable"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLinkableObject(lvl2, RED);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "GreenLinkable"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLinkableObject(lvl2, GREEN);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "BlueLinkable"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLinkableObject(lvl2, BLUE);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "VioletLinkable"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLinkableObject(lvl2, VIOLET);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "YellowLinkable"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLinkableObject(lvl2, YELLOW);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Links") && !savegame)
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ while (lvl2)
+ {
+ lvl3 = lvl2->FirstChildElement("polyline");
+ if (lvl3)
+ {
+ parsePolyline(lvl3, atof(lvl2->Attribute("x")), atof(lvl2->Attribute("y")), &_lines);
+ }
+ lvl2 = lvl2->NextSiblingElement("object");
+ }
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "LightLinks"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ while (lvl2)
+ {
+ lvl3 = lvl2->FirstChildElement("polyline");
+ if (lvl3)
+ {
+ parsePolyline(lvl3, atof(lvl2->Attribute("x")), atof(lvl2->Attribute("y")), &_lightLinks);
+ }
+ lvl2 = lvl2->NextSiblingElement("object");
+ }
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Entities"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseEntity(lvl2, savegame);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Objectives"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseObjective(lvl2);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Lights"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseLight(lvl2);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Props"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ parseProp(lvl2);
+ }
+ else if (!strcmp(lvl1->Attribute("name"), "Player"))
+ {
+ lvl2 = lvl1->FirstChildElement("object");
+ if (!strcmp(lvl2->Attribute("type"), "Player"))
+ {
+ _playerStartPos.x = atoi(lvl2->Attribute("x"));
+ _playerStartPos.y = atoi(lvl2->Attribute("y"));
+ }
+ }
+
+ lvl1 = lvl1->NextSiblingElement("objectgroup");
+ i++;
+ }
+
+ makeElevatorShafts();
+ setShaftOpenings();
+ makeStairwells();
+ calculateStairDirections();
+ calculateElevatorOrder();
+
+ size_t linkableCount = 0;
+ for (size_t j = 0; j < _entities.size(); j++)
+ {
+ if (std::dynamic_pointer_cast(_entities[j]))
+ {
+ linkableCount++;
+ }
+ }
+ Assert((_linkableEnts.size() == linkableCount));
+
+ _sniper.reset(new Enemy(0, 0, Left, false, Enemy_Sniper));
+
+ std::shared_ptr eg(new EnemyGun(0, 0, RED));
+ eg->setEnemy(_sniper.get());
+ _sniper->setGun(eg.get());
+ _entities.push_back(eg);
+
+ return true;
+}
+
+size_t Map::getNumberOfCollideVols()
+{
+ return _collideVols.size();
+}
+
+size_t Map::getNumberOfEnts()
+{
+ return _entities.size();
+}
+
+size_t Map::getNumberOfStairs()
+{
+ return _stairDoors.size();
+}
+
+size_t Map::getNumberOfStairwells()
+{
+ return _stairwells.size();
+}
+
+size_t Map::getNumberOfLights()
+{
+ return _lights.size();
+}
+
+size_t Map::getNumberOfLines()
+{
+ return _lines.size();
+}
+
+size_t Map::getNumberOfLightLinks()
+{
+ return _lightLinks.size();
+}
+
+size_t Map::getNumberOfShafts()
+{
+ return _shafts.size();
+}
+
+size_t Map::getNumberOfEnemies()
+{
+ return _enemyIndices.size();
+}
+
+CollisionVolume Map::getCollideVolAt(int i)
+{
+ return *_collideVols[i].get();
+}
+
+CollisionVolume* Map::getCollideVolPointerAt(int i)
+{
+ return _collideVols[i].get();
+}
+
+Entity* Map::getEntAt(size_t i)
+{
+ return _entities[i].get();
+}
+
+Enemy* Map::getEnemyAt(size_t i)
+{
+ Entity* ent = _entities[_enemyIndices[i]].get();
+ Assert(dynamic_cast(ent));
+ return static_cast(ent);
+}
+
+Stairs* Map::getStairsAt(size_t i)
+{
+ return _stairDoors[i].get();
+}
+
+Stairwell* Map::getStairwellAt(size_t i)
+{
+ return _stairwells[i].get();
+}
+
+FieldOfView* Map::getLightAt(size_t i)
+{
+ return _lights[i].get();
+}
+
+Line Map::getLineAt(size_t i)
+{
+ return _lines[i];
+}
+
+Line Map::getLightLinkAt(size_t i)
+{
+ return _lightLinks[i];
+}
+
+ElevatorShaft* Map::getShaftAt(size_t i)
+{
+ return _shafts[i].get();
+}
+
+unsigned int Map::getMapWidth()
+{
+ return _mapWidth;
+}
+
+unsigned int Map::getMapHeight()
+{
+ return _mapHeight;
+}
+
+int Map::indexOfCollideVol(CollisionVolume* vol)
+{
+ unsigned int i;
+ for (i = 0; i < _collideVols.size(); i++)
+ {
+ if (_collideVols[i].get() == vol)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+int Map::indexOfEntity(Entity* ent)
+{
+ unsigned int i;
+ for (i = 0; i < _entities.size(); i++)
+ {
+ if (_entities[i].get() == ent)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+int Map::indexOfFOV(FieldOfView* fov)
+{
+ unsigned int i;
+ for (i = 0; i < _lights.size(); i++)
+ {
+ if (_lights[i].get() == fov)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+void Map::parseLinkableObject(TiXmlElement* element, Circuit c)
+{
+ bool open;
+ bool handscanner;
+ bool facingRight;
+ float r, g, b;
+ TiXmlElement* lvl1, *lvl2;
+ while (element)
+ {
+ if (!element->Attribute("x") || !element->Attribute("y") || !element->Attribute("type"))
+ {
+ LOGF((stderr, "LinkableEntity has null value - ignoring.\n"));
+ }
+ else
+ {
+ int x = atoi(element->Attribute("x"));
+ int y = atoi(element->Attribute("y"));
+ if (!strcmp(element->Attribute("type"), "Switch"))
+ {
+ handscanner = false;
+ lvl1 = element->FirstChildElement("properties");
+
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "handscanner") &&
+ lvl2->Attribute("value") && !strcmp(lvl2->Attribute("value"), "yes"))
+ {
+ handscanner = true;
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ std::shared_ptr sw(new LightSwitch(x, y - ENTDIM, c, handscanner));
+ _entities.push_back(sw);
+ _linkableEnts.push_back(sw);
+ }
+ else if (!strcmp(element->Attribute("type"), "Door"))
+ {
+ open = false;
+
+ lvl1 = element->FirstChildElement("properties");
+
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "open") &&
+ lvl2->Attribute("value") && !strcmp(lvl2->Attribute("value"), "yes"))
+ {
+ open = true;
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ std::shared_ptr door(new Door(x, y - ENTDIM, c, open ? true : false, Door_Normal));
+ _collideVols.push_back(std::unique_ptr(door->getCollisionVolume()));
+ _entities.push_back(door);
+ _linkableEnts.push_back(door);
+ }
+ else if (!strcmp(element->Attribute("type"), "TrapDoor"))
+ {
+ std::shared_ptr td(new Door(x, y - ENTDIM, c, false, Door_Trap));
+ _collideVols.push_back(std::unique_ptr(td->getCollisionVolume()));
+ _entities.push_back(td);
+ _linkableEnts.push_back(td);
+ }
+ else if (!strcmp(element->Attribute("type"), "VaultDoor"))
+ {
+ std::shared_ptr vd(new Door(x, y - ENTDIM, c, false, Door_Vault));
+ _collideVols.push_back(std::unique_ptr(vd->getCollisionVolume()));
+ _collideVols.push_back(std::unique_ptr(vd->getCollisionVolume2()));
+ _entities.push_back(vd);
+ _linkableEnts.push_back(vd);
+ }
+ else if (!strcmp(element->Attribute("type"), "LightFixture"))
+ {
+ r = g = b = 1.0f;
+ lvl1 = element->FirstChildElement("properties");
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "red") &&
+ lvl2->Attribute("value"))
+ {
+ r = atof(lvl2->Attribute("value"));
+ }
+
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "green") &&
+ lvl2->Attribute("value"))
+ {
+ g = atof(lvl2->Attribute("value"));
+ }
+
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "blue") &&
+ lvl2->Attribute("value"))
+ {
+ b = atof(lvl2->Attribute("value"));
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ std::shared_ptr fixture(new LightFixture(x, y - ENTDIM, c, true));
+ std::unique_ptr fov(new FieldOfView(x + (ENTDIM / 2) - 8, y - (ENTDIM / 2) + 4, LIGHTRADIUS, 0, 180, true, FOV_LIGHT));
+ fov->setColors(r, g, b);
+ fixture->addFOV(fov.get());
+ _entities.push_back(fixture);
+ _linkableEnts.push_back(fixture);
+ _lights.push_back(std::move(fov));
+ }
+ else if (!strcmp(element->Attribute("type"), "Socket"))
+ {
+ std::shared_ptr socket(new PowerSocket(x, y - ENTDIM, c));
+ _entities.push_back(socket);
+ _linkableEnts.push_back(socket);
+ }
+ else if (!strcmp(element->Attribute("type"), "Scanner"))
+ {
+ std::shared_ptr scanner(new MotionScanner(x, y - ENTDIM, c));
+ _entities.push_back(scanner);
+ _linkableEnts.push_back(scanner);
+ }
+ else if (!strcmp(element->Attribute("type"), "Elevator"))
+ {
+ std::shared_ptr ed(new ElevatorDoor(x, y - ENTDIM));
+ std::shared_ptr sw(new ElevatorSwitch(x + 32, y - ENTDIM, c));
+ sw->registerDoor(ed.get());
+ ed->registerSwitch(sw.get());
+ _entities.push_back(ed);
+ _entities.push_back(sw);
+ _linkableEnts.push_back(sw);
+ }
+ else if (!strcmp(element->Attribute("type"), "SoundDetector"))
+ {
+ std::shared_ptr sd(new SoundDetector(x, y - ENTDIM, c));
+ _entities.push_back(sd);
+ _linkableEnts.push_back(sd);
+ }
+ else if (!strcmp(element->Attribute("type"), "SecurityCamera"))
+ {
+ facingRight = false;
+
+ lvl1 = element->FirstChildElement("properties");
+
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "direction") &&
+ lvl2->Attribute("value") && !strcmp(lvl2->Attribute("value"), "right"))
+ {
+ facingRight = true;
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ std::unique_ptr fov(new FieldOfView(facingRight ? x + ENTDIM/2 + 8 : x + ENTDIM/2 - 8,
+ y - ENTDIM/2 + 8, LIGHTRADIUS, facingRight ? 27 : -27, 27, true, FOV_CAMERA));
+
+ fov->setColors(0.5, 0.5, 0);
+
+ std::shared_ptr cam(new SecurityCamera(x , y - ENTDIM, c, facingRight ? Right : Left, fov.get()));
+ _entities.push_back(cam);
+ _linkableEnts.push_back(cam);
+ _lights.push_back(std::move(fov));
+ }
+ else if (!strcmp(element->Attribute("type"), "Alarm"))
+ {
+ std::shared_ptr alarm(new Alarm(x, y - ENTDIM, c));
+ _entities.push_back(alarm);
+ _linkableEnts.push_back(alarm);
+ }
+ }
+ element = element->NextSiblingElement("object");
+ }
+}
+
+void Map::parseCollisionVolume(TiXmlElement* element)
+{
+ while (element)
+ {
+ if (!element->Attribute("x") || !element->Attribute("y") || !element->Attribute("width") || !element->Attribute("height"))
+ {
+ LOGF((stderr, "Collision volume has null value - ignoring.\n"));
+ }
+ else
+ {
+ std::unique_ptr vol(new CollisionVolume());
+ vol->rect.x = atoi(element->Attribute("x"));
+ vol->rect.y = atoi(element->Attribute("y"));
+ vol->rect.w = atoi(element->Attribute("width"));
+ vol->rect.h = atoi(element->Attribute("height"));
+ vol->glass = element->Attribute("type") && !strcmp(element->Attribute("type"), "Glass");
+ vol->guardblock = element->Attribute("type") && !strcmp(element->Attribute("type"), "GuardBlock");
+ vol->active = !vol->guardblock;
+
+ vol->door = false;
+ _collideVols.push_back(std::move(vol));
+ }
+ element = element->NextSiblingElement("object");
+ }
+}
+
+void Map::parseEntity(TiXmlElement* element, bool savegame)
+{
+ bool facingRight;
+ bool startPatrolling;
+ TiXmlElement* lvl1, *lvl2;
+ while (element)
+ {
+ facingRight = false;
+ startPatrolling = false;
+
+ if (!element->Attribute("x") || !element->Attribute("y") || !element->Attribute("type"))
+ {
+ LOGF((stderr, "Entity has null value - ignoring.\n"));
+ }
+ else
+ {
+ int x = atoi(element->Attribute("x"));
+ int y = atoi(element->Attribute("y"));
+
+ lvl1 = element->FirstChildElement("properties");
+
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "direction") &&
+ lvl2->Attribute("value") && !strcmp(lvl2->Attribute("value"), "right"))
+ {
+ facingRight = true;
+ }
+
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "patrolling") &&
+ lvl2->Attribute("value") && !strcmp(lvl2->Attribute("value"), "yes"))
+ {
+ startPatrolling = true;
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ if (!strcmp(element->Attribute("type"), "Guard"))
+ {
+ Enemy* guard = new Enemy(x, y - ENTDIM, facingRight ? Right : Left, startPatrolling, Enemy_Guard);
+ _entities.push_back(std::shared_ptr(guard));
+ _enemyIndices.push_back(_entities.size() - 1);
+ std::shared_ptr eg(new EnemyGun(x, y, RED));
+ eg->setEnemy(guard);
+ guard->setGun(eg.get());
+ _entities.push_back(eg);
+ _linkableEnts.push_back(eg);
+ }
+
+ if (!strcmp(element->Attribute("type"), "Enforcer"))
+ {
+ Enemy* enforcer = new Enemy(x, y - ENTDIM, facingRight ? Right : Left, startPatrolling, Enemy_Enforcer);
+ _entities.push_back(std::shared_ptr(enforcer));
+ _enemyIndices.push_back(_entities.size() - 1);
+ std::shared_ptr eg(new EnemyGun(x, y, RED));
+ eg->setEnemy(enforcer);
+ enforcer->setGun(eg.get());
+ _entities.push_back(eg);
+ _linkableEnts.push_back(eg);
+ }
+
+ if (!strcmp(element->Attribute("type"), "Professional"))
+ {
+ Enemy* professional = new Enemy(x, y - ENTDIM, facingRight ? Right : Left, startPatrolling, Enemy_Professional);
+ _entities.push_back(std::shared_ptr(professional));
+ _enemyIndices.push_back(_entities.size() - 1);
+ std::shared_ptr eg(new EnemyGun(x, y, RED));
+ eg->setEnemy(professional);
+ professional->setGun(eg.get());
+ _entities.push_back(eg);
+ _linkableEnts.push_back(eg);
+ }
+
+ if (!strcmp(element->Attribute("type"), "Stairs"))
+ {
+ _stairDoors.push_back(std::unique_ptr(new Stairs(x, y - ENTDIM)));
+ }
+
+ if (!strcmp(element->Attribute("type"), "CircuitBoxG"))
+ {
+ _entities.push_back(std::shared_ptr(new CircuitBox(x, y - ENTDIM, GREEN)));
+ }
+
+ if (!strcmp(element->Attribute("type"), "CircuitBoxB"))
+ {
+ _entities.push_back(std::shared_ptr(new CircuitBox(x, y - ENTDIM, BLUE)));
+ }
+
+ if (!strcmp(element->Attribute("type"), "CircuitBoxY"))
+ {
+ _entities.push_back(std::shared_ptr(new CircuitBox(x, y - ENTDIM, YELLOW)));
+ }
+
+ if (!strcmp(element->Attribute("type"), "CircuitBoxV"))
+ {
+ _entities.push_back(std::shared_ptr(new CircuitBox(x, y - ENTDIM, VIOLET)));
+ }
+ }
+ element = element->NextSiblingElement("object");
+ }
+}
+
+void Map::parseLight(TiXmlElement* element)
+{
+ int direction, numangles;
+ TiXmlElement* lvl1, *lvl2;
+
+ while (element)
+ {
+ if (!element->Attribute("x") || !element->Attribute("y"))
+ {
+ LOGF((stderr, "Light has null value - ignoring.\n"));
+ }
+ else
+ {
+ direction = 0;
+ numangles = 180;
+ int x = atoi(element->Attribute("x"));
+ int y = atoi(element->Attribute("y"));
+
+ lvl1 = element->FirstChildElement("properties");
+
+ if (lvl1)
+ {
+ lvl2 = lvl1->FirstChildElement("property");
+ while (lvl2)
+ {
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "numangles") &&
+ lvl2->Attribute("value"))
+ {
+ numangles = atoi(lvl2->Attribute("value"));
+ }
+
+ if (lvl2->Attribute("name") && !strcmp(lvl2->Attribute("name"), "direction") &&
+ lvl2->Attribute("value"))
+ {
+ direction = atoi(lvl2->Attribute("value"));
+ }
+
+ lvl2 = lvl2->NextSiblingElement("property");
+ }
+ }
+
+ std::unique_ptr fov(new FieldOfView(x, y + 4, LIGHTRADIUS, direction, numangles, true, FOV_LIGHT));
+ _lights.push_back(std::move(fov));
+
+ }
+ element = element->NextSiblingElement("object");
+ }
+}
+
+void Map::parseProp(TiXmlElement* element)
+{
+ std::string prop = "";
+ while (element)
+ {
+ if (!element->Attribute("x") || !element->Attribute("y"))
+ {
+ LOGF((stderr, "Prop has null value - ignoring.\n"));
+ }
+ else
+ {
+ int x = atoi(element->Attribute("x"));
+ int y = atoi(element->Attribute("y"));
+
+ if (!strcmp(element->Attribute("type"), "Watercooler"))
+ {
+ prop = "watercooler";
+ }
+
+ if (!strcmp(element->Attribute("type"), "Shelf"))
+ {
+ prop = "shelf";
+ }
+
+ if (!strcmp(element->Attribute("type"), "Trashcan"))
+ {
+ prop = "trashcan";
+ }
+
+ if (!strcmp(element->Attribute("type"), "Couch"))
+ {
+ prop = "couch";
+ }
+
+ if (!strcmp(element->Attribute("type"), "Plant1"))
+ {
+ prop = "plant1";
+ }
+
+ if (!strcmp(element->Attribute("type"), "Plant2"))
+ {
+ prop = "plant2";
+ }
+
+ if (!strcmp(element->Attribute("type"), "SubwaySign"))
+ {
+ prop = "subwaysign";
+ }
+
+ if (!strcmp(element->Attribute("type"), "LightPanel"))
+ {
+ prop = "lightpanel";
+ }
+
+ if (prop != "")
+ {
+ _entities.push_back(std::shared_ptr(new Entity(x, y, Locator::getSpriteManager()->getIndex("./data/sprites/objects.sprites", prop))));
+ _entities[_entities.size() - 1]->setCollisionRectDimsAndPosition(x, y - ENTDIM, ENTDIM, ENTDIM, ENTDIM);
+ }
+ }
+ element = element->NextSiblingElement("object");
+ prop = "";
+ }
+}
+
+void Map::parsePolyline(TiXmlElement* element, float ox, float oy, std::vector* container)
+{
+ std::string points = std::string(element->Attribute("points"));
+ std::string p1 = points.substr(0, points.find(" "));
+ std::string p2 = points.substr(points.find(" ") + 1);
+ float x, y;
+
+ Line line;
+ getPolylineComponent(p1, &x, &y);
+ line.p1 = vec2f(x + ox, y + oy);
+ getPolylineComponent(p2, &x, &y);
+ line.p2 = vec2f(x + ox, y + oy);
+
+ container->push_back(line);
+}
+
+void Map::getPolylineComponent(std::string point, float* x, float* y)
+{
+ std::string p1 = point.substr(0, point.find(","));
+ std::string p2 = point.substr(point.find(",") + 1);
+
+ *x = atof(p1.c_str());
+ *y = atof(p2.c_str());
+}
+
+void Map::parseObjective(TiXmlElement* element)
+{
+ while (element)
+ {
+ if (!element->Attribute("x") || !element->Attribute("y") || !element->Attribute("type"))
+ {
+ LOGF((stderr, "Objective has null value - ignoring.\n"));
+ }
+ else
+ {
+ int x = atoi(element->Attribute("x"));
+ int y = atoi(element->Attribute("y"));
+
+ if (!strcmp(element->Attribute("type"), "Terminal"))
+ {
+ _entities.push_back(std::shared_ptr(new MainComputer(x, y - ENTDIM, true)));
+ }
+ else if (!strcmp(element->Attribute("type"), "Subway"))
+ {
+ _subwayPos.x = x;
+ _subwayPos.y = y;
+ _subwayFound = true;
+ }
+ }
+ element = element->NextSiblingElement("object");
+ }
+}
+
+void Map::makeElevatorShafts()
+{
+ size_t i;
+ ElevatorDoor* ed;
+ int iShaft;
+ for (i = 0; i < _entities.size(); i++)
+ {
+ if (dynamic_cast(_entities[i].get()))
+ {
+ ed = (ElevatorDoor*)_entities[i].get();
+ iShaft = doesElevatorShaftExist((int)ed->getPosition().x);
+ if (iShaft >= 0) //shaft already exists, just add to that.
+ {
+ _shafts[iShaft]->addDoor(ed);
+ ed->registerShaft(_shafts[iShaft].get());
+ }
+ else
+ {
+ std::unique_ptr shaft(new ElevatorShaft((int)ed->getPosition().x));
+ shaft->addDoor(ed);
+ ed->registerShaft(shaft.get());
+ _shafts.push_back(std::move(shaft));
+ }
+ }
+ }
+#if _WIN32
+ LOGF((stdout, "Created %i elevator shaft(s).\n", _shafts.size()));
+#else
+ LOGF((stdout, "Created %lu elevator shaft(s).\n", _shafts.size()));
+#endif
+}
+
+int Map::doesElevatorShaftExist(int x)
+{
+ size_t i;
+ for (i = 0; i < _shafts.size(); i++)
+ {
+ if (_shafts[i]->getX() == x)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+void Map::makeStairwells()
+{
+ size_t i;
+ Stairs* stairs;
+ int iStairWell;
+ for (i = 0; i < _stairDoors.size(); i++)
+ {
+ stairs = _stairDoors[i].get();
+ iStairWell = doesStairwellExist((int)stairs->getPosition().x);
+ if (iStairWell >= 0) //stairwell already exists, just add to that.
+ {
+ _stairwells[iStairWell]->addStairs(stairs);
+ stairs->registerStairwell(_stairwells[iStairWell].get());
+ }
+ else
+ {
+ std::unique_ptr well(new Stairwell((int)stairs->getPosition().x));
+ well->addStairs(stairs);
+ stairs->registerStairwell(well.get());
+ _stairwells.push_back(std::move(well));
+ }
+ }
+#if _WIN32
+ LOGF((stdout, "Created %i stairwell(s).\n", _shafts.size()));
+#else
+ LOGF((stdout, "Created %lu stairwell(s).\n", _shafts.size()));
+#endif
+}
+
+int Map::doesStairwellExist(int x)
+{
+ size_t i;
+ for (i = 0; i < _stairwells.size(); i++)
+ {
+ if (_stairwells[i]->getX() == x)
+ {
+ return (int)i;
+ }
+ }
+ return -1;
+}
+
+void Map::calculateStairDirections()
+{
+ size_t i;
+
+ for (i = 0; i < _stairwells.size(); i++)
+ {
+ _stairwells[i]->setDirections(_mapHeight);
+ }
+}
+
+void Map::calculateElevatorOrder()
+{
+ size_t i;
+
+ for (i = 0; i < _shafts.size(); i++)
+ {
+ _shafts[i]->calculateDoorOrders(_mapHeight);
+ }
+}
+
+void Map::setShaftOpenings()
+{
+ size_t i;
+ for (i = 0; i < _shafts.size(); i++)
+ {
+ _shafts[i]->setOpenDoorFirst();
+ }
+}
+
+vec2f Map::getPlayerStartPos()
+{
+ return _playerStartPos;
+}
+
+void Map::addSavedLink(vec2f start, vec2f end)
+{
+ Line line = {start, end};
+ _lines.push_back(line);
+}
+
+void Map::parseTileLayer(char* data)
+{
+ if (!_tilesetImage)
+ {
+ _mapTex = 0;
+ return;
+ }
+
+ SDL_Surface* mapImage;
+ SDL_Surface* mapImageCrosslink;
+
+#if SDL_BYTEORDER == SDL_BIG_ENDIAN
+ mapImage = SDL_CreateRGBSurface(SDL_SWSURFACE, _mapWidth, _mapHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+ mapImageCrosslink = SDL_CreateRGBSurface(SDL_SWSURFACE, _mapWidth, _mapHeight, 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
+#else
+ mapImage = SDL_CreateRGBSurface(SDL_SWSURFACE, _mapWidth, _mapHeight, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+ mapImageCrosslink = SDL_CreateRGBSurface(SDL_SWSURFACE, _mapWidth, _mapHeight, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
+#endif
+
+ SDL_FillRect(mapImage, nullptr, 0xFF00FF);
+ SDL_FillRect(mapImageCrosslink, nullptr, 0xFF00FF);
+
+ char* delim = (char*)",";
+ char* token = strtok(data, delim);
+ unsigned int i = 0;
+ int gid;
+ int tileset_col;
+ int tileset_row;
+ SDL_Rect srcrect;
+ SDL_Rect dstrect;
+ unsigned int row;
+ unsigned int col;
+ unsigned int tilesetWidthInTiles = _tilesetImage->w / TILEDIM;
+
+ while (token)
+ {
+ i++;
+ gid = atoi(token);
+
+ if (gid > 0)
+ {
+ gid--;
+
+ row = i / _tilesWide;
+ col = i - (row * _tilesWide) - 1;
+
+ tileset_col = gid % tilesetWidthInTiles;
+ tileset_row = gid / tilesetWidthInTiles;
+
+ srcrect.x = TILEDIM * tileset_col;
+ srcrect.y = TILEDIM * tileset_row;
+ srcrect.w = TILEDIM;
+ srcrect.h = TILEDIM;
+
+ dstrect.x = col * TILEDIM;
+ dstrect.y = row * TILEDIM;
+ dstrect.w = TILEDIM;
+ dstrect.h = TILEDIM;
+
+ SDL_BlitSurface(_tilesetImage, &srcrect, mapImage, &dstrect);
+ SDL_FillRect(mapImageCrosslink, &dstrect, 0x500000);
+ }
+
+ if (token)
+ token = strtok(nullptr, delim);
+ }
+
+ _mapTex = createTextureFromSurface(mapImage);
+ _crosslinkTex = createTextureFromSurface(mapImageCrosslink);
+
+ SDL_FreeSurface(mapImage);
+ SDL_FreeSurface(mapImageCrosslink);
+ SDL_FreeSurface(_tilesetImage);
+
+ delete [] token;
+}
+
+GLuint Map::getMapTexture()
+{
+ return _mapTex;
+}
+
+GLuint Map::getCrosslinkTexture()
+{
+ return _crosslinkTex;
+}
+
+void Map::clearLinks()
+{
+ _lines.clear();
+ size_t i;
+ for (i = 0; i < _linkableEnts.size(); i++)
+ {
+ _linkableEnts[i]->unlink();
+ }
+}
+
+bool Map::subwayFound()
+{
+ return _subwayFound;
+}
+
+vec2f Map::getSubwayPosition()
+{
+ return _subwayPos;
+}
+
+void Map::getLinkableIters(std::vector >::iterator* begin,
+ std::vector >::iterator* end)
+{
+ *begin = _linkableEnts.begin();
+ *end = _linkableEnts.end();
+}
+
+void Map::removeEnemyGun(EnemyGun* gun)
+{
+ size_t i;
+ int gunIndex = -1;
+ for (i = 0; i < _linkableEnts.size(); i++)
+ {
+ if (_linkableEnts[i].get() == gun)
+ {
+ gunIndex = i;
+ break;
+ }
+ }
+
+ if (gunIndex < 0)
+ {
+ return;
+ }
+ _linkableEnts.erase(_linkableEnts.begin() + gunIndex);
+}
+
+void Map::addMissingGuns()
+{
+ size_t i;
+ std::shared_ptr eg;
+ for (i = 0; i < _entities.size(); i++)
+ {
+ if (std::dynamic_pointer_cast(_entities[i]))
+ {
+ eg = std::static_pointer_cast(_entities[i]);
+ if (eg->getEnemy()->getType() == Enemy_Sniper)
+ {
+ continue;
+ }
+ if (std::find(_linkableEnts.begin(), _linkableEnts.end(), eg) == _linkableEnts.end())
+ {
+ _linkableEnts.push_back(eg);
+ }
+ }
+ }
+}
+
+void Map::addSniper()
+{
+ if (_subwayFound && std::find(_entities.begin(), _entities.end(), _sniper) == _entities.end())
+ {
+ _sniper->setAlive(true);
+ _sniper->loseSightOfPlayer(false, vec2f(0, 0));
+ _entities.push_back(_sniper);
+ _enemyIndices.push_back(_entities.size() - 1);
+ _sniper->setCollisionRectPosition(_subwayPos.x + ENTDIM - _sniper->getCollisionRect().w, _subwayPos.y - _sniper->getCollisionRect().h);
+ _sniper->alertToPosition(_subwayPos.x - 40, _subwayPos.y, ALERT_RUN, TARGET_PLAYER);
+ }
+}
+
+void Map::removeSniper()
+{
+ size_t i;
+ int sniperIndex = -1;
+ int indexInIndices = -1; //index in enemyIndices
+ for (i = 0; i < _entities.size(); i++)
+ {
+ if (_entities[i] == _sniper)
+ {
+ sniperIndex = i;
+ break;
+ }
+ }
+
+ if (sniperIndex == -1)
+ {
+ return;
+ }
+
+ for (i = 0; i < _enemyIndices.size(); i++)
+ {
+ if (_enemyIndices[i] == (size_t)sniperIndex)
+ {
+ indexInIndices = i;
+ break;
+ }
+ }
+
+ Assert((indexInIndices >= 0));
+
+ _entities.erase(_entities.begin() + sniperIndex);
+ _enemyIndices.erase(_enemyIndices.begin() + indexInIndices);
+ _sniper->changeState(IDLE);
+}
+
+Enemy* Map::getSniper()
+{
+ return _sniper.get();
+}
\ No newline at end of file
diff --git a/map.h b/map.h
new file mode 100644
index 0000000..5b0ccbe
--- /dev/null
+++ b/map.h
@@ -0,0 +1,119 @@
+/*
+Copyright 2013-2015 Rohit Nirmal
+
+This file is part of Clonepoint.
+
+Clonepoint 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, either version 3 of the License, or
+(at your option) any later version.
+
+Clonepoint 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 Clonepoint. If not, see .
+*/
+
+#ifndef MAP_H
+#define MAP_H
+
+#include
+#include
+
+class TiXmlElement;
+
+struct Line
+{
+ vec2f p1;
+ vec2f p2;
+};
+
+class Map
+{
+public:
+ Map();
+ ~Map();
+ bool loadFromFile(const char* filename, bool savegame);
+ size_t getNumberOfCollideVols();
+ size_t getNumberOfEnts();
+ size_t getNumberOfStairs();
+ size_t getNumberOfStairwells();
+ size_t getNumberOfLights();
+ size_t getNumberOfLines();
+ size_t getNumberOfLightLinks();
+ size_t getNumberOfShafts();
+ size_t getNumberOfEnemies();
+ int indexOfCollideVol(CollisionVolume* vol);
+ int indexOfEntity(Entity* ent);
+ int indexOfFOV(FieldOfView* fov);
+
+ CollisionVolume getCollideVolAt(int i);
+ CollisionVolume* getCollideVolPointerAt(int i);
+ Entity* getEntAt(size_t i);
+ Enemy* getEnemyAt(size_t i);
+ Stairs* getStairsAt(size_t i);
+ Stairwell* getStairwellAt(size_t i);
+ FieldOfView* getLightAt(size_t i);
+ ElevatorShaft* getShaftAt(size_t i);
+ Line getLineAt(size_t i);
+ Line getLightLinkAt(size_t i);
+ unsigned int getMapWidth();
+ unsigned int getMapHeight();
+ vec2f getPlayerStartPos();
+ void addSavedLink(vec2f start, vec2f end);
+ void clearLinks();
+ GLuint getMapTexture();
+ GLuint getCrosslinkTexture();
+ bool subwayFound();
+ vec2f getSubwayPosition();
+ void getLinkableIters(std::vector >::iterator* begin,
+ std::vector >::iterator* end);
+ void removeEnemyGun(EnemyGun* gun);
+ void addMissingGuns();
+ void addSniper();
+ void removeSniper();
+ Enemy* getSniper();
+private:
+ unsigned int _tilesHigh;
+ unsigned int _tilesWide;
+ unsigned int _mapWidth;
+ unsigned int _mapHeight;
+ vec2f _playerStartPos;
+ vec2f _subwayPos;
+ bool _subwayFound;
+ std::vector > _collideVols;
+ std::vector > _entities;
+ std::vector > _stairDoors;
+ std::vector > _stairwells;
+ std::vector > _lights;
+ std::vector > _shafts;
+ std::vector > _linkableEnts;
+ std::vector _lines;
+ std::vector