@ -0,0 +1,5 @@ | |||
#ifndef POOL_CPP | |||
#define POOL_CPP | |||
#include "pool.hpp" | |||
#endif |
@ -0,0 +1,157 @@ | |||
#ifndef POOL_HPP | |||
#define POOL_HPP | |||
#include <stdlib.h> | |||
#include <vector> | |||
#include <iostream> | |||
/* --Pool-- | |||
* Pool holds any data. It initializes the requested amount of data on construction. | |||
* Any subsequent calls to getNewData() and removeData() cost very little | |||
* because no more memory is allocated or freed. This provides significant | |||
* speed improvements. | |||
* | |||
* It uses a doubly linked list for faster iteration through the active data | |||
* and a singly linked list for iteration through free/inactive data. | |||
* | |||
* Vector is used to store the data due to its resize function, which allows | |||
* the pool's size to be set on initialization. The pool cannot be resized | |||
* after initialization because vector must move all data to get one complete | |||
* block. | |||
* | |||
* Note: You must compile with -std=c++11 if you want your data type's | |||
* constructors to be called. | |||
* | |||
* TODO: Need to construct inUse list to be in order (better locality)! | |||
* */ | |||
//Generic data container. Holds the requested data as well as pointers to the next | |||
//free data (if inactive), and the previous and next active/used data (if active) | |||
template<class T> | |||
class PoolData | |||
{ | |||
public: | |||
T data; | |||
bool isActive; | |||
PoolData<T>* nextFreeData; | |||
PoolData<T>* nextUsedData; | |||
PoolData<T>* prevUsedData; | |||
}; | |||
template<class R> | |||
class Pool | |||
{ | |||
private: | |||
std::vector<PoolData<R> > pool; //The pool data, stored in a STL vector | |||
PoolData<R>* firstFreeData; //The head of the free data linked list | |||
PoolData<R>* firstUsedData; //The head of the used data linked list | |||
unsigned int size; //The size of the pool | |||
unsigned int totalActiveData; //The number of active data in the pool | |||
//Prepare the data pointers as a linked list | |||
void resetPool() | |||
{ | |||
//Iterate through all data, resetting pointers and isActive status | |||
firstFreeData = &pool[0]; | |||
for (unsigned int i = 0; i < size - 1; ++i) | |||
{ | |||
PoolData<R>* currentData = &pool[i]; | |||
currentData->nextFreeData = &pool[i + 1]; | |||
currentData->nextUsedData = NULL; | |||
currentData->prevUsedData = NULL; | |||
currentData->isActive = false; | |||
} | |||
//Last datum needs NULL status to signal end of linked list | |||
PoolData<R>* lastData = &pool[size - 1]; | |||
lastData->nextFreeData = NULL; | |||
lastData->nextUsedData = NULL; | |||
lastData->prevUsedData = NULL; | |||
lastData->isActive = false; | |||
} | |||
public: | |||
//Initializes the Pool with newSize elements. Once this constructor | |||
//is called, no more memory is allocated. | |||
Pool(unsigned int newSize) | |||
{ | |||
size = newSize; | |||
totalActiveData = 0; | |||
firstFreeData = NULL; | |||
firstUsedData = NULL; | |||
pool.resize(size); | |||
resetPool(); | |||
} | |||
//Resets the pool. Note that this isn't cheap because it must fix up the linked list | |||
//that underlies the Pool structure | |||
void clear() | |||
{ | |||
totalActiveData = 0; | |||
firstFreeData = NULL; | |||
firstUsedData = NULL; | |||
resetPool(); | |||
} | |||
//Returns the first free data in the pool, or NULL if the pool is | |||
//full. | |||
PoolData<R>* getNewData() | |||
{ | |||
if (firstFreeData != NULL) //Make sure the pool isn't full | |||
{ | |||
PoolData<R>* freeData = firstFreeData; | |||
firstFreeData = firstFreeData->nextFreeData; | |||
//Link this data into the usedData linked list | |||
if (firstUsedData != NULL) firstUsedData->nextUsedData = freeData; | |||
freeData->nextUsedData = NULL; | |||
freeData->prevUsedData = firstUsedData; | |||
firstUsedData = freeData; | |||
freeData->isActive = true; | |||
totalActiveData++; | |||
return freeData; | |||
} | |||
return NULL; //Pool is full | |||
} | |||
//Sets the data to inactive so that it can be used again. | |||
void removeData(PoolData<R>* dataToRemove) | |||
{ | |||
dataToRemove->nextFreeData = firstFreeData; | |||
firstFreeData = dataToRemove; | |||
//Unlinks this data from the usedData linked list | |||
if (firstFreeData->prevUsedData) | |||
firstFreeData->prevUsedData->nextUsedData = firstFreeData->nextUsedData; | |||
totalActiveData--; | |||
dataToRemove->isActive = false; | |||
} | |||
//Gets data at the requested index. | |||
//Returns NULL if index isn't within range or data is not active | |||
//Use getNextActiveData if you want to iterate through the pool | |||
//(it uses nextUsedData to skip over inactive data) | |||
PoolData<R>* getActiveDataAtIndex(unsigned int index) | |||
{ | |||
//Index out of bounds | |||
if (index > size) return NULL; | |||
PoolData<R>* data = &pool[index]; | |||
if (data->isActive) return data; | |||
else return NULL; //Data isn't in use | |||
} | |||
//Uses prevUsedData to skip over inactive data. This function will | |||
//all fast iteration through active data in the pool. Break on NULL | |||
PoolData<R>* getNextActiveData(PoolData<R>* currentData) | |||
{ | |||
return currentData->prevUsedData; | |||
} | |||
//Returns the first active data (use in conjunction with getNextActiveData) | |||
//to traverse the pool. Returns NULL if the pool is empty | |||
PoolData<R>* getFirstActiveData() | |||
{ | |||
return firstUsedData; | |||
} | |||
//Returns the total number of active data in the pool | |||
unsigned int getTotalActiveData() | |||
{ | |||
return totalActiveData; | |||
} | |||
}; | |||
#endif | |||
@ -0,0 +1,5 @@ | |||
#ifndef QUADTREE_CPP | |||
#define QUADTREE_CPP | |||
#include "quadTree.hpp" | |||
#endif |
@ -0,0 +1,354 @@ | |||
#ifndef QUADTREE_HPP | |||
#define QUADTREE_HPP | |||
// For debug rendering | |||
#include "../graphics/graphics.hpp" | |||
#include <SFML/System.hpp> | |||
#include <SFML/Window.hpp> | |||
#include <SFML/Graphics.hpp> | |||
#include <list> | |||
#include <stdlib.h> /* system, NULL, EXIT_FAILURE */ | |||
#include "../collision/collision.hpp" | |||
#include <iostream> | |||
//TODO: Put into pool, remove std::vector data and replace with pool as well? | |||
const unsigned int MAX_TREE_DEPTH = 8; | |||
//If multiple QuadPoints are in the same place, tree will go to max depth, | |||
//then capacity will become MAX_DEPTH_CAPACITY to handle the leftovers | |||
const unsigned int MAX_DEPTH_CAPACITY = 100; | |||
const float POINT_SEARCH_WIDTH = 1; | |||
const float POINT_SEARCH_HEIGHT = 1; | |||
//QuadPoints store the coordinate of the data along with the data itself | |||
template<class R> | |||
class QuadPoint | |||
{ | |||
public: | |||
float x; | |||
float y; | |||
R data; | |||
}; | |||
/* --QuadTree-- | |||
* Template spacial quadtree | |||
* I referred to http://en.wikipedia.org/wiki/Quadtree while coding this | |||
* If >maxCapacity points are placed in the same position or cell, bad things | |||
* will happen :(. Bad things won't make the structure not work, but it will | |||
* make things less efficient. TODO: Fix this :( | |||
* Make sure things are recursively subdivided and spread no matter what, | |||
* unless n depth is reached (not so close points will still not be distributed?) | |||
* */ | |||
template<class T> | |||
class QuadTree | |||
{ | |||
protected: | |||
QuadTree* tL; | |||
QuadTree* tR; | |||
QuadTree* bL; | |||
QuadTree* bR; | |||
aabb bounds; | |||
int maxCapacity; | |||
std::list<QuadPoint<T> > data; | |||
QuadTree(unsigned int newMaxCapacity, float newX, float newY, float newWidth, float newHeight, unsigned int newDepth) | |||
{ | |||
depth = newDepth + 1; | |||
maxCapacity = newMaxCapacity; | |||
//Max depth reached; make sure this node holds a lot because | |||
//it won't subdivide any more | |||
if (depth >= MAX_TREE_DEPTH) | |||
{ | |||
std::cout << "Max depth reached; setting capacity to MAX_DEPTH_CAPACITY\n"; | |||
maxCapacity = MAX_DEPTH_CAPACITY; | |||
} | |||
tL=NULL; | |||
tR=NULL; | |||
bL=NULL; | |||
bR=NULL; | |||
bounds.setPosition(newX, newY); | |||
bounds.resize(newWidth, newHeight); | |||
} | |||
private: | |||
void subdivide() | |||
{ | |||
float halfWidth = bounds.w/2; | |||
float halfHeight = bounds.h/2; | |||
tL = new QuadTree<T>(maxCapacity, bounds.x, bounds.y, halfWidth, halfHeight, depth); | |||
tR = new QuadTree<T>(maxCapacity, bounds.x + halfWidth, bounds.y, halfWidth, halfHeight, depth); | |||
bL = new QuadTree<T>(maxCapacity, bounds.x, bounds.y + halfHeight, halfWidth, halfHeight, depth); | |||
bR = new QuadTree<T>(maxCapacity, bounds.x + halfWidth, bounds.y + halfHeight, halfWidth, halfHeight, depth); | |||
//Redistribute points into child nodes | |||
while(!data.empty()) | |||
{ | |||
QuadPoint<T> newDataPoint = data.back(); | |||
T newData = newDataPoint.data; | |||
float x = newDataPoint.x; | |||
float y = newDataPoint.y; | |||
if (tL->insert(newData, x, y)) | |||
{ | |||
data.pop_back(); | |||
continue; | |||
} | |||
if (tR->insert(newData, x, y)) | |||
{ | |||
data.pop_back(); | |||
continue; | |||
} | |||
if (bL->insert(newData, x, y)) | |||
{ | |||
data.pop_back(); | |||
continue; | |||
} | |||
if (bR->insert(newData, x, y)) | |||
{ | |||
data.pop_back(); | |||
continue; | |||
} | |||
//For some reason a point will not be accepted, so stop redistributing | |||
break; | |||
} | |||
} | |||
bool isPointInRange(float x, float y, aabb& range) | |||
{ | |||
//See if point is within range | |||
if (x <= range.x + range.w && x >= range.x && y <= range.y + range.h && y >= range.y) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
public: | |||
unsigned int depth; | |||
QuadTree(unsigned int newMaxCapacity, float newX, float newY, float newWidth, float newHeight) | |||
{ | |||
depth = 1; | |||
maxCapacity = newMaxCapacity; | |||
tL=NULL; | |||
tR=NULL; | |||
bL=NULL; | |||
bR=NULL; | |||
bounds.setPosition(newX, newY); | |||
bounds.resize(newWidth, newHeight); | |||
} | |||
~QuadTree() | |||
{ | |||
if (tL) | |||
{ | |||
delete tR; | |||
delete tL; | |||
delete bR; | |||
delete bL; | |||
} | |||
} | |||
//Checks if node and its children are empty | |||
bool isEmpty() | |||
{ | |||
if (!data.empty()) return false; | |||
if (!tL) return true; | |||
if (tL->isEmpty() && tR->isEmpty() && bL->isEmpty() && bR->isEmpty()) | |||
{ | |||
return true; | |||
} | |||
return false; | |||
} | |||
bool insert(T newData, float x, float y) | |||
{ | |||
//Make sure point is within the bounds of this node | |||
if (!isPointInRange(x, y, bounds)) | |||
{ | |||
//Point is not within bounds | |||
return false; | |||
} | |||
//Node isn't full yet, so add data to this node | |||
if (!tL && data.size() < maxCapacity) | |||
{ | |||
QuadPoint<T> newPoint; | |||
newPoint.x = x; | |||
newPoint.y = y; | |||
newPoint.data = newData; | |||
data.push_back(newPoint); | |||
return true; | |||
} | |||
//Safety max depth | |||
if (depth >= MAX_TREE_DEPTH) | |||
{ | |||
std::cout << "\033[31mWARNING: Max tree depth (" << MAX_TREE_DEPTH << ") reached!\033[0m\n"; | |||
} | |||
//Node is full; subdivide (if not already subdivided) | |||
if (!tL) | |||
{ | |||
subdivide(); | |||
} | |||
//If already subdivided, try inserting into child nodes | |||
if (tL->insert(newData, x, y)) return true; | |||
if (tR->insert(newData, x, y)) return true; | |||
if (bL->insert(newData, x, y)) return true; | |||
if (bR->insert(newData, x, y)) return true; | |||
// This shouldn't ever happen | |||
return false; | |||
} | |||
//Returns false if the point could not be removed (it doesn't exist) | |||
//data is used to make sure it is the point requested | |||
//NOTE: X and Y are not used when comparing the point, just for walking the tree, | |||
//so if you have two of the same data, it might not remove the one | |||
//at the position specified (the system assumes you are using pointers) | |||
bool remove(T searchData, float x, float y) | |||
{ | |||
//Point will not be in this node's bounds | |||
if(!isPointInRange(x, y, bounds)) return false; | |||
//Search through points for the data | |||
for (typename std::list<QuadPoint<T> >::iterator it = data.begin(); it!= data.end(); ++it) | |||
{ | |||
//Found the data point; erase it (inefficient only in large vectors) | |||
//TODO: Replace vector with list or something? | |||
if ((*it).data==searchData) | |||
{ | |||
data.erase(it); | |||
return true; | |||
} | |||
} | |||
//Search children (if any) | |||
if (!tL) return false; | |||
if (tL->remove(searchData, x, y) || tR->remove(searchData, x, y) | |||
|| bL->remove(searchData, x, y) || bR->remove(searchData, x, y)) | |||
{ | |||
//All children are empty; delete them | |||
if (isEmpty()) | |||
{ | |||
//TODO: Should this be a pool? | |||
delete tL; | |||
delete tR; | |||
delete bL; | |||
delete bR; | |||
tL = NULL; | |||
} | |||
return true; | |||
} | |||
//Data point not in quadtree | |||
return false; | |||
} | |||
//Fills the provided vector with resultant points (points | |||
//contained in the specified range). Returns total results | |||
unsigned int queryRange(aabb& range, std::vector<T>& results) | |||
{ | |||
unsigned int totalResults = 0; | |||
//Make sure range is touching this node | |||
if (!isColliding(&range, &bounds)) | |||
{ | |||
return 0; | |||
} | |||
//Add points in this node to results if contained in range | |||
for (typename std::list<QuadPoint<T> >::iterator it = data.begin(); it!=data.end(); ++it) | |||
{ | |||
if (isPointInRange((*it).x, (*it).y, range)) | |||
{ | |||
results.push_back((*it).data); | |||
totalResults++; | |||
} | |||
} | |||
//Let all child nodes (if any) add points | |||
if (!tL) | |||
{ | |||
return totalResults; | |||
} | |||
totalResults += tL->queryRange(range, results); | |||
totalResults += tR->queryRange(range, results); | |||
totalResults += bL->queryRange(range, results); | |||
totalResults += bR->queryRange(range, results); | |||
return totalResults; | |||
} | |||
//Fills the provided array with resultant points (points | |||
//contained in the specified range). Returns total results | |||
//Pass in the maximum for the array as well as the index | |||
//this function should start at (usually 0) | |||
unsigned int queryRange(aabb& range, T* results, int& currentIndex, int maxPoints) | |||
{ | |||
unsigned int totalResults = 0; | |||
//Make sure the array isn't full | |||
if (currentIndex >= maxPoints) | |||
{ | |||
std::cout << "WARNING: queryPoints(): Results array full! (Max points = "<< maxPoints<< ")\n"; | |||
return totalResults; | |||
} | |||
//Make sure range is touching this node | |||
if (!isColliding(&range, &bounds)) | |||
{ | |||
return 0; | |||
} | |||
//Add points in this node to results if contained in range | |||
for (typename std::list<QuadPoint<T> >::iterator it = data.begin(); it!=data.end(); ++it) | |||
{ | |||
if (isPointInRange((*it).x, (*it).y, range)) | |||
{ | |||
if (currentIndex < maxPoints) | |||
{ | |||
results[currentIndex] = (*it).data; | |||
totalResults++; | |||
currentIndex++; | |||
} | |||
else | |||
{ | |||
std::cout << "WARNING: queryPoints(): Results array full! (Max points = "<< maxPoints<< ")\n"; | |||
return totalResults; | |||
} | |||
} | |||
} | |||
//Let all child nodes (if any) add points | |||
if (!tL) | |||
{ | |||
return totalResults; | |||
} | |||
totalResults += tL->queryRange(range, results, currentIndex, maxPoints); | |||
totalResults += tR->queryRange(range, results, currentIndex, maxPoints); | |||
totalResults += bL->queryRange(range, results, currentIndex, maxPoints); | |||
totalResults += bR->queryRange(range, results, currentIndex, maxPoints); | |||
return totalResults; | |||
} | |||
void render(window* win, float viewX, float viewY) | |||
{ | |||
sf::RenderWindow* sfWin = win->getBase(); | |||
sf::RectangleShape rectangle; | |||
rectangle.setSize(sf::Vector2f(bounds.w, bounds.h)); | |||
rectangle.setFillColor(sf::Color::Transparent); | |||
if (!data.empty()) | |||
{ | |||
rectangle.setOutlineColor(sf::Color::Blue); | |||
} | |||
else rectangle.setOutlineColor(sf::Color::Green); | |||
if (depth >= MAX_TREE_DEPTH) rectangle.setOutlineColor(sf::Color::Red); | |||
rectangle.setOutlineThickness(2); | |||
rectangle.setPosition(viewX + bounds.x, viewY + bounds.y); | |||
sfWin->draw(rectangle); | |||
if (tL) | |||
{ | |||
tL->render(win, viewX, viewY); | |||
tR->render(win, viewX, viewY); | |||
bL->render(win, viewX, viewY); | |||
bR->render(win, viewX, viewY); | |||
} | |||
} | |||
}; | |||
#endif |
@ -0,0 +1,97 @@ | |||
#ifndef IMAGEMANAGER_CPP | |||
#define IMAGEMANAGER_CPP | |||
#include <iostream> | |||
#include "imageManager.hpp" | |||
ImageManager::ImageManager() | |||
{ | |||
} | |||
ImageManager::~ImageManager() | |||
{ | |||
} | |||
bool ImageManager::load(eptFile* spec) | |||
{ | |||
//Loop through all groups (representing images), loading images and subrects as we go | |||
std::string imageHandle; | |||
eptGroup* imageGroup = spec->getGroupFromIndex(0, imageHandle); | |||
for (int i = 1; imageGroup != NULL; i++) | |||
{ | |||
//Load the sprite | |||
sprite* newSprite = new sprite(); | |||
if (!newSprite->load(imageGroup->getAttribute("_file").c_str())) return false; | |||
sprites[imageHandle] = newSprite; | |||
std::cout << "Loaded image " << imageGroup->getAttribute("_file") << " successfully\n"; | |||
//Load the subrects | |||
std::string subRectHandle; | |||
std::string subRectStr = imageGroup->getAttributeFromIndex(0, subRectHandle); | |||
for (int n = 1; subRectStr != ""; n++) | |||
{ | |||
//Attributes preceded by _ are not subRects (ignore them) | |||
if (subRectHandle[0] != '_') | |||
{ | |||
SubRect newSubRect; | |||
//Get the subrect from the attr array ( {x, y, w, h} ) | |||
newSubRect.x = attrToArrayInt(subRectStr, 0); | |||
newSubRect.y = attrToArrayInt(subRectStr, 1); | |||
newSubRect.width = attrToArrayInt(subRectStr, 2); | |||
newSubRect.height = attrToArrayInt(subRectStr, 3); | |||
subRects[subRectHandle] = newSubRect; | |||
} | |||
//Get the next attribute | |||
subRectStr = imageGroup->getAttributeFromIndex(n, subRectHandle); | |||
} | |||
//Get the next group | |||
imageGroup = spec->getGroupFromIndex(i, imageHandle); | |||
} | |||
return true; | |||
} | |||
//Returns the sprite with the specified handle, or NULL if it doesn't exist | |||
sprite* ImageManager::getSprite(const std::string& spriteName) | |||
{ | |||
std::map<std::string, sprite*>::iterator findIt = sprites.find(spriteName); | |||
if (findIt==sprites.end()) return NULL; | |||
sprite* spr = findIt->second; | |||
//Reset rotation | |||
spr->setRotation(0); | |||
//Reset subrect | |||
spr->clearSubRect(); | |||
return spr; | |||
} | |||
//Returns the sprite with the desired subRect applied, or NULL if either | |||
//do not exist | |||
sprite* ImageManager::getSubRectSprite(const std::string& spriteName, const std::string& subRectName) | |||
{ | |||
//Grab the sprite | |||
sprite* spr = getSprite(spriteName); | |||
if (!spr) return NULL; | |||
//Grab the subrect | |||
std::map<std::string, SubRect>::iterator findIt = subRects.find(subRectName); | |||
if (findIt == subRects.end()) return NULL; | |||
SubRect* rect = &findIt->second; | |||
//Set the sprite subrect to the requested subRect | |||
spr->setSubRect(rect->x, rect->y, rect->width, rect->height); | |||
return spr; | |||
} | |||
//Returns the sprite with the desired subRect applied, or NULL if either | |||
//do not exist | |||
sprite* ImageManager::getSubRectSprite(const std::string& spriteName, const std::string& subRectName, SubRect& subRect) | |||
{ | |||
//Grab the sprite | |||
sprite* spr = getSprite(spriteName); | |||
if (!spr) return NULL; | |||
//Grab the subrect | |||
std::map<std::string, SubRect>::iterator findIt = subRects.find(subRectName); | |||
if (findIt == subRects.end()) return NULL; | |||
SubRect* rect = &findIt->second; | |||
//Set the sprite subrect to the requested subRect | |||
spr->setSubRect(rect->x, rect->y, rect->width, rect->height); | |||
//Pass the subRect pointer to the provided one (they want it) | |||
subRect = *rect; | |||
return spr; | |||
} | |||
#endif |
@ -0,0 +1,60 @@ | |||
#ifndef IMAGEMANAGER_HPP | |||
#define IMAGEMANAGER_HPP | |||
#include <map> | |||
#include "graphics.hpp" | |||
#include "../ept/eptParser.hpp" | |||
struct SubRect | |||
{ | |||
int x; | |||
int y; | |||
int width; | |||
int height; | |||
}; | |||
/**--ImageManager-- | |||
* ImageManager holds all sprites and subRects. It loads the images automatically | |||
* and provides a convenient interface for requesting them. The integration | |||
* of subRects encourages use of spritesheets. | |||
* */ | |||
class ImageManager | |||
{ | |||
private: | |||
std::map<std::string, SubRect> subRects; | |||
std::map<std::string, sprite*> sprites; | |||
public: | |||
ImageManager(); | |||
~ImageManager(); | |||
//Load all images in the spec | |||
bool load(eptFile* spec); | |||
//Returns the sprite with the specified handle, or NULL if it doesn't exist | |||
//Note that this function also resets the subrect and rotation of the sprite | |||
sprite* getSprite(const std::string& spriteName); | |||
//Returns the sprite with the desired subRect applied, or NULL if it | |||
//doesn't exist | |||
sprite* getSubRectSprite(const std::string& spriteName, const std::string& subRectName); | |||
//Returns the sprite with the desired subRect applied, or NULL if it | |||
//doesn't exist. Also returns a reference to the subRect used on the image | |||
sprite* getSubRectSprite(const std::string& spriteName, const std::string& subRectName, SubRect& subRect); | |||
}; | |||
/* EPT specification | |||
* <imageManager.1> | |||
* | |||
* //image handle | |||
* hud: | |||
* //Filename, preceded by _ | |||
* _file=data/images/hud.png | |||
* //All other attributes not preceded by '_' are treated as subrects | |||
* healthBackground={0, 0, 400, 100}; | |||
* healthForeground={100, 100, 400, 100}; | |||
* //(in the format {x, y, width, height}; ) | |||
* //In the future, animations will be defined via #, e.g. | |||
* #anim1={0, 0, 32, 32, 8, 24}; | |||
* //defined like so: {x, y, singleFrameW, h, totalFrames, FPS} | |||
* | |||
* You can then get the set up sprite by calling this (e.g.): | |||
* sprite* hudBackground = imageManager.getSubRectSprite("hud", "healthBackground"); | |||
* and it's ready to render! | |||
* */ | |||
#endif |
@ -0,0 +1,66 @@ | |||
#ifndef INPUTSTATE_CPP | |||
#define INPUTSTATE_CPP | |||
#include "inputState.hpp" | |||
#include <iostream> | |||
InputState::InputState() | |||
{ | |||
inputMan = NULL; | |||
inputSource = Source::INPUT_MANAGER; //By default | |||
} | |||
//Populates inputs map with the inputs specified in the spec; | |||
//sets Source depending on the second parameter | |||
void InputState::setup(eptGroup* spec, inputManager* newInputManager) | |||
{ | |||
//Prepare source | |||
inputMan = newInputManager; | |||
inputSource = Source::INPUT_MANAGER; | |||
//Prepare inputs map | |||
std::string attrName; | |||
std::string codeStr = spec->getAttributeFromIndex(0, attrName); | |||
for (int i = 1; codeStr != ""; i++) | |||
{ | |||
Input newInput; | |||
newInput.name = attrName; | |||
newInput.code = attrToInt(codeStr); | |||
newInput.state = 0; | |||
inputs[attrName] = newInput; | |||
//Get next attribute | |||
codeStr = spec->getAttributeFromIndex(i, attrName); | |||
} | |||
} | |||
//Updates the states of all inputs in the inputs map | |||
//Will eventually be able to read these from a file or network | |||
void InputState::update() | |||
{ | |||
for (std::map<std::string, Input>::iterator it = inputs.begin(); | |||
it != inputs.end(); ++it) | |||
{ | |||
Input* currentInput = &it->second; | |||
//Update the state of the current input (depending on source) | |||
switch (inputSource) | |||
{ | |||
case Source::INPUT_MANAGER: | |||
currentInput->state = inputMan->getState(currentInput->code); | |||
break; | |||
default: | |||
break; | |||
} | |||
} | |||
} | |||
//Returns the current state of the input (since last update()) or | |||
//0 if the input does not exist (which also causes an error to be printed) | |||
int InputState::getInputState(const std::string& inputName) | |||
{ | |||
std::map<std::string, Input>::iterator findIt = inputs.find(inputName); | |||
if (findIt == inputs.end()) | |||
{ | |||
std::cout << "ERROR: InputState.getInputState(): Entry for input " << inputName << " doesn't exist!\n"; | |||
return 0; | |||
} | |||
return findIt->second.state; | |||
} | |||
#endif | |||
@ -0,0 +1,85 @@ | |||
#ifndef INPUTSTATE_HPP | |||
#define INPUTSTATE_HPP | |||
#include <map> | |||
#include "input.hpp" | |||
#include "../ept/eptParser.hpp" | |||
//Input holds a named input with universal code and state variables | |||
struct Input | |||
{ | |||
int code; //Universal key and mouse code | |||
std::string name; //Name of this specific input (e.g. 'pause') | |||
int state; //Universal key state (0 or 1 for binary inputs | |||
//(e.g. space); -x to +x for variable inputs (e.g. | |||
//mouse)) | |||
}; | |||
/* --InputState-- | |||
* InputState stores the state of all inputs of interest (as defined by | |||
* an EPT spec) as abstract handles. This allows defining of inputs by | |||
* function rather than by input. | |||
* | |||
* For example, instead of querying a mouse button, you would query "attack", | |||
* which was defined in a EPT spec as SPACE. This makes very simple configurable | |||
* input! | |||
* | |||
* Additionally, InputStates can be stored for input recording, which allows | |||
* replays and makes testing easier. (TODO) | |||
* */ | |||
class InputState | |||
{ | |||
//Where the inputs will be collected from | |||
enum Source | |||
{ | |||
INPUT_MANAGER, //Get the input from a standard inputManager | |||
FILE, //Get the input from a file (TODO) | |||
NETWORK //Get the input from the network (TODO) | |||
}; | |||
private: | |||
std::map<std::string, Input> inputs; | |||
Source inputSource; | |||
inputManager* inputMan; | |||
public: | |||
InputState(); | |||
//Populates inputs map with the inputs specified in the spec; | |||
//sets Source depending on the second parameter | |||
void setup(eptGroup* spec, inputManager* newInputManager); | |||
//TODO (possibly) | |||
//void setup(eptFile* spec, InputRecording* recording); | |||
//void setup(eptFile* spec, InputNetworked* networkInput); | |||
//Updates the states of all inputs in the inputs map | |||
//Will eventually be able to read these from a file or network | |||
void update(); | |||
//Returns the current state of the input (since last update()) or | |||
//0 if the input does not exist (which also causes an error to be printed) | |||
int getInputState(const std::string& inputName); | |||
}; | |||
/* --EPT Specification-- | |||
* The EPT spec should be defined as follows: | |||
* <myInput.1> | |||
* //Group names do not matter as you will be passing in a single | |||
* //group (use them to pick the right input mode); this functions | |||
* // as a useful mechanism for having different input modes and | |||
* //configurations in the same .ept file | |||
* menuControls: | |||
* //Provide the name of the input as well as the universal code of | |||
* //the input; check base2.0/input/input.hpp for input codes | |||
* back=43; | |||
* accept=63; | |||
* gameplayControls: | |||
* forward=55; | |||
* backward=54; | |||
* pause=100; | |||
* //etc. | |||
* | |||
* You would then create an InputState like so: | |||
* InputState menuControlInputs; | |||
* menuControlInputs.setup(parser.getGroup("myInput.menuControls"), inputMan); | |||
* //And | |||
* InputState gameplayControlInputs; | |||
* gameplayControlInputs.setup(parser.getGroup("myInput.gameplayControls"), inputMan); | |||
* */ | |||
#endif |