mirror of
https://github.com/Motschen/Adventura.git
synced 2025-12-15 19:35:09 +01:00
Implement main logic
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -30,3 +30,5 @@
|
|||||||
*.exe
|
*.exe
|
||||||
*.out
|
*.out
|
||||||
*.app
|
*.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