mirror of
https://github.com/Motschen/Adventura.git
synced 2025-12-15 19:35:09 +01:00
feat: add pushable box blocks
This commit is contained in:
@@ -22,6 +22,9 @@ public:
|
|||||||
bool isNegative() {
|
bool isNegative() {
|
||||||
return x < 0 || y < 0;
|
return x < 0 || y < 0;
|
||||||
}
|
}
|
||||||
|
BlockPos add(int x, int y) {
|
||||||
|
return BlockPos(this->x + x, this->y + y);
|
||||||
|
}
|
||||||
BlockPos operator+(BlockPos offset) {
|
BlockPos operator+(BlockPos offset) {
|
||||||
return BlockPos(this->getX() + offset.getX(), this->getY() + offset.getY());
|
return BlockPos(this->getX() + offset.getX(), this->getY() + offset.getY());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public:
|
|||||||
Block GOAL = Block(Identifier("adventura", "goal"), 'O', Color::BRIGHT_GREEN, BlockSettingsBuilder().nonSolid().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 WALL = Block(Identifier("adventura", "wall"), '0', Color::BRIGHT_BLACK, BlockSettingsBuilder().collidable().build());
|
||||||
Block SPIKE = Block(Identifier("adventura", "spike"), '^', Color::BRIGHT_RED, BlockSettingsBuilder().lethal().build());
|
Block SPIKE = Block(Identifier("adventura", "spike"), '^', Color::BRIGHT_RED, BlockSettingsBuilder().lethal().build());
|
||||||
|
Block BOX = Block(Identifier("adventura", "box"), 'x', Color::BRIGHT_CYAN, BlockSettingsBuilder().pushable().collidable().gravity().build());
|
||||||
|
|
||||||
BlockRegistry() {
|
BlockRegistry() {
|
||||||
registerBlock(AIR);
|
registerBlock(AIR);
|
||||||
@@ -26,6 +27,7 @@ public:
|
|||||||
registerBlock(GOAL);
|
registerBlock(GOAL);
|
||||||
registerBlock(WALL);
|
registerBlock(WALL);
|
||||||
registerBlock(SPIKE);
|
registerBlock(SPIKE);
|
||||||
|
registerBlock(BOX);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Block getByEncoding(char encoding) {
|
const Block getByEncoding(char encoding) {
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ class BlockSettings {
|
|||||||
bool hasCollision() {
|
bool hasCollision() {
|
||||||
return hasCollision_;
|
return hasCollision_;
|
||||||
}
|
}
|
||||||
bool isMovable() {
|
bool hasGravity() {
|
||||||
return isMovable_;
|
return hasGravity_;
|
||||||
|
}
|
||||||
|
bool isPushable() {
|
||||||
|
return isPushable_;
|
||||||
}
|
}
|
||||||
bool isLethal() {
|
bool isLethal() {
|
||||||
return isLethal_;
|
return isLethal_;
|
||||||
@@ -23,12 +26,15 @@ class BlockSettings {
|
|||||||
void setSolid(bool isSolid) {
|
void setSolid(bool isSolid) {
|
||||||
this->isSolid_ = isSolid;
|
this->isSolid_ = isSolid;
|
||||||
}
|
}
|
||||||
void setMovable(bool isMovable) {
|
void setPushable(bool isMovable) {
|
||||||
this->isMovable_ = isMovable;
|
this->isPushable_ = isMovable;
|
||||||
}
|
}
|
||||||
void setCollision(bool hasCollision) {
|
void setCollision(bool hasCollision) {
|
||||||
this->hasCollision_ = hasCollision;
|
this->hasCollision_ = hasCollision;
|
||||||
}
|
}
|
||||||
|
void setGravity(bool hasGravity) {
|
||||||
|
this->hasGravity_ = hasGravity;
|
||||||
|
}
|
||||||
void setLethal(bool isLethal) {
|
void setLethal(bool isLethal) {
|
||||||
this->isLethal_ = isLethal;
|
this->isLethal_ = isLethal;
|
||||||
}
|
}
|
||||||
@@ -41,11 +47,12 @@ class BlockSettings {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool isSolid_ = true;
|
bool isSolid_ = true;
|
||||||
bool isMovable_ = false;
|
bool isPushable_ = false;
|
||||||
bool isClimbableFromTop_ = false;
|
bool isClimbableFromTop_ = false;
|
||||||
bool isClimbableFromBottom_ = false;
|
bool isClimbableFromBottom_ = false;
|
||||||
bool isLethal_ = false;
|
bool isLethal_ = false;
|
||||||
bool hasCollision_ = false;
|
bool hasCollision_ = false;
|
||||||
|
bool hasGravity_ = false;
|
||||||
};
|
};
|
||||||
class BlockSettingsBuilder {
|
class BlockSettingsBuilder {
|
||||||
public:
|
public:
|
||||||
@@ -53,14 +60,18 @@ class BlockSettingsBuilder {
|
|||||||
blockSettings.setSolid(false);
|
blockSettings.setSolid(false);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
BlockSettingsBuilder movable() {
|
BlockSettingsBuilder pushable() {
|
||||||
blockSettings.setMovable(true);
|
blockSettings.setPushable(true);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
BlockSettingsBuilder collidable() {
|
BlockSettingsBuilder collidable() {
|
||||||
blockSettings.setCollision(true);
|
blockSettings.setCollision(true);
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
BlockSettingsBuilder gravity() {
|
||||||
|
blockSettings.setGravity(true);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
BlockSettingsBuilder lethal() {
|
BlockSettingsBuilder lethal() {
|
||||||
blockSettings.setLethal(true);
|
blockSettings.setLethal(true);
|
||||||
return *this;
|
return *this;
|
||||||
|
|||||||
58
src/main.cpp
58
src/main.cpp
@@ -1,6 +1,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "world.hpp"
|
#include "world.hpp"
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "blockRegistry.hpp"
|
#include "blockRegistry.hpp"
|
||||||
@@ -17,19 +19,40 @@ void jumpBackOneLine();
|
|||||||
void printFile(string fileLocation, Color color);
|
void printFile(string fileLocation, Color color);
|
||||||
bool startWorld(string worldFile);
|
bool startWorld(string worldFile);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entry point of the program.
|
||||||
|
* If a world file is provided as an argument, play through that world.
|
||||||
|
* Otherwise, play through all worlds in the worlds directory.
|
||||||
|
* In case the player dies during gameplay, exit without printing the victory screen.
|
||||||
|
* If the player reaches the goal of the final level, print the victory screen and exit.
|
||||||
|
*/
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
if (argc > 1) {
|
if (argc > 1) {
|
||||||
if (!startWorld("./worlds/" + string(argv[1]))) return 0;
|
if (!startWorld("./worlds/" + string(argv[1]))) return 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
for (const auto & entry : fs::directory_iterator("./worlds"))
|
vector<string> worlds;
|
||||||
if (!startWorld(entry.path())) return 0;
|
for (auto & entry : fs::directory_iterator("./worlds")) {
|
||||||
|
worlds.push_back(entry.path());
|
||||||
|
}
|
||||||
|
// We use this to sort the worlds alphabetically, so that the game progresses in the correct order.
|
||||||
|
std::sort( worlds.begin(), worlds.end(), [](string a, string b) {
|
||||||
|
return a < b;
|
||||||
|
});
|
||||||
|
for (const auto & world : worlds)
|
||||||
|
if (!startWorld(world)) return 0;
|
||||||
}
|
}
|
||||||
printFile("./screens/victory.txt", Color::BRIGHT_GREEN);
|
printFile("./screens/victory.txt", Color::BRIGHT_GREEN);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start a new world defined in the file at worldFile.
|
||||||
|
* If the player reaches the goal, return true.
|
||||||
|
* In case they die, print the death screen and return false.
|
||||||
|
* @return true if the player reached the goal, false in case of death
|
||||||
|
*/
|
||||||
bool startWorld(string worldFile) {
|
bool startWorld(string worldFile) {
|
||||||
BlockRegistry blockRegistry = BlockRegistry();
|
BlockRegistry blockRegistry = BlockRegistry();
|
||||||
World world = World(blockRegistry);
|
World world = World(blockRegistry);
|
||||||
@@ -46,10 +69,25 @@ bool startWorld(string worldFile) {
|
|||||||
if (!player.isAlive()) printFile("./screens/death.txt", Color::BRIGHT_RED);
|
if (!player.isAlive()) printFile("./screens/death.txt", Color::BRIGHT_RED);
|
||||||
return player.hasReachedGoal();
|
return player.hasReachedGoal();
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Move the console cursor up by one line.
|
||||||
|
* Used to overwrite the previous line.
|
||||||
|
*/
|
||||||
void jumpBackOneLine() {
|
void jumpBackOneLine() {
|
||||||
std::cout << "\033[1A";
|
std::cout << "\033[1A";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redraws the game world and player state on the console.
|
||||||
|
* This function first moves the console cursor up by the number of lines
|
||||||
|
* equivalent to the world's height, effectively clearing previous output.
|
||||||
|
* It then calls the render function to display the current state of the world
|
||||||
|
* and the player.
|
||||||
|
*
|
||||||
|
* @param world Reference to the World object representing the game's world.
|
||||||
|
* @param player Reference to the Player object representing the player's state.
|
||||||
|
*/
|
||||||
|
|
||||||
void redraw(World &world, Player &player) {
|
void redraw(World &world, Player &player) {
|
||||||
//std::this_thread::sleep_for(std::chrono::seconds(1));
|
//std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
for (unsigned int y = 0; y <= world.getMaxY()+1; y++) {
|
for (unsigned int y = 0; y <= world.getMaxY()+1; y++) {
|
||||||
@@ -58,13 +96,19 @@ void redraw(World &world, Player &player) {
|
|||||||
render(world, player);
|
render(world, player);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renders the current state of the game world and player onto the console.
|
||||||
|
* It prints the world's blocks with their respective colors and encodings (characters).
|
||||||
|
* On positions that overlap with the player texture, the relevant character of the player's texture is printed instead.
|
||||||
|
*/
|
||||||
void render(World &world, Player &player) {
|
void render(World &world, Player &player) {
|
||||||
vector<vector<Block>> canvas = world.getFieldState();
|
vector<vector<Block>> canvas = world.getFieldState();
|
||||||
vector<vector<char>> playerTexture = player.mapToWorldspace();
|
vector<vector<char>> playerTexture = player.mapToWorldspace();
|
||||||
|
|
||||||
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 (!world.getBlockAt(BlockPos(x, y)).getSettings().isPushable()
|
||||||
|
&& playerTexture.size() > y && playerTexture.at(y).size() > x && playerTexture.at(y).at(x) != ' ') {
|
||||||
cout << Color::BRIGHT_YELLOW << 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) {
|
||||||
@@ -75,6 +119,14 @@ void render(World &world, Player &player) {
|
|||||||
cout << endl;
|
cout << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Prints the content of a file line by line onto the console,
|
||||||
|
* in the specified color.
|
||||||
|
* We use this to print our death and victory screens.
|
||||||
|
*
|
||||||
|
* @param fileLocation Path to the file to be printed.
|
||||||
|
* @param color Color to be used for the output.
|
||||||
|
*/
|
||||||
void printFile(string fileLocation, Color color) {
|
void printFile(string fileLocation, Color color) {
|
||||||
cout << color;
|
cout << color;
|
||||||
vector<string> file = readFileAsVector(fileLocation);
|
vector<string> file = readFileAsVector(fileLocation);
|
||||||
|
|||||||
@@ -5,6 +5,18 @@
|
|||||||
bool tryWalk(World& world, Player& player, bool left);
|
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 tryPushBlock(BlockPos& blockPos, World& world, bool left);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes the player's input and attempts to move the player in the game world
|
||||||
|
* based on the input character. Supports moving up, left, down, or right
|
||||||
|
* using the keys 'w', 'a', 's', 'd' as well as their upper-case equivalents (useful in case caps lock is pressed by accident).
|
||||||
|
*
|
||||||
|
* @param lastChar The character input representing the player's movement command.
|
||||||
|
* @param world Reference to the World object representing the game's world.
|
||||||
|
* @param player Reference to the Player object representing the player's state.
|
||||||
|
* @return true if the player's position was successfully updated, false otherwise.
|
||||||
|
*/
|
||||||
|
|
||||||
bool onInput(char lastChar, World& world, Player& player) {
|
bool onInput(char lastChar, World& world, Player& player) {
|
||||||
switch (lastChar) {
|
switch (lastChar) {
|
||||||
@@ -28,19 +40,44 @@ bool onInput(char lastChar, World& world, Player& player) {
|
|||||||
default: return false;
|
default: return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Attempts to move the player one block to the left or right.
|
||||||
|
*
|
||||||
|
* Checks if the neighbour block to the player's feet is not a solid block and
|
||||||
|
* if so, moves the player there. In case that block solid and the block above it
|
||||||
|
* is not solid, moves the player on top of that block.
|
||||||
|
* Otherwise, returns false.
|
||||||
|
*
|
||||||
|
* @param world Reference to the World object representing the game's world.
|
||||||
|
* @param player Reference to the Player object representing the player's state.
|
||||||
|
* @param left Whether to move left (true) or right (false).
|
||||||
|
* @return true if the player's position was successfully updated, false otherwise.
|
||||||
|
*/
|
||||||
bool tryWalk(World& world, Player& player, bool left) {
|
bool tryWalk(World& world, Player& player, bool left) {
|
||||||
BlockPos neighbourPosTorso = player.getPos()+(left ? BlockPos(-1, 0) : BlockPos(1, 0));
|
BlockPos neighbourPosTorso = player.getPos()+(left ? BlockPos(-1, 0) : BlockPos(1, 0));
|
||||||
BlockPos neighbourPosFeet = player.getPos()+(left ? BlockPos(-1, 1) : BlockPos(1, 1));
|
BlockPos neighbourPosFeet = player.getPos()+(left ? BlockPos(-1, 1) : BlockPos(1, 1));
|
||||||
|
tryPushBlock(neighbourPosFeet, world, left);
|
||||||
if (!world.getBlockAt(neighbourPosFeet).getSettings().hasCollision()) {
|
if (!world.getBlockAt(neighbourPosFeet).getSettings().hasCollision()) {
|
||||||
player.setPos(neighbourPosTorso);
|
player.setPos(neighbourPosTorso);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (!left && world.getBlockAt(neighbourPosFeet).getSettings().hasCollision() && !world.getBlockAt(neighbourPosTorso).getSettings().isSolid()) {
|
else if (world.getBlockAt(neighbourPosFeet).getSettings().hasCollision() && !world.getBlockAt(neighbourPosTorso).getSettings().isSolid()) {
|
||||||
left ? player.move(-1, -1) : player.move(1, -1);
|
left ? player.move(-1, -1) : player.move(1, -1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Attempts to move the player one block downwards.
|
||||||
|
*
|
||||||
|
* Checks if the block above the player's torso or the block above the player's feet is
|
||||||
|
* climbable from the top and if so, moves the player there.
|
||||||
|
* Otherwise, returns false.
|
||||||
|
*
|
||||||
|
* @param world Reference to the World object representing the game's world.
|
||||||
|
* @param player Reference to the Player object representing the player's state.
|
||||||
|
* @return true if the player's position was successfully updated, false otherwise.
|
||||||
|
*/
|
||||||
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);
|
||||||
@@ -48,10 +85,35 @@ bool tryGoDown(World& world, Player& player) {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Attempts to move the player one block upwards.
|
||||||
|
*
|
||||||
|
* Checks if the block above the player's torso or the block above the player's head is
|
||||||
|
* climbable from the bottom and if so, moves the player there.
|
||||||
|
* Otherwise, returns false.
|
||||||
|
*
|
||||||
|
* @param world Reference to the World object representing the game's world.
|
||||||
|
* @param player Reference to the Player object representing the player's state.
|
||||||
|
* @return true if the player's position was successfully updated, false otherwise.
|
||||||
|
*/
|
||||||
bool tryGoUp(World& world, Player& player) {
|
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()) {
|
if (world.getBlockAt(player.getPos()+BlockPos(0, 1)).getSettings().isClimbableFromBottom() || world.getBlockAt(player.getPos()+BlockPos(0, 2)).getSettings().isClimbableFromBottom()) {
|
||||||
player.move(0, -1);
|
player.move(0, -1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
bool tryPushBlock(BlockPos& blockPos, World& world, bool left) {
|
||||||
|
BlockPos neighbourBlockPos = blockPos+(left ? BlockPos(-1, 0) : BlockPos(1, 0));
|
||||||
|
if (world.getBlockAt(blockPos).getSettings().isPushable()) {
|
||||||
|
if (world.getBlockAt(neighbourBlockPos).getSettings().isPushable()) {
|
||||||
|
tryPushBlock(neighbourBlockPos, world, left);
|
||||||
|
}
|
||||||
|
if (world.getBlockAt(neighbourBlockPos) == world.getBlockRegistry().AIR) {
|
||||||
|
world.setBlockAt(neighbourBlockPos, world.getBlockAt(blockPos));
|
||||||
|
world.setBlockAt(blockPos, world.getBlockRegistry().AIR);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
@@ -11,7 +11,7 @@ public:
|
|||||||
{' ', 'o', ' '},
|
{' ', 'o', ' '},
|
||||||
{'/', '|', '\\'},
|
{'/', '|', '\\'},
|
||||||
{'/', ' ', '\\'}
|
{'/', ' ', '\\'}
|
||||||
}}; // Player pos is at the bottom center
|
}}; // Player pos is at the center '|' char
|
||||||
}
|
}
|
||||||
|
|
||||||
BlockPos getPos() {
|
BlockPos getPos() {
|
||||||
@@ -32,19 +32,19 @@ public:
|
|||||||
|
|
||||||
if (world.getBlockAt(pos) == world.getBlockRegistry().GOAL) reachedGoal = true;
|
if (world.getBlockAt(pos) == world.getBlockRegistry().GOAL) reachedGoal = true;
|
||||||
|
|
||||||
|
if (world.getBlockAt(pos.add(0, 2)) == world.getBlockRegistry().WATER) fallLength = 0;
|
||||||
isFreeFalling = !world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isSolid();
|
|
||||||
|
isFreeFalling = !world.getBlockAt(pos.add(0, 2)).getSettings().isSolid();
|
||||||
if (isFreeFalling) {
|
if (isFreeFalling) {
|
||||||
fallLength += 1;
|
fallLength += 1;
|
||||||
if (world.getBlockAt(pos+BlockPos(0, 2)) == world.getBlockRegistry().WATER) fallLength = 0;
|
move(0, 1);
|
||||||
move(BlockPos(0, 1));
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (fallLength > 5) alive = false;
|
if (fallLength > 5) alive = false;
|
||||||
fallLength = 0;
|
fallLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (world.getBlockAt(pos+BlockPos(0, 2)).getSettings().isLethal()) alive = false;
|
if (world.getBlockAt(pos.add(0, 2)).getSettings().isLethal()) alive = false;
|
||||||
}
|
}
|
||||||
bool isAlive() {
|
bool isAlive() {
|
||||||
return alive;
|
return alive;
|
||||||
@@ -75,7 +75,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
World world;
|
World& world;
|
||||||
std::array<std::array<char, 3>, 3> playerTexture;
|
std::array<std::array<char, 3>, 3> playerTexture;
|
||||||
BlockPos pos = BlockPos(0, 0);
|
BlockPos pos = BlockPos(0, 0);
|
||||||
bool alive = true;
|
bool alive = true;
|
||||||
|
|||||||
@@ -10,10 +10,23 @@ using std::vector;
|
|||||||
|
|
||||||
class World {
|
class World {
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* Create a World object using the blocks defined in BlockRegistry.
|
||||||
|
*
|
||||||
|
* @param blockRegistry The BlockRegistry to use.
|
||||||
|
*/
|
||||||
World(BlockRegistry blockRegistry) {
|
World(BlockRegistry blockRegistry) {
|
||||||
this->blockRegistry = blockRegistry;
|
this->blockRegistry = blockRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load the world from the given text file.
|
||||||
|
* - The character '|' is the player's starting position.
|
||||||
|
* - The characters in the file are mapped to the corresponding blocks in the block registry.
|
||||||
|
* - All other characters are kept as purely visual decoration blocks.
|
||||||
|
*
|
||||||
|
* @param fileLocation The location of the file to load.
|
||||||
|
*/
|
||||||
void loadFromFile(string fileLocation) {
|
void loadFromFile(string fileLocation) {
|
||||||
field = {};
|
field = {};
|
||||||
vector<string> file = readFileAsVector(fileLocation);
|
vector<string> file = readFileAsVector(fileLocation);
|
||||||
@@ -27,14 +40,35 @@ public:
|
|||||||
if (y > maxY) maxY = y;
|
if (y > maxY) maxY = y;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Sets the block at the given position in the world.
|
||||||
|
*
|
||||||
|
* In case the position is outside the current bounds of the world, the world will be automatically be expanded.
|
||||||
|
* If the position is negative, an error will be logged.
|
||||||
|
*
|
||||||
|
* @param pos The position to set the block at.
|
||||||
|
* @param block The block to set at that position.
|
||||||
|
*/
|
||||||
void setBlockAt(BlockPos pos, Block block) {
|
void setBlockAt(BlockPos pos, Block block) {
|
||||||
if (pos.isNegative()) return;
|
if (pos.isNegative()) cout << "Tried to set block at negative position: (x: " << pos.getX() << ", y:" << pos.getY() << ")" << endl;
|
||||||
while (field.size() <= pos.getUnsignedY()) field.push_back({});
|
while (field.size() <= pos.getUnsignedY()) field.push_back({});
|
||||||
while (field[pos.getUnsignedY()].size() <= pos.getUnsignedX()) field[pos.getUnsignedY()].push_back(blockRegistry.AIR);
|
while (field[pos.getUnsignedY()].size() <= pos.getUnsignedX()) field[pos.getUnsignedY()].push_back(blockRegistry.AIR);
|
||||||
|
|
||||||
field[pos.getUnsignedY()][pos.getX()] = block;
|
field[pos.getUnsignedY()][pos.getX()] = block;
|
||||||
|
if (block.getSettings().hasGravity() && containsPos(pos.add(0, 1)) && getBlockAt(pos.add(0, 1)) == blockRegistry.AIR) {
|
||||||
|
setBlockAt(pos.add(0, 1), block);
|
||||||
|
setBlockAt(pos, blockRegistry.AIR);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block at the given position in the world.
|
||||||
|
*
|
||||||
|
* In position is outside the current bounds of the world, the AIR block will be returned.
|
||||||
|
*
|
||||||
|
* @param pos The position to get the block at.
|
||||||
|
* @return The block at that position.
|
||||||
|
*/
|
||||||
Block& getBlockAt(BlockPos pos) {
|
Block& getBlockAt(BlockPos pos) {
|
||||||
if (pos.getUnsignedY() < field.size() && pos.getUnsignedX() < field[pos.getUnsignedY()].size()) {
|
if (pos.getUnsignedY() < field.size() && pos.getUnsignedX() < field[pos.getUnsignedY()].size()) {
|
||||||
return field[pos.getY()][pos.getX()];
|
return field[pos.getY()][pos.getX()];
|
||||||
@@ -42,21 +76,57 @@ public:
|
|||||||
//cout << "Out of bounds: " << pos.getX() << ", " << pos.getY() << endl;
|
//cout << "Out of bounds: " << pos.getX() << ", " << pos.getY() << endl;
|
||||||
return blockRegistry.AIR;
|
return blockRegistry.AIR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether the given position is within the bounds of the world, or not.
|
||||||
|
*
|
||||||
|
* @param pos The position to check.
|
||||||
|
* @return True if the position is non-negative and within the current bounds of the world, false otherwise.
|
||||||
|
*/
|
||||||
bool containsPos(BlockPos pos) {
|
bool containsPos(BlockPos pos) {
|
||||||
return !pos.isNegative() && pos.getUnsignedY() < field.size();
|
return !pos.isNegative() && pos.getUnsignedY() < field.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The current state of the world as a 2D vector of blocks.
|
||||||
|
*/
|
||||||
vector<vector<Block>> getFieldState() {
|
vector<vector<Block>> getFieldState() {
|
||||||
return field;
|
return field;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block registry for the world.
|
||||||
|
*
|
||||||
|
* @return The block registry containing all registered blocks.
|
||||||
|
*/
|
||||||
BlockRegistry getBlockRegistry() {
|
BlockRegistry getBlockRegistry() {
|
||||||
return blockRegistry;
|
return blockRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the biggest X (horizontal) value in the world.
|
||||||
|
*
|
||||||
|
* @return The maximum X value.
|
||||||
|
*/
|
||||||
unsigned int getMaxX() {
|
unsigned int getMaxX() {
|
||||||
return maxX;
|
return maxX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the biggest Y (vertical) value in the world.
|
||||||
|
*
|
||||||
|
* @return The maximum Y value.
|
||||||
|
*/
|
||||||
unsigned int getMaxY() {
|
unsigned int getMaxY() {
|
||||||
return maxY;
|
return maxY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the starting position of the player in the world.
|
||||||
|
*
|
||||||
|
* @return The BlockPos representing the starting position in the current level.
|
||||||
|
*/
|
||||||
|
|
||||||
BlockPos getStartPos() {
|
BlockPos getStartPos() {
|
||||||
return startPos;
|
return startPos;
|
||||||
}
|
}
|
||||||
|
|||||||
14
worlds/3.txt
14
worlds/3.txt
@@ -1,15 +1,15 @@
|
|||||||
|
|
||||||
----------------------
|
----------------------
|
||||||
00 <>
|
00
|
||||||
00 <++>
|
00
|
||||||
00 [+ +]
|
00
|
||||||
--------- [ ]
|
---------
|
||||||
0 o 00------ O [ 7 ]
|
0 o 00------ O
|
||||||
0 S /|\ 00 0 [ ]
|
0 S /|\ 00 0
|
||||||
0 / \ 00 0 ---------------
|
0 / \ 00 0 ---------------
|
||||||
---------------- 0 H 0
|
---------------- 0 H 0
|
||||||
0 H 0
|
0 H 0
|
||||||
0 H 0
|
0 H 0
|
||||||
0 H 0
|
0 H 0
|
||||||
0~~~~~~0---------0
|
0~~~~~~0---------0
|
||||||
00000000
|
00000000
|
||||||
14
worlds/4.txt
Normal file
14
worlds/4.txt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
|
||||||
|
o
|
||||||
|
S /|\
|
||||||
|
/ \
|
||||||
|
---
|
||||||
|
0
|
||||||
|
0 x x
|
||||||
|
0 -------------
|
||||||
|
0 H
|
||||||
|
H H
|
||||||
|
H H
|
||||||
|
H H O
|
||||||
|
~~~H H
|
||||||
|
------------------^^^^^----------------
|
||||||
Reference in New Issue
Block a user