Support multiple and custom levels

This commit is contained in:
Martin Prokoph
2025-01-15 17:42:28 +01:00
parent 2959d06db4
commit f5297dfb66
10 changed files with 127 additions and 14 deletions

View File

@@ -1,16 +1,21 @@
#pragma once #pragma once
#include "identifier.hpp" #include "identifier.hpp"
#include "color.hpp"
#include "blockSettings.hpp" #include "blockSettings.hpp"
class Block { class Block {
private: private:
Identifier id = Identifier("adventure", "missing"); Identifier id = Identifier("adventure", "missing");
char encoding; char encoding;
Color color;
BlockSettings settings; BlockSettings settings;
public: public:
Block(Identifier id, char encoding, BlockSettings settings) { Block(Identifier id, char encoding, BlockSettings settings) : Block(id, encoding, Color::RESET, settings) {};
Block(Identifier id, char encoding, Color color, BlockSettings settings) {
this->id = id; this->id = id;
this->encoding = encoding; this->encoding = encoding;
this->color = color;
this->settings = settings; this->settings = settings;
}; };
@@ -21,6 +26,9 @@ public:
Identifier getId() { Identifier getId() {
return id; return id;
} }
Color getColor() {
return color;
}
char getEncoding() { char getEncoding() {
return encoding; return encoding;
} }

View File

@@ -9,17 +9,23 @@ using std::string;
class BlockRegistry { class BlockRegistry {
public: public:
Block AIR = Block(Identifier("adventura", "air"), ' ', BlockSettingsBuilder().nonSolid().build()); Block AIR = Block(Identifier("adventura", "air"), ' ', BlockSettingsBuilder().nonSolid().build());
Block WATER = Block(Identifier("adventura", "water"), '~', Color::BRIGHT_BLUE, BlockSettingsBuilder().nonSolid().build());
Block PLATFORM = Block(Identifier("adventura", "platform"), '-', BlockSettingsBuilder().build()); Block PLATFORM = Block(Identifier("adventura", "platform"), '-', BlockSettingsBuilder().build());
Block LADDER = Block(Identifier("adventura", "ladder"), 'H', BlockSettingsBuilder().climbableFromBottom().climbableFromTop().build()); Block LADDER = Block(Identifier("adventura", "ladder"), 'H', Color::BRIGHT_MAGENTA, BlockSettingsBuilder().climbableFromBottom().climbableFromTop().build());
Block START = Block(Identifier("adventura", "start"), 'S', BlockSettingsBuilder().build()); Block START = Block(Identifier("adventura", "start"), 'S', BlockSettingsBuilder().nonSolid().build());
Block GOAL = Block(Identifier("adventura", "goal"), 'O', BlockSettingsBuilder().build()); Block GOAL = Block(Identifier("adventura", "goal"), 'O', Color::BRIGHT_GREEN, BlockSettingsBuilder().nonSolid().build());
Block WALL = Block(Identifier("adventura", "wall"), '0', Color::BRIGHT_BLACK, BlockSettingsBuilder().collidable().build());
Block SPIKE = Block(Identifier("adventura", "spike"), '^', Color::BRIGHT_RED, BlockSettingsBuilder().lethal().build());
BlockRegistry() { BlockRegistry() {
registerBlock(AIR); registerBlock(AIR);
registerBlock(WATER);
registerBlock(PLATFORM); registerBlock(PLATFORM);
registerBlock(LADDER); registerBlock(LADDER);
registerBlock(START); registerBlock(START);
registerBlock(GOAL); registerBlock(GOAL);
registerBlock(WALL);
registerBlock(SPIKE);
} }
const Block getByEncoding(char encoding) { const Block getByEncoding(char encoding) {

View File

@@ -4,9 +4,15 @@ class BlockSettings {
bool isSolid() { bool isSolid() {
return isSolid_; return isSolid_;
} }
bool hasCollision() {
return hasCollision_;
}
bool isMovable() { bool isMovable() {
return isMovable_; return isMovable_;
} }
bool isLethal() {
return isLethal_;
}
bool isClimbableFromTop() { bool isClimbableFromTop() {
return isClimbableFromTop_; return isClimbableFromTop_;
} }
@@ -20,6 +26,12 @@ class BlockSettings {
void setMovable(bool isMovable) { void setMovable(bool isMovable) {
this->isMovable_ = isMovable; this->isMovable_ = isMovable;
} }
void setCollision(bool hasCollision) {
this->hasCollision_ = hasCollision;
}
void setLethal(bool isLethal) {
this->isLethal_ = isLethal;
}
void setClimbableFromTop(bool isClimbableFromTop) { void setClimbableFromTop(bool isClimbableFromTop) {
this->isClimbableFromTop_ = isClimbableFromTop; this->isClimbableFromTop_ = isClimbableFromTop;
} }
@@ -32,6 +44,8 @@ class BlockSettings {
bool isMovable_ = false; bool isMovable_ = false;
bool isClimbableFromTop_ = false; bool isClimbableFromTop_ = false;
bool isClimbableFromBottom_ = false; bool isClimbableFromBottom_ = false;
bool isLethal_ = false;
bool hasCollision_ = false;
}; };
class BlockSettingsBuilder { class BlockSettingsBuilder {
public: public:
@@ -43,6 +57,14 @@ class BlockSettingsBuilder {
blockSettings.setMovable(true); blockSettings.setMovable(true);
return *this; return *this;
} }
BlockSettingsBuilder collidable() {
blockSettings.setCollision(true);
return *this;
}
BlockSettingsBuilder lethal() {
blockSettings.setLethal(true);
return *this;
}
BlockSettingsBuilder climbableFromTop() { BlockSettingsBuilder climbableFromTop() {
blockSettings.setClimbableFromTop(true); blockSettings.setClimbableFromTop(true);
return *this; return *this;

16
src/color.hpp Normal file
View File

@@ -0,0 +1,16 @@
#include <iostream>
enum class Color {
RESET = 0,
BRIGHT_BLACK= 90,
BRIGHT_RED= 91,
BRIGHT_GREEN= 92,
BRIGHT_YELLOW= 93,
BRIGHT_BLUE= 94,
BRIGHT_MAGENTA= 95,
BRIGHT_CYAN= 96,
BRIGHT_WHITE= 97
};
std::ostream& operator<<(std::ostream& os, Color color) {
return os << "\033[" << static_cast<int>(color) << "m";
}

View File

@@ -1,5 +1,6 @@
#include <string> #include <string>
#include <iostream> #include <iostream>
#include <filesystem>
#include "world.hpp" #include "world.hpp"
#include "player.hpp" #include "player.hpp"
#include "blockRegistry.hpp" #include "blockRegistry.hpp"
@@ -8,26 +9,33 @@
using std::string; using std::string;
using std::cout; using std::cout;
using std::endl; using std::endl;
namespace fs = std::filesystem;
void render(World &world, Player &player); void render(World &world, Player &player);
void redraw(World &world, Player &player); void redraw(World &world, Player &player);
void jumpBackOneLine(); void jumpBackOneLine();
bool startWorld(string worldFile);
int main() { int main() {
for (const auto & entry : fs::directory_iterator("./worlds"))
if (!startWorld(entry.path())) return 0;
return 0;
}
bool startWorld(string worldFile) {
BlockRegistry blockRegistry = BlockRegistry(); BlockRegistry blockRegistry = BlockRegistry();
World world = World(blockRegistry); World world = World(blockRegistry);
string worldFile = "worlds/world.txt";
world.loadFromFile(worldFile); world.loadFromFile(worldFile);
Player player = Player(world.getStartPos(), world); Player player = Player(world.getStartPos(), world);
render(world, player); render(world, player);
while (player.isAlive()) { while (player.isAlive() && !player.hasReachedGoal()) {
char lastChar; char lastChar;
cin >> lastChar; cin >> lastChar;
if (onInput(lastChar, world, player)) redraw(world, player); if (onInput(lastChar, world, player)) redraw(world, player);
else jumpBackOneLine(); else jumpBackOneLine();
} }
return 0; return player.hasReachedGoal();
} }
void jumpBackOneLine() { void jumpBackOneLine() {
std::cout << "\033[1A"; std::cout << "\033[1A";
@@ -48,10 +56,10 @@ void render(World &world, Player &player) {
for (unsigned int y = 0; y <= world.getMaxY(); y++) { for (unsigned int y = 0; y <= world.getMaxY(); y++) {
for (unsigned int x = 0; x <= world.getMaxX(); x++) { for (unsigned int x = 0; x <= world.getMaxX(); x++) {
if (playerTexture.size() > y && playerTexture.at(y).size() > x && playerTexture.at(y).at(x) != ' ') { if (playerTexture.size() > y && playerTexture.at(y).size() > x && playerTexture.at(y).at(x) != ' ') {
cout << playerTexture.at(y).at(x); cout << Color::BRIGHT_YELLOW << playerTexture.at(y).at(x);
} }
else if (canvas.size() > y && canvas.at(y).size() > x) { else if (canvas.size() > y && canvas.at(y).size() > x) {
cout << canvas.at(y).at(x).getEncoding(); cout << canvas.at(y).at(x).getColor() << canvas.at(y).at(x).getEncoding();
} }
else cout << ' '; else cout << ' ';
} }

View File

@@ -2,19 +2,20 @@
#include "world.hpp" #include "world.hpp"
#include "blockRegistry.hpp" #include "blockRegistry.hpp"
bool tryWalk(World& world, Player& player, bool left);
bool tryGoDown(World& world, Player& player); bool tryGoDown(World& world, Player& player);
bool tryGoUp(World& world, Player& player); bool tryGoUp(World& world, Player& player);
bool onInput(char lastChar, World& world, Player& player) { bool onInput(char lastChar, World& world, Player& player) {
switch (lastChar) { switch (lastChar) {
case ' ':
case 'w': case 'w':
case 'W': case 'W':
return tryGoUp(world, player); return tryGoUp(world, player);
case 'a': case 'a':
case 'A': case 'A':
player.move(-1, 0); return tryWalk(world, player, true);
return true;
case 's': case 's':
case 'S': case 'S':
@@ -22,12 +23,18 @@ bool onInput(char lastChar, World& world, Player& player) {
case 'd': case 'd':
case 'D': case 'D':
player.move(1, 0); return tryWalk(world, player, false);
return true;
default: return false; default: return false;
} }
} }
bool tryWalk(World& world, Player& player, bool left) {
if (!world.getBlockAt(player.getPos()+(left ? BlockPos(-1, 1) : BlockPos(1, 1))).getSettings().hasCollision()) {
player.move(left ? BlockPos(-1, 0) : BlockPos(1,0));
return true;
}
return false;
}
bool tryGoDown(World& world, Player& player) { bool tryGoDown(World& world, Player& player) {
if (world.getBlockAt(player.getPos()+BlockPos(0, 2)).getSettings().isClimbableFromTop() || world.getBlockAt(player.getPos()+BlockPos(0, 3)).getSettings().isClimbableFromTop()) { if (world.getBlockAt(player.getPos()+BlockPos(0, 2)).getSettings().isClimbableFromTop() || world.getBlockAt(player.getPos()+BlockPos(0, 3)).getSettings().isClimbableFromTop()) {
player.move(0, 1); player.move(0, 1);
@@ -40,5 +47,13 @@ bool tryGoUp(World& world, Player& player) {
player.move(0, -1); player.move(0, -1);
return true; return true;
} }
else if (world.getBlockAt(player.getPos()+BlockPos(1, 1)).getSettings().hasCollision() && !world.getBlockAt(player.getPos()+BlockPos(1, 0)).getSettings().isSolid()) {
player.move(1, -1);
return true;
}
else if (world.getBlockAt(player.getPos()+BlockPos(-1, 1)).getSettings().hasCollision() && !world.getBlockAt(player.getPos()+BlockPos(-1, 0)).getSettings().isSolid()) {
player.move(-1, -1);
return true;
}
return false; return false;
} }

View File

@@ -30,6 +30,8 @@ public:
} }
this->pos = pos; this->pos = pos;
if (world.getBlockAt(pos) == world.getBlockRegistry().GOAL) reachedGoal = true;
isFreeFalling = !world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isSolid(); isFreeFalling = !world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isSolid();
if (isFreeFalling) { if (isFreeFalling) {
move(BlockPos(0, 1)); move(BlockPos(0, 1));
@@ -37,10 +39,15 @@ public:
if (fallLength > 5) alive = false; if (fallLength > 5) alive = false;
} }
else fallLength = 0; else fallLength = 0;
if (world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isLethal()) alive = false;
} }
bool isAlive() { bool isAlive() {
return alive; return alive;
} }
bool hasReachedGoal() {
return reachedGoal;
}
vector<vector<char>> mapToWorldspace() { vector<vector<char>> mapToWorldspace() {
vector<vector<char>> map; vector<vector<char>> map;
for (unsigned int y = 0; y <= world.getMaxY(); y++) { for (unsigned int y = 0; y <= world.getMaxY(); y++) {
@@ -69,5 +76,6 @@ private:
BlockPos pos = BlockPos(0, 0); BlockPos pos = BlockPos(0, 0);
bool alive = true; bool alive = true;
bool isFreeFalling = false; bool isFreeFalling = false;
bool reachedGoal = false;
int fallLength = 0; int fallLength = 0;
}; };

15
worlds/2.txt Normal file
View File

@@ -0,0 +1,15 @@
o
S /|\
/ \
------------- -----------
H H
H H
H H
H --------- -------
-------- H 0 0
H H 0 0
H H 0 0 O
H H 0^^^^^0
----------------------------------------

15
worlds/3.txt Normal file
View File

@@ -0,0 +1,15 @@
----------------------
00
00
00
---------
0 o 00----- O
0 S /|\ 00 0
0 / \ 00 0 ---------------
---------------- 0 H 0
0 H 0
0 H 0
0 H 0
0~~~~~~0---------
--------