diff --git a/src/libUnuk/Engine/AStar.cpp b/src/libUnuk/Engine/AStar.cpp new file mode 100644 index 0000000..a04f9e3 --- /dev/null +++ b/src/libUnuk/Engine/AStar.cpp @@ -0,0 +1,92 @@ +#include "AStar.h" + +AStar::AStar(void) { + while(_openList.size() > 0) { + _openList.pop(); + } + _closedList.clear(); + _solution.clear(); +} + +AStar::~AStar(void) { + AStarBase* best = 0; + while(_openList.size() > 0) { + best = _openList.top(); + _openList.pop(); + delete best; + } + _closedList.clear(); +} + +/* + * Solved the situation given by the initial state with AStar, + * and returns a vector of AStarBase that solves the problem. + */ +std::vector AStar::Solve(AStarBase* initState) { + AStarBase* best = 0; + while(_openList.size() > 0) { + best = _openList.top(); + _openList.pop(); + delete best; + } + _closedList.clear(); + + _openList.push(initState); + return GetSolutionSequence(Search()); +} + +// Search for the best path. +AStarBase* AStar::Search(void) { + AStarBase* best = 0; + long key = 0; + std::vector child; + + while(_openList.size() > 0) { + while(_closedList.find(key) != _closedList.end()) { + // Take the best state, and check if it is on the closed list. + if(_openList.size() > 0) { + best = _openList.top(); + _openList.pop(); + } else + return 0; + + key = best->CalculateKey(); + } + + // Put best on the closed list. + _closedList[key] = best; + + // Check if best is our goal. + if(best->isGoal()) + return best; + + // Generate the children. + child = best->GenerateChildren(); + for(unsigned int i = 0; i < child.size(); i++) { + _openList.push(child[i]); + } + } + return 0; +} + +// Generate a solution sequence for a given state. +std::vector AStar::GetSolutionSequence(AStarBase* node) { + _solution.clear(); + AStarBase* state = node; + + while(state != 0) { + _closedList.erase(state->_key); + _solution.insert(_solution.begin(), state); + state = state->_parent; + } + + // Delete the states which are not part of the solution. + while(_closedList.size() > 0) { + state = _closedList.begin()->second; + _closedList.erase(state->_key); + delete state; + } + + return _solution; +} + diff --git a/src/libUnuk/Engine/AStar.h b/src/libUnuk/Engine/AStar.h new file mode 100644 index 0000000..4e9e7d3 --- /dev/null +++ b/src/libUnuk/Engine/AStar.h @@ -0,0 +1,30 @@ +#pragma once +#include +#include +#include + +#include "AStarBase.h" + +class AStar { +public: + AStar(void); + ~AStar(void); + + std::vector Solve(AStarBase* initState); + +private: + // Comparison structure. + struct Cmp : public std::binary_function { + bool operator()(AStarBase* a1, AStarBase* a2) const { + return (a1->_totalEstimatedCost >= a2->_totalEstimatedCost); + } + }; + + std::priority_queue, Cmp > _openList; + std::map _closedList; + + AStarBase* Search(void); + + std::vector GetSolutionSequence(AStarBase* node); + std::vector _solution; +}; diff --git a/src/libUnuk/Engine/AStarBase.h b/src/libUnuk/Engine/AStarBase.h new file mode 100644 index 0000000..67f5c3c --- /dev/null +++ b/src/libUnuk/Engine/AStarBase.h @@ -0,0 +1,26 @@ +#pragma once +#include + +class AStarBase { +public: + AStarBase(void) { }; + virtual ~AStarBase(void) { }; + + // Generate a unique key. + virtual long CalculateKey(void) = 0; + + // Aproximate the heuristic cost to the goal. + virtual double Estimate(void) = 0; + + // Have we reached the goal? + virtual bool isGoal(void) = 0; + + virtual std::vector GenerateChildren(void) = 0; + + AStarBase* _parent; + + double _pastCost; + double _totalEstimatedCost; + + long _key; +}; diff --git a/src/libUnuk/Engine/Character.cpp b/src/libUnuk/Engine/Character.cpp new file mode 100644 index 0000000..07271fe --- /dev/null +++ b/src/libUnuk/Engine/Character.cpp @@ -0,0 +1,204 @@ +#include "Character.h" + +// Pixels * 60 / sec +const float Character::CHARACTER_SPEED = 3.5f; + +static listcollisionList; +static list::iterator collisionIter; + +Character::Character(Map* mapArg) { + map = mapArg; + attacking = false; + directionFacing = FACING_DOWN; + _animationStage = ANIM_NO_FOOT; + _animationTimer.Start(); + _leftFoot = false; + _health = 100; + + xVel = 0.0f; + yVel = 0.0f; + + _texture = NULL; + + collisionList.push_front(this); +} + +Character::~Character(void) { + SDL_FreeSurface(_texture); + for(collisionIter = collisionList.begin(); collisionIter != collisionList.end(); collisionIter++) { + if((*collisionIter) == this) { + collisionList.erase(collisionIter); + break; + } + } +} + +void Character::LoadSprites(string filename, int wArg, int hArg) { + if(_texture != NULL) + SDL_FreeSurface(_texture); + + _texture = LoadImageAlpha(filename.c_str()); + + w = (float)wArg; + h = (float)hArg; + + for(int m_direction = 0; m_direction < 4; m_direction++) { + for(int m_action = 0; m_action < 4; m_action++) { + _sprites[m_direction][m_action].x = (Sint16)(w * m_action); + _sprites[m_direction][m_action].y = (Sint16)(h * m_direction); + _sprites[m_direction][m_action].w = (Sint16)w; + _sprites[m_direction][m_action].h = (Sint16)h; + } + } +} + +void Character::AddSpeachBubble(string text) { + _speachBubble.push_back(text); + + _speachBubbleText.SetLineWidth(200); + _speachBubbleText.SetTextBlended(text, small, 0, 0, 0, true); + + if(_speachBubbleTimer.IsStarted() == false) + _speachBubbleTimer.Start(); +} + +void Character::Render(void) { + // Draw some fancy speach bubbles. It is a bit of a mess, I am playing. + if(_speachBubble.size() != 0) { + if(_speachBubbleTimer.GetTicks() < SPEACH_BUBBLE_DISPLAY_LENGTH) { + roundedBoxRGBA(screen, (Sint16)((x + w / 2) - 100 - camera.x), + (Sint16)(y - 100 - camera.y), + (Sint16)((x + w / 2) + 100 - camera.x), + (Sint16)(y - 35 - camera.y), + 5, 255, 255, 255, 255); + + filledTrigonRGBA(screen, (Sint16)((x + w / 2) - 100 - camera.x), + (Sint16)(y - 100 - camera.y), + (Sint16)((x + w / 2) - 10 - camera.x), + (Sint16)(y - 40 - camera.y), + (Sint16)((x + w / 2) + 10 - camera.x), + (Sint16)(y - 40 - camera.y), + 255, 255, 255, 255); + + _speachBubbleText.Render((int)((x + w / 2) - 90), (int)y - 90); + } + } + + if(attacking && attackTimer.GetTicks() < ATTACKING_DISPLAY_LEN) { + ApplySurface((int)x, (int)y, _texture, screen, &_sprites[directionFacing][ANIM_ATTACK]); + return; + } + else if(attacking) + attacking = false; + + if(xVel == 0.0f && yVel == 0.0f) + ApplySurface((int)x, (int)y, _texture, screen, &_sprites[directionFacing][ANIM_NO_FOOT]); + else { + if(_animationTimer.GetTicks() > ANIMATION_SPEED) { + if(_animationStage == ANIM_NO_FOOT) { + if(_leftFoot == true) + _animationStage = ANIM_RIGHT_FOOT; + else + _animationStage = ANIM_LEFT_FOOT; + } + else if(_animationStage == ANIM_LEFT_FOOT) { + _animationStage = ANIM_NO_FOOT; + _leftFoot = true; + } + else if(_animationStage == ANIM_RIGHT_FOOT) { + _animationStage = ANIM_NO_FOOT; + _leftFoot = false; + } + _animationTimer.Start(); + } + ApplySurface((int)x, (int)y, _texture, screen, &_sprites[directionFacing][_animationStage]); + } +} + +void Character::Update(void) { + Move(); + + if(_speachBubble.size() != 0) { + if(_speachBubbleTimer.GetTicks() > SPEACH_BUBBLE_DISPLAY_LENGTH) { + _speachBubble.pop_front(); + + if(_speachBubble.size() != 0) { + _speachBubbleTimer.Start(); + } + } else { + if(_speachBubble.front() != _speachBubbleText.GetText()) { + _speachBubbleText.SetTextBlended(_speachBubble.front(), small, 0, 0, 0); + } + } + } +} + +void Character::Move(void) { + x += xVel; + tileX = (int)(((x + (w / 2)) / TILE_WIDTH)); + tileY = (int)(((y + (h / 2)) / TILE_HEIGHT)); + + // Check collisions. + if((x < 0) || (x + w) > levelWidth || (x + w) > SCREEN_WIDTH) x -= xVel; + if(CheckTileCollisions()) x -= xVel; + if(CheckEntityCollisions()) x -= xVel; + if(CheckCharacterCollisions()) x -= xVel; + + y += yVel; + tileX = (int)(((x + (w / 2)) / TILE_WIDTH)); + tileY = (int)(((y + (h / 2)) / TILE_HEIGHT)); + + if((y < 0) || (y + h) > levelHeight || (y + h) > SCREEN_HEIGHT) y -= yVel; + if(CheckTileCollisions()) y -= yVel; + if(CheckEntityCollisions()) y -= yVel; + if(CheckCharacterCollisions()) y -= yVel; +} + +/* + * Bounds checking only included in map.GetTileSolidity() and + * map.GetEntitySolidity(). Remember to add bounds checking + * if any other map method is used in a similar manner. + */ +bool Character::CheckTileCollisions(void) { + for(int i = -1; i < 2; i++) { + for(int j = -1; j < 2; j++) { + if(map->GetTileSolidity(tileX + i, tileY + j)) + if(CheckCollisionXY((int)x, (int)y, (int)w, (int)h, map->GetTileX(tileX + i, tileY + j), + map->GetTileY(tileX + i, tileY + j), TILE_WIDTH, TILE_HEIGHT)) + return true; + } + } + return false; +} + +bool Character::CheckEntityCollisions(void) { + for(int i = -1; i < 2; i++) { + for(int j = -1; j < 2; j++) { + if(map->GetEntitySolidity(tileX + i, tileY + j)) { + if(CheckCollisionXY((int)x, (int)y, (int)w, (int)h, map->GetEntityX(tileX + i, tileY + j), + map->GetEntityY(tileX + i, tileY + j), + map->GetEntityWidth(tileX + i, tileY + j), + map->GetEntityHeight(tileX + i, tileY + j))) + return true; + } + } + } + return false; +} + +bool Character::CheckCharacterCollisions(void) { + for(collisionIter = collisionList.begin(); + collisionIter != collisionList.end(); + collisionIter++) { + if((*collisionIter) != this) { + if(CheckCollisionXY((int)x, (int)y, (int)w, (int)h, + (int)(*collisionIter)->GetX(), + (int)(*collisionIter)->GetY(), + (int)(*collisionIter)->GetWidth(), + (int)(*collisionIter)->GetHeight())) { + return true; + } + } + } + return false; +} diff --git a/src/libUnuk/Engine/Character.h b/src/libUnuk/Engine/Character.h new file mode 100644 index 0000000..10c5e70 --- /dev/null +++ b/src/libUnuk/Engine/Character.h @@ -0,0 +1,117 @@ +#pragma once +#include +#include +#include + +#include "../../Unuk/Globals.h" +#include "../../Unuk/Constants.h" +#include "../Engine/MemClass.h" +#include "../Sprite/ApplySurface.h" +#include "../Sprite/ImageLoader.h" +#include "../Engine/Collision.h" +#include "../Map/Map.h" +#include "../System/Timer.h" +#include "../Ui/Text.h" +#include "../System/Debug.h" +using namespace std; + +class Map; + +class Character { +public: + Character(Map* mapArg); + ~Character(void); + + void LoadSprites(string filename, int wArg, int hArg); + + float GetX(void) { return x; } + float GetY(void) { return y; } + float GetWidth(void) { return w; } + float GetHeight(void) { return h; } + + void SetXY(float xArg, float yArg) { x = xArg, y = yArg; } + void SetXVelocity(float arg) { xVel = arg; } + void SetYVelocity(float arg) { yVel = arg; } + + void SetHealth(int health) { _health = health; } + int GetHealth(void) { return _health; } + + void AddSpeachBubble(string text); + + void Render(void); + void Update(void); + + inline void* operator new(size_t size) { + return gMemManager.Allocate(size); + } + + inline void operator delete(void* object) { + gMemManager.Free(object); + } + + inline void* operator new [](size_t size) { + return gMemManager.Allocate(size); + } + + inline void operator delete [](void* object) { + gMemManager.Free(object); + } + +protected: + void Move(void); + + bool CheckTileCollisions(void); + bool CheckEntityCollisions(void); + bool CheckCharacterCollisions(void); + + float x; + float y; + float w; + float h; + + float xVel; + float yVel; + + int tileX; + int tileY; + + Timer attackTimer; + bool attacking; + + int _health; + + Map* map; + + static const float CHARACTER_SPEED; + + int directionFacing; + static const int FACING_UP = 0; + static const int FACING_RIGHT = 1; + static const int FACING_DOWN = 2; + static const int FACING_LEFT = 3; + + static const int ANIM_LEFT_FOOT = 0; + static const int ANIM_NO_FOOT = 1; + static const int ANIM_RIGHT_FOOT = 2; + static const int ANIM_ATTACK = 3; + +private: + static const int ANIMATION_SPEED = 200; + static const int ATTACKING_DISPLAY_LEN = 150; + + static const int SPEACH_BUBBLE_DISPLAY_LENGTH = 6000; + + SDL_Surface* _texture; + + // [direction][action] + SDL_Rect _sprites[4][4]; + + Timer _animationTimer; + int _animationStage; + bool _leftFoot; + + list _speachBubble; + list::iterator _speachBubbleIter; + Timer _speachBubbleTimer; + Text _speachBubbleText; +}; diff --git a/src/libUnuk/Engine/Collision.cpp b/src/libUnuk/Engine/Collision.cpp new file mode 100644 index 0000000..8ca25be --- /dev/null +++ b/src/libUnuk/Engine/Collision.cpp @@ -0,0 +1,27 @@ +#include "Collision.h" + +bool CheckCollisionRect(SDL_Rect a, SDL_Rect b) { + if(a.y + a.h <= b.y) + return false; + if(a.y >= b.y + b.h) + return false; + if(a.x + a.w <= b.x) + return false; + if(a.x >= b.x + b.w) + return false; + + return true; +} + +bool CheckCollisionXY(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2) { + if(y1 + h1 <= y2) + return false; + if(y1 >= y2 + h2) + return false; + if(x1 + w1 <= x2) + return false; + if(x1 >= x2 + w2) + return false; + + return true; +} diff --git a/src/libUnuk/Engine/Collision.h b/src/libUnuk/Engine/Collision.h new file mode 100644 index 0000000..b477fd1 --- /dev/null +++ b/src/libUnuk/Engine/Collision.h @@ -0,0 +1,6 @@ +#pragma once +#define _COLLISION_H_ +#include + +bool CheckCollisionRect(SDL_Rect a, SDL_Rect b); +bool CheckCollisionXY(int x1, int y1, int w1, int h1, int x2, int y2, int w2, int h2); diff --git a/src/libUnuk/Engine/MemClass.h b/src/libUnuk/Engine/MemClass.h new file mode 100644 index 0000000..6e0ea71 --- /dev/null +++ b/src/libUnuk/Engine/MemClass.h @@ -0,0 +1,32 @@ +#pragma once +#include "MemManager.h" + +extern MemManager gMemManager; + +class MemClass { +public: + MemClass(void) : r(0), c(0) {} + MemClass(double a, double b): r(a), c(b) {} + + inline void* operator new(size_t size) { + return gMemManager.Allocate(size); + } + + inline void operator delete(void* object) { + gMemManager.Free(object); + } + + inline void* operator new [](size_t size) { + return gMemManager.Allocate(size); + } + + inline void operator delete [](void* object) { + gMemManager.Free(object); + } + +private: + // Real part. + double r; + // Complex part. + double c; +}; diff --git a/src/libUnuk/Engine/MemManager.cpp b/src/libUnuk/Engine/MemManager.cpp new file mode 100644 index 0000000..afb317e --- /dev/null +++ b/src/libUnuk/Engine/MemManager.cpp @@ -0,0 +1,206 @@ +#include "MemClass.h" +#include "MemManager.h" + +MemManager gMemManager; + +void BitMapEntry::SetBit(int position, bool flag) { + blocksAvailable += flag ? 1 : -1; + int elementNo = position / INT_SIZE; + int bitNo = position % INT_SIZE; + if(flag) + bitMap[elementNo] = bitMap[elementNo] | (1 << bitNo); + else + bitMap[elementNo] = bitMap[elementNo] & ~(1 << bitNo); +} + +void BitMapEntry::SetMultipleBits(int position, bool flag, int count) { + blocksAvailable += flag ? count : -count; + int elementNo = position / INT_SIZE; + int bitNo = position % INT_SIZE; + + int bitSize = (count <= INT_SIZE - bitNo) ? count : INT_SIZE - bitNo; + SetRangeOfInt(&bitMap[elementNo], bitNo + bitSize - 1, bitNo, flag); + count -= bitSize; + if(!count) return; + + int i = ++elementNo; + while(count >= 0) { + if(count <= INT_SIZE) { + SetRangeOfInt(&bitMap[i], count - 1, 0, flag); + return; + } else + bitMap[i] = flag ? unsigned (-1) : 0; + count -= 32; + i++; + } +} + +void BitMapEntry::SetRangeOfInt(int* element, int msb, int lsb, bool flag) { + if(flag) { + int mask = (unsigned(-1) << lsb) & (unsigned(-1) >> INT_SIZE - msb - 1); + *element |= mask; + } else { + int mask = (unsigned(-1) << lsb) & (unsigned(-1) >> INT_SIZE - msb - 1); + *element &= ~mask; + } +} + +MemClass* BitMapEntry::FirstFreeBlock(size_t size) { + for(int i = 0; i < BIT_MAP_ELEMENTS; i++) { + if(bitMap[i] == 0) + // There aint any bits free. + continue; + + // Yield the first bit position. This is a 1 + // in an int from the right. + int result = bitMap[i] & -(bitMap[i]); + void* address = 0; + int basePos = (INT_SIZE * i); + + switch(result) { + // Make the corresponfing bit 0 so block is no longer free. + case 0x00000001: return ComplexObjectAddress(basePos + 0); + case 0x00000002: return ComplexObjectAddress(basePos + 1); + case 0x00000004: return ComplexObjectAddress(basePos + 2); + case 0x00000008: return ComplexObjectAddress(basePos + 3); + case 0x00000010: return ComplexObjectAddress(basePos + 4); + case 0x00000020: return ComplexObjectAddress(basePos + 5); + case 0x00000040: return ComplexObjectAddress(basePos + 6); + case 0x00000080: return ComplexObjectAddress(basePos + 7); + case 0x00000100: return ComplexObjectAddress(basePos + 8); + case 0x00000200: return ComplexObjectAddress(basePos + 9); + case 0x00000400: return ComplexObjectAddress(basePos + 10); + case 0x00000800: return ComplexObjectAddress(basePos + 11); + case 0x00001000: return ComplexObjectAddress(basePos + 12); + case 0x00002000: return ComplexObjectAddress(basePos + 13); + case 0x00004000: return ComplexObjectAddress(basePos + 14); + case 0x00008000: return ComplexObjectAddress(basePos + 15); + case 0x00010000: return ComplexObjectAddress(basePos + 16); + case 0x00020000: return ComplexObjectAddress(basePos + 17); + case 0x00040000: return ComplexObjectAddress(basePos + 18); + case 0x00080000: return ComplexObjectAddress(basePos + 19); + case 0x00100000: return ComplexObjectAddress(basePos + 20); + case 0x00200000: return ComplexObjectAddress(basePos + 21); + case 0x00400000: return ComplexObjectAddress(basePos + 22); + case 0x00800000: return ComplexObjectAddress(basePos + 23); + case 0x01000000: return ComplexObjectAddress(basePos + 24); + case 0x02000000: return ComplexObjectAddress(basePos + 25); + case 0x04000000: return ComplexObjectAddress(basePos + 26); + case 0x08000000: return ComplexObjectAddress(basePos + 27); + case 0x10000000: return ComplexObjectAddress(basePos + 28); + case 0x20000000: return ComplexObjectAddress(basePos + 29); + case 0x40000000: return ComplexObjectAddress(basePos + 30); + case 0x80000000: return ComplexObjectAddress(basePos + 31); + default: break; + } + } + return 0; +} + +MemClass* BitMapEntry::ComplexObjectAddress(int pos) { + SetBit(pos, false); + return &((static_cast(Head()) + (pos / INT_SIZE)) [INT_SIZE - (pos % INT_SIZE + 1)]); +} + +void* BitMapEntry::Head(void) { + return gMemManager.GetMemoryPoolList()[index]; +} + +void* MemManager::Allocate(size_t size) { + // None array. + if(size == sizeof(MemClass)) { + set::iterator freeMapI = _freeMapEntries.begin(); + if(freeMapI != _freeMapEntries.end()) { + BitMapEntry* mapEntry = *freeMapI; + return mapEntry->FirstFreeBlock(size); + } else { + AllocateChunkAndInitBitMap(); + _freeMapEntries.insert(&(_bitMapEntryList[_bitMapEntryList.size() - 1])); + return _bitMapEntryList[_bitMapEntryList.size() - 1].FirstFreeBlock(size); + } + } else { + // Array. + if(_arrayMemoryList.empty()) { + return AllocateArrayMemory(size); + } else { + map::iterator infoI = _arrayMemoryList.begin(); + map::iterator infoEndI = _arrayMemoryList.end(); + + while(infoI != infoEndI) { + ArrayMemoryInfo info = (*infoI).second; + if(info.StartPosition != 0) + // Only search the memory blocks where allocation + // is done from first byte. + continue; + else { + BitMapEntry* entry = &_bitMapEntryList[info.memPoolListIndex]; + if(entry->blocksAvailable < (size / sizeof(MemClass))) + return AllocateArrayMemory(size); + else { + info.StartPosition = BIT_MAP_SIZE - entry->blocksAvailable; + info.Size = size / sizeof(MemClass); + MemClass* baseAddress = static_cast(_memoryPoolList[info.memPoolListIndex]) + info.StartPosition; + + _arrayMemoryList[baseAddress] = info; + SetMultipleBlockBits(&info, false); + + return baseAddress; + } + } + } + } + } + return 0; +} + +void* MemManager::AllocateArrayMemory(size_t size) { + void* chunkAddress = AllocateChunkAndInitBitMap(); + ArrayMemoryInfo info; + info.memPoolListIndex = _memoryPoolList.size() - 1; + info.StartPosition = 0; + info.Size = size / sizeof(MemClass); + _arrayMemoryList[chunkAddress] = info; + SetMultipleBlockBits(&info, false); + return chunkAddress; +} + +void* MemManager::AllocateChunkAndInitBitMap(void) { + BitMapEntry mapEntry; + MemClass* memoryBeginAddress = reinterpret_cast(new char[sizeof(MemClass) * BIT_MAP_SIZE]); + _memoryPoolList.push_back(memoryBeginAddress); + mapEntry.index = _memoryPoolList.size() - 1; + _bitMapEntryList.push_back(mapEntry); + return memoryBeginAddress; +} + +void MemManager::Free(void* object) { + if(_arrayMemoryList.find(object) == _arrayMemoryList.end()) + // Simple block deletion. + SetBlockBit(object, true); + else { + // Memory block deletion. + ArrayMemoryInfo *info = &_arrayMemoryList[object]; + SetMultipleBlockBits(info, true); + } +} + +void MemManager::SetBlockBit(void* object, bool flag) { + int i = _bitMapEntryList.size() - 1; + for(; i >= 0; i--) { + BitMapEntry* bitMap = &_bitMapEntryList[i]; + if((bitMap->Head() <= object) && (&(static_cast(bitMap->Head()))[BIT_MAP_SIZE - 1] >= object)) { + int position = static_cast(object)- static_cast(bitMap->Head()); + bitMap->SetBit(position, flag); + flag ? bitMap->blocksAvailable++ : bitMap->blocksAvailable--; + } + } +} + +void MemManager::SetMultipleBlockBits(ArrayMemoryInfo* info, bool flag) { + BitMapEntry* mapEntry = &_bitMapEntryList[info->memPoolListIndex]; + mapEntry->SetMultipleBits(info->StartPosition, flag, info->Size); +} + +vector& MemManager::GetMemoryPoolList(void) { + return _memoryPoolList; +} diff --git a/src/libUnuk/Engine/MemManager.h b/src/libUnuk/Engine/MemManager.h new file mode 100644 index 0000000..3ceee14 --- /dev/null +++ b/src/libUnuk/Engine/MemManager.h @@ -0,0 +1,88 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +using namespace std; + +const int BIT_MAP_SIZE = 1024; +const int INT_SIZE = sizeof(int) * 8; +const int BIT_MAP_ELEMENTS = BIT_MAP_SIZE / INT_SIZE; + +/* + * Memory Allocation Pattern. + * 11111111 11111111 11111111 + * 11111110 11111111 11111111 + * 11111100 11111111 11111111 + * + * If all bits for the first section becomes zero go to next section. + * + * 00000000 11111111 11111111 + * 00000000 11111110 11111111 + * 00000000 11111100 11111111 + * 00000000 11111000 11111111 + * + * The lookup inside the map becomes 0(1) for the first available free block. + */ + +class MemClass; + +typedef struct BitMapEntry { + int index; + int blocksAvailable; + int bitMap[BIT_MAP_SIZE]; + +public: + BitMapEntry():blocksAvailable(BIT_MAP_SIZE) { + // All blocks are free to begin with and bit value 1 + // in the map denotes available blocks. + memset(bitMap, 0xff, BIT_MAP_SIZE / sizeof(char)); + } + + void SetBit(int position, bool flag); + void SetMultipleBits(int position, bool flag, int count); + void SetRangeOfInt(int* element, int msb, int lsb, bool flag); + MemClass* FirstFreeBlock(size_t size); + MemClass* ComplexObjectAddress(int pos); + void* Head(void); +} BitMapEntry; + +typedef struct ArrayInfo { + int memPoolListIndex; + int StartPosition; + int Size; +} ArrayMemoryInfo; + +class IMemManager { +public: + virtual void* Allocate(size_t size) = 0; + virtual void Free(void* object) = 0; +}; + +class MemManager : public IMemManager { +public: + MemManager(void) {} + ~MemManager(void) {} + + void* Allocate(size_t size); + void Free(void* object); + vector& GetMemoryPoolList(void); + +private: + void* AllocateArrayMemory(size_t size); + void* AllocateChunkAndInitBitMap(void); + void SetBlockBit(void* object, bool flag); + void SetMultipleBlockBits(ArrayMemoryInfo* info, bool flag); + + // The following lists will maintain one to one correspondace + // and should be the same size. + vector _memoryPoolList; + vector _bitMapEntryList; + + set _freeMapEntries; + map _arrayMemoryList; +}; diff --git a/src/libUnuk/Engine/NPC.cpp b/src/libUnuk/Engine/NPC.cpp new file mode 100644 index 0000000..37fa4d7 --- /dev/null +++ b/src/libUnuk/Engine/NPC.cpp @@ -0,0 +1,59 @@ +#include "NPC.h" + +NPC::NPC(Map* mapArg) : Character(mapArg) { + _moveTimer.Start(); + + _moveChangeFrequency = 14000; + _moveDurationMax = 3000; + _moveDurationMin = 1000; +} + +NPC::~NPC(void) { + +} + +void NPC::Update(void) { + // Store the NPC's health. + // int health = GetHealth(); // not referenced + + Move(); + + if(xVel > 0) directionFacing = FACING_RIGHT; + else if(xVel < 0) directionFacing = FACING_LEFT; + else if(yVel > 0) directionFacing = FACING_DOWN; + else if(yVel < 0) directionFacing = FACING_UP; +} + +void NPC::Move(void) { + if(_moving && _moveTimer.GetTicks() > _moveDurationMax) { + xVel = 0.0f; + yVel = 0.0f; + _moving = false; + } + + if(_moving && _moveTimer.GetTicks() >= _moveDurationCurrent) { + xVel = 0.0f; + yVel = 0.0f; + _moving = false; + } + + if(_moveTimer.GetTicks() > _moveChangeFrequency) { + _moveTimer.Start(); + _moveDurationCurrent = _moveDurationMin + (rand() % (_moveDurationMax - _moveDurationMin)); + if(rand() % 2) { + yVel = 0.0f; + if(rand() % 2) + xVel = CHARACTER_SPEED; + else + xVel = -CHARACTER_SPEED; + } else { + xVel = 0.0f; + if(rand() % 2) + yVel = CHARACTER_SPEED; + else + yVel = -CHARACTER_SPEED; + } + _moving = true; + } + Character::Move(); +} diff --git a/src/libUnuk/Engine/NPC.h b/src/libUnuk/Engine/NPC.h new file mode 100644 index 0000000..5774690 --- /dev/null +++ b/src/libUnuk/Engine/NPC.h @@ -0,0 +1,26 @@ +#pragma once + +#include "Character.h" +#include "AStar.h" + +class NPC : public Character { +public: + NPC(Map* mapArg); + ~NPC(void); + + void Update(void); + +protected: + void Move(void); + +private: + int _moveChangeFrequency; + + int _moveDurationCurrent; + int _moveDurationMin; + int _moveDurationMax; + + bool _moving; + + Timer _moveTimer; +}; diff --git a/src/libUnuk/Engine/ParticleEmitter.cpp b/src/libUnuk/Engine/ParticleEmitter.cpp new file mode 100644 index 0000000..b029fab --- /dev/null +++ b/src/libUnuk/Engine/ParticleEmitter.cpp @@ -0,0 +1,80 @@ +#include "ParticleEmitter.h" + +ParticleEmitter::ParticleEmitter(void) { + +} + +ParticleEmitter::~ParticleEmitter(void) { + +} + +void ParticleEmitter::SetXY(int xArg, int yArg) { + x = xArg; + y = yArg; +} + +void ParticleEmitter::ForceXY(int xArg, int yArg) { + for(int i = 0; i < _particleCount; i++) { + m_particle[i].x = (float)xArg; + m_particle[i].y = (float)yArg; + } +} + +void ParticleEmitter::SetParticleCount(int countArg) { + _particleCount = countArg; + m_particle.resize(_particleCount); + + for(int i = 0; i < _particleCount; i++) { + m_particle[i].startTime = SDL_GetTicks(); + } +} + +void ParticleEmitter::SetParticleSpeed(float speedArg) { + _particleSpeed = speedArg; +} + +void ParticleEmitter::SetParticleType(string typeArg) { + if(!_particleTexture) { + SDL_FreeSurface(_particleTexture); + } + + string textureFilename = "../Data/Media/Images/Particles/" + typeArg + ".png"; + _particleTexture = LoadImageAlpha(textureFilename.c_str()); +} + +void ParticleEmitter::SetParticleLifetime(int lifetimeArg) { + _particleLifetime = lifetimeArg; + + for(int i = 0; i < _particleCount; i++) { + m_particle[i].lifetime = rand() % _particleLifetime + _particleLifetime / 4; + } +} + +void ParticleEmitter::Render(void) { + for(int i = 0; i < _particleCount; i++) { + ApplySurface((int)m_particle[i].x, (int)m_particle[i].y, _particleTexture, screen); + } +} + +void ParticleEmitter::Update(void) { + for(int i = 0; i < _particleCount; i++) { + if((int)SDL_GetTicks() - m_particle[i].startTime > m_particle[i].lifetime) { + // Reset the x and y coords. + m_particle[i].x = (float)x; + m_particle[i].y = (float)y; + + m_particle[i].xVel = (float)(rand() % 360); + m_particle[i].yVel = (float)(rand() % 360); + + if(rand() % 2) + m_particle[i].xVel = m_particle[i].xVel * -1.0f; + if(rand() % 2) + m_particle[i].yVel = m_particle[i].yVel * -1.0f; + + m_particle[i].startTime = SDL_GetTicks(); + } else { + m_particle[i].x += m_particle[i].xVel * _particleSpeed; + m_particle[i].y += m_particle[i].yVel * _particleSpeed; + } + } +} diff --git a/src/libUnuk/Engine/ParticleEmitter.h b/src/libUnuk/Engine/ParticleEmitter.h new file mode 100644 index 0000000..a554338 --- /dev/null +++ b/src/libUnuk/Engine/ParticleEmitter.h @@ -0,0 +1,52 @@ +#pragma once +#include +#include +#include +#include + +#include "../../Unuk/Globals.h" +#include "../Sprite/ImageLoader.h" +#include "../Sprite/ApplySurface.h" +using namespace std; + +struct Particle { + float x; + float y; + + float xVel; + float yVel; + + int lifetime; + int startTime; +}; + +class ParticleEmitter { +public: + ParticleEmitter(void); + ~ParticleEmitter(void); + + // distribute particles from the new x and y. + void SetXY(int xArg, int yArg); + // Move all the existing particles to the new x and y. + void ForceXY(int xArg, int yArg); + + void SetParticleCount(int countArg); + void SetParticleLifetime(int lifetimeArg); + void SetParticleSpeed(float speedArg); + void SetParticleType(string typeArg); + + void Render(void); + void Update(void); + +private: + vector m_particle; + + int x; + int y; + + int _particleCount; + int _particleLifetime; + float _particleSpeed; + + SDL_Surface* _particleTexture; +}; diff --git a/src/libUnuk/Engine/WorldManager.cpp b/src/libUnuk/Engine/WorldManager.cpp new file mode 100644 index 0000000..af23a6b --- /dev/null +++ b/src/libUnuk/Engine/WorldManager.cpp @@ -0,0 +1,54 @@ +#include "WorldManager.h" +#include "NPC.h" + +WorldManager::WorldManager(void) { +} + +WorldManager::~WorldManager(void) { + for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) { + NPC* npc = (*i); + delete npc; + } +} + +void WorldManager::Update(void) { + for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) { + NPC* npc = (*i); + npc->Update(); + } +} + +void WorldManager::Render(void) { + for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) { + NPC* npc = (*i); + npc->Render(); + } +} + +void WorldManager::AddNPC(NPC* npc) { + _npcs.push_back(npc); +} + +void WorldManager::RemoveNPC(int index) { + int npcsIndex = 0; + for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) { + NPC* npc = (*i); + if(npcsIndex == index) { + _npcs.erase(i); + delete npc; + } + npcsIndex++; + } +} + +NPC* WorldManager::GetNPC(int index) { + int npcsIndex = 0; + for(std::list::iterator i = _npcs.begin(); i != _npcs.end(); ++i) { + NPC* npc = (*i); + if(npcsIndex == index) { + return npc; + } + npcsIndex++; + } + return NULL; +} diff --git a/src/libUnuk/Engine/WorldManager.h b/src/libUnuk/Engine/WorldManager.h new file mode 100644 index 0000000..466571c --- /dev/null +++ b/src/libUnuk/Engine/WorldManager.h @@ -0,0 +1,20 @@ +#pragma once +#include + +class NPC; + +class WorldManager { +public: + WorldManager(void); + ~WorldManager(void); + + void Update(void); + void Render(void); + + void AddNPC(NPC* npc); + void RemoveNPC(int index); + NPC* GetNPC(int index); + +private: + std::list _npcs; +}; diff --git a/src/libUnuk/Map/Map.cpp b/src/libUnuk/Map/Map.cpp new file mode 100644 index 0000000..d3952c8 --- /dev/null +++ b/src/libUnuk/Map/Map.cpp @@ -0,0 +1,251 @@ +#include "Map.h" +#include "../Engine/NPC.h" + +Map::Map(void) { + +} + +Map::~Map(void) { + +} + +void Map::Load(const string filename) { + Unload(); + _currentMap = filename; + string fullMapPath = "../Data/Media/Maps/" + filename; + TiXmlDocument mapFile(fullMapPath.c_str()); + + assert(mapFile.LoadFile() == true); + + // Getting dirty with some XML. This seems like a nicer + // approach to loading maps, rather than parsing tet files. + TiXmlElement* rootElem = NULL; + TiXmlElement* lineElem = NULL; + TiXmlElement* tileElem = NULL; + TiXmlElement* dataElem = NULL; + + x = -1; + y = -1; + + // - Let's start parsing the map. + rootElem = mapFile.FirstChildElement("map"); + assert(rootElem != NULL); + if(rootElem) { + // - We want to tile one line at a time. line represents + // the row we are tiling. + lineElem = rootElem->FirstChildElement("line"); + assert(lineElem != NULL); + while(lineElem) { + y++; + x = -1; + + // - Then we will select the tile. and increment x to keep tiling that row. + tileElem = lineElem->FirstChildElement("tile"); + assert(tileElem != NULL); + while(tileElem) { + x++; + _tile[x][y].SetTileXY(x * TILE_WIDTH, y * TILE_HEIGHT); + + // - Apply a teture to the tile. + dataElem = tileElem->FirstChildElement("tileTexture"); + assert(dataElem != NULL); + stringstream tilePath; + tilePath << "../Data/Media/Images/Tiles/" << dataElem->GetText() << ".png"; + _tile[x][y].SetTileTexture(_tileTextures.Add(tilePath.str())); + // - Finished applying the texture, move to the next sibling. + + // - Check to see if the tile is solid or not. + dataElem = dataElem->NextSiblingElement("solidTile"); + assert(dataElem != NULL); + string tileSolidity = dataElem->GetText(); + assert(tileSolidity == "false" || tileSolidity == "true"); + if(tileSolidity == "false") + _tile[x][y].SetTileSolidity(false); + else + _tile[x][y].SetTileSolidity(true); + // + + // + dataElem = dataElem->NextSiblingElement("entityTexture"); + assert(dataElem != NULL); + string entityName = dataElem->GetText(); + if(entityName != "null") { + stringstream entityPath; + entityPath << "../Data/Media/Images/Entities/" << entityName << ".png"; + _tile[x][y].SetEntityTexture(_entityTextures.AddAlpha(entityPath.str())); + + _tile[x][y].SetEntityXY(_tile[x][y].GetTileX() + TILE_WIDTH / 2 - _tile[x][y].GetEntityWidth() / 2, + _tile[x][y].GetTileY() + TILE_HEIGHT / 2 - _tile[x][y].GetEntityHeight() / 2); + } + // + + // + dataElem = dataElem->NextSiblingElement("solidEntity"); + assert(dataElem != NULL); + string entitySolidity = dataElem->GetText(); + assert(entitySolidity == "false" || entitySolidity == "true"); + if(entitySolidity == "false") + _tile[x][y].SetEntitySolidity(false); + else + _tile[x][y].SetEntitySolidity(true); + // + + // + dataElem = dataElem->NextSiblingElement("zLevel"); + assert(dataElem != NULL); + _tile[x][y].SetZLevel(atoi(dataElem->GetText())); + // + + // + dataElem = dataElem->NextSiblingElement("mapTransition"); + assert(dataElem != NULL); + _tile[x][y].SetMapTransitionName(dataElem->GetText()); + // + + // + dataElem = dataElem->NextSiblingElement("mapTransX"); + assert(dataElem != NULL); + // int mapTransX = atoi(dataElem->GetText()); // not referenced + // + + // + dataElem = dataElem->NextSiblingElement("mapTransY"); + assert(dataElem != NULL); + // int mapTransY = atoi(dataElem->GetText()); // not referenced + // + + tileElem = tileElem->NextSiblingElement("tile"); + } + // + + lineElem = lineElem->NextSiblingElement("line"); + } + // + } + // + levelWidth = x * TILE_WIDTH; + levelHeight = y * TILE_HEIGHT; + + //character->Load(filename); + + NPC* npc = new NPC(this); + + npc->SetXY(300, 300); + npc->LoadSprites("../Data/Media/Images/Characters/template.png", 40,45); + _world.AddNPC(npc); + + npc = new NPC(this); + npc->SetXY(150, 350); + npc->LoadSprites("../Data/Media/Images/Characters/template.png", 40,45); + _world.AddNPC(npc); + + npc = new NPC(this); + npc->SetXY(100, 250); + npc->LoadSprites("../Data/Media/Images/Characters/template.png", 40,45); + _world.AddNPC(npc); +} + +void Map::Update(void) { + _world.Update(); + // Update the map so we can render when camera moves. +} + +void Map::Render(void) { + int xOrig = (camera.x / TILE_WIDTH) - 1; + int yOrig = (camera.y / TILE_HEIGHT) - 1; + + if (xOrig < 0) xOrig = 0; + if (yOrig < 0) yOrig = 0; + + int xEnd = xOrig + (SCREEN_WIDTH / TILE_WIDTH) + 3; + int yEnd = yOrig + (SCREEN_HEIGHT / TILE_HEIGHT) + 3; + + /* the fuck is this Allanis? --konom + if(xEnd < x) + xEnd++; + else + xEnd = x; + + if(yEnd < y) + yEnd++; + else + yEnd = y; + */ + + if (xEnd > x) xEnd = x; + if (yEnd > y) yEnd = y; + if (xEnd < 0) xEnd = 0; + if (yEnd < 0) yEnd = 0; + + if (xOrig > xEnd) xOrig = xEnd - 1; + if (yOrig > yEnd) yOrig = yEnd - 1; + + for(int i = xOrig; i < xEnd; i++) { + for(int j = yOrig; j < yEnd; j++) { + _tile[i][j].Render(); + } + } + + _world.Render(); +} + +void Map::Unload(void) { + _tileTextures.Unload(); + _entityTextures.Unload(); +} + +string Map::GetCurrentMap(void) { + return _currentMap; +} + +bool Map::GetTileSolidity(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetTileSolidity(); +} + +int Map::GetTileX(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetTileX(); +} + +int Map::GetTileY(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetTileY(); +} + +bool Map::GetEntitySolidity(int xArg, int yArg) { + if(xArg > x || yArg > y || yArg < 0 || yArg < 0) { + return false; + } + + return _tile[xArg + 1][yArg + 1].GetEntitySolitity(); +} + +int Map::GetEntityX(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetEntityX(); +} + +int Map::GetEntityY(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetEntityY(); +} + +int Map::GetEntityWidth(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetEntityWidth(); +} + +int Map::GetEntityHeight(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetEntityHeight(); +} + +int Map::GetTileZLevel(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetZLevel(); +} + +string Map::GetMapTransitionName(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetMapTransitionName(); +} + +int Map::GetMapTransitionX(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetMapTransitionX(); +} + +int Map::GetMapTransitionY(int xArg, int yArg) { + return _tile[xArg + 1][yArg + 1].GetMapTransitionY(); +} diff --git a/src/libUnuk/Map/Map.h b/src/libUnuk/Map/Map.h new file mode 100644 index 0000000..0d6a5cc --- /dev/null +++ b/src/libUnuk/Map/Map.h @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +#include "../../Unuk/Globals.h" +#include "../../Unuk/Constants.h" +#include "../Sprite/ImageLoader.h" +#include "../Sprite/ApplySurface.h" +#include "../Map/MapTile.h" +#include "../System/Debug.h" +#include "../Engine/WorldManager.h" +using namespace std; + +//class CharacterManager; + +class Map { +public: + Map(void); + ~Map(void); + + void Load(const string filename); + void Update(void); + void Render(void); + + bool GetTileSolidity(int xArg, int yArg); + int GetTileX(int xArg, int yArg); + int GetTileY(int xArg, int yArg); + + bool GetEntitySolidity(int xArg, int yArg); + int GetEntityX(int xArg, int yArg); + int GetEntityY(int xArg, int yArg); + int GetEntityWidth(int xArg, int yArg); + int GetEntityHeight(int xArg, int yArg); + + int GetTileZLevel(int xArg, int yArg); + + string GetMapTransitionName(int xArg, int yArg); + int GetMapTransitionX(int xArg, int yArg); + int GetMapTransitionY(int xArg, int yArg); + + string GetCurrentMap(void); + + WorldManager& GetWorld(void) { return _world; } + +private: + void Unload(void); + + string _currentMap; + int x; + int y; + + static const int TILE_ARRAY_SIZE = 150; + MapTile _tile[TILE_ARRAY_SIZE][TILE_ARRAY_SIZE]; + + TextureManager _tileTextures; + TextureManager _entityTextures; + + WorldManager _world; +}; diff --git a/src/libUnuk/Map/MapElement.cpp b/src/libUnuk/Map/MapElement.cpp new file mode 100644 index 0000000..102327a --- /dev/null +++ b/src/libUnuk/Map/MapElement.cpp @@ -0,0 +1,39 @@ +#include "MapElement.h" + +MapElement::MapElement(void) { + +} + +MapElement::~MapElement(void) { + +} + +void MapElement::SetTexture(SDL_Surface* arg) { + _texture = arg; +} + +void MapElement::Render(void) { + ApplySurface(x, y, _texture, screen); +} + +void MapElement::Update(void) { + +} + +void MapElement::SetSolidity(bool arg) { + _solid = arg; +} + +bool MapElement::GetSolidity(void) { + return _solid; +} + +void MapElement::SetXY(int xArg, int yArg) { + x = xArg, + y = yArg; +} + +int MapElement::GetX(void) { return x; } +int MapElement::GetY(void) { return y; } +int MapElement::GetWidth(void) { return _texture->w; } +int MapElement::GetHeight(void) { return _texture->h; } diff --git a/src/libUnuk/Map/MapElement.h b/src/libUnuk/Map/MapElement.h new file mode 100644 index 0000000..4d681cf --- /dev/null +++ b/src/libUnuk/Map/MapElement.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +#include "../../Unuk/Globals.h" +#include "../Sprite/ApplySurface.h" +#include "../Sprite/TextureManager.h" + +class MapElement { +public: + MapElement(void); + ~MapElement(void); + + static void SetTextureManager(TextureManager* arg); + + virtual void SetTexture(SDL_Surface* arg); + virtual void Render(void); + virtual void Update(void); + + void SetSolidity(bool arg); + bool GetSolidity(void); + + void SetXY(int xArg, int yArg); + int GetX(void); + int GetY(void); + int GetWidth(void); + int GetHeight(void); + +protected: + SDL_Surface* _texture; + + bool _solid; + + int x; + int y; +}; diff --git a/src/libUnuk/Map/MapEntities.cpp b/src/libUnuk/Map/MapEntities.cpp new file mode 100644 index 0000000..a662269 --- /dev/null +++ b/src/libUnuk/Map/MapEntities.cpp @@ -0,0 +1,16 @@ +#include "MapEntities.h" + +MapEntityGeneric::MapEntityGeneric(void) { + _texture = NULL; + +} + +MapEntityGeneric::~MapEntityGeneric(void) { + +} + +void MapEntityGeneric::Render(void) { + if(_texture != NULL) { + MapElement::Render(); + } +} diff --git a/src/libUnuk/Map/MapEntities.h b/src/libUnuk/Map/MapEntities.h new file mode 100644 index 0000000..530eb62 --- /dev/null +++ b/src/libUnuk/Map/MapEntities.h @@ -0,0 +1,19 @@ +#pragma once +/* + * Version of MapElement, that will check whether the SDL_Surface it + * owns is NULL or not and draws. + * + */ + +#include "MapElement.h" + +class MapEntityGeneric : public MapElement { +public: + MapEntityGeneric(void); + ~MapEntityGeneric(void); + + void Render(void); + +private: + +}; diff --git a/src/libUnuk/Map/MapTile.h b/src/libUnuk/Map/MapTile.h new file mode 100644 index 0000000..e32ed8d --- /dev/null +++ b/src/libUnuk/Map/MapTile.h @@ -0,0 +1,66 @@ +#pragma once +#include +#include +#include + +#include "../../Unuk/Constants.h" +#include "../Sprite/ApplySurface.h" +#include "../Map/MapElement.h" +#include "../Map/MapEntities.h" +using namespace std; + +class MapTile { +public: + MapTile(void) { } + ~MapTile(void) { } + + void Render(void) { _tile.Render(), _entity.Render(); } + + // Tile Mutators. + SDL_Surface* SetTileTexture(SDL_Surface* arg) { _tile.SetTexture(arg); return NULL; } + void SetTileSolidity(bool arg) { _tile.SetSolidity(arg); } + bool GetTileSolidity(void) { return _tile.GetSolidity(); } + // Well, it kinda helps if I lay the + // tiles rather than just get the + // return value right?? + void SetTileXY(int xArg, int yArg) { _tile.SetXY(xArg, yArg); } + int GetTileX(void) { return _tile.GetX(); } + int GetTileY(void) { return _tile.GetY(); } + + // Entity Mutators. + void SetEntityTexture(SDL_Surface* arg) { _entity.SetTexture(arg); } + void SetEntityXY(int xArg, int yArg) { _entity.SetXY(xArg, yArg); } + void SetEntitySolidity(bool arg) { _entity.SetSolidity(arg); } + bool GetEntitySolitity(void) { return _entity.GetSolidity(); } + + // Entity Mutators. + int GetEntityX(void) { return _entity.GetX(); } + int GetEntityY(void) { return _entity.GetY(); } + int GetEntityWidth(void) { return _entity.GetWidth(); } + int GetEntityHeight(void) { return _entity.GetHeight(); } + + // ZLevel Mutators. + void SetZLevel(int arg) { _zLevel = arg; } + int GetZLevel(void) { return _zLevel; } + + // Map Transition Mutators. + void SetMapTransitionName(string arg) { _mapTransitionName = arg; } + string GetMapTransitionName(void) { return _mapTransitionName; } + + void SetMapTransitionXY(int xArg, int yArg) { _mapTransitionX = xArg, _mapTransitionY = yArg; } + int GetMapTransitionX(void) { return _mapTransitionX; } + int GetMapTransitionY(void) { return _mapTransitionY; } + +private: + MapElement _tile; + MapEntityGeneric _entity; + + // -1 is a 'special' tile, the next tile that the player walks + // on is the players new zlevel. + int _zLevel; + + // If not 'null', switch map when the player walks on this tile. + string _mapTransitionName; + int _mapTransitionX; + int _mapTransitionY; +}; diff --git a/src/libUnuk/Sprite/ApplySurface.cpp b/src/libUnuk/Sprite/ApplySurface.cpp new file mode 100644 index 0000000..b6b88c8 --- /dev/null +++ b/src/libUnuk/Sprite/ApplySurface.cpp @@ -0,0 +1,23 @@ +#include "ApplySurface.h" + +void ApplySurface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip) { + assert(source != NULL); + + SDL_Rect offset; + + offset.x = (Sint16)(x - camera.x); + offset.y = (Sint16)(y - camera.y); + + SDL_BlitSurface(source, clip, destination, &offset); +} + +void ApplySurfaceLiteral(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip) { + assert(source != NULL); + + SDL_Rect offset; + + offset.x = (Sint16)x; + offset.y = (Sint16)y; + + SDL_BlitSurface(source, clip, destination, &offset); +} diff --git a/src/libUnuk/Sprite/ApplySurface.h b/src/libUnuk/Sprite/ApplySurface.h new file mode 100644 index 0000000..bc3f000 --- /dev/null +++ b/src/libUnuk/Sprite/ApplySurface.h @@ -0,0 +1,16 @@ +#pragma once +#include +#include +#include "../System/Debug.h" +#include "../../Unuk/Globals.h" + +/* Use for objects. + * Blit a surface to another surface. An + * optioanl clip argument can be given. The surface + * will be applied to the destination, taking into + * account the position of the camera. + */ +void ApplySurface(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL); + +// This one won't take the camera into account, so we could use it for GUI stuff. +void ApplySurfaceLiteral(int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect* clip = NULL); diff --git a/src/libUnuk/Sprite/ImageLoader.cpp b/src/libUnuk/Sprite/ImageLoader.cpp new file mode 100644 index 0000000..68b3397 --- /dev/null +++ b/src/libUnuk/Sprite/ImageLoader.cpp @@ -0,0 +1,37 @@ +#include "ImageLoader.h" + +SDL_Surface* LoadImage(const char* filename) { + SDL_Surface* loadedImage = NULL; + SDL_Surface* optimizedImage = NULL; + + // Initialize loadedImage with the file. + loadedImage = IMG_Load(filename); + if(loadedImage != NULL) { + // Then copy the image to a surface for us to blit later. + optimizedImage = SDL_DisplayFormat(loadedImage); + SDL_FreeSurface(loadedImage); + return optimizedImage; + } else { + // Tell us what file is missing and do some stupid error texture thing. + Debug::logger->message("ImageLoader: %s cannot be found!"); + return errorTexture; + } +} + +SDL_Surface* LoadImageAlpha(const char* filename) { + SDL_Surface* loadedImage = NULL; + SDL_Surface* optimizedImage = NULL; + + // Initialize loadedImage with the file. + loadedImage = IMG_Load(filename); + if(loadedImage != NULL) { + // Then copy the image to a surface and give us an alpha channel. + optimizedImage = SDL_DisplayFormatAlpha(loadedImage); + SDL_FreeSurface(loadedImage); + return optimizedImage; + } else { + // Tell us what file is missing and do some stupid error texture thing. + Debug::logger->message("ImageLoader: %s cannot be found!"); + return errorTexture; + } +} diff --git a/src/libUnuk/Sprite/ImageLoader.h b/src/libUnuk/Sprite/ImageLoader.h new file mode 100644 index 0000000..111b7ff --- /dev/null +++ b/src/libUnuk/Sprite/ImageLoader.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include "../../Unuk/Globals.h" +#include "../System/Debug.h" + +SDL_Surface* LoadImage(const char* filename); +SDL_Surface* LoadImageAlpha(const char* filename); diff --git a/src/libUnuk/Sprite/Texture.cpp b/src/libUnuk/Sprite/Texture.cpp new file mode 100644 index 0000000..5a0a11c --- /dev/null +++ b/src/libUnuk/Sprite/Texture.cpp @@ -0,0 +1,53 @@ +#include "Texture.h" + +Texture::Texture(void) { + _texture = NULL; +} + +Texture::~Texture(void) { + assert(_texture != NULL); + SDL_FreeSurface(_texture); +} + +void Texture::SetXY(int xArg, int yArg) { + x = xArg; + y = yArg; +} + +void Texture::Render(void) { + ApplySurface(x, y, _texture, screen); +} + +void Texture::Render(int xArg, int yArg) { + ApplySurface(xArg, yArg, _texture, screen); +} + +void Texture::RenderLiteral(void) { + ApplySurfaceLiteral(x, y, _texture, screen); +} + +void Texture::RenderLiteral(int xArg, int yArg) { + ApplySurfaceLiteral(xArg, yArg,_texture, screen); +} + +void Texture::Load(const char* filename) { + if(_texture != NULL) { + // Free the texture. + SDL_FreeSurface(_texture); + } + // Load the texture. + _texture = LoadImage(filename); +} + +void Texture::LoadAlpha(const char* filename) { + if(_texture != NULL) { + // Free the texture. + SDL_FreeSurface(_texture); + } + // Load the texture with an alpha channel. + _texture = LoadImageAlpha(filename); +} + +void Texture::SetAlpha(int alphaArg) { + SDL_SetAlpha(_texture, SDL_SRCALPHA, (Uint8)alphaArg); +} diff --git a/src/libUnuk/Sprite/Texture.h b/src/libUnuk/Sprite/Texture.h new file mode 100644 index 0000000..bfecf93 --- /dev/null +++ b/src/libUnuk/Sprite/Texture.h @@ -0,0 +1,33 @@ +#pragma once +#include "../../Unuk/Globals.h" +#include "ImageLoader.h" +#include "ApplySurface.h" + +class Texture { +public: + Texture(void); + ~Texture(void); + + int GetWidth(void) { return _texture->w; } + int GetHeight(void) { return _texture->h; } + int GetX(void) { return x; } + int GetY(void) { return y; } + + void SetXY(int xArg, int yArg); + + void Render(void); + void Render(int xArg, int yArg); + + void RenderLiteral(void); + void RenderLiteral(int xArg, int yArg); + + void Load(const char* filename); + void LoadAlpha(const char* filename); + void SetAlpha(int alphaArg); + +protected: + int x, y; + +private: + SDL_Surface* _texture; +}; diff --git a/src/libUnuk/Sprite/TextureManager.cpp b/src/libUnuk/Sprite/TextureManager.cpp new file mode 100644 index 0000000..8089c1d --- /dev/null +++ b/src/libUnuk/Sprite/TextureManager.cpp @@ -0,0 +1,55 @@ +#include "TextureManager.h" + +TextureManager::TextureManager(void) { + _allocated = 0; +} + +TextureManager::~TextureManager(void) { + Unload(); +} + +void TextureManager::Unload(void) { + for(int i = 0; i < _allocated; i++) { + SDL_FreeSurface(_textures[i].texture); + _textures[i].name.clear(); + } + _allocated = 0; +} + +SDL_Surface* TextureManager::Add(string filename) { + assert(_allocated < TEXTURE_ARR_SIZE - 1); + + // Has the texture been loaded already? + for(int i = 0; i < _allocated; i++) { + if(_textures[i].name == filename) { + return _textures[i].texture; + } + } + // If not, then load it. + _textures[_allocated].name = filename; + _textures[_allocated].texture = LoadImage(filename.c_str()); + + _allocated++; + + return _textures[_allocated - 1].texture; +} + +SDL_Surface* TextureManager::AddAlpha(string filename) { + assert(_allocated < TEXTURE_ARR_SIZE - 1); + + // Has the texture been loaded already? + for(int i = 0; i < _allocated; i++) { + if(_textures[i].name == filename) { + return _textures[i].texture; + } + } + + // If not, then load it. + + _textures[_allocated].name = filename; + _textures[_allocated].texture = LoadImageAlpha(filename.c_str()); + + _allocated++; + + return _textures[_allocated -1].texture; +} diff --git a/src/libUnuk/Sprite/TextureManager.h b/src/libUnuk/Sprite/TextureManager.h new file mode 100644 index 0000000..a12a96c --- /dev/null +++ b/src/libUnuk/Sprite/TextureManager.h @@ -0,0 +1,39 @@ +#pragma once + +/* + * The Texture Manager will keep a small "Database" + * of the name of the texture that is loaded and the + * actual texture so we can query it with the filename + * and it will return the teture if it is already in memory + * or load the tture if it is not. + */ + +#include +#include +#include +#include "ImageLoader.h" +using namespace std; + +class TextureManager { +public: + TextureManager(void); + ~TextureManager(void); + + void Unload(void); + + SDL_Surface* Add(string filename); + SDL_Surface* AddAlpha(string filename); + +private: + // The textureNode will hold the name and the texture. + struct textureNode { + SDL_Surface* texture; + string name; + }; + + // We should not need more than a hundred.. + static const int TEXTURE_ARR_SIZE = 100; + textureNode _textures[TEXTURE_ARR_SIZE]; + + int _allocated; +}; diff --git a/src/libUnuk/System/Debug.cpp b/src/libUnuk/System/Debug.cpp new file mode 100644 index 0000000..db6a8b6 --- /dev/null +++ b/src/libUnuk/System/Debug.cpp @@ -0,0 +1,101 @@ +#include +#include +#include +#include +#include "Debug.h" +#include "string" + + +using namespace std; + +// =================================================================== +// The Debug log allows us to display ever piece of data that +// populates our class components, anything that is loaded, serialized, +// de-serialized etc will be printed out to a text file. +// (Running our program in a terminal, this debug log will print to it.) +// =================================================================== + +Debug *Debug::logger = NULL; + +Debug::Debug(bool logToFile) { + time_t timestamp; + if(logToFile) { + _logFile.open("../Bin/Debug.log", ios::out); + if(!logToFile) { + // We can not open our log. + cerr << "Warning: Can not open Debug.log to write, continueing without logging\n"; + } else { + // Log File is open, let us give it a nice time stamp. + timestamp = time(NULL); + _logFile << "Log Started: " << ctime(×tamp) << endl; + } + } +} + +Debug::~Debug(void) { + time_t timestamp; + + // We only need to close the log if it is open. + if(_logFile) { + // Give it a closing timestamp. + timestamp = time(NULL); + _logFile << endl << "Log Closed: " << ctime(×tamp) << endl; + + // Close the log file. + _logFile.close(); + } +} + +void Debug::message(std::string msg) { + if(_logFile) { + _logFile << msg << endl; + } + cerr << msg << endl << endl; +} + +void Debug::message(const char *msg, ...) { + va_list vargList; // This is to handlle the variable arguments + + char outBuf[1024]; + unsigned short outLen; + + // This takes the arguments and puts them into the character array. + va_start(vargList, msg); + +#if defined WIN32 + outLen = (unsigned short)_vsnprintf(outBuf, sizeof(outBuf), msg, vargList); +#else + outLen = vsnprintf(outBuf, sizeof(outBuf), msg, vargList); +#endif + + va_end(vargList); + + if(outLen >= sizeof(outBuf)) { + outLen = sizeof(outBuf); + } + + if(_logFile) { + _logFile << outBuf << endl; + } + + cerr << outBuf << endl; +} + +bool Debug::openLog(bool logToFile) { + // Make sure the logger has not already been initialized. + if(logger != NULL) { + logger->message("Warning: Multiple calls to openLog()."); + return false; + } + logger = new Debug(logToFile); + return true; +} + +void Debug::closeLog(void) { + if(logger == NULL) { + cerr << "Warning: Call to closeLog() with NULL logger pointer." << endl; + return; + } + delete logger; + logger = NULL; +} diff --git a/src/libUnuk/System/Debug.h b/src/libUnuk/System/Debug.h new file mode 100644 index 0000000..96e93f0 --- /dev/null +++ b/src/libUnuk/System/Debug.h @@ -0,0 +1,20 @@ +#pragma once +#include +#include "string" + +class Debug { +public: + Debug(bool logToFile); + ~Debug(void); + + // Log an error message. + void message(std::string msg); + void message(const char *msg, ...); + static bool openLog(bool logToFile); + static void closeLog(void); + + static Debug *logger; + +private: + std::ofstream _logFile; +}; diff --git a/src/libUnuk/System/FPS.cpp b/src/libUnuk/System/FPS.cpp new file mode 100644 index 0000000..b8608c3 --- /dev/null +++ b/src/libUnuk/System/FPS.cpp @@ -0,0 +1,41 @@ +#include "FPS.h" + +FPS::FPS(int maxFPSArg) { + _maxFPS = maxFPSArg; + + _fps = 0; + _frame = 0; + + _frameTimer.Start(); + _fpsCalc.Start(); +} + +FPS::~FPS(void) { + +} + +void FPS::LimitFPS(void) { + // Calculate the FPS. + if(_fpsCalc.GetTicks() > 1000) { + + _fps = _frame / (_fpsCalc.GetTicks() / 1000); + + _fpsCalc.Start(); + _frame = 0; + } + + // Put a limitation on the FPS. + if(1000 / _maxFPS > _frameTimer.GetTicks()) { + // SDL_Delay does not accept a float so for higher framerate + // limits there's an innacuracy. This is as much as 3fps + // at a limit of 60fps. + SDL_Delay((1000 / _maxFPS) - _frameTimer.GetTicks()); + } + + _frameTimer.Start(); + _frame++; +} + +void FPS::SetMaxFPS(int maxFPSArg) { + _maxFPS = maxFPSArg; +} diff --git a/src/libUnuk/System/FPS.h b/src/libUnuk/System/FPS.h new file mode 100644 index 0000000..5a24263 --- /dev/null +++ b/src/libUnuk/System/FPS.h @@ -0,0 +1,25 @@ +#pragma once +#include "../../Unuk/Globals.h" +#include "Timer.h" + + +class FPS { +public: + FPS(int maxFPSArg); + ~FPS(void); + + void LimitFPS(void); + + void SetMaxFPS(int maxFPSArg); + int GetMaxFPS(void) { return _maxFPS; } + + int GetCurrentFPS(void) { return _fps; } + +private: + int _fps; + int _frame; + int _maxFPS; + + Timer _frameTimer; + Timer _fpsCalc; +}; diff --git a/src/libUnuk/System/Input.cpp b/src/libUnuk/System/Input.cpp new file mode 100644 index 0000000..a99b5d3 --- /dev/null +++ b/src/libUnuk/System/Input.cpp @@ -0,0 +1,82 @@ +#include +#include "Input.h" + +static mouse_t mouse; +static keyboard_t keyboard; + +bool _curr_key(int index) { + return(keyboard.keys[index] != 0); +} + +bool _old_key(int index) { + return(keyboard.oldKeys[index] != 0); +} + +bool _curr_mouse(int button) { + return((mouse.buttons * SDL_BUTTON(button)) != 0); +} + +bool _old_mouse(int button) { + return((mouse.oldButtons & SDL_BUTTON(button)) != 0); +} + +bool CreateInput(void) { + memset(&keyboard, 0, sizeof(keyboard_t)); + memset(&mouse, 0, sizeof(mouse_t)); + SDL_PumpEvents(); + SDL_PumpEvents(); + unsigned char* tempKeys = SDL_GetKeyState(&keyboard.keycount); + keyboard.keys = (unsigned char*)malloc(sizeof(char) * keyboard.keycount); + keyboard.oldKeys = (unsigned char*)malloc(sizeof(char) * keyboard.keycount); + + memcpy(keyboard.keys, tempKeys, sizeof(char) * keyboard.keycount); + mouse.buttons = SDL_GetMouseState(&mouse.dx, &mouse.dy); + return true; +} + +void UpdateInput(void) { + SDL_PumpEvents(); + keyboard.lastChar = -1; + mouse.oldx = mouse.dx; + mouse.oldy = mouse.dy; + mouse.oldButtons = SDL_GetMouseState(&mouse.dx, &mouse.dy); + + memcpy(keyboard.oldKeys, keyboard.keys, sizeof(char) * keyboard.keycount); + + unsigned char *tmp = SDL_GetKeyState(&keyboard.keycount); + memcpy(keyboard.keys, tmp, sizeof(char) * keyboard.keycount); + + keyboard.mods = SDL_GetModState(); + + SDL_Event event; + while(SDL_PollEvent(&event)) { + if(event.type == SDL_KEYDOWN) { + keyboard.lastChar = event.key.keysym.sym; + } + } +} + +char GetKey(void) { + if(keyboard.lastChar != -1) + return (char)keyboard.lastChar; + return 0; +} + +unsigned int GetX(void) { return mouse.dx; } +unsigned int GetY(void) { return mouse.dy; } +unsigned int GetOldX(void) { return mouse.oldx; } +unsigned int GetOldY(void) { return mouse.oldy; } +unsigned int GetMods(void) { return keyboard.mods; } +bool KeyDown(int index) { return(_curr_key(index) && !_old_key(index)); } +bool KeyStillDown(int index) { return(_curr_key(index) && _old_key(index)); } +bool KeyUp(int index) { return(!_curr_key(index) && _old_key(index)); } +bool KeyStillUp(int index) { return(!_curr_key(index) && !_old_key(index)); } +bool MouseDown(int button) { return(_curr_mouse(button) && !_old_mouse(button)); } +bool MouseStillDown(int button) { return(_curr_mouse(button) && _old_mouse(button)); } +bool MouseUp(int button) { return(!_curr_mouse(button) && _old_mouse(button)); } +bool MouseStillUp(int button) { return(!_curr_mouse(button) && !_old_mouse(button)); } + +void DestroyInput(void) { + free(keyboard.keys); + free(keyboard.oldKeys); +} diff --git a/src/libUnuk/System/Input.h b/src/libUnuk/System/Input.h new file mode 100644 index 0000000..988dfee --- /dev/null +++ b/src/libUnuk/System/Input.h @@ -0,0 +1,43 @@ +#pragma once +#include + +typedef struct mouse_s { + int dx, dy; + int oldx, oldy; + unsigned int buttons; + unsigned int oldButtons; +} mouse_t; + +typedef struct keyboard_s { + unsigned char *keys; + unsigned char *oldKeys; + int keycount; + int lastChar; + unsigned int mods; +} keyboard_t; + +typedef struct input_s { + mouse_t mouse; + keyboard_t keyboard; +} input_t; + + bool CreateInput(void); + void UpdateInput(void); + + char GetKey(void); + + unsigned int GetX(void); + unsigned int GetY(void); + unsigned int GetOldX(void); + unsigned int GetOldY(void); + unsigned int GetMods(void); + bool KeyDown(int index); + bool KeyStillDown(int index); + bool KeyUp(int index); + bool KeyStillUp(int index); + bool MouseDown(int button); + bool MouseStillDown(int button); + bool MouseUp(int button); + bool MouseStillUp(int button); + + void DestroyInput(void); diff --git a/src/libUnuk/System/Rect.cpp b/src/libUnuk/System/Rect.cpp new file mode 100644 index 0000000..d2b1d53 --- /dev/null +++ b/src/libUnuk/System/Rect.cpp @@ -0,0 +1,66 @@ +#include "Rect.h" + +Rect::Rect(void) { +} + +Rect::~Rect(void) { +} + +void Rect::SetXY(int xArg, int yArg) { + rect.x = (Sint16)xArg; + rect.y = (Sint16)yArg; +} + +void Rect::SetWidthHeight(int wArg, int hArg) { + rect.w = (Uint16)wArg; + rect.h = (Uint16)hArg; +} + +void Rect::SetRGB(Uint8 rArg, Uint8 gArg, Uint8 bArg) { + r = rArg; + g = gArg; + b = bArg; +} + +void Rect::SetRGB(SDL_Color colour) { + r = colour.r; + g = colour.g; + b = colour.b; +} + +void Rect::Draw(void) { + SDL_Rect offset; + + offset.x = rect.x - camera.x; + offset.y = rect.y - camera.y; + offset.w = rect.w; + offset.h = rect.h; + + SDL_FillRect(screen, &offset, SDL_MapRGB(screen->format, r, g, b)); +} + +void Rect::Draw(int xArg, int yArg) { + SDL_Rect offset; + + offset.x = (Sint16)(xArg - camera.x); + offset.y = (Sint16)(yArg - camera.y); + offset.w = (Sint16)rect.w; + offset.h = (Sint16)rect.h; + + SDL_FillRect(screen, &offset, SDL_MapRGB(screen->format, r, g, b)); +} + +void Rect::DrawLiteral(void) { + SDL_FillRect(screen, &rect, SDL_MapRGB(screen->format, r, g, b)); +} + +void Rect::DrawLiteral(int xArg, int yArg) { + SDL_Rect offset; + + offset.x = (Sint16)xArg; + offset.y = (Sint16)yArg; + offset.w = rect.w; + offset.h = rect.h; + + SDL_FillRect(screen, &offset, SDL_MapRGB(screen->format, r, g, b)); +} diff --git a/src/libUnuk/System/Rect.h b/src/libUnuk/System/Rect.h new file mode 100644 index 0000000..645f3d7 --- /dev/null +++ b/src/libUnuk/System/Rect.h @@ -0,0 +1,35 @@ +#pragma once +#include +#include "../../Unuk/Globals.h" +#include "../Sprite/ApplySurface.h" +using namespace std; + +class Rect { +public: + Rect(void); + ~Rect(void); + + int GetWidth(void) { return rect.w; } + int GetHeight(void) { return rect.h; } + int GetX(void) { return rect.x; } + int GetY(void) { return rect.y; } + + void SetXY(int xArg, int yArg); + void SetWidthHeight(int wArg, int hArg); + + void SetRGB(Uint8 rArg, Uint8 gArg, Uint8 bArg); + void SetRGB(SDL_Color); + + void Draw(void); + void Draw(int xArg, int yArg); + void DrawLiteral(void); + void DrawLiteral(int xArg, int yArg); + +protected: + Uint8 r; + Uint8 g; + Uint8 b; + +private: + SDL_Rect rect; +}; diff --git a/src/libUnuk/System/Timer.cpp b/src/libUnuk/System/Timer.cpp new file mode 100644 index 0000000..6b4f383 --- /dev/null +++ b/src/libUnuk/System/Timer.cpp @@ -0,0 +1,54 @@ +#include "Timer.h" + +Timer::Timer(void) { + _startTicks = 0; + _pausedTicks = 0; + _paused = false; + _started = false; +} + +Timer::~Timer(void) { +} + +void Timer::Start(void) { + _paused = false; + _started = true; + _startTicks = SDL_GetTicks(); +} + +void Timer::Stop(void) { + _paused = false; + _started = true; +} + +void Timer::Pause(void) { + assert(_paused == false); + _paused = true; + + _pausedTicks = SDL_GetTicks() - _startTicks; +} + +void Timer::Unpause(void) { + assert(_paused == true); + _paused = false; + + _startTicks = SDL_GetTicks() - _pausedTicks; + + _pausedTicks = 0; +} + +int Timer::GetTicks(void) { + if(_paused == true) + return _pausedTicks; + else if(_started == true) + return SDL_GetTicks() - _startTicks; + else + return 0; +} + +string Timer::GetTicksStr(void) { + stringstream str; + str << GetTicks() << "ms"; + + return str.str(); +} diff --git a/src/libUnuk/System/Timer.h b/src/libUnuk/System/Timer.h new file mode 100644 index 0000000..d0d51b1 --- /dev/null +++ b/src/libUnuk/System/Timer.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include +#include +#include +using namespace std; + +class Timer { +public: + Timer(void); + ~Timer(void); + + void Pause(void); + void Unpause(void); + void Start(void); + void Stop(void); + + bool IsPaused(void) { return _paused; } + bool IsStarted(void) { return _started; } + + int GetTicks(void); + + string GetTicksStr(void); + +private: + bool _paused; + bool _started; + + int _startTicks; + int _pausedTicks; +}; diff --git a/src/libUnuk/Ui/Button.cpp b/src/libUnuk/Ui/Button.cpp new file mode 100644 index 0000000..2c0bc20 --- /dev/null +++ b/src/libUnuk/Ui/Button.cpp @@ -0,0 +1,120 @@ +#include "Button.h" + +Button::Button(void) { + _highlighted = false; + _mouseOver = false; +} + +Button::~Button(void) { +} + +void Button::SetOutRGB(Uint8 r, Uint8 g, Uint8 b) { + _button.SetRGB(r, g, b); + _mouseOutColour.r = r; + _mouseOutColour.g = g; + _mouseOutColour.b = b; +} + +void Button::SetOutRGB(SDL_Color colour) { + _button.SetRGB(colour); + _mouseOutColour = colour; +} + +void Button::SetOverRGB(Uint8 r, Uint8 g, Uint8 b) { + _mouseOverColour.r = r; + _mouseOverColour.g = g; + _mouseOverColour.b = b; +} + +void Button::SetOverRGB(SDL_Color colour) { + _mouseOverColour = colour; +} + +void Button::SetHighlightRGB(Uint8 r, Uint8 g, Uint8 b) { + _highlightColour.r = r; + _highlightColour.g = g; + _highlightColour.b = b; +} + +void Button::SetHighlightRGB(SDL_Color colour) { + _highlightColour = colour; +} + +void Button::SetXY(int xArg, int yArg) { + x = xArg; + y = yArg; + _button.SetXY(x, y); + + _text.SetXY(x + 10, y + 10); +} + +void Button::SetTextRGB(Uint8 r, Uint8 g, Uint8 b) { + _textColour.r = r; + _textColour.g = g; + _textColour.b = b; + + _text.SetTextBlended(_text.GetText(), small, _textColour); +} + +void Button::SetTextRGB(SDL_Color colour) { + _textColour = colour; + _text.SetTextBlended(_text.GetText(), small, colour); +} + +void Button::SetText(string textArg) { + _text.SetTextBlended(textArg, small, _textColour); + + w = _text.GetWidth(); + h = _text.GetHeight(); + _button.SetWidthHeight(w + 20, h + 15); +} + +bool Button::CheckMouseOver(void) { + if(event.motion.x > _button.GetX() && event.motion.x < _button.GetX() + _button.GetWidth()) { + if(event.motion.y > _button.GetY() && event.motion.y < _button.GetY() + _button.GetHeight()) { + if(!_highlighted) { + _button.SetRGB(_mouseOverColour.r, _mouseOverColour.g, _mouseOverColour.b); + } + return true; + } + } + if(!_highlighted) { + _button.SetRGB(_mouseOutColour); + } + return false; +} + +void Button::SetHighlighted(bool highlighted) { + if(_highlighted != highlighted) { + if(highlighted) { + _button.SetRGB(_highlightColour); + } else { + if(!_mouseOver) { + _button.SetRGB(_mouseOverColour); + } else { + _button.SetRGB(_textColour); + } + } + } + _highlighted = highlighted; +} + +void Button::Render(void) { + _button.Draw(); + _text.Render(); +} + +void Button::Render(int xArg, int yArg) { + _button.Draw(xArg, yArg); + _text.Render(xArg, yArg); +} + +void Button::RenderLiteral(void) { + _button.DrawLiteral(); + _text.RenderLiteral(); +} + +void Button::RenderLiteral(int xArg, int yArg) { + _button.DrawLiteral(xArg, yArg); + _text.RenderLiteral(xArg, yArg); +} diff --git a/src/libUnuk/Ui/Button.h b/src/libUnuk/Ui/Button.h new file mode 100644 index 0000000..56ba241 --- /dev/null +++ b/src/libUnuk/Ui/Button.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include + +#include "../../Unuk/Globals.h" +#include "../System/Input.h" +#include "../Ui/Text.h" +#include "../System/Rect.h" + +class Button { +public: + Button(void); + ~Button(void); + + void SetOutRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOutRGB(SDL_Color); + void SetOverRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOverRGB(SDL_Color); + + void SetTextRGB(Uint8 r, Uint8 g, Uint8 b); + void SetTextRGB(SDL_Color); + void SetText(string textArg); + + void SetHighlightRGB(Uint8 r, Uint8 g, Uint8 b); + void SetHighlightRGB(SDL_Color); + + void SetHighlighted(bool highlighted); + + void SetXY(int xArg, int yArg); + + int GetX(void) const { return x; } + int GetY(void) const { return y; } + int GetWidth(void) const { return w; } + int GetHeight(void) const { return h; } + + bool GetHighlighted(void) { return _highlighted; } + + bool CheckMouseOver(void); + + void Render(void); + void Render(int xArg, int yArg); + void RenderLiteral(void); + void RenderLiteral(int xArg, int yArg); + +private: + SDL_Color _mouseOutColour; + SDL_Color _mouseOverColour; + SDL_Color _textColour; + SDL_Color _highlightColour; + + int x; + int y; + int w; + int h; + + Rect _button; + Text _text; + + bool _highlighted; + + bool _mouseOver; +}; diff --git a/src/libUnuk/Ui/ButtonGroup.cpp b/src/libUnuk/Ui/ButtonGroup.cpp new file mode 100644 index 0000000..4bdfd02 --- /dev/null +++ b/src/libUnuk/Ui/ButtonGroup.cpp @@ -0,0 +1,101 @@ +#include "ButtonGroup.h" + +ButtonGroup::ButtonGroup(void) { + _selectedButton = -1; +} + +ButtonGroup::~ButtonGroup(void) { + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + Button* btn = (*i); + delete btn; + } +} + +void ButtonGroup::CheckMouseOverDummy(void) { + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + (*i)->CheckMouseOver(); + } +} + +int ButtonGroup::CheckMouseOver(void) { + int buttonIndex = 0; + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + if((*i)->CheckMouseOver()) { + return buttonIndex; + } + buttonIndex++; + } + return -1; +} + +void ButtonGroup::RenderLiteral(void) { + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + (*i)->RenderLiteral(); + } +} + +void ButtonGroup::AddButton(Button* button) { + _buttons.push_back(button); +} + +void ButtonGroup::RemoveButton(int index) { + int buttonsIndex = 0; + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + if(buttonsIndex == index) { + _buttons.erase(i); + break; + } + buttonsIndex++; + } +} + +void ButtonGroup::SelectNext(void) { + if(_selectedButton == -1) { + HighlightNewSelection(0); + } else { + HighlightNewSelection(_selectedButton + 1); + } +} + +void ButtonGroup::SelectPrevious(void) { + if(_selectedButton == -1) { + HighlightNewSelection(0); + } else { + HighlightNewSelection(_selectedButton - 1); + } +} + +void ButtonGroup::SetSelectedButton(int button) { + HighlightNewSelection(button); +} + +void ButtonGroup::HighlightNewSelection(int newButton) { + if(_selectedButton != -1) { + // Turn off highlight for currently highlighted button + GetButton(_selectedButton)->SetHighlighted(false); + } + + _selectedButton = newButton; + + // If < 0 then up was pressed when first index was selected + // If >= _buttons.size() then down was pressed when last index was selected + if(_selectedButton < 0) { + _selectedButton = _buttons.size() - 1; + } else if(_selectedButton >= (int)_buttons.size()) { + _selectedButton = 0; + } + + // Highlight new selection + GetButton(_selectedButton)->SetHighlighted(true); +} + +Button* ButtonGroup::GetButton(int index) { + int buttonsIndex = 0; + for(std::list::iterator i = _buttons.begin(); i != _buttons.end(); ++i) { + if(buttonsIndex == index) { + return (*i); + } + buttonsIndex++; + } + return NULL; +} diff --git a/src/libUnuk/Ui/ButtonGroup.h b/src/libUnuk/Ui/ButtonGroup.h new file mode 100644 index 0000000..05a4a76 --- /dev/null +++ b/src/libUnuk/Ui/ButtonGroup.h @@ -0,0 +1,33 @@ +#pragma once +#include + +#include "../Ui/Button.h" +#include "../Engine/MemClass.h" + +class ButtonGroup { +public: + ButtonGroup(void); + ~ButtonGroup(void); + + void CheckMouseOverDummy(void); + int CheckMouseOver(void); + + void RenderLiteral(void); + + void AddButton(Button* button); + void RemoveButton(int index); + + void SelectNext(void); + void SelectPrevious(void); + + int GetSelectedButton(void) { return _selectedButton; } + void SetSelectedButton(int button); + + Button* GetButton(int index); + +private: + void HighlightNewSelection(int newButton); + + std::list _buttons; + int _selectedButton; +}; diff --git a/src/libUnuk/Ui/ButtonToggle.cpp b/src/libUnuk/Ui/ButtonToggle.cpp new file mode 100644 index 0000000..3451aca --- /dev/null +++ b/src/libUnuk/Ui/ButtonToggle.cpp @@ -0,0 +1,35 @@ +#include "ButtonToggle.h" + +ButtonToggle::ButtonToggle(void) { + +} + +ButtonToggle::~ButtonToggle(void) { + +} + +void ButtonToggle::SetOverRGB(Uint8 r, Uint8 g, Uint8 b) { + Button::SetOverRGB(r, g, b); +} + +void ButtonToggle::SetOverRGB(SDL_Color colour) { + Button::SetOverRGB(colour); +} + +void ButtonToggle::SetOffRGB(Uint8 r, Uint8 g, Uint8 b) { + ButtonToggle::SetOutRGB(r, g, b); +} + +void ButtonToggle::SetOffRGB(SDL_Color colour) { + Button::SetOutRGB(colour); +} + +void ButtonToggle::SetOnRGB(Uint8 r, Uint8 g, Uint8 b) { + _onColour.r = r; + _onColour.g = g; + _onColour.b = b; +} + +void ButtonToggle::SetOnRGB(SDL_Color colour) { + _onColour = colour; +} diff --git a/src/libUnuk/Ui/ButtonToggle.h b/src/libUnuk/Ui/ButtonToggle.h new file mode 100644 index 0000000..83c5362 --- /dev/null +++ b/src/libUnuk/Ui/ButtonToggle.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include + +#include "Button.h" + +class ButtonToggle : protected Button { +public: + ButtonToggle(void); + ~ButtonToggle(void); + + void SetOverRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOverRGB(SDL_Color); + void SetOffRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOffRGB(SDL_Color); + void SetOnRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOnRGB(SDL_Color); + + void SetTextRGB(Uint8 r, Uint8 g, Uint8 b); + void SetOnText(string); + void SetOffText(string); + + void SetState(bool); + bool GetState(void) { return _state; } + + bool CheckMouseOver(void); + + void Render(void); + +private: + bool _state; + + SDL_Colour _onColour; + + Text _onText; +}; diff --git a/src/libUnuk/Ui/Font.cpp b/src/libUnuk/Ui/Font.cpp new file mode 100644 index 0000000..bee12a5 --- /dev/null +++ b/src/libUnuk/Ui/Font.cpp @@ -0,0 +1,11 @@ +#include "Font.h" + +TTF_Font* Font(const char* filename, const int size) { + ifstream font(filename); + + if(font.is_open() == false) { + Debug::logger->message("Font: %s cannot be found."); + exit(-1); + } + return TTF_OpenFont(filename, size); +} diff --git a/src/libUnuk/Ui/Font.h b/src/libUnuk/Ui/Font.h new file mode 100644 index 0000000..fc2617b --- /dev/null +++ b/src/libUnuk/Ui/Font.h @@ -0,0 +1,8 @@ +#pragma once +#include +#include +#include +#include "../System/Debug.h" +using namespace std; + +TTF_Font* Font(const char* filename, const int size); diff --git a/src/libUnuk/Ui/IngameMenu.cpp b/src/libUnuk/Ui/IngameMenu.cpp new file mode 100644 index 0000000..c67b91a --- /dev/null +++ b/src/libUnuk/Ui/IngameMenu.cpp @@ -0,0 +1,99 @@ +#include "IngameMenu.h" + +IngameMenu::IngameMenu(void) { + _active = false; + + Button* btnResume = new Button(); + btnResume->SetOutRGB(200, 200, 200); + btnResume->SetOverRGB(255, 255, 255); + btnResume->SetTextRGB(0, 0, 0); + btnResume->SetText("Resume Game"); + btnResume->SetHighlightRGB(255, 128, 0); + btnResume->SetHighlighted(false); + btnResume->SetXY(SCREEN_WIDTH / 2 - btnResume->GetWidth() / 2, 50); + + Button* btnSaveGame = new Button(); + btnSaveGame->SetOutRGB(200, 200, 200); + btnSaveGame->SetOverRGB(255, 255, 255); + btnSaveGame->SetTextRGB(0, 0, 0); + btnSaveGame->SetText("SaveGame"); + btnSaveGame->SetHighlightRGB(255, 128, 0); + btnSaveGame->SetHighlighted(false); + btnSaveGame->SetXY(SCREEN_WIDTH / 2 - btnSaveGame->GetWidth() / 2, 100); + + Button* btnLoadGame = new Button(); + btnLoadGame->SetOutRGB(200, 200, 200); + btnLoadGame->SetOverRGB(255, 255, 255); + btnLoadGame->SetTextRGB(0, 0, 0); + btnLoadGame->SetText("LoadGame"); + btnLoadGame->SetHighlightRGB(255, 128, 0); + btnLoadGame->SetHighlighted(false); + btnLoadGame->SetXY(SCREEN_WIDTH / 2 - btnLoadGame->GetWidth() / 2, 150); + + Button* btnOptions = new Button(); + btnOptions->SetOutRGB(200, 200, 200); + btnOptions->SetOverRGB(255, 255, 255); + btnOptions->SetTextRGB(0, 0, 0); + btnOptions->SetText("Options"); + btnOptions->SetHighlightRGB(255, 128, 0); + btnOptions->SetHighlighted(false); + btnOptions->SetXY(SCREEN_WIDTH / 2 - btnOptions->GetWidth() / 2, 200); + + Button* btnExitToMenu = new Button(); + btnExitToMenu->SetOutRGB(200, 200, 200); + btnExitToMenu->SetOverRGB(255, 255, 255); + btnExitToMenu->SetTextRGB(0, 0, 0); + btnExitToMenu->SetText("Exit To Main Menu"); + btnExitToMenu->SetHighlightRGB(255, 128, 0); + btnExitToMenu->SetHighlighted(false); + btnExitToMenu->SetXY(SCREEN_WIDTH / 2 - btnExitToMenu->GetWidth() / 2, 250); + + _buttons.AddButton(btnResume); + _buttons.AddButton(btnSaveGame); + _buttons.AddButton(btnLoadGame); + _buttons.AddButton(btnOptions); + _buttons.AddButton(btnExitToMenu); +} + +IngameMenu::~IngameMenu(void) { +} + +ingameMenuNavVal_t IngameMenu::HandleInput(void) { + while(SDL_PollEvent(&event)) { + _buttons.CheckMouseOverDummy(); + + if(event.key.type == SDL_KEYDOWN) { + if(event.key.keysym.sym == SDLK_ESCAPE) { + return ingameMenuResume; + } else if(event.key.keysym.sym == SDLK_DOWN) { + _buttons.SelectNext(); + } else if(event.key.keysym.sym == SDLK_UP) { + _buttons.SelectPrevious(); + } else if(event.key.keysym.sym == SDLK_RETURN) { + switch(_buttons.GetSelectedButton()) { + case 0: return ingameMenuResume; + case 1: return ingameMenuSaveGame; + case 2: return ingameMenuLoadGame; + case 3: return ingameMenuOptions; + case 4: return ingameMenuMainMenu; + } + } + } + else if(event.type == SDL_MOUSEBUTTONUP) { + if(event.button.button == SDL_BUTTON_LEFT) { + switch(_buttons.CheckMouseOver()) { + case 0: return ingameMenuResume; + case 1: return ingameMenuSaveGame; + case 2: return ingameMenuLoadGame; + case 3: return ingameMenuOptions; + case 4: return ingameMenuMainMenu; + } + } + } + } + return ingameMenuNothing; +} + +void IngameMenu::Render(void) { + _buttons.RenderLiteral(); +} \ No newline at end of file diff --git a/src/libUnuk/Ui/IngameMenu.h b/src/libUnuk/Ui/IngameMenu.h new file mode 100644 index 0000000..ef2b84e --- /dev/null +++ b/src/libUnuk/Ui/IngameMenu.h @@ -0,0 +1,32 @@ +#pragma once + +#include "../../Unuk/Globals.h" +#include "../../Unuk/Constants.h" +#include "ButtonGroup.h" +#include "ButtonToggle.h" + +enum ingameMenuNavVal_t { + ingameMenuNothing, + ingameMenuResume, + ingameMenuSaveGame, + ingameMenuLoadGame, + ingameMenuOptions, + ingameMenuMainMenu +}; + +class IngameMenu { +public: + IngameMenu(void); + ~IngameMenu(void); + + ingameMenuNavVal_t HandleInput(void); + void Render(void); + + void SetStatus(bool arg) { _active = arg; } + bool GetStatus(void) { return _active; } + +private: + bool _active; + + ButtonGroup _buttons; +}; diff --git a/src/libUnuk/Ui/MainMenu.cpp b/src/libUnuk/Ui/MainMenu.cpp new file mode 100644 index 0000000..3b75bcf --- /dev/null +++ b/src/libUnuk/Ui/MainMenu.cpp @@ -0,0 +1,171 @@ +#include "MainMenu.h" + +MainMenu::MainMenu(void) { + Button* btnNewGame = new Button(); + btnNewGame->SetOutRGB(200, 200, 200); + btnNewGame->SetOverRGB(255, 255, 255); + btnNewGame->SetTextRGB(0, 0, 0); + btnNewGame->SetText("New Game"); + btnNewGame->SetXY(100, 150); + btnNewGame->SetHighlightRGB(255, 128, 0); + btnNewGame->SetHighlighted(false); + + Button* btnLoadGame = new Button(); + btnLoadGame->SetOutRGB(200, 200, 200); + btnLoadGame->SetOverRGB(255, 255, 255); + btnLoadGame->SetTextRGB(0, 0, 0); + btnLoadGame->SetText("Load Game"); + btnLoadGame->SetHighlightRGB(255, 128, 0); + btnLoadGame->SetHighlighted(false); + btnLoadGame->SetXY(100, 200); + + Button* btnOptions = new Button(); + btnOptions->SetOutRGB(200, 200, 200); + btnOptions->SetOverRGB(255, 255, 255); + btnOptions->SetTextRGB(0, 0, 0); + btnOptions->SetText("Options"); + btnOptions->SetHighlightRGB(255, 128, 0); + btnOptions->SetHighlighted(false); + btnOptions->SetXY(100, 250); + + Button* btnExit = new Button(); + btnExit->SetOutRGB(200, 200, 200); + btnExit->SetOverRGB(255, 255, 255); + btnExit->SetTextRGB(0, 0, 0); + btnExit->SetText("Exit"); + btnExit->SetHighlightRGB(255, 128, 0); + btnExit->SetHighlighted(false); + btnExit->SetXY(100, 300); + + grpMain.AddButton(btnNewGame); + grpMain.AddButton(btnLoadGame); + grpMain.AddButton(btnOptions); + grpMain.AddButton(btnExit); + + btnNewGameActive = false; + + lblNewGame.SetXY(275, 160); + lblNewGame.SetTextBlended("This will delete your current game, are you sure?", vsmall, 0, 0, 0); + + rectNewGame.SetRGB(200, 200, 200); + rectNewGame.SetXY(250, 150); + rectNewGame.SetWidthHeight(lblNewGame.GetWidth() + 50, 90); + + lblMenu.SetXY(100, 75); + lblMenu.SetTextBlended("Unuk", vlarge, 0, 0, 0); + + Button* btnNewGameYes = new Button(); + btnNewGameYes->SetOutRGB(20, 150, 20); + btnNewGameYes->SetOverRGB(20, 255, 20); + btnNewGameYes->SetTextRGB(0, 0, 0); + btnNewGameYes->SetText("Yes"); + btnNewGameYes->SetHighlightRGB(255, 128, 0); + btnNewGameYes->SetHighlighted(false); + btnNewGameYes->SetXY(rectNewGame.GetX() + rectNewGame.GetWidth() / 2 - 40 - btnNewGameYes->GetWidth(), 190); + + Button* btnNewGameNo = new Button(); + btnNewGameNo->SetOutRGB(150, 20, 20); + btnNewGameNo->SetOverRGB(255, 20, 20); + btnNewGameNo->SetTextRGB(0, 0, 0); + btnNewGameNo->SetText("No"); + btnNewGameNo->SetHighlightRGB(255, 128, 0); + btnNewGameNo->SetHighlighted(false); + btnNewGameNo->SetXY(rectNewGame.GetX() + rectNewGame.GetWidth() / 2 + 40 - btnNewGameNo->GetWidth(), 190); + + grpNewGame.AddButton(btnNewGameYes); + grpNewGame.AddButton(btnNewGameNo); + + //m_background.Load("MainMenu"); + + camera.x = 0; + camera.y = 0; +} + +MainMenu::~MainMenu(void) { + +} + +mainMenuNavVal_t MainMenu::Run(void) { + FPS fpsLimiter(20); + + bool running = true; + while(running) { + Render(); + SDL_Flip(screen); + + while(SDL_PollEvent(&event)) { + grpMain.CheckMouseOverDummy(); + + if(btnNewGameActive) { + grpNewGame.CheckMouseOverDummy(); + } + + if(event.key.type == SDL_KEYDOWN) { + if(event.key.keysym.sym == SDLK_DOWN) { + if(btnNewGameActive) { + grpNewGame.SelectNext(); + } else { + grpMain.SelectNext(); + } + } else if(event.key.keysym.sym == SDLK_UP) { + if(btnNewGameActive) { + grpNewGame.SelectPrevious(); + } else { + grpMain.SelectPrevious(); + } + } else if(event.key.keysym.sym == SDLK_RETURN) { + if(btnNewGameActive) { + switch(grpNewGame.GetSelectedButton()) { + case 0: return mainMenuNewGame; break; + case 1: btnNewGameActive = false; break; + } + } else { + switch(grpMain.GetSelectedButton()) { + case 0: btnNewGameActive = !btnNewGameActive; break; + case 1: return mainMenuLoadGame; + case 2: return mainMenuOptions; + case 3: return mainMenuExitGame; + } + } + } + } + else if(event.type == SDL_MOUSEBUTTONUP) { + if(event.button.button == SDL_BUTTON_LEFT) { + switch(grpMain.CheckMouseOver()) { + case 0: btnNewGameActive = !btnNewGameActive; break; + case 1: return mainMenuLoadGame; + case 2: return mainMenuOptions; + case 3: return mainMenuExitGame; + } + + if(btnNewGameActive) { + switch(grpNewGame.CheckMouseOver()) { + case 0: return mainMenuNewGame; break; + case 1: btnNewGameActive = false; break; + } + } + } + } + else if(event.type == SDL_QUIT) { + return mainMenuExitGame; + } + } + + fpsLimiter.LimitFPS(); + } + return mainMenuExitGame; +} + +void MainMenu::Render(void) { + //m_background.Render(); + + lblMenu.Render(); + + grpMain.RenderLiteral(); + + if(btnNewGameActive) { + rectNewGame.Draw(); + lblNewGame.Render(); + grpNewGame.RenderLiteral(); + } +} diff --git a/src/libUnuk/Ui/MainMenu.h b/src/libUnuk/Ui/MainMenu.h new file mode 100644 index 0000000..fc79dd0 --- /dev/null +++ b/src/libUnuk/Ui/MainMenu.h @@ -0,0 +1,37 @@ +#pragma once + +#include "../../Unuk/Constants.h" +#include "../System/FPS.h" +#include "../Ui/ButtonGroup.h" +#include "../Map/Map.h" +#include "../System/Rect.h" +#include "../Ui/Text.h" + +enum mainMenuNavVal_t { + mainMenuNewGame, + mainMenuLoadGame, + mainMenuOptions, + mainMenuExitGame +}; + +class MainMenu { +public: + MainMenu(void); + ~MainMenu(void); + + mainMenuNavVal_t Run(void); + +private: + void Render(void); + + Map _background; + + Text lblMenu; + + ButtonGroup grpMain; + + bool btnNewGameActive; + Rect rectNewGame; + Text lblNewGame; + ButtonGroup grpNewGame; +}; diff --git a/src/libUnuk/Ui/Text.cpp b/src/libUnuk/Ui/Text.cpp new file mode 100644 index 0000000..f76861b --- /dev/null +++ b/src/libUnuk/Ui/Text.cpp @@ -0,0 +1,269 @@ +#include "Text.h" + +TTF_Font* Text::vSmallFont = NULL; +TTF_Font* Text::smallFont = NULL; +TTF_Font* Text::mediumFont = NULL; +TTF_Font* Text::largeFont = NULL; +TTF_Font* Text::vLargeFont = NULL; + +const static int lineSpacing = 3; + +Text::Text(void) { + x = 0; + y = 0; + w = 0; + h = 0; + lineWidth=50; +} + +Text::~Text(void) { + if(!_lines.empty()) { + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_FreeSurface(*it); + } + _lines.clear(); + } +} + +void Text::LoadFonts(void) { + // Load the fonts if they are not already in memory. + assert(vSmallFont == NULL); + assert(smallFont == NULL); + assert(mediumFont == NULL); + assert(largeFont == NULL); + assert(vLargeFont == NULL); + + vSmallFont = Font("../Data/Media/Fonts/Enigma_2.ttf", 16); + smallFont = Font("../Data/Media/Fonts/Enigma_2.ttf", 23); + mediumFont = Font("../Data/Media/Fonts/Enigma_2.ttf", 27); + largeFont = Font("../Data/Media/Fonts/Enigma_2.ttf", 32); + vLargeFont = Font("../Data/Media/Fonts/Enigma_2.ttf", 48); +} + +void Text::FreeFonts(void) { + // If the fonts are loaded, then free them. + assert(vSmallFont != NULL); + assert(smallFont != NULL); + assert(mediumFont != NULL); + assert(largeFont != NULL); + assert(vLargeFont != NULL); + + TTF_CloseFont(vSmallFont); + TTF_CloseFont(smallFont); + TTF_CloseFont(mediumFont); + TTF_CloseFont(largeFont); + TTF_CloseFont(vLargeFont); +} + +void Text::SetXY(int xArg, int yArg) { + x = xArg; + y = yArg; +} + +int Text::SetTextBlended(string textArg, textSizes_t size, SDL_Color colour,bool wordWrap) { + _textContents = textArg; + + if(!_lines.empty()) { + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_FreeSurface(*it); + } + _lines.clear(); + } + + TTF_Font* font = NULL; + if(size == vsmall) { + font = vSmallFont; + } else if(size == small) { + font = smallFont; + } else if(size == medium) { + font = mediumFont; + } else if(size == large) { + font = largeFont; + } else { + font = vLargeFont; + } + + std::string finalTextContents = textArg; + + if(wordWrap) { + finalTextContents = DoWordWrap(font, finalTextContents); + } + + std::list lines; + std::string line; + for(int i = 0; i < (int)finalTextContents.size(); i++) { + char c = finalTextContents.at(i); + if(c == '\n') { + lines.push_back(line); + line.clear(); + } else { + line += c; + } + } + if (!line.empty()) { + lines.push_back(line); + } + + for(std::list::iterator it = lines.begin(); it != lines.end(); ++it) { + SDL_Surface* lineSurf = TTF_RenderText_Blended(font, it->c_str(), colour); + + int linePixelWidth; + int linePixelHeight; + TTF_SizeText(font, it->c_str(), &linePixelWidth, &linePixelHeight); + + if(linePixelWidth > w) { + w = linePixelWidth; + } + + h += linePixelHeight + lineSpacing; + + _lines.push_back(lineSurf); + } + + return 1; +} + +int Text::SetTextBlended(string textArg, textSizes_t size, Uint8 r, Uint8 g, Uint8 b, bool wordWrap) { + SDL_Color f = { r, g, b }; + return SetTextBlended(textArg, size, f, wordWrap); +} + +int Text::SetTextShaded(string textArg, textSizes_t size, SDL_Color colour, SDL_Color bgColour, bool wordWrap) { + _textContents = textArg; + + if(!_lines.empty()) { + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_FreeSurface(*it); + } + _lines.clear(); + } + + TTF_Font* font = NULL; + if(size == vsmall) { + font = vSmallFont; + } else if(size == small) { + font = smallFont; + } else if(size == medium) { + font = mediumFont; + } else if(size == large) { + font = largeFont; + } else { + font = vLargeFont; + } + + std::string finalTextContents = textArg; + + if(wordWrap) { + finalTextContents = DoWordWrap(font, finalTextContents); + } + + std::list lines; + std::string line; + for(int i = 0; i < (int)finalTextContents.size(); i++) { + char c = finalTextContents.at(i); + if(c == '\n') { + lines.push_back(line); + line.clear(); + } else { + line += c; + } + } + if (!line.empty()) { + lines.push_back(line); + } + + for(std::list::iterator it = lines.begin(); it != lines.end(); ++it) { + SDL_Surface* lineSurf = TTF_RenderText_Shaded(font, it->c_str(), colour, bgColour); + + int linePixelWidth; + int linePixelHeight; + TTF_SizeText(font, it->c_str(), &linePixelWidth, &linePixelHeight); + + if(linePixelWidth > w) { + w = linePixelWidth; + } + + h += linePixelHeight + lineSpacing; + + _lines.push_back(lineSurf); + } + + return 1; +} + +int Text::SetTextShaded(string textArg, textSizes_t size, Uint8 rF, Uint8 gF, Uint8 bF, Uint8 rB, Uint8 gB, Uint8 bB, bool wordWrap) { + SDL_Color f = { rF, gF, bF }; + SDL_Color b = { rB, gB, bB }; + return SetTextShaded(textArg, size, f, b, wordWrap); +} + +void Text::Render(void) { + int yOffset = 0; + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_Surface* lineSurf = *it; + ApplySurface(x, y + yOffset, lineSurf, screen); + yOffset += lineSurf->h + lineSpacing; + } +} + +void Text::Render(int xArg, int yArg) { + int yOffset = 0; + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_Surface* lineSurf = *it; + ApplySurface(x + xArg, y + yArg + yOffset, lineSurf, screen); + yOffset += lineSurf->h + lineSpacing; + } +} + +void Text::RenderLiteral(void) { + int yOffset = 0; + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_Surface* lineSurf = *it; + ApplySurfaceLiteral(x, y + yOffset, lineSurf, screen); + yOffset += lineSurf->h + lineSpacing; + } +} + +void Text::RenderLiteral(int xArg, int yArg) { + int yOffset = 0; + for(std::list::iterator it = _lines.begin(); it != _lines.end(); ++it) { + SDL_Surface* lineSurf = *it; + ApplySurfaceLiteral(x + xArg, y + yArg + yOffset, lineSurf, screen); + yOffset += lineSurf->h + lineSpacing; + } +} + +std::string Text::DoWordWrap(TTF_Font* fontArg, const std::string& textArg) { + int leftSpace = lineWidth; + + char* tokenizedText = strdup(textArg.c_str()); + char* tokenizedTextOrigin = tokenizedText; + + std::string wrappedText(textArg); + int offsetInWrappedText = 0; + + int spaceWidth; + TTF_SizeText(fontArg, " ", &spaceWidth, NULL); + + char* word = strtok(tokenizedText, " "); + + while(word) { + int wordWidth; + TTF_SizeText(fontArg, word, &wordWidth, NULL); + + if ((wordWidth + spaceWidth) > leftSpace) { + wrappedText.insert((word - tokenizedTextOrigin) + offsetInWrappedText, "\n"); + offsetInWrappedText++; + + leftSpace = lineWidth - wordWidth; + } else { + leftSpace -= wordWidth + spaceWidth; + } + + word = strtok(NULL, " "); + } + + // delete[] tokenizedText; + + return wrappedText; +} diff --git a/src/libUnuk/Ui/Text.h b/src/libUnuk/Ui/Text.h new file mode 100644 index 0000000..580e1f0 --- /dev/null +++ b/src/libUnuk/Ui/Text.h @@ -0,0 +1,62 @@ +#pragma once +#include +#include +#include +#include + +#include "../../Unuk/Globals.h" +#include "../Sprite/ApplySurface.h" +#include "../Ui/Font.h" +#include "../System/Debug.h" +using namespace std; + +enum textSizes_t { vsmall, small, medium, large, vlarge }; + +class Text { +public: + Text(void); + ~Text(void); + + static void LoadFonts(void); + static void FreeFonts(void); + + int GetWidth(void) { return w; } + int GetHeight(void) { return h; } + int GetX(void) { return x; } + int GetY(void) { return y; } + + SDL_Color GetColour(void) { return _textColour; } + + void SetXY(int xArg, int yArg); + + int GetLineWidth() { return lineWidth; } + void SetLineWidth(int lineWidthArg) { lineWidth = lineWidthArg; } + + int SetTextBlended(string textArg, textSizes_t size, SDL_Color, bool wordWrap=false); + int SetTextBlended(string textArg, textSizes_t size, Uint8 r, Uint8 g, Uint8 b, bool wordWrap=false); + int SetTextShaded(string textArg, textSizes_t size, SDL_Color, SDL_Color, bool wordWrap=false); + int SetTextShaded(string textArg, textSizes_t size, Uint8 rF, Uint8 gF, Uint8 bF, Uint8 rB, Uint8 gB, Uint8 bB, bool wordWrap=false); + + string GetText(void) { return _textContents; } + + void Render(void); + void Render(int xArg, int yArg); + void RenderLiteral(void); + void RenderLiteral(int xArg, int yArg); + +private: + int x, y, w, h; + int lineWidth; + + string _textContents; + SDL_Color _textColour; + std::list _lines; + + std::string DoWordWrap(TTF_Font* fontArg, const std::string& textArg); + + static TTF_Font* vSmallFont; + static TTF_Font* smallFont; + static TTF_Font* mediumFont; + static TTF_Font* largeFont; + static TTF_Font* vLargeFont; +};