Skip to content

Commit

Permalink
next level teleporters
Browse files Browse the repository at this point in the history
  • Loading branch information
Cadecraft committed Mar 31, 2024
1 parent b00da87 commit b557897
Show file tree
Hide file tree
Showing 14 changed files with 183 additions and 54 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ add_executable(PlatScifi
src/worldobjects/player.h
src/worldobjects/staticcollider.h
src/worldobjects/staticdeadly.h
src/worldobjects/leveltp.h
src/worldobjects/spike.h
)
target_link_libraries(PlatScifi PRIVATE sfml-graphics)
Expand Down
7 changes: 5 additions & 2 deletions src/assets/level_0.csv
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Example level (level_0.csv)
// Example level (level_0)
nextLevelName level_1
// Bottom colliders
StaticCollider, 3, 25, 6, 2
StaticCollider, 9, 27, 10, 2
Expand All @@ -11,4 +12,6 @@ StaticCollider, 23, 29, 44, 19
// Spike
Spike, 28, 28
// Static deadly section
StaticDeadly, 33, 28, 5, 2
StaticDeadly, 33, 28, 5, 2
// Ending teleporter
LevelTp, 55, 28
7 changes: 7 additions & 0 deletions src/assets/level_1.csv
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Example level (level_1)
// Cycle back to the start
nextLevelName level_0
// Bottom collider
StaticCollider, 2, 30, 30, 2
// Next level
LevelTp, 50, 29
36 changes: 30 additions & 6 deletions src/gamestate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,28 @@ void GameState::spawnObject(WorldObject* object) {
objects.push_back(object);
}

