PLAYTESTINGTIME
This commit is contained in:
@@ -82,6 +82,7 @@ Dungeon::Dungeon()
|
|||||||
Player->HitBox = new Collider{ 0, 0, 28, 36 };
|
Player->HitBox = new Collider{ 0, 0, 28, 36 };
|
||||||
|
|
||||||
Lives = new olc::Renderable();
|
Lives = new olc::Renderable();
|
||||||
|
Lives->Load("res/heart.png");
|
||||||
|
|
||||||
EnemyRenderable = new olc::Renderable();
|
EnemyRenderable = new olc::Renderable();
|
||||||
EnemyRenderable->Load("res/player.png");
|
EnemyRenderable->Load("res/player.png");
|
||||||
@@ -168,6 +169,11 @@ Dungeon::Dungeon()
|
|||||||
SoundFootsteps.setLoop(true);
|
SoundFootsteps.setLoop(true);
|
||||||
SoundFootsteps.setVolume(20.f);
|
SoundFootsteps.setVolume(20.f);
|
||||||
|
|
||||||
|
SoundBufferHit.loadFromFile("res/hit.wav");
|
||||||
|
SoundHit.setBuffer(SoundBufferHit);
|
||||||
|
SoundHit.setLoop(false);
|
||||||
|
SoundHit.setVolume(40.f);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Dungeon::Generate()
|
void Dungeon::Generate()
|
||||||
@@ -483,7 +489,7 @@ void Dungeon::Update(olc::PixelGameEngine* engine, float fTime)
|
|||||||
ActiveCamera->Update(fTime);
|
ActiveCamera->Update(fTime);
|
||||||
CollisionTick(engine);
|
CollisionTick(engine);
|
||||||
|
|
||||||
if (!HasBegun) return;
|
if (!HasBegun || !IsAlive || HasWon) return;
|
||||||
|
|
||||||
engine->SetDrawTarget(1);
|
engine->SetDrawTarget(1);
|
||||||
|
|
||||||
@@ -493,7 +499,7 @@ void Dungeon::Update(olc::PixelGameEngine* engine, float fTime)
|
|||||||
int enemyBoundsBottom = enemyBoundsTop + 720.0f;
|
int enemyBoundsBottom = enemyBoundsTop + 720.0f;
|
||||||
|
|
||||||
// spawn enemies
|
// spawn enemies
|
||||||
if (rand() % 100 < 1)
|
if (rand() % 100 < 1 && Enemies.size() < 20)
|
||||||
{
|
{
|
||||||
Enemy* enemy = new Enemy();
|
Enemy* enemy = new Enemy();
|
||||||
enemy->Type = EEntity::Type::Enemy;
|
enemy->Type = EEntity::Type::Enemy;
|
||||||
@@ -515,10 +521,11 @@ void Dungeon::Update(olc::PixelGameEngine* engine, float fTime)
|
|||||||
|
|
||||||
float distanceFromPlayer = vecDistance(Player->Coords, enemy->Coords);
|
float distanceFromPlayer = vecDistance(Player->Coords, enemy->Coords);
|
||||||
_Logger.Debug(enemy->Coords.x, " ", enemy->Coords.y, " ", distanceFromPlayer);
|
_Logger.Debug(enemy->Coords.x, " ", enemy->Coords.y, " ", distanceFromPlayer);
|
||||||
if (distanceFromPlayer > 300)
|
if (distanceFromPlayer > 300.0f)
|
||||||
{
|
{
|
||||||
enemy->HitBox = new Collider{ 0, 0, 28, 36 };
|
enemy->HitBox = new Collider{ 0, 0, 28, 36 };
|
||||||
Enemies.push_back(enemy);
|
Enemies.push_back(enemy);
|
||||||
|
StartedSpawning = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -534,18 +541,37 @@ void Dungeon::Update(olc::PixelGameEngine* engine, float fTime)
|
|||||||
enemy->dxdy = static_cast<float>(TileSize) * (fTime * (Player->Speed / 1.5f) * olc::vf2d(desiredLocation - enemy->Coords).norm());
|
enemy->dxdy = static_cast<float>(TileSize) * (fTime * (Player->Speed / 1.5f) * olc::vf2d(desiredLocation - enemy->Coords).norm());
|
||||||
enemy->Coords += enemy->dxdy;
|
enemy->Coords += enemy->dxdy;
|
||||||
|
|
||||||
|
float distanceFromPlayer = vecDistance(Player->Coords, enemy->Coords);
|
||||||
|
if (distanceFromPlayer > 550.0f)
|
||||||
|
{
|
||||||
|
Enemies.erase(Enemies.begin() + i);
|
||||||
|
delete enemy->HitBox;
|
||||||
|
delete enemy;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
bool colliding = EntityCollide(Player, enemy, engine);
|
bool colliding = EntityCollide(Player, enemy, engine);
|
||||||
if (!colliding) continue;
|
if (!colliding) continue;
|
||||||
|
SoundHit.play();
|
||||||
Player->Life--;
|
Player->Life--;
|
||||||
Enemies.erase(Enemies.begin() + i);
|
Enemies.erase(Enemies.begin() + i);
|
||||||
delete enemy->HitBox;
|
delete enemy->HitBox;
|
||||||
delete enemy;
|
delete enemy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StartedSpawning != true) return;
|
||||||
|
|
||||||
if (Player->Life <= 0)
|
if (Player->Life <= 0)
|
||||||
{
|
{
|
||||||
IsAlive = false;
|
IsAlive = false;
|
||||||
_Logger.Info("Died, health ", Player->Life);
|
_Logger.Info("Died, health ", Player->Life);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Enemies.size() == 0)
|
||||||
|
{
|
||||||
|
HasWon = true;
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -564,8 +590,6 @@ void Dungeon::Draw(olc::PixelGameEngine* engine, float fTime)
|
|||||||
// Maps not gonna be big enough for me to care about optimistaion
|
// Maps not gonna be big enough for me to care about optimistaion
|
||||||
// maybe i should care? i don't
|
// maybe i should care? i don't
|
||||||
|
|
||||||
// Entities are always (tilesize / 3) * 2
|
|
||||||
|
|
||||||
// Dungeon Layer
|
// Dungeon Layer
|
||||||
engine->SetDrawTarget(4);
|
engine->SetDrawTarget(4);
|
||||||
|
|
||||||
@@ -621,6 +645,7 @@ void Dungeon::Draw(olc::PixelGameEngine* engine, float fTime)
|
|||||||
{
|
{
|
||||||
SoundFire.play();
|
SoundFire.play();
|
||||||
LastPlayFire = true;
|
LastPlayFire = true;
|
||||||
|
IsLightOn = true;
|
||||||
HasFireLit = true;
|
HasFireLit = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -660,7 +685,54 @@ void Dungeon::Draw(olc::PixelGameEngine* engine, float fTime)
|
|||||||
|
|
||||||
engine->DrawString({15, 15}, std::to_string(Enemies.size()), olc::WHITE, 5);
|
engine->DrawString({15, 15}, std::to_string(Enemies.size()), olc::WHITE, 5);
|
||||||
|
|
||||||
|
for (int i = 1; i <= Player->Life; i++)
|
||||||
|
engine->DrawDecal({static_cast<float>(engine->ScreenWidth() - (static_cast<float>(i) * Lives->Sprite()->width)) ,0.0f}, Lives->Decal());
|
||||||
|
|
||||||
|
if (!IsAlive)
|
||||||
|
{
|
||||||
|
SoundFire.stop();
|
||||||
|
engine->SetDrawTarget(uint8_t(0));
|
||||||
|
engine->Clear({38, 36, 40});
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 - 76}, "your demons overran you", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 - 38}, "the machine's manifestations won", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2}, "you are nothing to them", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 + 38}, "you never were", olc::WHITE, 4);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasWon)
|
||||||
|
{
|
||||||
|
IsFireLit = false;
|
||||||
|
SoundFire.stop();
|
||||||
|
engine->SetDrawTarget(uint8_t(0));
|
||||||
|
engine->Clear({38, 36, 40});
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 - 76}, "you did it..", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 - 38}, "you outran all your demons", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2}, "but your demons never really leave", olc::WHITE, 4);
|
||||||
|
engine->DrawString({15, engine->ScreenHeight() / 2 + 76}, "welcome to the great machine", olc::WHITE, 4);
|
||||||
|
engine->DrawString({5, engine->ScreenHeight() - 15}, "Press enter to enter the great machine", olc::WHITE);
|
||||||
|
|
||||||
|
if (engine->GetKey(olc::ENTER).bPressed)
|
||||||
|
{
|
||||||
|
_Logger.Info("Restart");
|
||||||
|
Player->Life = 7;
|
||||||
|
Player->Coords = { 5.0f, 5.0f };
|
||||||
|
ActiveCamera->TrackEntity(Player);
|
||||||
|
IsFireLit = true;
|
||||||
|
StartedSpawning = false;
|
||||||
|
IsAlive = true;
|
||||||
|
HasBegun = false;
|
||||||
|
HasWon = false;
|
||||||
|
FireAccumilator = 0.0f;
|
||||||
|
WasFireLit = false;
|
||||||
|
HasFireLit = false;
|
||||||
|
LastPlayFire = false;
|
||||||
|
PlayFire = false;
|
||||||
|
IsLightOn = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ class Dungeon
|
|||||||
Playable* Player;
|
Playable* Player;
|
||||||
Camera* ActiveCamera;
|
Camera* ActiveCamera;
|
||||||
|
|
||||||
|
bool StartedSpawning = false;
|
||||||
olc::Renderable* EnemyRenderable;
|
olc::Renderable* EnemyRenderable;
|
||||||
olc::AnimatedSprite* EnemyAnimator;
|
olc::AnimatedSprite* EnemyAnimator;
|
||||||
std::vector<Enemy*> Enemies;
|
std::vector<Enemy*> Enemies;
|
||||||
@@ -66,6 +67,9 @@ class Dungeon
|
|||||||
sf::SoundBuffer SoundBufferFootsteps;
|
sf::SoundBuffer SoundBufferFootsteps;
|
||||||
sf::Sound SoundFootsteps;
|
sf::Sound SoundFootsteps;
|
||||||
|
|
||||||
|
sf::SoundBuffer SoundBufferHit;
|
||||||
|
sf::Sound SoundHit;
|
||||||
|
|
||||||
~Dungeon();
|
~Dungeon();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -1,689 +0,0 @@
|
|||||||
#include "Dungeon.hpp"
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <sstream>
|
|
||||||
|
|
||||||
#include "olcPixelGameEngine.hpp"
|
|
||||||
#include "olcPGEX_AnimatedSprite.hpp"
|
|
||||||
|
|
||||||
#include "Collisions.hpp"
|
|
||||||
#include "Things.hpp"
|
|
||||||
#include "Camera.hpp"
|
|
||||||
#include "Logger.hpp"
|
|
||||||
|
|
||||||
|
|
||||||
void PerlinNoise1D(int nCount, float *fSeed, int nOctaves, float fBias, float *fOutput)
|
|
||||||
{
|
|
||||||
for (int x = 0; x < nCount; x++)
|
|
||||||
{
|
|
||||||
float fNoise = 0.0f;
|
|
||||||
float fScaleAcc = 0.0f;
|
|
||||||
float fScale = 1.0f / fBias;
|
|
||||||
|
|
||||||
for (int o = 0; o < nOctaves; o++)
|
|
||||||
{
|
|
||||||
int nPitch = nCount >> o;
|
|
||||||
int nSample1 = (x / nPitch) * nPitch;
|
|
||||||
int nSample2 = (nSample1 + nPitch) % nCount;
|
|
||||||
|
|
||||||
float fBlend = (float)(x - nSample1) / (float)nPitch;
|
|
||||||
|
|
||||||
float fSample = (1.0f - fBlend) * fSeed[nSample1] + fBlend * fSeed[nSample2];
|
|
||||||
|
|
||||||
fScaleAcc += fScale;
|
|
||||||
fNoise += fSample * fScale;
|
|
||||||
// fScale = fScale / fBias;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Scale to seed range
|
|
||||||
fOutput[x] = fNoise / fScaleAcc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int perlinSize = 10000;
|
|
||||||
static float* perlinSeed;
|
|
||||||
static float* perlinOutput;
|
|
||||||
|
|
||||||
void SetupPerlin()
|
|
||||||
{
|
|
||||||
perlinSeed = (float*)malloc(sizeof(float) * perlinSize);
|
|
||||||
perlinOutput = (float*)malloc(sizeof(float) * perlinSize);
|
|
||||||
for (int i = 0; i < perlinSize; i++)
|
|
||||||
perlinSeed[i] = static_cast<float>(rand()) / static_cast<float>(RAND_MAX);
|
|
||||||
|
|
||||||
PerlinNoise1D(perlinSize, perlinSeed, 8, 2.0f, perlinOutput);
|
|
||||||
}
|
|
||||||
|
|
||||||
float GetNextPerlin()
|
|
||||||
{
|
|
||||||
static int i = 0;
|
|
||||||
i++;
|
|
||||||
if (i > perlinSize)
|
|
||||||
i = 0;
|
|
||||||
return perlinOutput[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Dungeon::Dungeon()
|
|
||||||
: _Logger(Logger::getInstance())
|
|
||||||
{
|
|
||||||
SetupPerlin();
|
|
||||||
|
|
||||||
ActiveCamera = new Camera();
|
|
||||||
ActiveCamera->Coords = { 0, 0 };
|
|
||||||
ActiveCamera->ViewPort = { 1280, 720 };
|
|
||||||
|
|
||||||
Player = new Playable();
|
|
||||||
|
|
||||||
Player->Coords = { 2, 2 };
|
|
||||||
Player->Type = EEntity::Type::Player;
|
|
||||||
// Relative to player TL corner
|
|
||||||
// not really used ? lol
|
|
||||||
Player->HitBox = new Collider{ 0, 0, 28, 36 };
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
EnemyRenderable = new olc::Renderable();
|
|
||||||
EnemyRenderable->Load("res/player.png");
|
|
||||||
EnemyAnimator = new olc::AnimatedSprite();
|
|
||||||
EnemyAnimator->mode = olc::AnimatedSprite::SPRITE_MODE::SINGLE;
|
|
||||||
EnemyAnimator->type = olc::AnimatedSprite::SPRITE_TYPE::DECAL;
|
|
||||||
EnemyAnimator->spriteSheet = EnemyRenderable;
|
|
||||||
EnemyAnimator->SetSpriteSize({28, 36});
|
|
||||||
|
|
||||||
EnemyAnimator->AddState("idle", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{28, 0}
|
|
||||||
});
|
|
||||||
EnemyAnimator->SetState("idle");
|
|
||||||
|
|
||||||
|
|
||||||
Player->Renderable = new olc::Renderable();
|
|
||||||
Player->Renderable->Load("res/player.png");
|
|
||||||
Player->Animator = new olc::AnimatedSprite();
|
|
||||||
Player->Animator->mode = olc::AnimatedSprite::SPRITE_MODE::SINGLE;
|
|
||||||
Player->Animator->type = olc::AnimatedSprite::SPRITE_TYPE::DECAL;
|
|
||||||
Player->Animator->spriteSheet = Player->Renderable;
|
|
||||||
Player->Animator->SetSpriteSize({28, 36});
|
|
||||||
|
|
||||||
Player->Animator->AddState("idle", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{28, 0}
|
|
||||||
});
|
|
||||||
|
|
||||||
Player->Animator->AddState("north", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{0, 110},
|
|
||||||
{28, 110},
|
|
||||||
{56, 110},
|
|
||||||
{84, 110}
|
|
||||||
});
|
|
||||||
Player->Animator->AddState("east", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{0, 36},
|
|
||||||
{28, 36},
|
|
||||||
{56, 36},
|
|
||||||
{84, 36}
|
|
||||||
});
|
|
||||||
Player->Animator->AddState("south", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{0, 0},
|
|
||||||
{28, 0},
|
|
||||||
{56, 0},
|
|
||||||
{84, 0}
|
|
||||||
});
|
|
||||||
Player->Animator->AddState("west", 0.127f, olc::AnimatedSprite::PLAY_MODE::LOOP, std::vector<olc::vi2d>{
|
|
||||||
{0, 72},
|
|
||||||
{28, 72},
|
|
||||||
{56, 72},
|
|
||||||
{84, 72}
|
|
||||||
});
|
|
||||||
Player->Animator->SetState("idle");
|
|
||||||
|
|
||||||
ActiveCamera->TrackEntity(Player);
|
|
||||||
ActiveCamera->Update(0.0f);
|
|
||||||
|
|
||||||
TileSetDictionary = new TileDictionary();
|
|
||||||
TileSetDictionary->Register();
|
|
||||||
|
|
||||||
TileSet = new olc::Renderable();
|
|
||||||
TileSet->Load("res/dungeon_tileset.png");
|
|
||||||
_Logger.Debug("Texture Loaded: ", TileSet, " ", TileSet->Sprite()->width, " ", TileSet->Sprite()->height);
|
|
||||||
|
|
||||||
FireOverlay = new olc::Renderable();
|
|
||||||
FireOverlay->Load("res/torch.png");
|
|
||||||
|
|
||||||
SoundBufferFireLighting.loadFromFile("res/fire_start.wav");
|
|
||||||
SoundFireLighting.setBuffer(SoundBufferFireLighting);
|
|
||||||
SoundFireLighting.setLoop(false);
|
|
||||||
SoundFireLighting.setVolume(50.f);
|
|
||||||
|
|
||||||
SoundBufferFire.loadFromFile("res/fire.wav");
|
|
||||||
SoundFire.setBuffer(SoundBufferFire);
|
|
||||||
SoundFire.setLoop(true);
|
|
||||||
SoundFire.setVolume(100.f);
|
|
||||||
|
|
||||||
SoundBufferAmbient.loadFromFile("res/ambient.ogg");
|
|
||||||
SoundAmbient.setBuffer(SoundBufferAmbient);
|
|
||||||
SoundAmbient.setLoop(true);
|
|
||||||
SoundAmbient.play();
|
|
||||||
|
|
||||||
SoundBufferFootsteps.loadFromFile("res/run.ogg");
|
|
||||||
SoundFootsteps.setBuffer(SoundBufferFootsteps);
|
|
||||||
SoundFootsteps.setLoop(true);
|
|
||||||
SoundFootsteps.setVolume(20.f);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dungeon::Generate()
|
|
||||||
{
|
|
||||||
srand(time(NULL));
|
|
||||||
|
|
||||||
DungeonWidth = 0;
|
|
||||||
DungeonHeight = 0;
|
|
||||||
|
|
||||||
// Generate a dungeon
|
|
||||||
// Loosely follows the algorithm in section 3.3 "Agent Based Growing"
|
|
||||||
// http://pcgbook.com/wp-content/uploads/chapter03.pdf
|
|
||||||
|
|
||||||
struct Room
|
|
||||||
{
|
|
||||||
int x, y, w, h;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto randomRoom = [](int x, int y)
|
|
||||||
{ return new Room{ x, y, (rand() % 7) + 3, (rand() % 7) + 3 }; };
|
|
||||||
|
|
||||||
std::vector<olc::vi2d> tiles;
|
|
||||||
|
|
||||||
// Starting at 0,0
|
|
||||||
|
|
||||||
int directionChance = 5;
|
|
||||||
int roomChance = 5;
|
|
||||||
int dungeonMinSize = 3000;
|
|
||||||
|
|
||||||
struct Agent
|
|
||||||
{
|
|
||||||
// 0 up 1 right 2 down 3 left
|
|
||||||
int x, y, direction;
|
|
||||||
};
|
|
||||||
|
|
||||||
Agent* agent = new Agent();
|
|
||||||
agent->x = 0; agent->y = 0;
|
|
||||||
agent->direction = rand() % 4;
|
|
||||||
|
|
||||||
_Logger.Debug("Agent ", agent->x, " ", agent->y, " ", agent->direction);
|
|
||||||
|
|
||||||
auto addTile = [&](int x, int y) {
|
|
||||||
for (auto i : tiles)
|
|
||||||
if (i.x == x && i.y == y)
|
|
||||||
return;
|
|
||||||
tiles.push_back({ x, y });
|
|
||||||
};
|
|
||||||
|
|
||||||
addTile(0, 0);
|
|
||||||
|
|
||||||
while (tiles.size() < dungeonMinSize)
|
|
||||||
{
|
|
||||||
switch (agent->direction)
|
|
||||||
{
|
|
||||||
case 0: agent->y -= 1; break;
|
|
||||||
case 1: agent->x += 1; break;
|
|
||||||
case 2: agent->y += 1; break;
|
|
||||||
case 3: agent->x -= 1; break;
|
|
||||||
}
|
|
||||||
addTile(agent->x, agent->y);
|
|
||||||
|
|
||||||
if (rand() % 200 < directionChance)
|
|
||||||
{
|
|
||||||
agent->direction = rand() % 4;
|
|
||||||
directionChance = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
directionChance += 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rand() % 300 < roomChance)
|
|
||||||
{
|
|
||||||
Room* room = randomRoom(agent->x, agent->y);
|
|
||||||
for (int x = room->x; x < room->w + room->x; x++)
|
|
||||||
for (int y = room->y; y < room->h + room->y; y++)
|
|
||||||
addTile(x, y);
|
|
||||||
delete room;
|
|
||||||
roomChance = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
roomChance += 5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (auto& tile : tiles)
|
|
||||||
{
|
|
||||||
if (DungeonWidth <= tile.x) DungeonWidth = tile.x;
|
|
||||||
if (DungeonHeight <= tile.y) DungeonHeight = tile.y;
|
|
||||||
|
|
||||||
ETile::Type tileType = rand() % 100 > 1 ? ETile::Type::Floor : ETile::Type::FloorV1;
|
|
||||||
|
|
||||||
auto DoesTileExist = [&](olc::vi2d tile)
|
|
||||||
{
|
|
||||||
return std::find(tiles.begin(), tiles.end(), tile) != tiles.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
bool tileBelow = DoesTileExist({ tile.x, tile.y + 1 });
|
|
||||||
bool tileAbove = DoesTileExist({ tile.x, tile.y - 1 });
|
|
||||||
bool tileLeft = DoesTileExist({ tile.x - 1, tile.y });
|
|
||||||
bool tileRight = DoesTileExist({ tile.x + 1, tile.y });
|
|
||||||
|
|
||||||
// Order here is very important! stop fucking it up
|
|
||||||
|
|
||||||
if (!tileAbove)
|
|
||||||
tileType = ETile::Type::WallU;
|
|
||||||
if (!tileRight)
|
|
||||||
tileType = ETile::Type::WallR;
|
|
||||||
if (!tileBelow)
|
|
||||||
tileType = ETile::Type::WallD;
|
|
||||||
if (!tileLeft)
|
|
||||||
tileType = ETile::Type::WallL;
|
|
||||||
|
|
||||||
if (!tileAbove && !tileBelow)
|
|
||||||
tileType = ETile::Type::PathAcross;
|
|
||||||
if (!tileRight && !tileLeft)
|
|
||||||
tileType = ETile::Type::PathUp;
|
|
||||||
|
|
||||||
if (!tileAbove && !tileLeft)
|
|
||||||
tileType = ETile::Type::WallTLCorner;
|
|
||||||
if (!tileAbove && !tileRight)
|
|
||||||
tileType = ETile::Type::WallTRCorner;
|
|
||||||
if (!tileBelow && !tileLeft)
|
|
||||||
tileType = ETile::Type::WallBLCorner;
|
|
||||||
if (!tileBelow && !tileRight)
|
|
||||||
tileType = ETile::Type::WallBRCorner;
|
|
||||||
|
|
||||||
if (!tileAbove && !tileBelow && !tileRight)
|
|
||||||
tileType = ETile::Type::PathRight;
|
|
||||||
if (!tileAbove && !tileBelow && !tileLeft)
|
|
||||||
tileType = ETile::Type::PathLeft;
|
|
||||||
if (!tileRight && !tileLeft && !tileAbove)
|
|
||||||
tileType = ETile::Type::PathTop;
|
|
||||||
if (!tileRight && !tileLeft && !tileBelow)
|
|
||||||
tileType = ETile::Type::PathBottom;
|
|
||||||
|
|
||||||
// Is the tile below free? if so, make it 3D
|
|
||||||
if (!tileBelow)
|
|
||||||
{
|
|
||||||
Tile* psuedo3DTile = new Tile({ tile.x, tile.y + 1 },
|
|
||||||
ETile::Type::ThreeDStandard, ETile::State::Default);
|
|
||||||
DungeonTiles[psuedo3DTile->Coords] = psuedo3DTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
Tile* t = new Tile(tile, tileType, ETile::State::Default);
|
|
||||||
DungeonTiles[t->Coords] = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dungeon::Input(olc::PixelGameEngine* engine, float fTime)
|
|
||||||
{
|
|
||||||
|
|
||||||
olc::vf2d oldCoords = Player->Coords;
|
|
||||||
|
|
||||||
static std::string state = "idle";
|
|
||||||
static std::string lastState = "idle";
|
|
||||||
static bool WasMoving = false;
|
|
||||||
bool IsMoving = false;
|
|
||||||
|
|
||||||
if (engine->GetKey(olc::W).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y -= static_cast<float>(TileSize) * (fTime * Player->Speed);
|
|
||||||
if (state != "north")
|
|
||||||
state = "north";
|
|
||||||
IsMoving = true;
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::A).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.x -= static_cast<float>(TileSize) * (fTime * Player->Speed);
|
|
||||||
if (state != "west")
|
|
||||||
state = "west";
|
|
||||||
IsMoving = true;
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::S).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y += static_cast<float>(TileSize) * (fTime * Player->Speed);
|
|
||||||
if (state != "south")
|
|
||||||
state = "south";
|
|
||||||
IsMoving = true;
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::D).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.x += static_cast<float>(TileSize) * (fTime * Player->Speed);
|
|
||||||
if (state != "east")
|
|
||||||
state = "east";
|
|
||||||
IsMoving = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (engine->GetKey(olc::W).bHeld && engine->GetKey(olc::A).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y += static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
Player->Coords.x += static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
if (state != "west")
|
|
||||||
state = "west";
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::W).bHeld && engine->GetKey(olc::D).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y += static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
Player->Coords.x -= static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
if (state != "east")
|
|
||||||
state = "east";
|
|
||||||
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::S).bHeld && engine->GetKey(olc::D).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y -= static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
Player->Coords.x -= static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
if (state != "east")
|
|
||||||
state = "east";
|
|
||||||
}
|
|
||||||
if (engine->GetKey(olc::S).bHeld && engine->GetKey(olc::A).bHeld)
|
|
||||||
{
|
|
||||||
Player->Coords.y -= static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
Player->Coords.x += static_cast<float>(TileSize) * (fTime * (Player->Speed / 3.0f));
|
|
||||||
if (state != "west")
|
|
||||||
state = "west";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!WasMoving && IsMoving)
|
|
||||||
{
|
|
||||||
SoundFootsteps.setPlayingOffset(sf::seconds(0.05f));
|
|
||||||
SoundFootsteps.play();
|
|
||||||
}
|
|
||||||
if (WasMoving && !IsMoving)
|
|
||||||
{
|
|
||||||
SoundFootsteps.stop();
|
|
||||||
}
|
|
||||||
WasMoving = IsMoving;
|
|
||||||
|
|
||||||
if (oldCoords == Player->Coords)
|
|
||||||
{
|
|
||||||
state = "idle";
|
|
||||||
SoundFootsteps.pause();
|
|
||||||
}
|
|
||||||
if (state != lastState)
|
|
||||||
Player->Animator->SetState(state);
|
|
||||||
lastState = state;
|
|
||||||
|
|
||||||
// Map collisions
|
|
||||||
olc::vi2d currentTile = { static_cast<int>(Player->Coords.x / TileSize), static_cast<int>(Player->Coords.y / TileSize) };
|
|
||||||
|
|
||||||
static olc::vi2d lastTile;
|
|
||||||
|
|
||||||
if (lastTile != currentTile)
|
|
||||||
_Logger.Debug("Player Tile: ", currentTile.x, " ", currentTile.y);
|
|
||||||
|
|
||||||
auto IsMapMember = [&] (int x, int y) {
|
|
||||||
std::unordered_map<olc::vi2d, Tile*>::const_iterator found = DungeonTiles.find({x, y});
|
|
||||||
return found != DungeonTiles.end();
|
|
||||||
};
|
|
||||||
|
|
||||||
auto IsNeighbouringMapMember = [&] (int x, int y) {
|
|
||||||
if (IsMapMember(x + 1, y)) return true;
|
|
||||||
if (IsMapMember(x - 1, y)) return true;
|
|
||||||
if (IsMapMember(x, y + 1)) return true;
|
|
||||||
if (IsMapMember(x, y - 1)) return true;
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
||||||
// get nearby collidables
|
|
||||||
std::vector<Tile*> nearbyCollidables;
|
|
||||||
|
|
||||||
for (int x = currentTile.x - 1; x <= currentTile.x + 1; x++)
|
|
||||||
for (int y = currentTile.y - 1; y <= currentTile.y + 1; y++)
|
|
||||||
{
|
|
||||||
//_Logger.Debug("IsMapMember ", x, " ", y, " ", IsMapMember(x,y));
|
|
||||||
//_Logger.Debug("IsNeighbouringMapMember ", x, " ", y, " ", IsNeighbouringMapMember(x,y));
|
|
||||||
|
|
||||||
// If isnt in the map but borders a map tile, add a void type for collision
|
|
||||||
if (IsMapMember(x,y))
|
|
||||||
{
|
|
||||||
nearbyCollidables.push_back(DungeonTiles[{x, y}]);
|
|
||||||
} else
|
|
||||||
{
|
|
||||||
if (IsNeighbouringMapMember(x,y))
|
|
||||||
nearbyCollidables.push_back(new Tile({x, y}, ETile::Type::Void, ETile::State::Default));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Passes ownership back
|
|
||||||
CollisionInfo collisionInfo;
|
|
||||||
bool colliding = EntityCollideDungeon(Player, nearbyCollidables, TileSize, &collisionInfo, engine);
|
|
||||||
|
|
||||||
// collision response
|
|
||||||
if (colliding)
|
|
||||||
Player->Coords = oldCoords;
|
|
||||||
|
|
||||||
//if (collisionInfo.CollidingY)
|
|
||||||
//Player->Coords.y = oldCoords.y;
|
|
||||||
//if (collisionInfo.CollidingX)
|
|
||||||
//Player->Coords.x = oldCoords.x;
|
|
||||||
|
|
||||||
// delete nearbyCollidables that are void
|
|
||||||
for (int i = 0; i < nearbyCollidables.size(); i++)
|
|
||||||
{
|
|
||||||
if (nearbyCollidables[i]->Type == ETile::Type::Void)
|
|
||||||
delete nearbyCollidables[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
lastTile = currentTile;
|
|
||||||
}
|
|
||||||
|
|
||||||
template<typename T>
|
|
||||||
float vecDistance(T v1, T v2)
|
|
||||||
{
|
|
||||||
return sqrt(pow(v2.x - v1.x, 2.0f) + pow(v2.y - v1.y, 2.0f) * 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void Dungeon::Update(olc::PixelGameEngine* engine, float fTime)
|
|
||||||
{
|
|
||||||
ActiveCamera->Update(fTime);
|
|
||||||
CollisionTick(engine);
|
|
||||||
|
|
||||||
if (!HasBegun) return;
|
|
||||||
|
|
||||||
engine->SetDrawTarget(1);
|
|
||||||
|
|
||||||
int enemyBoundsLeft = static_cast<int>(Player->Coords.x - (1280.0f / 2.0f));
|
|
||||||
int enemyBoundsTop = static_cast<int>(Player->Coords.y - (720.0f / 2.0f));
|
|
||||||
int enemyBoundsRight = enemyBoundsLeft + 1280.0f;
|
|
||||||
int enemyBoundsBottom = enemyBoundsTop + 720.0f;
|
|
||||||
|
|
||||||
// spawn enemies
|
|
||||||
if (rand() % 100 < 1)
|
|
||||||
{
|
|
||||||
Enemy* enemy = new Enemy();
|
|
||||||
enemy->Type = EEntity::Type::Enemy;
|
|
||||||
enemy->Renderable = EnemyRenderable;
|
|
||||||
enemy->Animator = EnemyAnimator;
|
|
||||||
|
|
||||||
// This is naive I KNOW. fuck off
|
|
||||||
// stops integer divisions by 0
|
|
||||||
if (enemyBoundsLeft == 0) enemyBoundsLeft = 1;
|
|
||||||
if (enemyBoundsTop == 0) enemyBoundsTop = 1;
|
|
||||||
|
|
||||||
auto rangedRand = [](float min, float max) -> int {
|
|
||||||
return static_cast<int>(min) + rand() % ((static_cast<int>(max) + 1) - static_cast<int>(min));
|
|
||||||
};
|
|
||||||
|
|
||||||
enemy->Coords = { static_cast<float>(rangedRand(enemyBoundsLeft, enemyBoundsRight)), static_cast<float>(rangedRand(enemyBoundsTop, enemyBoundsBottom) ) };
|
|
||||||
|
|
||||||
enemy->dxdy = { static_cast<float>(rand() % 10 - 5), static_cast<float>(rand() % 10 - 5) };
|
|
||||||
|
|
||||||
float distanceFromPlayer = vecDistance(Player->Coords, enemy->Coords);
|
|
||||||
_Logger.Debug(enemy->Coords.x, " ", enemy->Coords.y, " ", distanceFromPlayer);
|
|
||||||
if (distanceFromPlayer > 300)
|
|
||||||
{
|
|
||||||
enemy->HitBox = new Collider{ 0, 0, 28, 36 };
|
|
||||||
Enemies.push_back(enemy);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delete enemy;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
olc::vf2d desiredLocation = Player->Coords;
|
|
||||||
for (int i = 0; i < Enemies.size(); i++)
|
|
||||||
{
|
|
||||||
Enemy* enemy = Enemies[i];
|
|
||||||
|
|
||||||
enemy->dxdy = static_cast<float>(TileSize) * (fTime * (Player->Speed / 1.5f) * olc::vf2d(desiredLocation - enemy->Coords).norm());
|
|
||||||
enemy->Coords += enemy->dxdy;
|
|
||||||
|
|
||||||
bool colliding = EntityCollide(Player, enemy, engine);
|
|
||||||
if (!colliding) continue;
|
|
||||||
Player->Life--;
|
|
||||||
Enemies.erase(Enemies.begin() + i);
|
|
||||||
delete enemy->HitBox;
|
|
||||||
delete enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Player->Life <= 0)
|
|
||||||
{
|
|
||||||
IsAlive = false;
|
|
||||||
_Logger.Info("Died, health ", Player->Life);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
olc::Pixel pixelMultiply(const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest)
|
|
||||||
{
|
|
||||||
olc::Pixel ret;
|
|
||||||
ret.r = pDest.r * pSource.r;
|
|
||||||
ret.g = pDest.g * pSource.g;
|
|
||||||
ret.b = pDest.b * pSource.b;
|
|
||||||
ret.a = pDest.a * pSource.a;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Dungeon::Draw(olc::PixelGameEngine* engine, float fTime)
|
|
||||||
{
|
|
||||||
// Maps not gonna be big enough for me to care about optimistaion
|
|
||||||
// maybe i should care? i don't
|
|
||||||
|
|
||||||
// Entities are always (tilesize / 3) * 2
|
|
||||||
|
|
||||||
// Dungeon Layer
|
|
||||||
engine->SetDrawTarget(4);
|
|
||||||
|
|
||||||
engine->SetPixelMode(olc::Pixel::ALPHA);
|
|
||||||
for (std::pair<olc::vi2d, Tile*> tile : DungeonTiles)
|
|
||||||
|
|
||||||
engine->DrawPartialDecal({ static_cast<float>((tile.first.x * TileSize) - ActiveCamera->Coords.x), static_cast<float>((tile.first.y * TileSize) - ActiveCamera->Coords.y) },
|
|
||||||
{ static_cast<float>(TileSize), static_cast<float>(TileSize) }, TileSet->Decal(), TileSetDictionary->Dictionary[tile.second->Type], { 16, 16 });
|
|
||||||
|
|
||||||
// Entity Layer
|
|
||||||
engine->SetDrawTarget(3);
|
|
||||||
|
|
||||||
// Draw character
|
|
||||||
Player->Animator->Draw(fTime, {Player->Coords.x - ActiveCamera->Coords.x, Player->Coords.y - ActiveCamera->Coords.y});
|
|
||||||
|
|
||||||
for (int i = 0; i < Enemies.size(); i++)
|
|
||||||
{
|
|
||||||
Enemies[i]->Animator->SetState("idle");
|
|
||||||
Enemies[i]->Animator->Draw(fTime, {Enemies[i]->Coords.x - ActiveCamera->Coords.x, Enemies[i]->Coords.y - ActiveCamera->Coords.y});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Lighting layers
|
|
||||||
engine->SetDrawTarget(2);
|
|
||||||
|
|
||||||
static bool WasFireLit = false;
|
|
||||||
static bool HasFireLit = false;
|
|
||||||
static float FireAccumilator = 0.0f;
|
|
||||||
|
|
||||||
if (engine->GetKey(olc::F).bPressed) IsFireLit = true;
|
|
||||||
|
|
||||||
if (!IsLightOn && !HasFireLit)
|
|
||||||
{
|
|
||||||
engine->FillRectDecal({0.0f, 0.0f}, {static_cast<float>(engine->ScreenWidth()), static_cast<float>(engine->ScreenHeight())}, olc::Pixel(0,0,0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IsFireLit) return;
|
|
||||||
|
|
||||||
// Wait for fire to get going to render
|
|
||||||
if (WasFireLit != IsFireLit)
|
|
||||||
{
|
|
||||||
SoundFireLighting.setPlayingOffset(sf::seconds(0.3f));
|
|
||||||
SoundFireLighting.play();
|
|
||||||
}
|
|
||||||
WasFireLit = true;
|
|
||||||
static bool LastPlayFire = false;
|
|
||||||
static bool PlayFire = false;
|
|
||||||
FireAccumilator += fTime;
|
|
||||||
if (FireAccumilator > 1.4f)
|
|
||||||
PlayFire = true;
|
|
||||||
if (FireAccumilator < 1.4f)
|
|
||||||
return;
|
|
||||||
if (LastPlayFire != PlayFire)
|
|
||||||
{
|
|
||||||
SoundFire.play();
|
|
||||||
LastPlayFire = true;
|
|
||||||
HasFireLit = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
HasBegun = true;
|
|
||||||
|
|
||||||
static std::function<olc::Pixel(const int x, const int y, const olc::Pixel& pSource, const olc::Pixel& pDest)> fPixelMultiply = pixelMultiply;
|
|
||||||
|
|
||||||
// loads to make it more chaotic
|
|
||||||
float lightScale = 1.2f + GetNextPerlin(); GetNextPerlin(); GetNextPerlin(); GetNextPerlin(); GetNextPerlin();
|
|
||||||
|
|
||||||
float lightX = static_cast<float>(Player->Coords.x - ActiveCamera->Coords.x) - ((FireOverlay->Sprite()->width * lightScale) / 2.0f) + static_cast<float>(Player->HitBox->w) / 2.0f;
|
|
||||||
float lightY = static_cast<float>(Player->Coords.y - ActiveCamera->Coords.y) - ((FireOverlay->Sprite()->height * lightScale) / 2.0f) + static_cast<float>(Player->HitBox->h) / 2.0f;
|
|
||||||
|
|
||||||
float lightLeft = lightX + 1.0f;
|
|
||||||
float lightRight = lightX + FireOverlay->Sprite()->width * lightScale - 1.0f;
|
|
||||||
float lightTop = lightY + 1.0f;
|
|
||||||
float lightBottom = lightY + FireOverlay->Sprite()->height * lightScale - 1.0f;
|
|
||||||
|
|
||||||
// orange tint
|
|
||||||
engine->FillRectDecal({0.0f, 0.0f}, {static_cast<float>(engine->ScreenWidth()), static_cast<float>(engine->ScreenHeight())}, olc::Pixel(255, 123, 0, 30));
|
|
||||||
|
|
||||||
engine->SetPixelMode(fPixelMultiply);
|
|
||||||
engine->DrawDecal({ lightX, lightY }, FireOverlay->Decal(), {lightScale, lightScale}, olc::RED);
|
|
||||||
engine->SetPixelMode(olc::Pixel::ALPHA);
|
|
||||||
|
|
||||||
// top
|
|
||||||
engine->FillRectDecal({0.0f, 0.0f}, {static_cast<float>(engine->ScreenWidth()), lightTop}, olc::BLACK);
|
|
||||||
// right
|
|
||||||
engine->FillRectDecal({lightRight, 0.0f}, {static_cast<float>(engine->ScreenWidth()) - lightRight, static_cast<float>(engine->ScreenHeight())}, olc::BLACK);
|
|
||||||
// bottom
|
|
||||||
engine->FillRectDecal({0.0f, lightBottom}, {static_cast<float>(engine->ScreenWidth()), static_cast<float>(engine->ScreenHeight()) - lightBottom}, olc::BLACK);
|
|
||||||
// left
|
|
||||||
engine->FillRectDecal({0.0f, 0.0f}, {lightLeft, static_cast<float>(engine->ScreenHeight())}, olc::BLACK);
|
|
||||||
|
|
||||||
// UI Layer
|
|
||||||
engine->SetDrawTarget(1);
|
|
||||||
|
|
||||||
engine->DrawString({15, 15}, std::to_string(Enemies.size()), olc::WHITE, 5);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
Dungeon::~Dungeon()
|
|
||||||
{
|
|
||||||
delete Player->HitBox;
|
|
||||||
delete Player->Renderable;
|
|
||||||
delete Player->Animator;
|
|
||||||
delete Player;
|
|
||||||
|
|
||||||
delete ActiveCamera;
|
|
||||||
delete TileSetDictionary;
|
|
||||||
delete TileSet;
|
|
||||||
for (std::pair<olc::vi2d, Tile*> tile : DungeonTiles)
|
|
||||||
delete tile.second;
|
|
||||||
|
|
||||||
for (auto enemy : Enemies)
|
|
||||||
{
|
|
||||||
delete enemy->HitBox;
|
|
||||||
delete enemy;
|
|
||||||
}
|
|
||||||
|
|
||||||
free(perlinSeed);
|
|
||||||
free(perlinOutput);
|
|
||||||
}
|
|
||||||
BIN
res/ActionVFX_Bullet_Hits_SFX_0322.zip
Normal file
BIN
res/ActionVFX_Bullet_Hits_SFX_0322.zip
Normal file
Binary file not shown.
BIN
res/heart.png
BIN
res/heart.png
Binary file not shown.
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 5.0 KiB |
BIN
res/hit.wav
Normal file
BIN
res/hit.wav
Normal file
Binary file not shown.
Reference in New Issue
Block a user