commit 45f0b31d570d5b1b69d5cd170b5fdb9834e8f41b Author: Felix Ableitner Date: Sun Sep 9 22:50:15 2012 +0200 Initial commit after git corruption, old repo deleted. Working: Rendering Resources Physics Player movement with mouse Shooting with mouse Tiles diff --git a/source/Game.cpp b/source/Game.cpp new file mode 100644 index 0000000..9b0e647 --- /dev/null +++ b/source/Game.cpp @@ -0,0 +1,219 @@ +/* + * Game.cpp + * + * Created on: 05.07.2012 + * Author: Felix + */ + +#include "Game.h" + +#include "abstract/Actor.h" +#include "sprite/Cover.h" +#include "util/Loader.h" +#include "util/ResourceManager.h" +#include "util/String.h" +#include "util/Log.h" + +/// Goal amount of frames per second. +const int Game::FPS_GOAL = 60; + +/// Milliseconds per tick at FPS_GOAL. +const float Game::TICKS_GOAL = 1000 / Game::FPS_GOAL; + +/** + * Initializes game, including window and objects (sprites). + */ +Game::Game(const Vector2i& resolution) : + mWorld(b2Vec2(0, 0)), + mWindow(sf::VideoMode(resolution.x, resolution.y, 32), "Roguelike Shooter", + sf::Style::Close | sf::Style::Titlebar), + mView(Vector2f(0, 0), Vector2f(resolution)), + //mFps("test"), + mTileManager(mWorld), + mPlayer(mWorld, mCollection), + mElapsed(0), + mQuit(false), + mPaused(false) { + mWindow.setFramerateLimit(FPS_GOAL); + mWindow.setKeyRepeatEnabled(true); + for (int i = 0; i < 500; i += 50) { + mCollection.insert(std::shared_ptr(new Cover(Vector2f(i, i), Vector2i(20, 20), + mWorld)), Collection::LEVEL_STATIC); + } + mTileManager.generate(); + mWorld.SetContactListener(this); +} + +/** + * Closes window. + */ +Game::~Game() { + mWindow.close(); +} + +/** + * Runs the game loop. + */ +void +Game::loop() { + sf::Uint32 left = 0; + while (!mQuit) { + + input(); + + for (; !mPaused && (left >= TICKS_GOAL); left -= TICKS_GOAL) { + Actor::think(TICKS_GOAL); + mWorld.Step(1.0f / FPS_GOAL, 8, 3); + + mCollection.checkDelete(); + } + + //mFps.setString(getFps()); + + tick(); + left += mElapsed; + + render(); + } +} + +/** + * Saves ticks since last call. + */ +void +Game::tick() { + mElapsed = mClock.restart().asMilliseconds(); + if (mPaused) { + mElapsed = 0; + } +} + +/** + * Handles general game input. + */ +void +Game::input() { + sf::Event event; + while (mWindow.pollEvent(event)) { + switch (event.type) { + case sf::Event::Closed: + mQuit = true; + break; + case sf::Event::KeyPressed: + keyDown(event); + break; + case sf::Event::KeyReleased: + keyUp(event); + break; + case sf::Event::MouseButtonReleased: + mouseUp(event); + break; + case sf::Event::MouseMoved: + mPlayer.setCrosshairPosition(convertCoordinates(event.mouseMove.x, event.mouseMove.y)); + break; + default: + break; + } + } +} + +/** + * Handles key up event. This is used for events that only fire once per keypress. + */ +void +Game::keyUp(const sf::Event& event) { + switch (event.key.code) { + case sf::Keyboard::Escape: + mQuit = true; + break; + case sf::Keyboard::Space: + mPaused = !mPaused; + break; + default: + break; + } +} + +/** + * Handles key down event. This is used for any events that refire automatically. + */ +void +Game::keyDown(const sf::Event& event) { + switch (event.key.code) { + default: + break; + } +} + +/** + * Converts a screen coordinate to a world coordinate. + */ +sf::Vector2 +Game::convertCoordinates(int x, int y) { + return mWindow.convertCoords(Vector2i(x, y), mView); +} + +/** + * Handles mouse key up events. + */ +void +Game::mouseUp(const sf::Event& event) { + switch (event.mouseButton.button) { + case sf::Mouse::Left: + mPlayer.fire(); + break; + case sf::Mouse::Right: + mPlayer.move(convertCoordinates(event.mouseButton.x, event.mouseButton.y)); + break; + default: + break; + } +} + +/** + * Renders world and GUI. + */ +void +Game::render() { + mWindow.clear(); + + mView.setCenter(mPlayer.getPosition()); + + // Render world and dynamic stuff. + mWindow.setView(mView); + + mWindow.draw(mTileManager); + mWindow.draw(mCollection); + mWindow.draw(mPlayer); + + // Render GUI and static stuff. + mWindow.setView(mWindow.getDefaultView()); + + //mWindow.draw(mFps); + + mWindow.display(); +} + +/** + * Returns current FPS as string. + */ +sf::String +Game::getFps() { + return str((mElapsed != 0) ? 1000.0f / mElapsed : 0.0f, 2); +} + +/** + * Begin of collision, call callback function on both objects. + */ +void +Game::BeginContact(b2Contact* contact) { + Physical& first = *static_cast(contact->GetFixtureA()->GetBody()->GetUserData()); + Physical& second = *static_cast(contact->GetFixtureB()->GetBody()->GetUserData()); + + if (!first.doesCollide(second) || !second.doesCollide(first)) { + contact->SetEnabled(false); + return; + } + first.onCollide(second, second.getCategory()); + second.onCollide(first, first.getCategory()); +} diff --git a/source/Game.h b/source/Game.h new file mode 100644 index 0000000..f38d98d --- /dev/null +++ b/source/Game.h @@ -0,0 +1,71 @@ +/* + * Game.h + * + * Created on: 05.07.2012 + * Author: Felix + */ + +#ifndef DG_GAME_H_ +#define DG_GAME_H_ + +#include +#include + +#include + +#include + +#include "TileManager.h" +#include "sprite/Player.h" +#include "util/Collection.h" + +/* + * Use vertex for tiles. + */ +class Game : private sf::NonCopyable, public b2ContactListener { +// Public functions. +public: + Game(const Vector2i& resolution); + ~Game(); + + void loop(); + void BeginContact(b2Contact* contact); + +// Private functions. +private: + void input(); + void render(); + void tick(); + + void keyDown(const sf::Event& event); + void keyUp(const sf::Event& event); + void mouseUp(const sf::Event& event); + + sf::String getFps(); + sf::Vector2 convertCoordinates(int x, int y); + +// Private variables. +private: + static const int FPS_GOAL; + static const float TICKS_GOAL; + + b2World mWorld; + + sf::RenderWindow mWindow; + sf::Clock mClock; + sf::View mView; + //sf::Text mFps; + + Collection mCollection; + TileManager mTileManager; + Player mPlayer; + + /// Milliseconds since the last tick. + sf::Uint32 mElapsed; + + bool mQuit; + bool mPaused; +}; + + +#endif /* DG_GAME_H_ */ diff --git a/source/TileManager.cpp b/source/TileManager.cpp new file mode 100755 index 0000000..c31d5c7 --- /dev/null +++ b/source/TileManager.cpp @@ -0,0 +1,116 @@ +/* + * TileManager.cpp + * + * Created on: 08.08.2012 + * Author: Felix + */ + +#include "TileManager.h" + +#include + +#include "util/Loader.h" +#include "util/ResourceManager.h" +#include "abstract/Sprite.h" + +const Vector2i TileManager::TILE_SIZE = Vector2i(100, 100); + +/** + * Loads tile resources. + * + * @param world Box2D world to create (physical) tiles in. + */ +TileManager::TileManager(b2World& world) : + mWorld(world) { +} + +/** + * Constructs a tile. + * + * @param pType Type of the tile to create. + * @param pPosition Position of the tile in tile coordinates. + * @param world Box2D world object. + */ +TileManager::Tile::Tile(Type type, const TilePosition& position, b2World& world) : + Sprite(getTexture(type), PhysicalData(Vector2f(position.x * TILE_SIZE.x, position.y * TILE_SIZE.y), + TILE_SIZE, world, CATEGORY_WORLD, (type == TYPE_FLOOR) ? MASK_NONE : MASK_ALL, false)), + mType(type) { +} + +/** + * Returns a texture key for a certain tile type. + * + * @param type The type of tile to load a resource key for. + * @return Resource key to the correct texture. + */ +std::shared_ptr +TileManager::Tile::getTexture(Type type) { + sf::String filename; + switch (type) { + case TYPE_FLOOR: + filename = "floor.png"; + break; + case TYPE_WALL: + filename = "wall.png"; + break; + default: + throw new aurora::Exception("Invalid tile type."); + } + return ResourceManager::i().acquire(Loader::i().fromFile(filename)); +} + +/** + * Returns the Type of this tile. + */ +TileManager::Type +TileManager::Tile::getType() const { + return mType; +} + +/** + * Returns the position of the tile with tile width/height as a unit. + */ +TileManager::TilePosition +TileManager::Tile::getTilePosition() const { + return TilePosition(getPosition().x / TILE_SIZE.x, getPosition().y / TILE_SIZE.y); +} + +/** + * Fills the world with predefined tiles. + */ +void +TileManager::generate() { + for (int x = 0; x < 10; x++) + for (int y = 0; y < 10; y++) + setTile(TilePosition(x, y), TYPE_WALL); + + for (int x = 1; x < 9; x++) + for (int y = 1; y < 9; y++) + setTile(TilePosition(x, y), TYPE_FLOOR); +} + +/** + * Insert a tile at the position. Deletes an existing tile first if one is at the position. + * + * @param position Grid coordinate of the tile (not pixel coordinate). + * @param type Type of tile to be inserted. + */ +void +TileManager::setTile(const TilePosition& position, Type type) { + for (auto it = mTiles.begin(); it != mTiles.end(); it++) { + if ((*it)->getTilePosition() == position) { + mTiles.erase(it); + } + } + mTiles.push_back(std::unique_ptr(new Tile(type, position, mWorld))); +} + +/** + * \copydoc sf::Drawable::draw + */ +void +TileManager::draw(sf::RenderTarget& target, sf::RenderStates states) const { + for (auto it = mTiles.begin(); it != mTiles.end(); it++) { + target.draw((**it), states); + } +} diff --git a/source/TileManager.h b/source/TileManager.h new file mode 100755 index 0000000..d555770 --- /dev/null +++ b/source/TileManager.h @@ -0,0 +1,79 @@ +/* + * TileManager.h + * + * Created on: 08.08.2012 + * Author: Felix + */ + +#ifndef DG_TILEMANAGER_H_ +#define DG_TILEMANAGER_H_ + +#include +#include +#include + +#include + +#include + +#include "util/Vector.h" +#include "abstract/Sprite.h" + +class TileManager : public sf::Drawable { +// Public constants. +public: + /// The size of a single tile (pixels). + static const Vector2i TILE_SIZE; + +// Public functions. +public: + TileManager(b2World& world); + + void generate(); + +// Private types. +private: + enum Type { + TYPE_FLOOR, + TYPE_WALL + }; + + /** + * Uses the length/width of a tile as a unit. + */ + typedef Vector2i TilePosition; + + class Tile; + +// Private functions. +private: + void draw(sf::RenderTarget& target, sf::RenderStates states) const; + void setTile(const TilePosition& position, Type type); + +// Private variables. +private: + b2World& mWorld; + std::vector > mTiles; +}; + +/** + * Holds information about a single tile. + */ +class TileManager::Tile : public Sprite { +// Public functions. +public: + Tile(Type type, const TilePosition& position, b2World& world); + + Type getType() const; + TilePosition getTilePosition() const; + + static std::shared_ptr getTexture(Type type); + +// Private variables. +private: + Type mType; +}; + + + +#endif /* DG_TILEMANAGER_H_ */ diff --git a/source/abstract/Actor.cpp b/source/abstract/Actor.cpp new file mode 100755 index 0000000..2d4cc84 --- /dev/null +++ b/source/abstract/Actor.cpp @@ -0,0 +1,41 @@ +/* + * Actor.cpp + * + * Created on: 02.09.2012 + * Author: Felix + */ + +#include "Actor.h" + +#include +#include + +std::vector Actor::mInstances = std::vector(); + +/** + * Saves pointer to this instance in static var for think(). + */ +Actor::Actor() { + mInstances.push_back(this); +} + +/** + * Deletes pointer from think() static var. + */ +Actor::~Actor() { + auto it = std::find(mInstances.begin(), mInstances.end(), this); + assert(it != mInstances.end()); + mInstances.erase(it); +} + +/** + * Calls onThink on all Actor instances. + * + * @param elapsedTime Amount of time to simulate. + */ +void +Actor::think(float elapsedTime) { + for (auto i : mInstances) { + i->onThink(elapsedTime); + } +} diff --git a/source/abstract/Actor.h b/source/abstract/Actor.h new file mode 100755 index 0000000..d3781f8 --- /dev/null +++ b/source/abstract/Actor.h @@ -0,0 +1,38 @@ +/* + * Actor.h + * + * Created on: 02.09.2012 + * Author: Felix + */ + +#ifndef DG_ACTOR_H_ +#define DG_ACTOR_H_ + +#include + +/** + * Provides think function for AI. + */ +class Actor { +// Public functions. +public: + Actor(); + virtual ~Actor() = 0; + + static void think(float elapsedTime); + +// Protected functions. +protected: + /** + * Implement this function for any (regular) AI computations. + * + * @param elapsedTime Amount of time to simulate. + */ + virtual void onThink(float elapsedTime) = 0; + +// Private variables. +private: + static std::vector mInstances; +}; + +#endif /* DG_ACTOR_H_ */ diff --git a/source/abstract/Physical.cpp b/source/abstract/Physical.cpp new file mode 100755 index 0000000..d3dee01 --- /dev/null +++ b/source/abstract/Physical.cpp @@ -0,0 +1,166 @@ +/* + * Physical.cpp + * + * Created on: 11.08.2012 + * Author: Felix + */ + +#include "Physical.h" + +#include + +#include + +/** + * Initializes Box2D body. + * + * @param data Data needed for construction. + */ +Physical::Physical(const PhysicalData& data) : + mDelete(false) { + assert(data.size != Vector2i()); + assert(data.category); + + b2BodyDef bodyDef; + // not moving -> static body + // moving -> dynamic body + // bullet -> kinematic body + bodyDef.type = (data.moving) ? + (data.bullet) + ? b2_dynamicBody + : b2_dynamicBody + : b2_staticBody; + bodyDef.position = vector(data.position); + bodyDef.allowSleep = true; + bodyDef.fixedRotation = true; + bodyDef.bullet = data.bullet; + bodyDef.userData = this; + + mBody = data.world.CreateBody(&bodyDef); + + b2PolygonShape boxShape; + boxShape.SetAsBox(pixelToMeter(data.size.x) / 2, pixelToMeter(data.size.y) / 2); + + b2FixtureDef fixtureDef; + fixtureDef.shape = &boxShape; + fixtureDef.density = 1.0f; + fixtureDef.filter.categoryBits = data.category; + fixtureDef.filter.maskBits = ~data.maskExclude; + fixtureDef.restitution = 0; + + mBody->CreateFixture(&fixtureDef); +} + +/** + * Removes body from world. + */ +Physical::~Physical() { + mBody->GetWorld()->DestroyBody(mBody); +} + +/** + * Initializes container. + * + * @link Physical::PhysicalData + */ +Physical::PhysicalData::PhysicalData( const Vector2f& position, const Vector2i& size, + b2World& world, uint16 category, uint16 maskExclude, bool moving, bool bullet) : + position(position), + size(size), + world(world), + category(category), + maskExclude(maskExclude), + moving(moving), + bullet(bullet) { +} + +/** + * Returns the position of the sprite (center). + */ +Vector2f +Physical::getPosition() const { + return vector(mBody->GetPosition()); +} + +/** + * Returns the movement speed of the body. + */ +Vector2f +Physical::getSpeed() const { + return vector(mBody->GetLinearVelocity()); +} + +/** + * Returns the rotation of the body (converted to an SFML angle). + */ +float +Physical::getAngle() const { + return - thor::toDegree(mBody->GetAngle()); +} + +/** + * Returns true if this object should be deleted. + */ +bool +Physical::getDelete() const { + return mDelete; +} + +uint16 +Physical::getCategory() const { + return mBody->GetFixtureList()->GetFilterData().categoryBits; +} + +/** + * This method filters collisions with other physicals. Implement it if you want to + * limit collisions to/with certain objects. Default implementation always returns true. + * + * @param other The Physical this object is about to collide with. + * @return True if the objects should collide. + */ +bool +Physical::doesCollide(Physical& other) { + return true; +} + +/** + * Called when a collision with another body occured. Override this method + * to manage collision events. + * + * @param other Reference to the other Physical in the collision. + * @param category The Category of the other object (as passed in constructor). + */ +void +Physical::onCollide(Physical& other, uint16 type) { +} + +/** + * Set to true to mark this object for deletion from the world. + */ +void +Physical::setDelete(bool value) { + mDelete = value; +} + +/** + * Sets movement speed and direction of the body. Set either value to zero to stop movement. + * + * @param direction The direction the body moves in, does not have to be normalized. + * @param speed The value of the movement speed to be used. + */ +void +Physical::setSpeed(Vector2f direction, float speed) { + if (direction != Vector2f()) { + direction = thor::unitVector(direction); + } + direction *= speed; + mBody->SetLinearVelocity(vector(direction)); +} + +/** + * Sets the angle of the body based on the direction of a vector. + */ +void +Physical::setAngle(float angle) { + mBody->SetTransform(mBody->GetPosition(), - thor::toRadian(angle)); +} diff --git a/source/abstract/Physical.h b/source/abstract/Physical.h new file mode 100755 index 0000000..82d80d8 --- /dev/null +++ b/source/abstract/Physical.h @@ -0,0 +1,90 @@ +/* + * Physical.h + * + * Created on: 11.08.2012 + * Author: Felix + */ + +#ifndef DG_PHYSICAL_H_ +#define DG_PHYSICAL_H_ + +#include + +#include "../util/Vector.h" + +/** + * An object with physical properties. + * + * @warning May only handle bodies with one fixture. + */ +class Physical { +// Public types. +public: + /** + * POD container that carries all data required to construct this class. + */ + class PhysicalData { + public: + PhysicalData() = default; + PhysicalData(const Vector2f& position, const Vector2i& size, b2World& world, + uint16 category, uint16 maskExclude, bool moving, bool bullet = false); + const Vector2f& position; //< World position of the body in pixel coordinates. + const Vector2i& size; //< Pixel size of the body. + b2World& world; //< Box2D world object. + uint16 category; //< The category for collision filtering. Only one may be set. @link Physical::Category + uint16 maskExclude; //< All categories set here will have collisions disabled with this object. + bool moving; //< True if the body may move on its own (player, monster). + bool bullet; //< True if the object is a bullet. + }; + + /** + * Categories of physical objects, for Box2D collision filtering. + * + * @warning An object may only have one category. + */ + enum Category { + CATEGORY_NONSOLID = 0, + CATEGORY_WORLD = 1 << 1, + CATEGORY_ACTOR = 1 << 2, + CATEGORY_PARTICLE = 1 << 3 + }; + + /** + * Common Box2D collision masking values. + */ + enum Mask { + MASK_NONE = 0xffff, //< Disables any collisions. + MASK_ALL = 0 //< Enables all collisions. + }; + +// Public functions. +public: + Physical(const PhysicalData& data); + virtual ~Physical() = 0; + + Vector2f getPosition() const; + Vector2f getSpeed() const; + float getAngle() const; + bool getDelete() const; + uint16 getCategory() const; + + virtual bool doesCollide(Physical& other); + virtual void onCollide(Physical& other, uint16 category); + +// Protected functions. +protected: + void setDelete(bool value); + void setSpeed(Vector2f direction, float speed); + void setAngle(float angle); + +// Protected variables. +protected: + // Currently protected to allow for (debug only) direct player input. + b2Body* mBody; + +// Private variables. +private: + bool mDelete; +}; + +#endif /* DG_PHYSICAL_H_ */ diff --git a/source/abstract/Sprite.cpp b/source/abstract/Sprite.cpp new file mode 100644 index 0000000..bd97e59 --- /dev/null +++ b/source/abstract/Sprite.cpp @@ -0,0 +1,58 @@ +/* + * Sprite.cpp + * + * Created on: 22.07.2012 + * Author: Felix + */ + +#include "Sprite.h" + +#include "../util/Loader.h" +#include "../util/Log.h" +#include "../util/String.h" +#include "../util/ResourceManager.h" + +/** + * Loads sprite from ResourceManager, sets world position. + * + * @param texturePath Relative path to the texture file in the resource folder. + */ +Sprite::Sprite(const sf::String& texturePath, const PhysicalData& data) : + Physical(data), + mTexture(ResourceManager::i().acquire(Loader::i().fromFile(texturePath))), + mSize(data.size) { +} + +/** + * Loads sprite from ResourceManager, sets world position. Use this if the texture has already been loaded. + * + * @param texture Pointer to the texture to be used (must already be loaded). + */ +Sprite::Sprite(const std::shared_ptr& texture, const PhysicalData& data) : + Physical(data), + mTexture(texture), + mSize(data.size) { + +} +/** + * Does nothing. + */ +Sprite::~Sprite() { +} + +/** + * \copydoc sf::Drawable::draw + */ +void +Sprite::draw(sf::RenderTarget& target, sf::RenderStates states) const { + // Create a temporary shape to apply box2d body transformations to. + sf::RectangleShape shape = sf::RectangleShape(Vector2f(mSize)); + shape.setTexture(&*mTexture, true); + shape.setOrigin(Vector2f(mSize.x / 2, mSize.y / 2)); + shape.setTextureRect(sf::IntRect(Vector2i(0, 0), mSize)); + + shape.setPosition(getPosition()); + shape.setRotation(getAngle()); + + target.draw(shape, states); +} diff --git a/source/abstract/Sprite.h b/source/abstract/Sprite.h new file mode 100644 index 0000000..2a9b1b1 --- /dev/null +++ b/source/abstract/Sprite.h @@ -0,0 +1,36 @@ +/* + * Sprite.h + * + * Created on: 22.07.2012 + * Author: Felix + */ + +#ifndef DG_SPRITE_H_ +#define DG_SPRITE_H_ + +#include + +#include + +#include "Physical.h" +#include "../util/Vector.h" + +/** + * Represents a drawable object. + * + * Handles drawing to world. + */ +class Sprite : public sf::Drawable, public Physical { +public: + Sprite(const sf::String& texturePath, const PhysicalData& data); + Sprite(const std::shared_ptr& texture, const PhysicalData& data); + virtual ~Sprite() = 0; + +private: + virtual void draw(sf::RenderTarget& target, sf::RenderStates states) const; + + std::shared_ptr mTexture; + Vector2i mSize; +}; + +#endif /* DG_SPRITE_H_ */ diff --git a/source/effects/Bullet.cpp b/source/effects/Bullet.cpp new file mode 100755 index 0000000..3abf1a5 --- /dev/null +++ b/source/effects/Bullet.cpp @@ -0,0 +1,45 @@ +/* + * Bullet.cpp + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#include "Bullet.h" + +#include "../util/Log.h" + +const float Bullet::SPEED = 500.0f; + +/** + * Places a bullet in the world. + * + * @param position World position of the bullet. + * @param world Box2d world. + * @param texture Texture to display for bullet. + */ +Bullet::Bullet(const Vector2f& position, b2World& world, + const std::shared_ptr& texture, Physical& shooter, float direction) : + Particle(texture, PhysicalData(position, Vector2i(20, 20), world, CATEGORY_PARTICLE, + CATEGORY_PARTICLE, true, true)), + mShooter(shooter) { + setSpeed(angle(direction), SPEED); + setAngle(direction); +} + +/** + * @copydoc Physical::onCollide + */ +void +Bullet::onCollide(Physical& other, uint16 type) { + // Make sure we do not damage twice. + if (!getDelete()) { + // Call onShot on other, with damage as param. + setDelete(true); + } +} + +bool +Bullet::doesCollide(Physical& other) { + return &other != &mShooter; +} diff --git a/source/effects/Bullet.h b/source/effects/Bullet.h new file mode 100755 index 0000000..2aa7799 --- /dev/null +++ b/source/effects/Bullet.h @@ -0,0 +1,31 @@ +/* + * Bullet.h + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#ifndef DG_BULLET_H_ +#define DG_BULLET_H_ + +#include "../particle/Particle.h" + +/** + * Bullet particle fired by a weapon, may damage actors. + */ +class Bullet : public Particle { +// Public functions. +public: + Bullet(const Vector2f& position, b2World& world, const std::shared_ptr& texture, + Physical& shooter, float direction); + + void onCollide(Physical& other, uint16 category); + bool doesCollide(Physical& other); + +// Private variables. +private: + static const float SPEED; + Physical& mShooter; +}; + +#endif /* DG_BULLET_H_ */ diff --git a/source/items/Weapon.cpp b/source/items/Weapon.cpp new file mode 100755 index 0000000..52bf026 --- /dev/null +++ b/source/items/Weapon.cpp @@ -0,0 +1,38 @@ +/* + * Weapon.cpp + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#include "Weapon.h" + +#include "../util/Collection.h" +#include "../effects/Bullet.h" +#include "../util/Loader.h" +#include "../util/ResourceManager.h" + +Weapon::Weapon(Physical& holder, Collection& collection, b2World& world) : + Emitter(collection), + mHolder(holder), + mBulletTexture(ResourceManager::i().acquire(Loader::i().fromFile("bullet.png"))), + mWorld(world) { +} + +Weapon::~Weapon() { +} + +/** + * Call on any button press/refire. + */ +void +Weapon::fire() { + // Only call if has ammo, consider firing rate etc. + emit(); +} + +std::shared_ptr +Weapon::createParticle() { + return std::shared_ptr(new Bullet(mHolder.getPosition(), mWorld, mBulletTexture, + mHolder, mHolder.getAngle())); +} diff --git a/source/items/Weapon.h b/source/items/Weapon.h new file mode 100755 index 0000000..4f70e71 --- /dev/null +++ b/source/items/Weapon.h @@ -0,0 +1,37 @@ +/* + * Weapon.h + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#ifndef DG_WEAPON_H_ +#define DG_WEAPON_H_ + +#include + +#include "../abstract/Physical.h" +#include "../particle/Emitter.h" + +/** + * Loading mechanism: + * - pass enum value and load mapped xml + * - pass xml filename + */ +class Weapon : public Emitter { +public: + Weapon(Physical& holder, Collection& collection, b2World& world); + ~Weapon(); + + void fire(); + +protected: + std::shared_ptr createParticle(); + +private: + Physical& mHolder; + std::shared_ptr mBulletTexture; + b2World& mWorld; +}; + +#endif /* DG_WEAPON_H_ */ diff --git a/source/main.cpp b/source/main.cpp new file mode 100644 index 0000000..0603dbd --- /dev/null +++ b/source/main.cpp @@ -0,0 +1,23 @@ +/* + * main.cpp + * + * Created on: 19.07.2012 + * Author: Felix + */ + +#include "Game.h" +#include "util/Loader.h" + +/** + * Creates Game object. + */ +int main(int argc, char* argv[]) { + Loader::i().setFolder("resources/"); + Loader::i().setSubFolder("textures/"); + + Game game(Vector2i(800, 600)); + + game.loop(); + + return 0; +} diff --git a/source/particle/Emitter.cpp b/source/particle/Emitter.cpp new file mode 100755 index 0000000..03dc5be --- /dev/null +++ b/source/particle/Emitter.cpp @@ -0,0 +1,23 @@ +/* + * Emitter.cpp + * + * Created on: 15.08.2012 + * Author: Felix + */ + +#include "Emitter.h" + +Emitter::Emitter(Collection& collection) : + mCollection(collection) { +} + +Emitter::~Emitter() { +} + +/** + * Inserts a new particle into the system, using createParticle(). + */ +void +Emitter::emit() { + mCollection.insert(createParticle(), Collection::LEVEL_PARTICLE); +} diff --git a/source/particle/Emitter.h b/source/particle/Emitter.h new file mode 100755 index 0000000..a7cc41b --- /dev/null +++ b/source/particle/Emitter.h @@ -0,0 +1,32 @@ +/* + * Emitter.h + * + * Created on: 15.08.2012 + * Author: Felix + */ + +#ifndef DG_EMITTER_H_ +#define DG_EMITTER_H_ + +#include "../abstract/Physical.h" +#include "../util/Collection.h" +#include "Particle.h" + +class Emitter { +// Public functions. +public: + Emitter(Collection& collection); + virtual ~Emitter(); + +// Protected functions. +protected: + void emit(); + /// Creates a particle. Allows to use a user-defined particle class and custom settings. + virtual std::shared_ptr createParticle() = 0; + +// Private variables. +private: + Collection& mCollection; +}; + +#endif /* DG_EMITTER_H_ */ diff --git a/source/particle/Particle.cpp b/source/particle/Particle.cpp new file mode 100755 index 0000000..3a90a1e --- /dev/null +++ b/source/particle/Particle.cpp @@ -0,0 +1,16 @@ +/* + * Particle.cpp + * + * Created on: 15.08.2012 + * Author: Felix + */ + +#include "Particle.h" + +Particle::Particle(const std::shared_ptr& texture, const PhysicalData& data) : + Sprite(texture, data) { +} + +Particle::~Particle() { +} + diff --git a/source/particle/Particle.h b/source/particle/Particle.h new file mode 100755 index 0000000..2f8b4b8 --- /dev/null +++ b/source/particle/Particle.h @@ -0,0 +1,22 @@ +/* + * Particle.h + * + * Created on: 15.08.2012 + * Author: Felix + */ + +#ifndef DG_PARTICLE_H_ +#define DG_PARTICLE_H_ + +#include "../abstract/Sprite.h" + +/** + * Prototype for a particle. + */ +class Particle : public Sprite { +public: + Particle(const std::shared_ptr& texture, const PhysicalData& data); + virtual ~Particle(); +}; + +#endif /* DG_PARTICLE_H_ */ diff --git a/source/sprite/Cover.cpp b/source/sprite/Cover.cpp new file mode 100755 index 0000000..526dd9d --- /dev/null +++ b/source/sprite/Cover.cpp @@ -0,0 +1,13 @@ +/* + * Cover.cpp + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#include "Cover.h" + +Cover::Cover(const Vector2f& position, const Vector2i& size, b2World& world) : + Sprite("cover.png", PhysicalData(position, size, world, CATEGORY_WORLD, MASK_ALL, false)) { +} + diff --git a/source/sprite/Cover.h b/source/sprite/Cover.h new file mode 100755 index 0000000..35f1eb2 --- /dev/null +++ b/source/sprite/Cover.h @@ -0,0 +1,21 @@ +/* + * Cover.h + * + * Created on: 12.08.2012 + * Author: Felix + */ + +#ifndef DG_COVER_H_ +#define DG_COVER_H_ + +#include "../abstract/Sprite.h" + +/** + * A wall that can be placed anywhere (not limited by tiles) and have any (rectangular) size. + */ +class Cover : public Sprite { +public: + Cover(const Vector2f& position, const Vector2i& size, b2World& world); +}; + +#endif /* DG_COVER_H_ */ diff --git a/source/sprite/Player.cpp b/source/sprite/Player.cpp new file mode 100644 index 0000000..871eeef --- /dev/null +++ b/source/sprite/Player.cpp @@ -0,0 +1,74 @@ +/* + * Player.cpp + * + * Created on: 21.07.2012 + * Author: Felix + */ + +#include "Player.h" + +#include + +#include "../util/Vector.h" +#include "../items/Weapon.h" + +const float Player::SPEED = 100.0f; + +/** + * Initializes Sprite. + */ +Player::Player(b2World& world, Collection& collection) : + Sprite("player.png", PhysicalData(Vector2f(200.0f, 100.0f), Vector2i(50, 50), world, + CATEGORY_ACTOR, MASK_ALL, true)), + mWeapon(*this, collection, world), + mDestination(Vector2i(50, 50)) { +} + +/** + * Sets the point where to look and shoot at. + * + * @param Absolute world coordinates of the crosshair. + */ +void +Player::setCrosshairPosition(const Vector2f& position) { + mCrosshairPosition = position - getPosition(); +} +/** + * Fire the attacked Weapon, emitting a Bullet object. + */ +void +Player::fire() { + mWeapon.fire(); +} + +/** + * Moves the player to a destination point. + * + * @param destination Absolute world coordinate of the destination point. + */ +void +Player::move(const Vector2f& destination) { + mDestination = destination; + // Convert to relative destination. + setSpeed(mDestination - getPosition(), SPEED); +} + +void +Player::onThink(float elapsedTime) { + // Stop if we are close enough. + if (thor::length(mDestination - getPosition()) < 1.0f) { + setSpeed(Vector2f(), 0); + } + // Look towards crosshair. + setAngle(angle(mCrosshairPosition)); +} + +/** + * Stop movement if we collide with anything except bullets. + */ +void +Player::onCollide(Physical& other, uint16 category) { + if (category != CATEGORY_PARTICLE) { + mDestination = getPosition(); + } +} diff --git a/source/sprite/Player.h b/source/sprite/Player.h new file mode 100644 index 0000000..9e79323 --- /dev/null +++ b/source/sprite/Player.h @@ -0,0 +1,46 @@ +/* + * Player.h + * + * Created on: 21.07.2012 + * Author: Felix + */ + +#ifndef DG_PLAYER_H_ +#define DG_PLAYER_H_ + +#include +#include + +#include "../abstract/Actor.h" +#include "../abstract/Sprite.h" +#include "../items/Weapon.h" +#include "../util/Vector.h" + +class Sprite; + +/** + * Player object. + */ +class Player : public Sprite, public Actor { +// Public functions. +public: + Player(b2World& world, Collection& collection); + + void setCrosshairPosition(const Vector2f& position); + void fire(); + void move(const Vector2f& destination); + +// Protected functions. +protected: + void onCollide(Physical& other, uint16 category); + void onThink(float elapsedTime); + +// Private variables. +private: + static const float SPEED; + Weapon mWeapon; //< Weapon object used for Player::fire(). + Vector2f mDestination; //< Absolute position of the movement destination. + Vector2f mCrosshairPosition; //< Relative position of the point to fire at (mouse cursor). +}; + +#endif /* DG_PLAYER_H_ */ diff --git a/source/util/Collection.cpp b/source/util/Collection.cpp new file mode 100755 index 0000000..debb13d --- /dev/null +++ b/source/util/Collection.cpp @@ -0,0 +1,65 @@ +/* + * Collection.cpp + * + * Created on: 29.08.2012 + * Author: Felix + */ + +#include "Collection.h" + +#include + +/** + * Insert a drawable into the group. Drawables should only be handled with shared_ptr. + * An object can't be inserted more than once at the same level. + */ +void +Collection::insert(std::shared_ptr drawable, Level level) { + auto item = std::find(mDrawables[level].begin(), mDrawables[level].end(), drawable); + if (item == mDrawables[level].end()) { + mDrawables[level].push_back(drawable); + } +} + +/** + * Removes a drawable from the group. + */ +void +Collection::remove(std::shared_ptr drawable) { + for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) { + auto item = std::find(v->second.begin(), v->second.end(), drawable); + if (item != v->second.end()) { + v->second.erase(item); + } + } +} + +/** + * Deletes any sprites which return true for getDelete(). + */ +void +Collection::checkDelete() { + for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) { + for (auto item = v->second.begin(); item != v->second.end(); ) { + if ((*item)->getDelete()) { + item = v->second.erase(item); + } + else { + item++; + } + } + } +} + +/** + * Draws all elements in the group. + */ +void +Collection::draw(sf::RenderTarget& target, sf::RenderStates states) const { + for (auto v = mDrawables.begin(); v != mDrawables.end(); v++) { + for (auto item : v->second) { + target.draw(static_cast(*item), states); + } + } +} + diff --git a/source/util/Collection.h b/source/util/Collection.h new file mode 100755 index 0000000..1ce1fa1 --- /dev/null +++ b/source/util/Collection.h @@ -0,0 +1,48 @@ +/* + * Collection.h + * + * Created on: 29.08.2012 + * Author: Felix + */ + +#ifndef DG_COLLECTION_H_ +#define DG_COLLECTION_H_ + +#include +#include + +#include + +#include "../abstract/Sprite.h" + +/** + * A collection of sprites, which can be put into different layers. + */ +class Collection : public sf::Drawable { +// Public types. +public: + /** + * Determines in what order sprites are rendered, dynamics and actors should be on top. + */ + enum Level { + LEVEL_STATIC, + LEVEL_PARTICLE, + LEVEL_ACTOR + }; + +// Public functions. +public: + void insert(std::shared_ptr drawable, Level level); + void remove(std::shared_ptr drawable); + void checkDelete(); + +// Private functions. +private: + void draw(sf::RenderTarget& target, sf::RenderStates states) const; + +// Private variables. +private: + std::map > > mDrawables; +}; + +#endif /* DG_COLLECTION_H_ */ diff --git a/source/util/Loader.h b/source/util/Loader.h new file mode 100755 index 0000000..753a8a5 --- /dev/null +++ b/source/util/Loader.h @@ -0,0 +1,137 @@ +/* + * Loader.h + * + * Created on: 13.08.2012 + * Author: Felix + */ + +#ifndef DG_LOADER_H_ +#define DG_LOADER_H_ + +#include +#include +#include + +#include + +#include + +#include "Singleton.h" + +/** + * This class allows to set default resource folders and subfolders, which means that these + * folders only have to be set once, and not be included in the creation of every single + * resource key. + * + * If the general resource folder or a specific resource folder is not set, the current + * directory is searched. + * + * R is any resource that can be loaded with Thor's resource loader. + * + * Any folder/file parameter can be a full path and is relative to the current directory, + * or the directory set by the higher variables. + * + * @code + * Loader l; + * l.setFolder("resources/"); + * l.setSubFolder("textures/"); + * thor::ResourceKey t = l.fromFile("myimage.png"); + * + * ResourceManager r; + * r.acquire(t); // This loads from "resources/textures/myimage.png". + * // folder subfolder file + * @endcode + */ +class Loader : public Singleton { +// Public functions. +public: + /** + * Sets the general resource folder path. + */ + inline void setFolder(const std::string& folder) {mFolder = folder;}; + + /** + * Sets the resource subfolder for the specific type. + */ + template void + setSubFolder(std::string path) { + getLoader()->setSubFolder(path); + } + + /** + * Loads a resource from a file, looking in the specific resource folder for this type. + */ + template thor::ResourceKey + fromFile(const std::string& file) { + return static_cast* >(getLoader().get())->fromFile(mFolder, file); + } + +// Private types. +private: + /** + * We need this to save templates of different types in the same container. + */ + class LoaderBase { + public: + virtual void setSubFolder(const std::string& path) = 0; + }; + + /** + * This class forwards the loading of each individual type to Thor. + */ + template + class SpecificLoader : public LoaderBase { + public: + /** + * Sets the subfolder for the current type. + */ + void + setSubFolder(const std::string& path) { + mSubfolder = path; + } + + /** + * Loads a resource from file via Thor. + * + * @param folder The general resource folder + * @param file Path/name of the file within the resource subfolder. + */ + thor::ResourceKey + fromFile(const std::string& folder, const std::string& file) { + return thor::Resources::fromFile(folder + mSubfolder + file); + } + + private: + std::string mSubfolder; + }; + +// Private functions. +private: + /** + * For Singleton behaviour. + */ + Loader() = default; + friend class Singleton; + + /** + * Gets the correct loader for each template type, creates it if it does not exist. + */ + template std::unique_ptr& + getLoader() { + auto loader = mLoaders.find(typeid(T)); + if (loader != mLoaders.end()) { + return static_cast&>(loader->second); + } + else { + return (*mLoaders.insert(std::pair > + (typeid(T), std::unique_ptr(new SpecificLoader))).first).second; + } + }; + +// Private variables. +private: + std::string mFolder; + std::map > mLoaders; +}; + +#endif /* DG_LOADER_H_ */ diff --git a/source/util/Log.h b/source/util/Log.h new file mode 100644 index 0000000..fdac49d --- /dev/null +++ b/source/util/Log.h @@ -0,0 +1,37 @@ +/* + * Log.h + * + * Created on: 25.07.2012 + * Author: Felix + */ + +#ifndef DG_LOG_H_ +#define DG_LOG_H_ + +#include + +/** + * \def LOG_E(str) + * Log an error to the error stream. + */ +#define LOG_E(str) std::cerr << "Error: " << __FILE__ << ":" << __LINE__ << " \"" << str << "\"" << std::endl + +/** + * \def LOG_E(str) + * Log a warning to the output stream. + */ +#define LOG_W(str) std::cout << "Warning: " << __FILE__ << ":" << __LINE__ << " \"" << str << "\"" << std::endl + +/** + * \def LOG_E(str) + * Log a debug message to the output stream. + */ +#define LOG_D(str) std::cout << "Debug: " << __FILE__ << ":" << __LINE__ << " \"" << str << "\"" << std::endl + +/** + * \def LOG_E(str) + * Log an info to the output stream. + */ +#define LOG_I(str) std::cout << "Info: " << __FILE__ << ":" << __LINE__ << " \"" << str << "\"" << std::endl + +#endif /* DG_LOG_H_ */ \ No newline at end of file diff --git a/source/util/ResourceManager.h b/source/util/ResourceManager.h new file mode 100644 index 0000000..b84289a --- /dev/null +++ b/source/util/ResourceManager.h @@ -0,0 +1,26 @@ +/* + * ResourceManager.h + * + * Created on: 22.07.2012 + * Author: Felix + */ + +#ifndef DG_RESOURCEMANAGER_H_ +#define DG_RESOURCEMANAGER_H_ + +#include + +#include + +#include "Singleton.h" + +/** + * Loads and manages all resources by providing Singleton access to Thor ResourceManager. + */ +class ResourceManager : public thor::MultiResourceCache, public Singleton { +private: + friend class Singleton; + ResourceManager() = default; +}; + +#endif /* DG_RESOURCEMANAGER_H_ */ diff --git a/source/util/Singleton.h b/source/util/Singleton.h new file mode 100644 index 0000000..4925cae --- /dev/null +++ b/source/util/Singleton.h @@ -0,0 +1,31 @@ +/* + * Singleton.h + * + * Created on: 04.07.2012 + * Author: Felix + */ + +#ifndef DG_SINGLETON_H_ +#define DG_SINGLETON_H_ + +#include + +/** + * Template class for inheriting singleton behaviour. + * + * To use, just make a subclass with only a private default constructor and Singleton + * as friend class. + */ +template +class Singleton : public sf::NonCopyable { + public: + /** + * Get the instance of this class. + */ + static T& i() { + static T s; + return s; + }; +}; + +#endif /* DG_SINGLETON_H_ */ diff --git a/source/util/String.h b/source/util/String.h new file mode 100644 index 0000000..add34c3 --- /dev/null +++ b/source/util/String.h @@ -0,0 +1,50 @@ +/* + * String.h + * + * Created on: 19.07.2012 + * Author: Felix + */ + +/** + * Use this as a replacement for std::to_string as MingW does not support it. + */ + +#ifndef DG_STRING_H_ +#define DG_STRING_H_ + +#include +#include + +#include + +/** + * Converts any value to a string. + * + * @param val Any variable. + * @return val converted to string. + */ +template +sf::String +str(T val) { + std::stringstream out; + out << val; + return out.str(); +} + +/** + * Converts floating point variable to string, + * + * @param val Any floating point variable. + * @param digits Number of decimal places to round to. + * @return val converted to string. + */ +template +sf::String +str(T val, int digits) { + std::stringstream out; + out.precision(digits); + out << val; + return out.str(); +} + +#endif /* DG_STRING_H_ */ diff --git a/source/util/Vector.h b/source/util/Vector.h new file mode 100755 index 0000000..6df33c9 --- /dev/null +++ b/source/util/Vector.h @@ -0,0 +1,84 @@ +/* + * Vector.h + * + * Created on: 03.08.2012 + * Author: Felix + */ + +#ifndef VECTOR_H_ +#define VECTOR_H_ + +#include + +#include + +#include + +#include + +/** + * 2D floating point vector with x/y members. + */ +typedef sf::Vector2f Vector2f; + +/** + * 2D integer vector with x/y members. + */ +typedef sf::Vector2i Vector2i; + +/** + * Constant for conversion between Box2D vectors and SFML vectors. + */ +static const int PIXELS_PER_METER = 25; + +/** + * Converts a distance from pixels to meters. + */ +inline float +pixelToMeter(float in) { + return in / PIXELS_PER_METER; +} + +/** + * Converts a distance from meters to pixels. + */ +inline float +meterToPixel(float in) { + return in * PIXELS_PER_METER; +} + +/** + * Converts Box2D metric vector to SFML pixel vector. + */ +inline Vector2f +vector(const b2Vec2& in) { + return Vector2f(meterToPixel(in.x), meterToPixel(in.y)); +} + +/** + * Converts SFML pixel vector to Box2D metric vector. + */ +inline b2Vec2 +vector(const Vector2f& in) { + return b2Vec2(pixelToMeter(in.x), pixelToMeter(in.y)); +} + +/** + * Converts a vector to an SFML angle with the same direction. + */ +inline float +angle(Vector2f in) { + return 180 - thor::toDegree(atan2(in.x, in.y)); +} + +/** + * Converts an SFML angle to a unit vector with the same direction. + */ +inline Vector2f +angle(float in) { + in = thor::toRadian(180 - in); + return Vector2f(sin(in), cos(in)); +} + + +#endif /* VECTOR_H_ */