void GameState::update() {
UpdateResult GameState::update() {
for (WorldObject* object : objects) {
object->update(worldState, objects);
UpdateResult updateResult = object->update(worldState, objects);
if (updateResult == UpdateResult::NextLevel) {
// TODO: improve this
return UpdateResult::NextLevel;
}
}
return UpdateResult::None;
}

std::string GameState::getLevelName() {
return levelName;
}

std::string GameState::getNextLevelName() {
return nextLevelName;
}

void GameState::updateLevelNames(std::string newLevelName, std::string newNextLevelName) {
levelName = newLevelName;
nextLevelName = newNextLevelName;
}

std::vector<WorldObject*>::iterator GameState::objectsBegin() {
Expand All @@ -19,10 +37,16 @@ std::vector<WorldObject*>::iterator GameState::objectsEnd() {
}

void GameState::clear() {
while (!objects.empty()) {
// TODO: good memory freeing?
delete objects.back();
objects.pop_back();
auto it = objects.begin();
while (it != objects.end()) {
if ((*it)->hasAttribute(ObjectAttribute::Persist)) {
// Do not delete
it++;
} else {
// TODO: good memory freeing?
delete *it;
it = objects.erase(it);
}
}
}

Expand Down
12 changes: 10 additions & 2 deletions src/gamestate.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,25 @@
class GameState {
private:
WorldState worldState;
std::string levelName;
std::string nextLevelName;
std::vector<WorldObject*> objects;

public:
// Spawn an object
void spawnObject(WorldObject* object);
// Update
void update();
UpdateResult update();
// Get the current level name
std::string getLevelName();
// Get the next level name
std::string getNextLevelName();
// Update the current level name
void updateLevelNames(std::string newLevelName, std::string newNextLevelName);
// Get all objects
std::vector<WorldObject*>::iterator objectsBegin();
std::vector<WorldObject*>::iterator objectsEnd();
// Clear all items
// Clear all objects from the world, aside from Persisting objects
void clear();
// Destructor to free memory
~GameState();
Expand Down
9 changes: 7 additions & 2 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ int main() {
gameState.spawnObject(player);

// Debug: load a basic level from a txt file
worldSpawner.spawnWorld(gameState, "assets/level_0.csv");
worldSpawner.spawnWorld(gameState, "level_0");
/*// Example level (level_0.csv)
// Bottom colliders
gameState.spawnObject(new StaticCollider(3, 25, 6, 2));
Expand Down Expand Up @@ -83,7 +83,12 @@ int main() {
}

// Update the game and world
gameState.update();
UpdateResult updateResult = gameState.update();
if (updateResult == UpdateResult::NextLevel) {
// Load the next level, if possible
// TODO: impl better
worldSpawner.spawnWorld(gameState, gameState.getNextLevelName());
}

// Render the game and world
window.clear(sf::Color(111, 201, 252));
Expand Down
8 changes: 6 additions & 2 deletions src/worldobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@
// The result of an update function
enum class UpdateResult {
None,
Destroy
Destroy,
NextLevel // TODO: better naming
};

// The attributes that an object can have
enum class ObjectAttribute {
OverlapDetect,
Collision,
Deadly
Deadly,
LevelTeleport,
Persist // Persist across levels
};

// Stores a world object
Expand Down
1 change: 1 addition & 0 deletions src/worldobjectincludes.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@
#include "worldobjects/staticcollider.h"
#include "worldobjects/spike.h"
#include "worldobjects/staticdeadly.h"
#include "worldobjects/leveltp.h"
42 changes: 42 additions & 0 deletions src/worldobjects/leveltp.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#pragma once

#include "../worldobject.h"

// An object that teleports the player to another level
class LevelTp : public WorldObject {
private:
long frameCount = 0;

public:
// Constructor
LevelTp(double spawnx, double spawny) : WorldObject() {
locx = spawnx;
locy = spawny;
this->width = 1;
this->height = 1;
this->objectAttributes.insert(ObjectAttribute::OverlapDetect);
this->objectAttributes.insert(ObjectAttribute::LevelTeleport);
}

// Override update: check with player
UpdateResult update(WorldState& worldState, std::vector<WorldObject*>& objects) {
// TODO: stuff
return UpdateResult::None;
}

// Override rendering
RenderData getRenderData() {
frameCount++;
// TODO: image?
return {
RenderType::Rectangle,
coordType,
locx + sin(frameCount / 20.0) * 0.3,
locy + cos(frameCount / 20.0) * 0.3,
width,
height,
{ 66, 135, 245 },
""
};
}
};
19 changes: 19 additions & 0 deletions src/worldobjects/player.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ class Player : public WorldObject {
Player(double spawnx, double spawny) : WorldObject() {
locx = spawnx;
locy = spawny;
objectAttributes.insert(ObjectAttribute::Persist);
}

// Input: acceleration
Expand Down Expand Up @@ -58,6 +59,7 @@ class Player : public WorldObject {
for (WorldObject* object : objects) {
if (object == this) continue;
bool collided = false;
bool overlapped = false;
if (object->hasAttribute(ObjectAttribute::Collision)) {
// X movement pushout
// todo: better way of doing the 0.1 thing
Expand All @@ -68,6 +70,7 @@ class Player : public WorldObject {
locx -= velx;
velx = 0;
collided = true;
overlapped = true;
}
}
if (locx + width >= object->getLocx() && locx < object->getLocx() + object->getWidth()) {
Expand All @@ -77,17 +80,33 @@ class Player : public WorldObject {
locy -= vely;
vely = 0;
collided = true;
overlapped = true;
// todo: should not be on ground on a bottom corner
onGround = true;
}
}
}
if (object->hasAttribute(ObjectAttribute::OverlapDetect)) {
// Check overlap only
if (locy + height >= object->getLocy() && locy < object->getLocy() + object->getHeight()) {
if (locx + width > object->getLocx() && locx < object->getLocx() + object->getWidth()) {
// Hit
overlapped = true;
}
}
}
// Collision stuff
if (collided && object->hasAttribute(ObjectAttribute::Deadly)) {
// Die
// todo: impl
teleport(5, 10);
}
if (overlapped && object->hasAttribute(ObjectAttribute::LevelTeleport)) {
// Teleport to the next level
// TODO: impl better
teleport(5, 10);
return UpdateResult::NextLevel;
}
}
return UpdateResult::None;
}
Expand Down
4 changes: 2 additions & 2 deletions src/worldobjects/spike.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ class Spike : public WorldObject {
RenderType::Image,
coordType,
locx,
locy - sin(frameCount / 400) * 0.3,
locy - sin(frameCount / 80.0) * 0.3,
width,
height + sin(frameCount / 400) * 0.3,
height + sin(frameCount / 80.0) * 0.3,
{ 255, 0, 0 },
"assets/spike.png"
};
Expand Down
2 changes: 1 addition & 1 deletion src/worldobjects/staticdeadly.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ class StaticDeadly : public WorldObject {
objectAttributes.insert(ObjectAttribute::Deadly);
}

// Override update: gravity
// Override update
UpdateResult update(WorldState& worldState, std::vector<WorldObject*>& objects) {
return UpdateResult::None;
}
Expand Down
51 changes: 51 additions & 0 deletions src/worldspawner.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#include "worldspawner.h"

void WorldSpawner::spawnWorld(GameState& gameState, std::string levelName) {
// Load the file based on the level name
// todo: error handling
std::ifstream file("assets/" + levelName + ".csv");
std::string line;
std::string newNextLevelName = "level_0";
// Clear the world
gameState.clear();
// Update
while (std::getline(file, line)) {
// Parse line
std::vector<std::string> parsed;
std::string thisitem = "";
for (char c : line) {
if (c == ',') {
parsed.push_back(thisitem);
thisitem = "";
} else if (c != ' ' && c != '\n') {
thisitem += c;
}
}
if (thisitem != "") parsed.push_back(thisitem);
// Process arguments
// todo: error handling with try catch
if (parsed[0].rfind("//", 0) == 0) {
// Comment: skip
} else if (parsed[0] == "nextLevelName") {
newNextLevelName = parsed[1];
} else if (parsed[0] == "StaticCollider") {
gameState.spawnObject(new StaticCollider(
std::stoi(parsed[1]), std::stoi(parsed[2]), std::stoi(parsed[3]), std::stoi(parsed[4])
));
} else if (parsed[0] == "Spike") {
gameState.spawnObject(new Spike(
std::stoi(parsed[1]), std::stoi(parsed[2])
));
} else if (parsed[0] == "StaticDeadly") {
gameState.spawnObject(new StaticDeadly(
std::stoi(parsed[1]), std::stoi(parsed[2]), std::stoi(parsed[3]), std::stoi(parsed[4])
));
} else if (parsed[0] == "LevelTp") {
gameState.spawnObject(new LevelTp(
std::stoi(parsed[1]), std::stoi(parsed[2])
));
}
}
// Successful load: update the game state
gameState.updateLevelNames(levelName, newNextLevelName);
}
38 changes: 1 addition & 37 deletions src/worldspawner.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,5 @@ class WorldSpawner {
StaticCollider, 9, 27, 10, 2
StaticCollider, 19, 25, 6, 2
*/
void spawnWorld(GameState& gameState, std::string worldfile) {
// Load the file
// todo: error handling
std::ifstream file(worldfile);
std::string line;
while (std::getline(file, line)) {
// Parse line
std::vector<std::string> parsed;
std::string thisitem = "";
for (char c : line) {
if (c == ',') {
parsed.push_back(thisitem);
thisitem = "";
} else if (c != ' ' && c != '\n') {
thisitem += c;
}
}
if (thisitem != "") parsed.push_back(thisitem);
// Process arguments
// todo: error handling with try catch
if (parsed[0].rfind("//", 0) == 0) {
// Comment: skip
} else if (parsed[0] == "StaticCollider") {
gameState.spawnObject(new StaticCollider(
std::stoi(parsed[1]), std::stoi(parsed[2]), std::stoi(parsed[3]), std::stoi(parsed[4])
));
} else if (parsed[0] == "Spike") {
gameState.spawnObject(new Spike(
std::stoi(parsed[1]), std::stoi(parsed[2])
));
} else if (parsed[0] == "StaticDeadly") {
gameState.spawnObject(new StaticDeadly(
std::stoi(parsed[1]), std::stoi(parsed[2]), std::stoi(parsed[3]), std::stoi(parsed[4])
));
}
}
}
void spawnWorld(GameState& gameState, std::string levelName);
};

0 comments on commit b557897

Please sign in to comment.