mirror of
https://github.com/Motschen/Adventura.git
synced 2025-12-13 02:25:08 +01:00
Implement main logic
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,3 +30,5 @@
|
||||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
/.vscode
|
||||
/build
|
||||
|
||||
38
src/block.hpp
Normal file
38
src/block.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include "identifier.hpp"
|
||||
#include "blockSettings.hpp"
|
||||
|
||||
class Block {
|
||||
private:
|
||||
Identifier id = Identifier("adventure", "missing");
|
||||
char encoding;
|
||||
BlockSettings settings;
|
||||
public:
|
||||
Block(Identifier id, char encoding, BlockSettings settings) {
|
||||
this->id = id;
|
||||
this->encoding = encoding;
|
||||
this->settings = settings;
|
||||
};
|
||||
|
||||
BlockSettings getSettings() {
|
||||
return settings;
|
||||
}
|
||||
|
||||
Identifier getId() {
|
||||
return id;
|
||||
}
|
||||
char getEncoding() {
|
||||
return encoding;
|
||||
}
|
||||
void setEncoding(char encoding) {
|
||||
this->encoding = encoding;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out) {
|
||||
out << encoding;
|
||||
return out;
|
||||
}
|
||||
bool operator==(Block otherBlock) {
|
||||
return this->getId() == otherBlock.getId();
|
||||
}
|
||||
};
|
||||
31
src/blockPos.hpp
Normal file
31
src/blockPos.hpp
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
class BlockPos {
|
||||
int x;
|
||||
int y;
|
||||
public:
|
||||
BlockPos(int x, int y) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
}
|
||||
int getX() {
|
||||
return x;
|
||||
}
|
||||
int getY() {
|
||||
return y;
|
||||
}
|
||||
unsigned int getUnsignedX() {
|
||||
return static_cast<unsigned int>(x);
|
||||
}
|
||||
unsigned int getUnsignedY() {
|
||||
return static_cast<unsigned int>(y);
|
||||
}
|
||||
bool isNegative() {
|
||||
return x < 0 || y < 0;
|
||||
}
|
||||
BlockPos operator+(BlockPos offset) {
|
||||
return BlockPos(this->getX() + offset.getX(), this->getY() + offset.getY());
|
||||
}
|
||||
BlockPos operator-(BlockPos offset) {
|
||||
return BlockPos(this->getX() - offset.getX(), this->getY() - offset.getY());
|
||||
}
|
||||
};
|
||||
38
src/blockRegistry.hpp
Normal file
38
src/blockRegistry.hpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "block.hpp"
|
||||
|
||||
using std::vector;
|
||||
using std::string;
|
||||
|
||||
class BlockRegistry {
|
||||
public:
|
||||
Block AIR = Block(Identifier("adventura", "air"), ' ', BlockSettingsBuilder().nonSolid().build());
|
||||
Block PLATFORM = Block(Identifier("adventura", "platform"), '-', BlockSettingsBuilder().build());
|
||||
Block LADDER = Block(Identifier("adventura", "ladder"), 'H', BlockSettingsBuilder().climbableFromBottom().climbableFromTop().build());
|
||||
Block START = Block(Identifier("adventura", "start"), 'S', BlockSettingsBuilder().build());
|
||||
Block GOAL = Block(Identifier("adventura", "goal"), 'O', BlockSettingsBuilder().build());
|
||||
|
||||
BlockRegistry() {
|
||||
registerBlock(AIR);
|
||||
registerBlock(PLATFORM);
|
||||
registerBlock(LADDER);
|
||||
registerBlock(START);
|
||||
registerBlock(GOAL);
|
||||
}
|
||||
|
||||
const Block getByEncoding(char encoding) {
|
||||
for (Block block : registeredBlocks) {
|
||||
if (block.getEncoding() == encoding) return block;
|
||||
}
|
||||
return AIR;
|
||||
}
|
||||
|
||||
private:
|
||||
Block registerBlock(Block& block) {
|
||||
registeredBlocks.push_back(block);
|
||||
return block;
|
||||
}
|
||||
vector<Block> registeredBlocks;
|
||||
};
|
||||
59
src/blockSettings.hpp
Normal file
59
src/blockSettings.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
class BlockSettings {
|
||||
public:
|
||||
BlockSettings() {}
|
||||
bool isSolid() {
|
||||
return isSolid_;
|
||||
}
|
||||
bool isMovable() {
|
||||
return isMovable_;
|
||||
}
|
||||
bool isClimbableFromTop() {
|
||||
return isClimbableFromTop_;
|
||||
}
|
||||
bool isClimbableFromBottom() {
|
||||
return isClimbableFromBottom_;
|
||||
}
|
||||
|
||||
void setSolid(bool isSolid) {
|
||||
this->isSolid_ = isSolid;
|
||||
}
|
||||
void setMovable(bool isMovable) {
|
||||
this->isMovable_ = isMovable;
|
||||
}
|
||||
void setClimbableFromTop(bool isClimbableFromTop) {
|
||||
this->isClimbableFromTop_ = isClimbableFromTop;
|
||||
}
|
||||
void setClimbableFromBottom(bool isClimbableFromBottom) {
|
||||
this->isClimbableFromBottom_ = isClimbableFromBottom;
|
||||
}
|
||||
|
||||
private:
|
||||
bool isSolid_ = true;
|
||||
bool isMovable_ = false;
|
||||
bool isClimbableFromTop_ = false;
|
||||
bool isClimbableFromBottom_ = false;
|
||||
};
|
||||
class BlockSettingsBuilder {
|
||||
public:
|
||||
BlockSettingsBuilder nonSolid() {
|
||||
blockSettings.setSolid(false);
|
||||
return *this;
|
||||
}
|
||||
BlockSettingsBuilder movable() {
|
||||
blockSettings.setMovable(true);
|
||||
return *this;
|
||||
}
|
||||
BlockSettingsBuilder climbableFromTop() {
|
||||
blockSettings.setClimbableFromTop(true);
|
||||
return *this;
|
||||
}
|
||||
BlockSettingsBuilder climbableFromBottom() {
|
||||
blockSettings.setClimbableFromBottom(true);
|
||||
return *this;
|
||||
}
|
||||
BlockSettings build() {
|
||||
return blockSettings;
|
||||
}
|
||||
private:
|
||||
BlockSettings blockSettings = BlockSettings();
|
||||
};
|
||||
26
src/elementPos.hpp
Normal file
26
src/elementPos.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#pragma once
|
||||
class Pos {
|
||||
protected:
|
||||
int x;
|
||||
int y;
|
||||
public:
|
||||
Pos(int x, int y) {
|
||||
this->x = x;
|
||||
this->y = y;
|
||||
}
|
||||
int getX() {
|
||||
return x;
|
||||
}
|
||||
int getY() {
|
||||
return y;
|
||||
}
|
||||
bool isNegative() {
|
||||
return x < 0 || y < 0;
|
||||
}
|
||||
Pos operator+(Pos offset) {
|
||||
return Pos(this->getX() + offset.getX(), this->getY() + offset.getY());
|
||||
}
|
||||
Pos operator-(Pos offset) {
|
||||
return Pos(this->getX() - offset.getX(), this->getY() - offset.getY());
|
||||
}
|
||||
};
|
||||
51
src/fileutils.hpp
Normal file
51
src/fileutils.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <fstream>
|
||||
#include <streambuf>
|
||||
|
||||
using std::cin;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
string readInput(string feedback);
|
||||
string readFile(const string& fileLocation);
|
||||
|
||||
/**
|
||||
* Reads a string from the user. The string is expected to be the first
|
||||
* argument before a comma.
|
||||
*
|
||||
* @return The string read from the user.
|
||||
*/
|
||||
string readInput(string feedback) {
|
||||
string name;
|
||||
cout << feedback << endl;
|
||||
getline(std::cin, name);
|
||||
return name;
|
||||
}
|
||||
|
||||
string readFile(const string& fileLocation) {
|
||||
std::ifstream ifs(fileLocation);
|
||||
return string((std::istreambuf_iterator <char>( ifs )),
|
||||
(std::istreambuf_iterator<char >()));
|
||||
}
|
||||
vector<string> readFileAsVector(const string& fileLocation) {
|
||||
string rawFile = readFile(fileLocation);
|
||||
vector<string> file;
|
||||
string currentLine = "";
|
||||
for (char c : rawFile) {
|
||||
if (c == '\n' || c == '\r') {
|
||||
file.push_back(currentLine);
|
||||
currentLine = "";
|
||||
}
|
||||
else {
|
||||
currentLine += c;
|
||||
}
|
||||
}
|
||||
file.push_back(currentLine);
|
||||
return file;
|
||||
}
|
||||
27
src/identifier.hpp
Normal file
27
src/identifier.hpp
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
#include <string>
|
||||
|
||||
using std::string;
|
||||
|
||||
class Identifier {
|
||||
public:
|
||||
std::string nameSpace;
|
||||
std::string path;
|
||||
|
||||
Identifier(std::string nameSpace, std::string path) : nameSpace(nameSpace), path(path) {}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out) {
|
||||
out << nameSpace << ":" << path;
|
||||
return out;
|
||||
}
|
||||
std::istream& operator>>(std::istream& in) {
|
||||
string input;
|
||||
in >> input;
|
||||
nameSpace = input.substr(0, input.find(":"));
|
||||
path = input.substr(input.find(":") + 1, input.length());
|
||||
return in;
|
||||
}
|
||||
bool operator==(Identifier otherId) {
|
||||
return this->nameSpace == otherId.nameSpace && this->path == otherId.path;
|
||||
}
|
||||
};
|
||||
60
src/main.cpp
Normal file
60
src/main.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include "world.hpp"
|
||||
#include "player.hpp"
|
||||
#include "blockRegistry.hpp"
|
||||
#include "movementHandler.hpp"
|
||||
|
||||
using std::string;
|
||||
using std::cout;
|
||||
using std::endl;
|
||||
|
||||
void render(World &world, Player &player);
|
||||
void redraw(World &world, Player &player);
|
||||
void jumpBackOneLine();
|
||||
|
||||
int main() {
|
||||
BlockRegistry blockRegistry = BlockRegistry();
|
||||
World world = World(blockRegistry);
|
||||
|
||||
string worldFile = "worlds/world.txt";
|
||||
world.loadFromFile(worldFile);
|
||||
Player player = Player(world.getStartPos(), world);
|
||||
render(world, player);
|
||||
while (player.isAlive()) {
|
||||
char lastChar;
|
||||
cin >> lastChar;
|
||||
if (onInput(lastChar, world, player)) redraw(world, player);
|
||||
else jumpBackOneLine();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
void jumpBackOneLine() {
|
||||
std::cout << "\033[1A";
|
||||
}
|
||||
|
||||
void redraw(World &world, Player &player) {
|
||||
//std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
for (unsigned int y = 0; y <= world.getMaxY()+1; y++) {
|
||||
jumpBackOneLine();
|
||||
}
|
||||
render(world, player);
|
||||
}
|
||||
|
||||
void render(World &world, Player &player) {
|
||||
vector<vector<Block>> canvas = world.getFieldState();
|
||||
vector<vector<char>> playerTexture = player.mapToWorldspace();
|
||||
|
||||
for (unsigned int y = 0; y <= world.getMaxY(); y++) {
|
||||
for (unsigned int x = 0; x <= world.getMaxX(); x++) {
|
||||
if (playerTexture.size() > y && playerTexture.at(y).size() > x && playerTexture.at(y).at(x) != ' ') {
|
||||
cout << playerTexture.at(y).at(x);
|
||||
}
|
||||
else if (canvas.size() > y && canvas.at(y).size() > x) {
|
||||
cout << canvas.at(y).at(x).getEncoding();
|
||||
}
|
||||
else cout << ' ';
|
||||
}
|
||||
cout << endl;
|
||||
}
|
||||
}
|
||||
44
src/movementHandler.hpp
Normal file
44
src/movementHandler.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
#include "player.hpp"
|
||||
#include "world.hpp"
|
||||
#include "blockRegistry.hpp"
|
||||
|
||||
bool tryGoDown(World& world, Player& player);
|
||||
bool tryGoUp(World& world, Player& player);
|
||||
|
||||
bool onInput(char lastChar, World& world, Player& player) {
|
||||
switch (lastChar) {
|
||||
case 'w':
|
||||
case 'W':
|
||||
return tryGoUp(world, player);
|
||||
|
||||
case 'a':
|
||||
case 'A':
|
||||
player.move(-1, 0);
|
||||
return true;
|
||||
|
||||
case 's':
|
||||
case 'S':
|
||||
return tryGoDown(world, player);
|
||||
|
||||
case 'd':
|
||||
case 'D':
|
||||
player.move(1, 0);
|
||||
return true;
|
||||
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
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()) {
|
||||
player.move(0, 1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool tryGoUp(World& world, Player& player) {
|
||||
if (world.getBlockAt(player.getPos()+BlockPos(0, 1)).getSettings().isClimbableFromBottom() || world.getBlockAt(player.getPos()+BlockPos(0, 2)).getSettings().isClimbableFromBottom()) {
|
||||
player.move(0, -1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
73
src/player.hpp
Normal file
73
src/player.hpp
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include "blockPos.hpp"
|
||||
|
||||
class Player {
|
||||
public:
|
||||
Player(BlockPos pos, World& world) : world(world) {
|
||||
this->pos = pos;
|
||||
this->world = world;
|
||||
playerTexture = {{
|
||||
{' ', 'o', ' '},
|
||||
{'/', '|', '\\'},
|
||||
{'/', ' ', '\\'}
|
||||
}}; // Player pos is at the bottom center
|
||||
}
|
||||
|
||||
BlockPos getPos() {
|
||||
return pos;
|
||||
}
|
||||
void move(int x, int y) {
|
||||
move(BlockPos(x, y));
|
||||
}
|
||||
void move(BlockPos offset) {
|
||||
setPos(pos + offset);
|
||||
}
|
||||
void setPos(BlockPos pos) {
|
||||
if (!world.containsPos(pos)) {
|
||||
alive = false;
|
||||
return;
|
||||
}
|
||||
this->pos = pos;
|
||||
|
||||
isFreeFalling = !world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isSolid();
|
||||
if (isFreeFalling) {
|
||||
move(BlockPos(0, 1));
|
||||
fallLength += 1;
|
||||
if (fallLength > 5) alive = false;
|
||||
}
|
||||
else fallLength = 0;
|
||||
}
|
||||
bool isAlive() {
|
||||
return alive;
|
||||
}
|
||||
vector<vector<char>> mapToWorldspace() {
|
||||
vector<vector<char>> map;
|
||||
for (unsigned int y = 0; y <= world.getMaxY(); y++) {
|
||||
for (unsigned int x = 0; x <= world.getMaxX(); x++) {
|
||||
while (map.size() <= y) map.push_back({});
|
||||
while (map[y].size() <= x) map[y].push_back(' ');
|
||||
|
||||
int yOffset = y-pos.getY() + 1;
|
||||
int xOffset = x-pos.getX() + 1;
|
||||
|
||||
char encoding = ' ';
|
||||
if (yOffset >= 0 && yOffset < static_cast<int>(playerTexture.size()) &&
|
||||
xOffset >= 0 && xOffset < static_cast<int>(playerTexture.at(yOffset).size())) {
|
||||
encoding = playerTexture.at(yOffset).at(xOffset);
|
||||
}
|
||||
|
||||
map[y][x] = encoding;
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private:
|
||||
World world;
|
||||
std::array<std::array<char, 3>, 3> playerTexture;
|
||||
BlockPos pos = BlockPos(0, 0);
|
||||
bool alive = true;
|
||||
bool isFreeFalling = false;
|
||||
int fallLength = 0;
|
||||
};
|
||||
69
src/world.hpp
Normal file
69
src/world.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include "fileutils.hpp"
|
||||
#include "block.hpp"
|
||||
#include "blockRegistry.hpp"
|
||||
#include "blockPos.hpp"
|
||||
|
||||
using std::vector;
|
||||
|
||||
class World {
|
||||
public:
|
||||
World(BlockRegistry blockRegistry) {
|
||||
this->blockRegistry = blockRegistry;
|
||||
}
|
||||
|
||||
void loadFromFile(string fileLocation) {
|
||||
field = {};
|
||||
vector<string> file = readFileAsVector(fileLocation);
|
||||
|
||||
for (unsigned int y = 0; y < file.size(); y++) {
|
||||
for (unsigned int x = 0; x < file.at(y).size(); x++) {
|
||||
setBlockAt(BlockPos(x, y), blockRegistry.getByEncoding(file.at(y).at(x)));
|
||||
if (file.at(y).at(x) == '|') startPos = BlockPos(x, y);
|
||||
if (x > maxX) maxX = x;
|
||||
}
|
||||
if (y > maxY) maxY = y;
|
||||
}
|
||||
}
|
||||
void setBlockAt(BlockPos pos, Block block) {
|
||||
if (pos.isNegative()) return;
|
||||
while (field.size() <= pos.getUnsignedY()) field.push_back({});
|
||||
while (field[pos.getUnsignedY()].size() <= pos.getUnsignedX()) field[pos.getUnsignedY()].push_back(blockRegistry.AIR);
|
||||
|
||||
field[pos.getUnsignedY()][pos.getX()] = block;
|
||||
}
|
||||
|
||||
Block& getBlockAt(BlockPos pos) {
|
||||
if (pos.getUnsignedY() < field.size() && pos.getUnsignedX() < field[pos.getUnsignedY()].size()) {
|
||||
return field[pos.getY()][pos.getX()];
|
||||
}
|
||||
//cout << "Out of bounds: " << pos.getX() << ", " << pos.getY() << endl;
|
||||
return blockRegistry.AIR;
|
||||
}
|
||||
bool containsPos(BlockPos pos) {
|
||||
return !pos.isNegative() && pos.getUnsignedY() < field.size();
|
||||
}
|
||||
vector<vector<Block>> getFieldState() {
|
||||
return field;
|
||||
}
|
||||
BlockRegistry getBlockRegistry() {
|
||||
return blockRegistry;
|
||||
}
|
||||
unsigned int getMaxX() {
|
||||
return maxX;
|
||||
}
|
||||
unsigned int getMaxY() {
|
||||
return maxY;
|
||||
}
|
||||
BlockPos getStartPos() {
|
||||
return startPos;
|
||||
}
|
||||
private:
|
||||
BlockRegistry blockRegistry;
|
||||
vector<vector<Block>> field;
|
||||
unsigned int maxX = 0;
|
||||
unsigned int maxY = 0;
|
||||
BlockPos startPos = BlockPos(0, 0);
|
||||
};
|
||||
13
worlds/world.txt
Normal file
13
worlds/world.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
o
|
||||
S /|\ O
|
||||
/ \
|
||||
--------- -------- -------
|
||||
H H
|
||||
H H
|
||||
H H
|
||||
H ------------
|
||||
-------- H
|
||||
H H
|
||||
H H
|
||||
H H
|
||||
----------------
|
||||
Reference in New Issue
Block a user