init repo
This commit is contained in:
commit
ee8cc69a8b
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
cmake-*
|
||||
.idea/
|
11
CMakeLists.txt
Normal file
11
CMakeLists.txt
Normal file
@ -0,0 +1,11 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(textadv)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(SOURCE_FILES main.cpp Room.cpp Room.h wordwrap.h wordwrap.cpp State.cpp State.h strings.h
|
||||
GameObject.cpp
|
||||
FoodObject.h
|
||||
Serializable.cpp
|
||||
FoodObject.cpp)
|
||||
add_executable(textadv ${SOURCE_FILES})
|
7
CMakeLists_1.txt
Normal file
7
CMakeLists_1.txt
Normal file
@ -0,0 +1,7 @@
|
||||
cmake_minimum_required(VERSION 3.8)
|
||||
project(textadv)
|
||||
|
||||
set(CMAKE_CXX_STANDARD 11)
|
||||
|
||||
set(SOURCE_FILES main.cpp Room.cpp Room.h wordwrap.h wordwrap.cpp State.cpp State.h strings.h)
|
||||
add_executable(textadv ${SOURCE_FILES})
|
53
FoodObject.cpp
Normal file
53
FoodObject.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#include "FoodObject.h"
|
||||
|
||||
|
||||
namespace tqcq {
|
||||
} // namespace tqcq
|
||||
FoodObject::FoodObject(const std::string &name, const std::string &keyword, const std::string &description,
|
||||
int energy)
|
||||
: GameObject(name, keyword, description) {
|
||||
object_type = Food;
|
||||
setEnergy(energy);
|
||||
}
|
||||
|
||||
int FoodObject::getEnergy() const {
|
||||
return energy;
|
||||
}
|
||||
|
||||
std::string FoodObject::ToString() const {
|
||||
Packer packer;
|
||||
packer.pack(GameObject::ToString());
|
||||
packer.pack(energy);
|
||||
return packer.ToHexStr();
|
||||
}
|
||||
|
||||
void FoodObject::FromString(const std::string &str) {
|
||||
Packer packer;
|
||||
std::string game_object_hex;
|
||||
packer.FromHexStr(str);
|
||||
|
||||
packer.unpack(energy);
|
||||
packer.unpack(game_object_hex);
|
||||
|
||||
GameObject::FromString(game_object_hex);
|
||||
}
|
||||
|
||||
FoodObject::FoodObject()
|
||||
: GameObject(), energy(0)
|
||||
{
|
||||
object_type = Food;
|
||||
}
|
||||
|
||||
void FoodObject::setEnergy(int _energy) {
|
||||
_energy = std::min(_energy, 10);
|
||||
_energy = std::max(_energy, 1);
|
||||
energy = _energy;
|
||||
}
|
||||
|
||||
std::string FoodObject::getInfo() const {
|
||||
return GameObject::getInfo() + " (" + std::to_string(energy) + ")";
|
||||
}
|
24
FoodObject.h
Normal file
24
FoodObject.h
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#ifndef TEXTADV_FOODOBJECT_H
|
||||
#define TEXTADV_FOODOBJECT_H
|
||||
|
||||
#include "GameObject.h"
|
||||
|
||||
class FoodObject : public GameObject {
|
||||
int energy;
|
||||
public:
|
||||
FoodObject();
|
||||
FoodObject(const std::string &name, const std::string &keyword, const std::string &description, int energy);
|
||||
std::string getInfo() const override;
|
||||
|
||||
int getEnergy() const;
|
||||
void setEnergy(int energy);
|
||||
|
||||
std::string ToString() const override;
|
||||
void FromString(const std::string &str) override;
|
||||
};
|
||||
|
||||
#endif //TEXTADV_FOODOBJECT_H
|
105
GameObject.cpp
Normal file
105
GameObject.cpp
Normal file
@ -0,0 +1,105 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#include "GameObject.h"
|
||||
#include "FoodObject.h"
|
||||
|
||||
std::map<int, GameObject *> GameObject::game_object_map;
|
||||
int GameObject::game_object_id_counter = 1;
|
||||
|
||||
GameObject::GameObject()
|
||||
: game_object_id(game_object_id_counter++), object_type(Default) {
|
||||
}
|
||||
|
||||
GameObject::GameObject(const std::string &name, const std::string &keyword, const std::string &description)
|
||||
: game_object_id(game_object_id_counter++), object_type(Default), name(name), keyword(keyword),
|
||||
description(description) {
|
||||
}
|
||||
|
||||
const std::string &GameObject::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
const std::string &GameObject::getKeyword() const {
|
||||
return keyword;
|
||||
}
|
||||
|
||||
const std::string &GameObject::getDescription() const {
|
||||
return description;
|
||||
}
|
||||
|
||||
std::string GameObject::ToString() const {
|
||||
Packer packer;
|
||||
packer.pack(game_object_id);
|
||||
packer.pack(name);
|
||||
packer.pack(keyword);
|
||||
packer.pack(description);
|
||||
|
||||
return packer.ToHexStr();
|
||||
}
|
||||
|
||||
void GameObject::FromString(const std::string &str) {
|
||||
Packer packer;
|
||||
packer.FromHexStr(str);
|
||||
packer.unpack(description);
|
||||
packer.unpack(keyword);
|
||||
packer.unpack(name);
|
||||
packer.unpack(game_object_id);
|
||||
}
|
||||
|
||||
GameObject *GameObject::CreateGameObject(GameObject::Type type, bool add_to_map) {
|
||||
GameObject *object = nullptr;
|
||||
switch (type) {
|
||||
case Default:
|
||||
object = new GameObject();
|
||||
break;
|
||||
case Food:
|
||||
object = new FoodObject();
|
||||
break;
|
||||
default:
|
||||
return nullptr;
|
||||
}
|
||||
if (add_to_map) AddGameObject(object);
|
||||
return object;
|
||||
}
|
||||
|
||||
int GameObject::getGameObjectId() const {
|
||||
return game_object_id;
|
||||
}
|
||||
|
||||
GameObject::Type GameObject::getObjectType() const {
|
||||
return object_type;
|
||||
}
|
||||
|
||||
GameObject *GameObject::getGameObjectById(int id) {
|
||||
auto it = game_object_map.find(id);
|
||||
return it == game_object_map.end() ? nullptr : it->second;
|
||||
}
|
||||
|
||||
void GameObject::AddGameObject(GameObject *object) {
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
game_object_map.insert({object->getGameObjectId(), object});
|
||||
}
|
||||
|
||||
GameObject::~GameObject() {
|
||||
game_object_map.erase(game_object_id);
|
||||
}
|
||||
|
||||
void GameObject::setName(const std::string &_name) {
|
||||
name = _name;
|
||||
}
|
||||
|
||||
void GameObject::setKeyword(const std::string &_keyword) {
|
||||
keyword = _keyword;
|
||||
}
|
||||
|
||||
void GameObject::setDescription(const std::string &_description) {
|
||||
description = _description;
|
||||
}
|
||||
|
||||
std::string GameObject::getInfo() const {
|
||||
return keyword + ": " + name;
|
||||
}
|
63
GameObject.h
Normal file
63
GameObject.h
Normal file
@ -0,0 +1,63 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#ifndef TEXTADV_GAMEOBJECT_H
|
||||
#define TEXTADV_GAMEOBJECT_H
|
||||
#include "Serializable.h"
|
||||
#include <string>
|
||||
|
||||
class GameObject : public Serializable {
|
||||
public:
|
||||
enum Type {
|
||||
Default, // GameObject
|
||||
Food, // FoodObject
|
||||
TYPE_MAX_COUNT,
|
||||
};
|
||||
|
||||
|
||||
static int game_object_id_counter;
|
||||
/**
|
||||
* @brief Create a GameObject object
|
||||
*/
|
||||
static std::map<int, GameObject*> game_object_map;
|
||||
static GameObject* getGameObjectById(int id);
|
||||
/**
|
||||
* @brief Create a GameObject object
|
||||
* @param type
|
||||
* @return
|
||||
*/
|
||||
static GameObject* CreateGameObject(Type type, bool add_to_map = true);
|
||||
static void AddGameObject(GameObject* object);
|
||||
|
||||
GameObject();
|
||||
GameObject(const std::string &name, const std::string &keyword, const std::string &description);
|
||||
~GameObject() override;
|
||||
|
||||
virtual std::string getInfo() const;
|
||||
|
||||
const std::string& getName() const;
|
||||
void setName(const std::string& name);
|
||||
|
||||
const std::string& getKeyword() const;
|
||||
void setKeyword(const std::string& keyword);
|
||||
|
||||
const std::string& getDescription() const;
|
||||
void setDescription(const std::string& description);
|
||||
|
||||
int getGameObjectId() const;
|
||||
virtual Type getObjectType() const;
|
||||
|
||||
std::string ToString() const override;
|
||||
void FromString(const std::string &str) override;
|
||||
|
||||
protected:
|
||||
int game_object_id;
|
||||
Type object_type;
|
||||
std::string name;
|
||||
std::string keyword;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
|
||||
#endif //TEXTADV_GAMEOBJECT_H
|
204
Room.cpp
Normal file
204
Room.cpp
Normal file
@ -0,0 +1,204 @@
|
||||
|
||||
#include "Room.h"
|
||||
#include "wordwrap.h"
|
||||
#include <algorithm>
|
||||
|
||||
/**
|
||||
* Stores a static list of all rooms.
|
||||
*/
|
||||
std::map<int, Room *> Room::room_map;
|
||||
int Room::room_id_counter = 1;
|
||||
|
||||
/**
|
||||
* Room default constructor.
|
||||
* @param _name Room's name.
|
||||
* @param _desc Room's description.
|
||||
*/
|
||||
Room::Room(const string *_name, const string *_desc) :
|
||||
name(_name), description(_desc), north_id(-1), south_id(-1), east_id(-1), west_id(-1),
|
||||
room_id(room_id_counter++), objects() {};
|
||||
|
||||
/**
|
||||
* Remove destroyed rooms from the static list.
|
||||
*/
|
||||
Room::~Room() {
|
||||
Room::room_map.erase(room_id);
|
||||
delete name;
|
||||
delete description;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the description of a room (the name and long description)
|
||||
*/
|
||||
void Room::describe() const {
|
||||
wrapOut(this->name);
|
||||
wrapEndPara();
|
||||
wrapOut(this->description);
|
||||
wrapEndPara();
|
||||
for (const auto &obj_id: this->objects) {
|
||||
auto obj = GameObject::getGameObjectById(obj_id);
|
||||
std::string obj_desc = obj->getInfo();
|
||||
wrapOut(&obj_desc);
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Statically creates a room and then adds it to the global list.
|
||||
* @param _name Name for the new room.
|
||||
* @param _desc Description for the new room.
|
||||
* @return A pointer to the newly created room.
|
||||
*/
|
||||
Room *Room::addRoom(const string *_name, const string *_desc) {
|
||||
auto *newRoom = new Room(_name, _desc);
|
||||
newRoom->setRoomId(room_id_counter++);
|
||||
Room::room_map.insert({newRoom->getRoomId(), newRoom});
|
||||
return newRoom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an existing room to the static list.
|
||||
* @param room Pointer to the room to add.
|
||||
* @return
|
||||
*/
|
||||
void Room::addRoom(Room *room) {
|
||||
Room::room_map.insert({room->getRoomId(), room});
|
||||
}
|
||||
|
||||
|
||||
string Room::ToString() const {
|
||||
Packer packer;
|
||||
|
||||
for (const auto &obj_id: this->objects) {
|
||||
packer.pack(obj_id);
|
||||
}
|
||||
packer.pack(*name)
|
||||
.pack(*description)
|
||||
.pack(room_id)
|
||||
.pack(north_id)
|
||||
.pack(south_id)
|
||||
.pack(east_id)
|
||||
.pack(west_id)
|
||||
.pack(this->objects.size());
|
||||
return packer.ToHexStr();
|
||||
}
|
||||
|
||||
void Room::FromString(const string &str) {
|
||||
std::string *new_name = new std::string();
|
||||
std::string *new_desc = new std::string();
|
||||
|
||||
Packer packer;
|
||||
packer.FromHexStr(str);
|
||||
size_t objects_size;
|
||||
packer.unpack(objects_size)
|
||||
.unpack(west_id)
|
||||
.unpack(east_id)
|
||||
.unpack(south_id)
|
||||
.unpack(north_id)
|
||||
.unpack(room_id)
|
||||
.unpack(*new_desc)
|
||||
.unpack(*new_name);
|
||||
for (int i = 0; i < objects_size; i++) {
|
||||
int id;
|
||||
packer.unpack(id);
|
||||
objects.push_back(id);
|
||||
}
|
||||
if (name) delete name;
|
||||
if (description) delete description;
|
||||
name = new_name;
|
||||
description = new_desc;
|
||||
}
|
||||
|
||||
int Room::getNorthId() const {
|
||||
return north_id;
|
||||
}
|
||||
|
||||
void Room::setNorthId(int _north_id) {
|
||||
north_id = _north_id;
|
||||
}
|
||||
|
||||
int Room::getSouthId() const {
|
||||
return south_id;
|
||||
}
|
||||
|
||||
void Room::setSouthId(int _south_id) {
|
||||
south_id = _south_id;
|
||||
}
|
||||
|
||||
int Room::getEastId() const {
|
||||
return east_id;
|
||||
}
|
||||
|
||||
void Room::setEastId(int _east_id) {
|
||||
east_id = _east_id;
|
||||
}
|
||||
|
||||
int Room::getWestId() const {
|
||||
return west_id;
|
||||
}
|
||||
|
||||
void Room::setWestId(int _west_id) {
|
||||
west_id = _west_id;
|
||||
}
|
||||
|
||||
int Room::getRoomId() const {
|
||||
return room_id;
|
||||
}
|
||||
|
||||
void Room::setRoomId(int _room_id) {
|
||||
room_id = _room_id;
|
||||
}
|
||||
|
||||
Room::Room() : Room(nullptr, nullptr) {
|
||||
}
|
||||
|
||||
Room *Room::getNorth() const {
|
||||
auto iter = Room::room_map.find(north_id);
|
||||
return iter == Room::room_map.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
Room *Room::getSouth() const {
|
||||
auto iter = Room::room_map.find(south_id);
|
||||
return iter == Room::room_map.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
Room *Room::getEast() const {
|
||||
auto iter = Room::room_map.find(east_id);
|
||||
return iter == Room::room_map.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
Room *Room::getWest() const {
|
||||
auto iter = Room::room_map.find(west_id);
|
||||
return iter == Room::room_map.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
Room *Room::getRoomById(int id) {
|
||||
auto iter = Room::room_map.find(id);
|
||||
return iter == Room::room_map.end() ? nullptr : iter->second;
|
||||
}
|
||||
|
||||
void Room::AddGameObject(int object_id) {
|
||||
objects.push_back(object_id);
|
||||
objects.sort();
|
||||
}
|
||||
|
||||
void Room::RemoveGameObject(int object_id) {
|
||||
objects.remove(object_id);
|
||||
}
|
||||
|
||||
int Room::FindGameObject(const string &keyword) const {
|
||||
for (const auto &obj_id: this->objects) {
|
||||
auto obj = GameObject::getGameObjectById(obj_id);
|
||||
if (obj->getKeyword() == keyword) {
|
||||
return obj_id;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool Room::IsInRoom(int object_id) const {
|
||||
return std::find(objects.begin(), objects.end(), object_id) != objects.end();
|
||||
}
|
||||
|
||||
|
||||
|
125
Room.h
Normal file
125
Room.h
Normal file
@ -0,0 +1,125 @@
|
||||
|
||||
|
||||
|
||||
#ifndef TEXTADV_ROOM_H
|
||||
#define TEXTADV_ROOM_H
|
||||
|
||||
#include <string>
|
||||
#include <forward_list>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include "GameObject.h"
|
||||
#include "Serializable.h"
|
||||
|
||||
using std::string;
|
||||
|
||||
/**
|
||||
* Represents a room (accessible location in the game).
|
||||
*/
|
||||
class Room : public Serializable {
|
||||
/**
|
||||
* Short name used as a header.
|
||||
*/
|
||||
const string* name;
|
||||
/**
|
||||
* Full description of the room.
|
||||
*/
|
||||
const string* description;
|
||||
/**
|
||||
* Room ID
|
||||
*/
|
||||
int room_id;
|
||||
/**
|
||||
* Pointer to room that is north of this one.
|
||||
*/
|
||||
|
||||
int north_id;
|
||||
int south_id;
|
||||
int east_id;
|
||||
int west_id;
|
||||
|
||||
// int: game object id
|
||||
std::list<int> objects;
|
||||
public:
|
||||
/**
|
||||
* for serialization
|
||||
*/
|
||||
Room();
|
||||
|
||||
/**
|
||||
* Constructs a new Room.
|
||||
* @param _name Name of the room.
|
||||
* @param _desc Description of the room.
|
||||
*/
|
||||
Room(const string *_name, const string *_desc);
|
||||
|
||||
/**
|
||||
* Removes a destroyed room from the global list if it's there.
|
||||
*/
|
||||
~Room();
|
||||
|
||||
/**
|
||||
* Outputs the name and description of the room
|
||||
* in standard format.
|
||||
*/
|
||||
void describe() const;
|
||||
|
||||
/**
|
||||
* List storing all rooms that have been registered via addRoom().
|
||||
*/
|
||||
// static std::list<Room*> rooms;
|
||||
|
||||
static int room_id_counter;
|
||||
/**
|
||||
* Map storing all rooms that have been registered via addRoom().
|
||||
*/
|
||||
static std::map<int, Room *> room_map;
|
||||
/**
|
||||
* Returns a pointer to the room with the given ID.
|
||||
* @param room_id
|
||||
* @return
|
||||
*/
|
||||
static Room* getRoomById(int room_id);
|
||||
/**
|
||||
* Creates a new Room with the given parameters and register it with the static list.
|
||||
* @param _name Name of the room.
|
||||
* @param _desc Description of the room.
|
||||
*/
|
||||
static Room* addRoom(const string* _name, const string* _desc);
|
||||
static void addRoom(Room* room);
|
||||
|
||||
int getRoomId() const;
|
||||
void setRoomId(int _room_id);
|
||||
|
||||
|
||||
Room* getNorth() const;
|
||||
int getNorthId() const;
|
||||
void setNorthId(int _north_id);
|
||||
|
||||
Room* getSouth() const;
|
||||
int getSouthId() const;
|
||||
void setSouthId(int _south_id);
|
||||
|
||||
Room* getEast() const;
|
||||
int getEastId() const;
|
||||
void setEastId(int _east_id);
|
||||
|
||||
Room* getWest() const;
|
||||
int getWestId() const;
|
||||
void setWestId(int _west_id);
|
||||
|
||||
void AddGameObject(int object_id);
|
||||
void RemoveGameObject(int object_id);
|
||||
bool IsInRoom(int object_id) const;
|
||||
/**
|
||||
* Find a game object in this room by keyword.
|
||||
* @param keyword
|
||||
* @return game object id, or -1 if not found
|
||||
*/
|
||||
int FindGameObject(const std::string &keyword) const;
|
||||
|
||||
string ToString() const override;
|
||||
void FromString(const string &str) override;
|
||||
};
|
||||
|
||||
#endif //TEXTADV_ROOM_H
|
15
Serializable.cpp
Normal file
15
Serializable.cpp
Normal file
@ -0,0 +1,15 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#include "Serializable.h"
|
||||
|
||||
void Serializable::serialize(std::ostream &os) const {
|
||||
os << ToString();
|
||||
}
|
||||
|
||||
void Serializable::deserialize(std::istream &is) {
|
||||
std::string str;
|
||||
is >> str;
|
||||
FromString(str);
|
||||
}
|
228
Serializable.h
Normal file
228
Serializable.h
Normal file
@ -0,0 +1,228 @@
|
||||
//
|
||||
// Created by tqcq on 2023/11/14.
|
||||
//
|
||||
|
||||
#ifndef TEXTADV_SERIALIZABLE_H
|
||||
#define TEXTADV_SERIALIZABLE_H
|
||||
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
|
||||
class Serializable {
|
||||
public:
|
||||
|
||||
|
||||
virtual ~Serializable() = default;
|
||||
|
||||
virtual std::string ToString() const = 0;
|
||||
virtual void FromString(const std::string &str) = 0;
|
||||
|
||||
virtual void serialize(std::ostream &os) const;
|
||||
virtual void deserialize(std::istream &is);
|
||||
friend std::ostream &operator<<(std::ostream &os, const Serializable &obj) {
|
||||
obj.serialize(os);
|
||||
return os;
|
||||
}
|
||||
friend std::istream &operator>>(std::istream &is, Serializable &obj) {
|
||||
obj.deserialize(is);
|
||||
return is;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class Packer {
|
||||
public:
|
||||
enum Type {
|
||||
CHAR = 0,
|
||||
INT,
|
||||
LONG,
|
||||
SIZE_T,
|
||||
DOUBLE,
|
||||
FLOAT,
|
||||
STRING,
|
||||
VECTOR,
|
||||
MAP,
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
Type type;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
struct Packet {
|
||||
union {
|
||||
PacketHeader header;
|
||||
struct {
|
||||
Type type;
|
||||
size_t size;
|
||||
};
|
||||
};
|
||||
std::string data;
|
||||
};
|
||||
|
||||
Packer& pack(char c) {
|
||||
data.push_back({CHAR, sizeof(char), std::string(sizeof(char), c)});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(int i) {
|
||||
data.push_back({INT, sizeof(int), std::string((char*)&i, sizeof(int))});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(long l) {
|
||||
data.push_back({LONG, sizeof(long), std::string((char*)&l, sizeof(long))});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(size_t s) {
|
||||
data.push_back({SIZE_T, sizeof(size_t), std::string((char*)&s, sizeof(size_t))});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(double d) {
|
||||
data.push_back({DOUBLE, sizeof(double), std::string((char*)&d, sizeof(double))});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(float f) {
|
||||
data.push_back({FLOAT, sizeof(float), std::string((char*)&f, sizeof(float))});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(const std::string &str) {
|
||||
data.push_back({STRING, str.size(), str});
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(const Serializable &obj) {
|
||||
pack(obj.ToString());
|
||||
return *this;
|
||||
}
|
||||
Packer& pack(const Serializable* obj) {
|
||||
pack(obj->ToString());
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(char &c) {
|
||||
Packet &d = data.back();
|
||||
if (d.type != CHAR) throw std::runtime_error("Type mismatch");
|
||||
c = d.data[0];
|
||||
data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
Packer& unpack(int &i) {
|
||||
Packet &d = data.back();
|
||||
if (d.type != INT) throw std::runtime_error("Type mismatch");
|
||||
i = *(int*)d.data.data();
|
||||
data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(long &l) {
|
||||
Packet &d = data.back();
|
||||
if (d.type != LONG) throw std::runtime_error("Type mismatch");
|
||||
l = *(long*)d.data.data();
|
||||
data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(size_t& s) {
|
||||
Packet &d = data.back();
|
||||
if (d.type != SIZE_T) throw std::runtime_error("Type mismatch");
|
||||
s = *(size_t*)d.data.data();
|
||||
data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(double &d) {
|
||||
Packet &packet = this->data.back();
|
||||
if (packet.type != DOUBLE) throw std::runtime_error("Type mismatch");
|
||||
d = *(double*)packet.data.data();
|
||||
this->data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(float &f) {
|
||||
Packet &packet = this->data.back();
|
||||
if (packet.type != FLOAT) throw std::runtime_error("Type mismatch");
|
||||
f = *(float*)packet.data.data();
|
||||
this->data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(std::string &str) {
|
||||
Packet &packet= this->data.back();
|
||||
if (packet.type != STRING) throw std::runtime_error("Type mismatch");
|
||||
str = packet.data;
|
||||
this->data.pop_back();
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(Serializable &obj) {
|
||||
std::string str;
|
||||
unpack(str);
|
||||
obj.FromString(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
Packer& unpack(Serializable* obj) {
|
||||
std::string str;
|
||||
unpack(str);
|
||||
obj->FromString(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string ToHexStr() const {
|
||||
static const std::string hex_str = "0123456789ABCDEF";
|
||||
std::string str;
|
||||
for (const auto& d : data) {
|
||||
// convert header to hex
|
||||
auto* ptr = (unsigned char*)&d.header;
|
||||
auto* end = ptr + sizeof(d.header);
|
||||
for (; ptr != end; ++ptr) {
|
||||
str.push_back(hex_str[*ptr >> 4]);
|
||||
str.push_back(hex_str[*ptr & 0x0F]);
|
||||
}
|
||||
|
||||
// convert data to hex
|
||||
ptr = (unsigned char*)d.data.data();
|
||||
end = ptr + d.data.size();
|
||||
for (; ptr != end; ++ptr) {
|
||||
str.push_back(hex_str[*ptr >> 4]);
|
||||
str.push_back(hex_str[*ptr & 0x0F]);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
void FromHexStr(const std::string& str) {
|
||||
static const std::string hex_str = "0123456789ABCDEF";
|
||||
data.clear();
|
||||
int idx = 0;
|
||||
for (;idx < str.size();) {
|
||||
Packet packet;
|
||||
// convert header from hex
|
||||
auto* ptr = (unsigned char*)&packet.header;
|
||||
auto* end = ptr + sizeof(packet.header);
|
||||
for (; ptr != end; ++ptr) {
|
||||
auto hi = hex_str.find(str[idx++]);
|
||||
auto lo = hex_str.find(str[idx++]);
|
||||
if (hi == std::string::npos || lo == std::string::npos) throw std::runtime_error("Invalid hex string");
|
||||
*ptr = (hi << 4) | lo;
|
||||
}
|
||||
|
||||
packet.data.resize(packet.header.size);
|
||||
for (size_t i = 0; i < packet.header.size; ++i) {
|
||||
auto hi = hex_str.find(str[idx++]);
|
||||
auto lo = hex_str.find(str[idx++]);
|
||||
if (hi == std::string::npos || lo == std::string::npos) throw std::runtime_error("Invalid hex string");
|
||||
packet.data[i] = (hi << 4) | lo;
|
||||
}
|
||||
|
||||
data.emplace_back(packet);
|
||||
}
|
||||
}
|
||||
private:
|
||||
std::vector<Packet> data;
|
||||
};
|
||||
|
||||
|
||||
|
||||
#endif //TEXTADV_SERIALIZABLE_H
|
108
State.cpp
Normal file
108
State.cpp
Normal file
@ -0,0 +1,108 @@
|
||||
|
||||
|
||||
#include "State.h"
|
||||
#include <algorithm>
|
||||
#include <ctime>
|
||||
|
||||
/**
|
||||
* Current state of the game.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Display the description of the room the player is in. */
|
||||
|
||||
void State::announceLoc() const {
|
||||
Room::getRoomById(currentRoomId)->describe();
|
||||
}
|
||||
|
||||
int State::seed = time(nullptr);
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param startRoom Pointer to the room to start in.
|
||||
*/
|
||||
State::State(int room_id) : currentRoomId(room_id),inventory_sorted(true),inventory(),HP(100) {};
|
||||
|
||||
/**
|
||||
* Move to a specified room and print its description.
|
||||
* @param target Pointer to the room to move to.
|
||||
*/
|
||||
void State::goTo(Room *target) {
|
||||
if (target != nullptr) {
|
||||
this->currentRoomId = target->getRoomId();
|
||||
this->announceLoc();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a pointer to the current room.
|
||||
* @return Pointer to the current room.
|
||||
*/
|
||||
Room *State::getCurrentRoom() const {
|
||||
return Room::room_map.find(currentRoomId)->second;
|
||||
}
|
||||
|
||||
int State::getHP() const {
|
||||
return HP;
|
||||
}
|
||||
|
||||
void State::setHP(int HP) {
|
||||
HP = std::min(HP, 100);
|
||||
HP = std::max(HP, 0);
|
||||
this->HP = HP;
|
||||
}
|
||||
|
||||
string State::ToString() const {
|
||||
Packer packer;
|
||||
for (auto it = inventory.begin(); it != inventory.end(); it++) {
|
||||
packer.pack(*it);
|
||||
}
|
||||
packer.pack(inventory.size())
|
||||
.pack(HP)
|
||||
.pack(currentRoomId);
|
||||
return packer.ToHexStr();
|
||||
}
|
||||
|
||||
void State::FromString(const string &str) {
|
||||
inventory.clear();
|
||||
Packer packer;
|
||||
packer.FromHexStr(str);
|
||||
size_t inventory_size;
|
||||
|
||||
packer.unpack(currentRoomId)
|
||||
.unpack(HP)
|
||||
.unpack(inventory_size);
|
||||
for (int i = 0; i < inventory_size; i++) {
|
||||
int id;
|
||||
packer.unpack(id);
|
||||
inventory.push_back(id);
|
||||
}
|
||||
}
|
||||
|
||||
std::list<int> State::getInventory() {
|
||||
inventory_sorted = true;
|
||||
inventory.sort();
|
||||
return inventory;
|
||||
}
|
||||
|
||||
bool State::IsInInventory(int object_id) const {
|
||||
return std::find(inventory.begin(), inventory.end(), object_id) != inventory.end();
|
||||
}
|
||||
|
||||
void State::addToInventory(int object_id) {
|
||||
inventory.push_back(object_id);
|
||||
inventory_sorted = false;
|
||||
}
|
||||
|
||||
void State::removeFromInventory(int object_id) {
|
||||
inventory.remove(object_id);
|
||||
inventory_sorted = false;
|
||||
}
|
||||
|
||||
int State::NextRandom() {
|
||||
const int a = 1103515245;
|
||||
const int c = 12345;
|
||||
const int m = 1 << 31;
|
||||
seed = (a * seed + c) % m;
|
||||
return (int)((unsigned int)seed >> 1);
|
||||
}
|
38
State.h
Normal file
38
State.h
Normal file
@ -0,0 +1,38 @@
|
||||
|
||||
|
||||
#ifndef TEXTADV_STATE_H
|
||||
#define TEXTADV_STATE_H
|
||||
|
||||
|
||||
#include "Room.h"
|
||||
#include "GameObject.h"
|
||||
|
||||
class State : public Serializable {
|
||||
int currentRoomId;
|
||||
// int: game object id
|
||||
bool inventory_sorted;
|
||||
std::list<int> inventory;
|
||||
int HP;
|
||||
public:
|
||||
static int seed;
|
||||
explicit State(int room_id);
|
||||
void goTo(Room* target);
|
||||
void announceLoc() const;
|
||||
Room* getCurrentRoom() const;
|
||||
|
||||
int getHP() const;
|
||||
void setHP(int HP);
|
||||
std::list<int> getInventory();
|
||||
void addToInventory(int object_id);
|
||||
void removeFromInventory(int object_id);
|
||||
bool IsInInventory(int object_id) const;
|
||||
|
||||
static int NextRandom();
|
||||
|
||||
string ToString() const override;
|
||||
|
||||
void FromString(const string &str) override;
|
||||
};
|
||||
|
||||
|
||||
#endif //TEXTADV_STATE_H
|
398
main.cpp
Normal file
398
main.cpp
Normal file
@ -0,0 +1,398 @@
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <memory>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
#include <forward_list>
|
||||
#include "Room.h"
|
||||
#include "wordwrap.h"
|
||||
#include "State.h"
|
||||
#include "strings.h"
|
||||
#include <map>
|
||||
#include "FoodObject.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
using std::string;
|
||||
using std::unique_ptr;
|
||||
|
||||
string commandBuffer;
|
||||
State *currentState;
|
||||
|
||||
/**
|
||||
* Print out the command prompt then read a command into the provided string buffer.
|
||||
* @param buffer Pointer to the string buffer to use.
|
||||
*/
|
||||
void inputCommand(string *buffer) {
|
||||
buffer->clear();
|
||||
std::cout << "> ";
|
||||
std::getline(std::cin, *buffer);
|
||||
}
|
||||
|
||||
bool LoadFromFile(const std::string& file) {
|
||||
std::ifstream ifs(file);
|
||||
if (!ifs.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string str;
|
||||
ifs >> str;
|
||||
ifs.close();
|
||||
|
||||
GameObject::game_object_map.clear();
|
||||
Room::room_map.clear();
|
||||
|
||||
Packer packer;
|
||||
packer.FromHexStr(str);
|
||||
{
|
||||
size_t objects_size;
|
||||
packer.unpack(objects_size);
|
||||
for (int i = 0; i < objects_size; i++) {
|
||||
int type;
|
||||
int id;
|
||||
GameObject *obj = nullptr;
|
||||
packer.unpack(type);
|
||||
packer.unpack(id);
|
||||
obj = GameObject::CreateGameObject(static_cast<GameObject::Type>(type), false);
|
||||
packer.unpack(*obj);
|
||||
GameObject::AddGameObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
size_t rooms_size;
|
||||
packer.unpack(rooms_size);
|
||||
for (int i = 0; i < rooms_size; i++) {
|
||||
int id;
|
||||
Room *room = new Room();
|
||||
|
||||
packer.unpack(id);
|
||||
packer.unpack(*room);
|
||||
Room::room_map.insert({id, room});
|
||||
}
|
||||
}
|
||||
|
||||
packer.unpack(GameObject::game_object_id_counter)
|
||||
.unpack(Room::room_id_counter)
|
||||
.unpack(State::seed)
|
||||
.unpack(*currentState);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SaveToFile(const std::string& file) {
|
||||
Packer packer;
|
||||
packer.pack(*currentState)
|
||||
.pack(State::seed)
|
||||
.pack(Room::room_id_counter)
|
||||
.pack(GameObject::game_object_id_counter);
|
||||
for (const auto& item : Room::room_map) {
|
||||
packer.pack(item.second);
|
||||
packer.pack(item.first);
|
||||
}
|
||||
packer.pack(Room::room_map.size());
|
||||
|
||||
for (const auto& item : GameObject::game_object_map) {
|
||||
packer.pack(item.second);
|
||||
packer.pack(item.first);
|
||||
packer.pack(item.second->getObjectType());
|
||||
}
|
||||
packer.pack(GameObject::game_object_map.size());
|
||||
|
||||
std::ofstream ofs(file, std::ios::trunc | std::ios::out);
|
||||
if (!ofs.is_open()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ofs << packer.ToHexStr();
|
||||
ofs.flush();
|
||||
ofs.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CreateRandomGameObject(Room *room) {
|
||||
int cnt = (currentState->NextRandom() % 5) + 1;
|
||||
while (cnt--) {
|
||||
GameObject::Type type = static_cast<GameObject::Type>(rand() % GameObject::TYPE_MAX_COUNT);
|
||||
GameObject *obj = GameObject::CreateGameObject(type);
|
||||
GameObject::AddGameObject(obj);
|
||||
|
||||
// use id as keyword
|
||||
obj->setKeyword(std::to_string(obj->getGameObjectId()));
|
||||
|
||||
room->AddGameObject(obj->getGameObjectId());
|
||||
|
||||
switch (type) {
|
||||
case GameObject::Food: {
|
||||
FoodObject *food = dynamic_cast<FoodObject *>(obj);
|
||||
food->setName("food");
|
||||
food->setEnergy((currentState->NextRandom() % 10 + 1));
|
||||
food->setDescription("This is a food. energy: " + std::to_string(food->getEnergy()) + ".");
|
||||
break;
|
||||
}
|
||||
case GameObject::Default: {
|
||||
obj->setName("object");
|
||||
obj->setDescription("This is an object.");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the map.
|
||||
*/
|
||||
void initRooms() {
|
||||
// Create a map that associates rooms with their corresponding exit directions and adjacent rooms
|
||||
std::map<Room *, std::map<std::string, Room *>> roomConnections;
|
||||
|
||||
// Helper function to create a room and add it to the list of rooms in the Room class
|
||||
auto createRoom = [&](Room *room, const string &name, const string &desc) {
|
||||
room = new Room(&name, &desc);
|
||||
Room::addRoom(room);
|
||||
// add random game object
|
||||
CreateRandomGameObject(room);
|
||||
return room;
|
||||
};
|
||||
|
||||
Room *r1, *r2, *r3, *r4;
|
||||
|
||||
// Create and initialize room objects
|
||||
r1 = createRoom(r1, r1name, r1desc);
|
||||
r2 = createRoom(r2, r2name, r2desc);
|
||||
r3 = createRoom(r3, r3name, r3desc);
|
||||
r4 = createRoom(r4, r4name, r4desc);
|
||||
|
||||
// Associate rooms with adjacent rooms and their corresponding directions
|
||||
roomConnections[r1] = {{"north", r2}};
|
||||
roomConnections[r2] = {{"south", r1},
|
||||
{"east", r3}};
|
||||
roomConnections[r3] = {{"west", r2},
|
||||
{"south", r4}};
|
||||
roomConnections[r4] = {{"north", r3}};
|
||||
|
||||
// Iterate through the map to set exits for each room
|
||||
for (const auto &entry: roomConnections) {
|
||||
Room *currentRoom = entry.first;
|
||||
const auto &exits = entry.second;
|
||||
|
||||
for (const auto &exit: exits) {
|
||||
const string &direction = exit.first;
|
||||
Room *connectedRoom = exit.second;
|
||||
|
||||
// Set the respective exit based on the direction
|
||||
if (direction == "north") currentRoom->setNorthId(connectedRoom->getRoomId());
|
||||
else if (direction == "east") currentRoom->setEastId(connectedRoom->getRoomId());
|
||||
else if (direction == "south") currentRoom->setSouthId(connectedRoom->getRoomId());
|
||||
else if (direction == "west") currentRoom->setWestId(connectedRoom->getRoomId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets up the game state.
|
||||
*/
|
||||
void initState() {
|
||||
currentState = new State(Room::room_map.begin()->first);
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &str) {
|
||||
std::vector<std::string> result;
|
||||
std::istringstream iss(str);
|
||||
for (std::string s; iss >> s;)
|
||||
result.push_back(s);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The main game loop.
|
||||
*/
|
||||
void gameLoop() {
|
||||
bool gameOver = false;
|
||||
while (!gameOver) {
|
||||
/* Ask for a command. */
|
||||
bool commandOk = false;
|
||||
inputCommand(&commandBuffer);
|
||||
|
||||
/* The first word of a command would normally be the verb. The first word is the text before the first
|
||||
* space, or if there is no space, the whole string. */
|
||||
auto endOfVerb = static_cast<uint8_t>(commandBuffer.find(' '));
|
||||
|
||||
/* We could copy the verb to another string but there's no reason to, we'll just compare it in place. */
|
||||
/* Command to go north. */
|
||||
if ((commandBuffer.compare(0, endOfVerb, "north") == 0) || (commandBuffer.compare(0, endOfVerb, "n") == 0)) {
|
||||
commandOk = true; /* Confirm command has been handled */
|
||||
/* See if there's a north exit */
|
||||
Room *northRoom = currentState->getCurrentRoom()->getNorth();
|
||||
if (northRoom == nullptr) { /* there isn't */
|
||||
wrapOut(&badExit); /* Output the "can't go there" message */
|
||||
wrapEndPara();
|
||||
} else { /* There is */
|
||||
currentState->goTo(northRoom); /* Update state to that room - this will also describe it */
|
||||
}
|
||||
}
|
||||
/* Command to go east. */
|
||||
if ((commandBuffer.compare(0, endOfVerb, "east") == 0) || (commandBuffer.compare(0, endOfVerb, "e") == 0)) {
|
||||
commandOk = true;
|
||||
Room *eastRoom = currentState->getCurrentRoom()->getEast();
|
||||
if (eastRoom == nullptr) {
|
||||
wrapOut(&badExit);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->goTo(eastRoom);
|
||||
}
|
||||
}
|
||||
|
||||
/* Command to go south. */
|
||||
if ((commandBuffer.compare(0, endOfVerb, "south") == 0) || (commandBuffer.compare(0, endOfVerb, "s") == 0)) {
|
||||
commandOk = true;
|
||||
Room *southRoom = currentState->getCurrentRoom()->getSouth();
|
||||
if (southRoom == nullptr) {
|
||||
wrapOut(&badExit);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->goTo(southRoom);
|
||||
}
|
||||
}
|
||||
|
||||
/* Command to go west. */
|
||||
if ((commandBuffer.compare(0, endOfVerb, "west") == 0) || (commandBuffer.compare(0, endOfVerb, "w") == 0)) {
|
||||
commandOk = true;
|
||||
Room *westRoom = currentState->getCurrentRoom()->getWest();
|
||||
if (westRoom == nullptr) {
|
||||
wrapOut(&badExit);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->goTo(westRoom);
|
||||
}
|
||||
}
|
||||
|
||||
if (commandBuffer.compare(0, endOfVerb, "inventory") == 0) {
|
||||
commandOk = true;
|
||||
for (auto obj_id : currentState->getInventory()) {
|
||||
auto obj = GameObject::getGameObjectById(obj_id);
|
||||
wrapOut(obj->getKeyword() + ": " + obj->getName());
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
|
||||
// check get <keyword>, drop <keyword>, eat <keyword>
|
||||
if (!commandOk) {
|
||||
std::vector<std::string> words = split(commandBuffer);
|
||||
int id = words.size() >= 2 ? std::strtol(words[1].c_str(), nullptr, 10) : 0;
|
||||
|
||||
if (words.size() >= 2 && words[0].compare("load") == 0) {
|
||||
commandOk = true;
|
||||
if (!LoadFromFile(words[1])) {
|
||||
wrapOut(&loadGameFail);
|
||||
} else {
|
||||
wrapOut(&loadGameSuccess);
|
||||
}
|
||||
wrapEndPara();
|
||||
}
|
||||
|
||||
if (words.size() >= 2 && words[0].compare("save") == 0) {
|
||||
commandOk = true;
|
||||
if (!SaveToFile(words[1])) {
|
||||
wrapOut(&saveGameFail);
|
||||
} else {
|
||||
wrapOut(&saveGameSuccess);
|
||||
}
|
||||
wrapEndPara();
|
||||
}
|
||||
|
||||
if (id > 0 && words[0].compare("get") == 0) {
|
||||
commandOk = true;
|
||||
if (currentState->IsInInventory(id)) {
|
||||
wrapOut(&itemInInventory);
|
||||
wrapEndPara();
|
||||
} else if (!currentState->getCurrentRoom()->IsInRoom(id)) {
|
||||
wrapOut(&itemNotFound);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->getCurrentRoom()->RemoveGameObject(id);
|
||||
currentState->addToInventory(id);
|
||||
wrapOut(itemGot + GameObject::getGameObjectById(id)->getName());
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
|
||||
if (id > 0 && words[0].compare("drop") == 0) {
|
||||
commandOk = true;
|
||||
if (currentState->getCurrentRoom()->IsInRoom(id)) {
|
||||
wrapOut(&itemInRoom);
|
||||
wrapEndPara();
|
||||
} else if (!currentState->IsInInventory(id)) {
|
||||
wrapOut(&itemNotFound);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->removeFromInventory(id);
|
||||
currentState->getCurrentRoom()->AddGameObject(id);
|
||||
wrapOut(itemDropped + GameObject::getGameObjectById(id)->getName());
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
|
||||
if (id > 0 && words[0].compare("eat") == 0) {
|
||||
commandOk = true;
|
||||
if (!currentState->IsInInventory(id)) {
|
||||
wrapOut(&itemNotFound);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
GameObject *obj = GameObject::getGameObjectById(id);
|
||||
if (obj->getObjectType() != GameObject::Food) {
|
||||
wrapOut(&itemNotFound);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
FoodObject *food = dynamic_cast<FoodObject *>(obj);
|
||||
currentState->setHP(currentState->getHP() + food->getEnergy());
|
||||
currentState->removeFromInventory(id);
|
||||
wrapOut(itemEaten + GameObject::getGameObjectById(id)->getName());
|
||||
wrapOut("Current HP: " + std::to_string(currentState->getHP()));
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (id > 0 && words[0].compare("examine") == 0) {
|
||||
commandOk = true;
|
||||
if (!currentState->IsInInventory(id) && !currentState->getCurrentRoom()->IsInRoom(id)) {
|
||||
wrapOut(&itemNotFound);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
wrapOut(GameObject::getGameObjectById(id)->getDescription());
|
||||
wrapEndPara();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Quit command */
|
||||
if ((commandBuffer.compare(0, endOfVerb, "quit") == 0)) {
|
||||
commandOk = true;
|
||||
gameOver = true;
|
||||
}
|
||||
|
||||
/* If commandOk hasn't been set, command wasn't understood, display error message */
|
||||
if (!commandOk) {
|
||||
wrapOut(&badCommand);
|
||||
wrapEndPara();
|
||||
} else {
|
||||
currentState->setHP(currentState->getHP() - 1);
|
||||
gameOver = gameOver && (currentState->getHP() <= 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int main() {
|
||||
initWordWrap();
|
||||
initRooms();
|
||||
initState();
|
||||
currentState->announceLoc();
|
||||
gameLoop();
|
||||
return 0;
|
||||
}
|
36
strings.h
Normal file
36
strings.h
Normal file
@ -0,0 +1,36 @@
|
||||
|
||||
|
||||
#ifndef TEXTADV_STRINGS_H
|
||||
#define TEXTADV_STRINGS_H
|
||||
|
||||
|
||||
#include <string>
|
||||
|
||||
const std::string r1name = "Room 1";
|
||||
const std::string r1desc = "You are in room 1. It's really quite boring, but then, it's just for testing really. There's a passage to the north.";
|
||||
const std::string r2name = "Blue Room";
|
||||
const std::string r2desc = "You are the blue room. You know because it's blue. That's about all though. There's a passage to the south.";
|
||||
const std::string r3name = "Secret Chamber";
|
||||
const std::string r3desc = "You've discovered a secret chamber. It's filled with ancient artifacts and mysterious symbols.";
|
||||
|
||||
const std::string r4name = "Library";
|
||||
const std::string r4desc = "You find yourself in a vast library, with shelves full of dusty old books.";
|
||||
|
||||
const std::string badExit = "You can't go that way.";
|
||||
const std::string badCommand = "I don't understand that.";
|
||||
|
||||
const std::string itemInInventory = "You already have that.";
|
||||
const std::string itemInRoom = "You can't drop that.";
|
||||
const std::string itemNotFound = "You don't see that here.";
|
||||
const std::string itemGot = "You pick up the ";
|
||||
const std::string itemDropped = "You drop the ";
|
||||
const std::string itemEaten = "You eat the ";
|
||||
|
||||
const std::string loadGameSuccess = "Game loaded successfully.";
|
||||
const std::string loadGameFail = "Failed to load game.";
|
||||
const std::string saveGameSuccess = "Game saved successfully.";
|
||||
const std::string saveGameFail = "Failed to save game.";
|
||||
|
||||
|
||||
|
||||
#endif //TEXTADV_STRINGS_H
|
81
wordwrap.cpp
Normal file
81
wordwrap.cpp
Normal file
@ -0,0 +1,81 @@
|
||||
|
||||
|
||||
|
||||
#include <cstdint>
|
||||
// #include <afxres.h>
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
|
||||
|
||||
/* Library routines to output text and "word wrap" so that words are not broken across lines. */
|
||||
|
||||
uint16_t consoleWidth = 80;
|
||||
uint16_t currentConsoleOffset = 5;
|
||||
|
||||
/**
|
||||
* Set up the word wrap library.
|
||||
*/
|
||||
void initWordWrap() {
|
||||
/* Find the width of the console window. */
|
||||
// CONSOLE_SCREEN_BUFFER_INFO csbi{};
|
||||
// GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE),&csbi);
|
||||
/* We subtract 2 because Windows doesn't allow for the right hand scroll bar when reporting
|
||||
* width of command prompt windows. */
|
||||
// consoleWidth = static_cast<uint16_t>(csbi.srWindow.Right - csbi.srWindow.Left - 2);
|
||||
currentConsoleOffset = 0;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Output a string with word wrap.
|
||||
* @param text Pointer to the string to output.
|
||||
*/
|
||||
void wrapOut(const std::string *text) {
|
||||
size_t len = text->length(); // Length of the string
|
||||
size_t position = 0; // How much of the string we've printed
|
||||
std::ostream_iterator<char> iout(std::cout); // Iterators for the string and display
|
||||
std::string::const_iterator strInt = text->begin();
|
||||
const char *rawString = text->data();
|
||||
while (position < len) {
|
||||
// How much space do we have left on the current console line?
|
||||
size_t left = (consoleWidth - currentConsoleOffset);
|
||||
if ((len - position) < left) { // Can we fit the whole string on this line?
|
||||
std::cout << &rawString[position] << " "; // Yes, just do it.
|
||||
currentConsoleOffset += len + 1; // Update our position on this line.
|
||||
break;
|
||||
} else { // We can't fit the whole string on this line.
|
||||
size_t lastSpace = left; // This would be the last character we did fit on this line
|
||||
// Move this pointer back until we find a space
|
||||
while ((rawString[position + lastSpace] != ' ') && (lastSpace > 0)) {
|
||||
lastSpace--;
|
||||
}
|
||||
// Print up to that space using the output iterator
|
||||
std::copy(strInt + position, strInt + position + lastSpace, iout);
|
||||
// Print an end-of-line, unless the string ended exactly at the end of the line in which case the
|
||||
// cursor has automatically moved to the next line
|
||||
if (lastSpace < left) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
position += lastSpace + 1; // Mark how much of the string we've printed
|
||||
currentConsoleOffset = 0; // Since we just created a new line, we're now at the start of it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wrapOut(const std::string& text) {
|
||||
wrapOut(&text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends a paragraph of word wrapped output.
|
||||
*/
|
||||
void wrapEndPara() {
|
||||
// If we aren't at the end of a line already, end the line
|
||||
if (currentConsoleOffset != 0) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
// And print a blank line
|
||||
std::cout << std::endl;
|
||||
// Since we just started a new line, we're now at the start of it
|
||||
currentConsoleOffset = 0;
|
||||
}
|
14
wordwrap.h
Normal file
14
wordwrap.h
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
#ifndef TEXTADV_WORDWRAP_H
|
||||
#define TEXTADV_WORDWRAP_H
|
||||
|
||||
#include <string>
|
||||
|
||||
void initWordWrap();
|
||||
void wrapOut(const std::string *text);
|
||||
void wrapOut(const std::string& text);
|
||||
void wrapEndPara();
|
||||
|
||||
|
||||
#endif //TEXTADV_WORDWRAP_H
|
Loading…
x
Reference in New Issue
Block a user