From cc2230e0039f06a7478878adcbc9ef028a223243 Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Sat, 23 Apr 2016 22:31:27 -0400 Subject: more rewrites --- include/common.hpp | 5 + include/entities.hpp | 348 +++++++++++++++++++++++++++++++++++++-------------- include/ui.hpp | 2 +- include/world.hpp | 30 +++-- main.cpp | 97 ++++++-------- src/entities.cpp | 164 ++++++++++++++---------- src/gameplay.cpp | 292 ++++++++++++++++++------------------------ src/inventory.cpp | 11 +- src/ui.cpp | 50 ++++---- src/ui_action.cpp | 6 +- src/world.cpp | 182 ++++++++++++--------------- xcf/cat.xcf | Bin 0 -> 1900 bytes 12 files changed, 654 insertions(+), 533 deletions(-) create mode 100644 xcf/cat.xcf diff --git a/include/common.hpp b/include/common.hpp index 310bf7f..9211d56 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -98,6 +98,11 @@ struct _vec2 { bool operator==(const _vec2 &v) { return (x == v.x) && (y == v.y); } + template + const _vec2 operator=(const T &n) { + x = y = n; + return *this; + } }; typedef struct _vec2 vec2; diff --git a/include/entities.hpp b/include/entities.hpp index 4a2420e..460dc93 100644 --- a/include/entities.hpp +++ b/include/entities.hpp @@ -1,197 +1,339 @@ +/* ---------------------------------------------------------------------------- +** The entity stuffs. +** +** Entities. +** --------------------------------------------------------------------------*/ #ifndef ENTITIES_H #define ENTITIES_H +#define DEBUG + +/* ---------------------------------------------------------------------------- +** Includes section +** --------------------------------------------------------------------------*/ +// local game includes #include #include #include #include -#include - -#define DEBUG - -#define NPCp(n) ((NPC *)n) -#define Structurep(n) ((Structures *)n) -#define Mobp(n) ((Mob *)n) - -#define PLAYER_INV_SIZE 43 // The size of the player's inventory -#define NPC_INV_SIZE 3 // Size of an NPC's inventory +/* ---------------------------------------------------------------------------- +** Structures section +** --------------------------------------------------------------------------*/ +/** + * An entity type enumerator for identifying entities. + */ enum _TYPE { - OBJECTT = -2, - STRUCTURET, - PLAYERT, - NPCT, - MERCHT, - MOBT + OBJECTT = -2, /**< an object (Object) */ + STRUCTURET, /**< a structure (Structures *) */ + PLAYERT, /**< the player (Player *) */ + NPCT, /**< an NPC (NPC *) */ + MERCHT, /**< a merchant (Merchant *) */ + MOBT /**< A mob (Mob *) */ }; +/** + * An enumerator for entity gender. + */ enum GENDER{ - MALE, - FEMALE + MALE, /**< male */ + FEMALE /**< female */ }; +/** + * An enumerator for mob types.. 'species'. + * The subtype of a Mob will affect what texture is used to draw it as well as + * how the Mob will behave. + */ enum MOB_SUB { - MS_RABBIT = 1, - MS_BIRD, - MS_TRIGGER, - MS_DOOR, - MS_PAGE + MS_RABBIT = 1, /**< rabbits */ + MS_BIRD, /**< birds */ + MS_TRIGGER, /**< triggers, used to cue cutscenes */ + MS_DOOR, /**< doors, for exiting arenas */ + MS_PAGE /**< pages, cues page overlay */ }; +/** + * An enumerator for strcture types. + * The subtype of a structure will affect how it is drawn and how it functions. + */ enum BUILD_SUB{ - TOWN_HALL = 0, - HOUSE = 1, - HOUSE2 = 2, - HOUSE3 = 3, - HOUSE4 = 4, - FOUNTAIN = 5, - LAMP_POST = 6, - FIRE_PIT = 7, - STALL_MARKET = 70, - STALL_TRADER = 71 + TOWN_HALL = 0, /**< a town hall */ + HOUSE, /**< a generic house */ + HOUSE2, /**< a generic house of a different style */ + HOUSE3, /**< a generic house of a different style */ + HOUSE4, /**< a generic house of a different style */ + FOUNTAIN, /**< a fountain, creates water particles */ + LAMP_POST, /**< a lamppost, creates light */ + FIRE_PIT, /**< a firepit, creates fire particles / light */ + STALL_MARKET = 70, /**< a stall for a merchant */ + STALL_TRADER /**< TODO */ }; -class Trade{ -public: +/** + * A structure for tracking potential trades between the player and a merchant. + */ +struct Trade { + // the names of the items up for trade std::string item[2]; + // how much of each item to trade int quantity[2]; - Trade(int qo, std::string o, int qt, std::string t); - Trade() {} + + // constructs a trade with the given values + Trade(int qo, std::string o, int qt, std::string t) { + item[0] = o; + item[1] = t; + quantity[0] = qo; + quantity[1] = qt; + } + + // creates an empty trade item + Trade(void) { + item[0] = ""; + item[1] = ""; + quantity[0] = 0; + quantity[1] = 0; + } }; +typedef struct Trade Trade; + +/* ---------------------------------------------------------------------------- +** Variables section +** --------------------------------------------------------------------------*/ +// the size of the player's inventory +extern const unsigned int PLAYER_INV_SIZE; +// the size of an NPC's inventory +extern const unsigned int NPC_INV_SIZE; + +/* ---------------------------------------------------------------------------- +** Classes / function prototypes section +** --------------------------------------------------------------------------*/ + +// a prototype of the world class, necessary for some function prototypes class World; +/** + * The particle class, handles a single particle. + */ class Particles{ public: + // the location of the particle vec2 loc; + + // the width of the particle, in pixels float width; + + // the height of the particle, in pixels float height; + + // the velocity of the particle, in pixels vec2 vel; + + // the color of the particle Color color; + + // TODO vec2 index; + + // the amount of milliseconds left for the particle to live float duration; + + // when true, the particle will move bool canMove; + + // TODO bool fountain; + + // when true, the particle will be affected by gravity bool gravity; + + // when true, draws the particle behind structures bool behind; + + // when true, the particle will bounce on impact with ground bool bounce; - Particles(float x, float y, float w, float h, float vx, float vy, Color c, float d) { - loc.x = x; - loc.y = y; - vel.x = vx; - vel.y = vy; + + // creates a particle with the desired characteristics + Particles(float x, float y, float w, float h, float vx, float vy, Color c, float d){ + loc = vec2 {x, y}; + vel = vec2 {vx, vy}; width = w; height = h; color = c; duration = d; - fountain = false; gravity = true; + fountain = false; behind = false; bounce = false; index = Texture::getIndex(c); } - ~Particles() { - } - void draw() { - glColor3ub(255,255,255); + + // allows the particle to be destroyed + ~Particles(void){} + + // draws the particle + void draw(void) const { + glColor3ub(255, 255, 255); glBegin(GL_QUADS); - glTexCoord2f(.25*index.x, .125*index.y); glVertex2i(loc.x, loc.y); - glTexCoord2f(.25*index.x, .125*index.y); glVertex2i(loc.x + width, loc.y); - glTexCoord2f(.25*index.x, .125*index.y); glVertex2i(loc.x + width, loc.y + height); - glTexCoord2f(.25*index.x, .125*index.y); glVertex2i(loc.x, loc.y + width); + vec2 tc = vec2 {0.25f * index.x, 0.125f * index.y}; + glTexCoord2f(tc.x, tc.y); glVertex2i(loc.x , loc.y); + glTexCoord2f(tc.x, tc.y); glVertex2i(loc.x + width, loc.y); + glTexCoord2f(tc.x, tc.y); glVertex2i(loc.x + width, loc.y + height); + glTexCoord2f(tc.x, tc.y); glVertex2i(loc.x , loc.y + height); glEnd(); } + + // updates a particle void update(float _gravity, float ground_y) { // handle ground collision if (loc.y < ground_y) { loc.y = ground_y; + + // handle bounce if (bounce) { vel.y *= -0.2f; - vel.x /= 4; + vel.x /= 4.0f; } else { - vel.x = vel.y = 0; + vel = 0.0f; canMove = false; } - } else if (gravity && vel.y > -1) + } + + // handle gravity + else if (gravity && vel.y > -1.0f) { vel.y -= _gravity * deltaTime; + } } + + // returns true if the particle should be killed bool kill(float delta) { return (duration -= delta) <= 0; } }; -void initEntity(); - +/** + * The entity class. + * This class contains common functions and variables for all types of + * entities, i.e. a common structure. + */ class Entity{ -public: +protected: + // an incrementer for invincibility after a hit + unsigned int hitCooldown; + + // an incrementer for triggering change of movement with wander() + int ticksToUse; + + // entity handles an applied hit (sword) if set true + bool forcedMove; + + // if set false, entity will be destroyed + bool alive; + + // if not null, the entity will move towards this one Entity *followee; - Inventory *inv; - /* - * Movement variables - */ + // TODO + float targetx; +public: + // contains the entity's coordinates, in pixels vec2 loc; + + // contains the entity's velocity, in pixels vec2 vel; + // the entity's width, in pixels float width; + + // the entity's height, in pixels float height; - float speed; // A speed factor for X movement + // a speed multiplier, applied to velocity + float speed; - unsigned int hitCooldown; + // when true player may interact, and the entity's name will be drawn + bool near; + + // when true, the entity can move + bool canMove; - /* - * Movement flags - */ + // tells direction entity is facing + bool right, left; - bool near; // Causes name to display - bool canMove; // Enables movement - bool right,left; // Direction faced by Entity - bool alive; - bool hit; - bool forcedMove; - unsigned char ground; // Shows how the Entity is grounded (if it is) + // set to 1 if entity is on the ground, 0 if in the air + unsigned char ground; - /* - * Health variables - */ + // the entity's inventory + Inventory *inv; + // the entity's health float health; - float maxHealth; - /* - * Identification variables - */ + // the most health the entity can have + float maxHealth; + // the type of the entity _TYPE type; - int subtype; - char *name; + // the entity's subtype, if applicable + int subtype; + + // the entity's name, randomly generated on spawn + char *name; + + // the entity's gender GENDER gender; + // a texture handler for the entity Texturec *tex; - Texturec *ntex; - float targetx; - - unsigned int randDialog; + // TODO + Texturec *ntex; + // draws the entity to the screen void draw(void); + + // spawns the entity at the given coordinates void spawn(float, float); - int ticksToUse; // Used by wander() + // allows the entity to wander, according to what class is deriving this. + virtual void wander(int){} + + // allows the entity to interact with the player + virtual void interact(void){} - virtual void wander(int) {} - virtual void interact() {} + // causes the entity to move to the given x coordinate + void moveTo(float dest_x); + // causes the entity to follow the one provided void follow(Entity *e); + // causes the entity to take a player-inflicted hit + void takeHit(unsigned int _health, unsigned int cooldown); + + // handles hits if they've been taken + void handleHits(void); + + // insures that the entity is dead + void die(void); + + // checks if the entity is alive + bool isAlive(void) const; + + // checks if the entity is hit in some way + bool isHit(void) const; + + // returns true if this entity is near the one provided bool isNear(Entity e); + + // returns true if the coordinate is within the entity bool isInside(vec2 coord) const; - virtual ~Entity() {} + // frees memory taken by the entity + virtual ~Entity(){} }; class Player : public Entity{ @@ -219,16 +361,22 @@ public: class NPC : public Entity { +private: + // the number of the random dialog to use + unsigned int randDialog; + + unsigned int dialogCount; + public: - std::vectoraiFunc; int dialogIndex; NPC(); - NPC(NPC *n); ~NPC(); - void addAIFunc(int (*func)(NPC *),bool preload); - void clearAIFunc(void); + void drawThingy(void) const; + + void addAIFunc(bool preload); + virtual void interact(); virtual void wander(int); }; @@ -329,6 +477,18 @@ constexpr Object *Objectp(Entity *e) { return (Object *)e; } +constexpr NPC *NPCp(Entity *e) { + return (NPC *)e; +} + +constexpr Structures *Structurep(Entity *e) { + return (Structures *)e; +} + +constexpr Mob *Mobp(Entity *e) { + return (Mob *)e; +} + #endif // ENTITIES_H /** diff --git a/include/ui.hpp b/include/ui.hpp index 144a87e..477c9c3 100644 --- a/include/ui.hpp +++ b/include/ui.hpp @@ -108,7 +108,7 @@ namespace ui { */ void drawBox(vec2 c1, vec2 c2); - void dialogBox(const char *name,const char *opt,bool passive,const char *text,...); + void dialogBox(std::string name, std::string opt, bool passive, std::string text, ...); void merchantBox(const char *name,Trade trade,const char *opt,bool passive,const char *text,...); void merchantBox(); void closeBox(); diff --git a/include/world.hpp b/include/world.hpp index 9a1d701..c6eaf06 100644 --- a/include/world.hpp +++ b/include/world.hpp @@ -11,7 +11,7 @@ ** Includes section ** --------------------------------------------------------------------------*/ -// 'local' game includes +// local game includes #include #include @@ -85,8 +85,8 @@ public: vec2 start, end; bool in; - Village(const char *meme, World *w); - ~Village(void) {} + Village(std::string meme, World *w); + ~Village(void){} }; /** @@ -248,30 +248,46 @@ public: Village *addVillage(std::string name, World *world); }; -/* - * IndoorWorld - Indoor settings stored in a World class +/** + * IndoorWorld - Indoor settings stored in a World class */ - class IndoorWorld : public World { private: + // like lines, but split into floors std::vector> floor; + + // the x coordinate to start each floor at std::vector fstart; + // handles physics for a single entity void singleDetect(Entity *e); public: + + // creates an IndoorWorld object IndoorWorld(void); + + // frees memory used by this object ~IndoorWorld(void); + // adds a floor of the desired width void addFloor(unsigned int width); + + // adds a floor at the desired x coordinate with the given width void addFloor(unsigned int width, unsigned int start); + + // attempts to move the entity provided to the given floor bool moveToFloor(Entity *e, unsigned int _floor); + // checks for a floor above the given entity bool isFloorAbove(Entity *e); + + // checks for a floor below the given entity bool isFloorBelow(Entity *e); - void draw(Player *p); // Draws the world (ignores layers) + // draws the world about the player + void draw(Player *p); }; /** diff --git a/main.cpp b/main.cpp index 8e07818..fa72494 100644 --- a/main.cpp +++ b/main.cpp @@ -287,10 +287,7 @@ int main(int argc, char *argv[]){ SDL_ShowCursor(SDL_DISABLE); - //glEnable(GL_CULL_FACE); - Texture::initColorIndex(); - initEntity(); /* * Initializes our shaders so that the game has shadows. @@ -682,73 +679,57 @@ void logic(){ if (player->inv->usingi) { for (auto &e : currentWorld->entity) { - e->hit = false; - - if (player->inv->usingi && !e->hit && - player->inv->detectCollision({ e->loc.x, e->loc.y }, { e->loc.x + e->width, e->loc.y + e->height})) { - e->health -= 25; - e->hit = true; - e->forcedMove = true; - e->hitCooldown = 10; - e->vel.x = 0.5f * (player->left ? -1 : 1); - e->vel.y = 0.2f; + if (player->inv->usingi && !e->isHit() && + player->inv->detectCollision(vec2 { e->loc.x, e->loc.y }, vec2 { e->loc.x + e->width, e->loc.y + e->height})) { + e->takeHit(25, 10); break; - //for(int r = 0; r < (rand()%5);r++) - // currentWorld->addParticle(rand()%HLINE*3 + n->loc.x - .05f,n->loc.y + n->height*.5, HLINE,HLINE, -(rand()%10)*.01,((rand()%4)*.001-.002), {(rand()%75+10)/100.0f,0,0}, 10000); - //if (e->health <= 0) { - //for(int r = 0; r < (rand()%30)+15;r++) - // currentWorld->addParticle(rand()%HLINE*3 + n->loc.x - .05f,n->loc.y + n->height*.5, HLINE,HLINE, -(rand()%10)*.01,((rand()%10)*.01-.05), {(rand()%75)+10/100.0f,0,0}, 10000); } } player->inv->usingi = false; } for (auto &e : currentWorld->entity) { - if (e->alive) { - if (e->type == NPCT || e->type == MERCHT || e->type == OBJECTT) { - - if (e->type == OBJECTT && ObjectSelected) { + if (e->isAlive() && ((e->type == NPCT) || (e->type == MERCHT) || (e->type == OBJECTT))) { + if (e->type == OBJECTT && ObjectSelected) { + e->near = false; + continue; + } else if (e->canMove) { + e->wander((rand() % 120 + 30)); + if (NPCSelected) { e->near = false; continue; - } else { // has to be NPC - if (e->canMove) { - e->wander((rand() % 120 + 30)); - if (NPCSelected) { - e->near = false; - continue; - } - } } + } - if(e->isInside(ui::mouse) && player->isNear(*e)) { - if (e->type == OBJECTT) - ObjectSelected = true; - else - NPCSelected = true; - e->near = true; + if(e->isInside(ui::mouse) && player->isNear(*e)) { + e->near = true; + if (e->type == OBJECTT) + ObjectSelected = true; + else + NPCSelected = true; + + if ((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_RIGHT)) && !ui::dialogBoxExists) + e->interact(); + } else { + e->near = false; + } + } else if (e->type == MOBT) { + e->near = player->isNear(*e); - if ((SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_RIGHT)) && !ui::dialogBoxExists) - e->interact(); - } else - e->near = false; - } else if (e->type == MOBT) { - e->near = player->isNear(*e); - - switch (e->subtype) { - case MS_RABBIT: - case MS_BIRD: - e->wander((rand()%240 + 15)); - break; - case MS_TRIGGER: - case MS_PAGE: - e->wander(0); - break; - case MS_DOOR: - break; - default: - std::cout<<"Unhandled mob of subtype "<subtype<<"."<subtype) { + case MS_RABBIT: + case MS_BIRD: + e->wander((rand()%240 + 15)); + break; + case MS_TRIGGER: + case MS_PAGE: + e->wander(0); + break; + case MS_DOOR: + break; + default: + std::cout<<"Unhandled mob of subtype "<subtype<<"."< + #include +#include -#include #include extern std::istream *names; @@ -12,13 +14,15 @@ extern unsigned int tickCount; // main.cpp extern std::string xmlFolder; -GLuint waterTex; - -std::vector AIpreload; // A dynamic array of AI functions that are being preloaded -std::vector AIpreaddr; // A dynamic array of pointers to the NPC's that are being assigned the preloads +// a dynamic array of pointers to the NPC's that are being assigned the preloads +std::vector aipreload; -#define RAND_DIALOG_COUNT 14 +// the size of the player's inventory +const unsigned int PLAYER_INV_SIZE = 43; +// the size of an NPC's inventory +const unsigned int NPC_INV_SIZE = 3; +static const unsigned int RAND_DIALOG_COUNT = 14; const char *randomDialog[RAND_DIALOG_COUNT] = { "What a beautiful day it is.", "Have you ever went fast? I have.", @@ -36,11 +40,8 @@ const char *randomDialog[RAND_DIALOG_COUNT] = { "What's a bagel? I don't know because I'm mormon" }; -void initEntity() { - waterTex = Texture::loadTexture("assets/waterTex.png"); -} - -void getRandomName(Entity *e) { +void getRandomName(Entity *e) +{ unsigned int tempNum,max=0; char *bufs; @@ -76,17 +77,9 @@ void getRandomName(Entity *e) { delete[] bufs; } -Trade::Trade(int qo, std::string o, int qt, std::string t) { - item[0] = o; - item[1] = t; - - quantity[0] = qo; - quantity[1] = qt; - - std::cout << "Trading: " << quantity[0] << " " << item[0] << " for " << quantity[1] << " " << item[1] << std::endl; -} - -void Entity::spawn(float x, float y) { //spawns the entity you pass to it based off of coords and global entity settings +// spawns the entity you pass to it based off of coords and global entity settings +void Entity::spawn(float x, float y) +{ loc.x = x; loc.y = y; vel.x = 0; @@ -99,7 +92,6 @@ void Entity::spawn(float x, float y) { //spawns the entity you pass to it based near = false; //canMove = true; ground = false; - hit = false; forcedMove = false; ticksToUse = 0; @@ -122,7 +114,52 @@ void Entity::spawn(float x, float y) { //spawns the entity you pass to it based followee = NULL; } -Player::Player() { //sets all of the player specific traits on object creation +void Entity::takeHit(unsigned int _health, unsigned int cooldown) +{ + // modify variables + health = fmax(health - _health, 0); + forcedMove = true; + hitCooldown = cooldown; + + // pushback + vel.x = player->left ? -0.5f : 0.5f; + vel.y = 0.2f; +} + +void Entity::handleHits(void) +{ + if (!forcedMove) + return; + + // reduce knockback + if ((vel.x > 0.0005f) || (vel.x < -0.0005f)) + vel.x *= 0.6f; + else + forcedMove = false; +} + +void Entity::die(void) +{ + alive = false; + health = 0; +} + +bool Entity::isAlive(void) const +{ + return alive; +} + +bool Entity::isHit(void) const +{ + return forcedMove; +} + +void Entity::moveTo(float dest_x) +{ + targetx = dest_x; +} + +Player::Player(){ //sets all of the player specific traits on object creation width = HLINE * 10; height = HLINE * 16; @@ -168,11 +205,8 @@ NPC::NPC() { //sets all of the NPC specific traits on object creation dialogIndex = 0; } -NPC::~NPC() { - while(!aiFunc.empty()) { - aiFunc.pop_back(); - } - +NPC::~NPC() +{ delete inv; delete tex; delete[] name; @@ -268,6 +302,7 @@ Mob::Mob(int sub) { inv = new Inventory(NPC_INV_SIZE); } + Mob::~Mob() { delete inv; delete tex; @@ -322,23 +357,30 @@ bool Entity::isNear(Entity e) { return pow(e.loc.x - loc.x, 2) + pow(e.loc.y - loc.y, 2) <= pow(40 * HLINE, 2); } -void Entity::draw(void) { //draws the entities +void NPC::drawThingy(void) const +{ + if (dialogCount) { + auto w = width / 3; + glColor3ub(255, 255, 0); + glRectf(loc.x + w, loc.y + height, loc.x + w * 2, loc.y + height + w); + } +} + +void Entity::draw(void) +{ glPushMatrix(); glColor3ub(255,255,255); if (!alive) return; - if (type==NPCT) { - if (NPCp(this)->aiFunc.size()) { - glColor3ub(255,255,0); - glRectf(loc.x+width/3,loc.y+height,loc.x+width*2/3,loc.y+height+width/3); - } - if (gender == MALE) { - glColor3ub(255,255,255); - }else if (gender == FEMALE) { - glColor3ub(255,105,180); - } + if (type == NPCT) { + NPCp(this)->drawThingy(); + + if (gender == MALE) + glColor3ub(255, 255, 255); + else if (gender == FEMALE) + glColor3ub(255, 105, 180); } if (left) { glScalef(-1.0f,1.0f,1.0f); @@ -467,37 +509,31 @@ wander(int timeRun) ticksToUse--; } -void NPC::addAIFunc(int (*func)(NPC *),bool preload) { - if (preload) { // Preload AI functions so that they're given after - // the current dialog box is closed - AIpreload.push_back(func); - AIpreaddr.push_back(this); - } - else aiFunc.push_back(func); +void NPC::addAIFunc(bool preload) +{ + if (preload) + aipreload.push_back(this); + else + ++dialogCount; } -void NPC::clearAIFunc(void) { - aiFunc.clear(); -} +extern int commonAIFunc(NPC *speaker); void NPC::interact() { //have the npc's interact back to the player std::thread([this]{ - int (*func)(NPC *); loc.y += 5; canMove=false; left = (player->loc.x < loc.x); right = !left; - if (aiFunc.size()) { - func=aiFunc.front(); - - if (!func(this)) { - if (aiFunc.size())aiFunc.erase(aiFunc.begin()); - } - }else{ - ui::dialogBox(name,NULL,false,randomDialog[randDialog]); + if (dialogCount && dialogIndex != 9999) { + if (!commonAIFunc(this)) + dialogCount--; + } else { + ui::dialogBox(name, "", false, randomDialog[randDialog]); } + ui::waitForDialog(); canMove=true; }).detach(); @@ -588,8 +624,8 @@ void Merchant::interact() { void Object::interact(void) { std::thread([this]{ - if (questObject && alive) { - ui::dialogBox(player->name, ":Yes:No", false, pickupDialog.c_str()); + if(questObject && alive){ + ui::dialogBox(player->name, ":Yes:No", false, pickupDialog); ui::waitForDialog(); if (ui::dialogOptChosen == 1) { player->inv->addItem(iname, 1); @@ -763,10 +799,10 @@ void Player::save(void) { for(auto &i : inv->items) data.append(std::to_string((int)i.count) + "\n" + std::to_string((int)i.id) + "\n"); - data.append((std::string)(currentXML.c_str() + 4) + "\n"); + data.append(std::string(currentXML.data() + 4) + "\n"); data.append("dOnE\0"); - out.write(data.c_str(),data.size()); + out.write(data.data(),data.size()); out.close(); } @@ -810,7 +846,7 @@ void Player::sspawn(float x,float y) { } std::getline(data,ddata); - currentWorld = loadWorldFromXMLNoSave(ddata.c_str()); + currentWorld = loadWorldFromXMLNoSave(ddata); in.close(); } diff --git a/src/gameplay.cpp b/src/gameplay.cpp index e314be4..603ede7 100644 --- a/src/gameplay.cpp +++ b/src/gameplay.cpp @@ -14,10 +14,9 @@ extern Menu optionsMenu; extern std::string xmlFolder; -extern void mainLoop(void); // main.cpp +extern std::vector aipreload; -extern std::vector AIpreaddr; // entities.cpp -extern std::vector AIpreload; // entities.cpp +extern void mainLoop(void); // main.cpp std::vector dopt; @@ -27,197 +26,154 @@ inline void segFault() { (*((int *)NULL))++; } -int commonAIFunc(NPC *speaker) { +int commonAIFunc(NPC *speaker) +{ XMLDocument xml; XMLElement *exml,*oxml; static unsigned int oldidx = 9999; - const char *name; + std::string name; unsigned int idx = 0; bool stop = false; - /* - * Load the current world's XML file into memory for reading. - */ - + // load the XML file and find the dialog tags xml.LoadFile(currentXML.c_str()); exml = xml.FirstChildElement("Dialog"); - /* - * Search for the correct dialog block. - */ - - while(strcmp(exml->Attribute("name"),speaker->name)) + // search for the dialog block associated with this npc + while (exml->StrAttribute("name") != speaker->name) exml = exml->NextSiblingElement(); + // search for the desired text block exml = exml->FirstChildElement(); - - /* - * Search for which text block should be used. - */ - - do{ - if (!strcmp(exml->Name(),"text")) { - if (exml->UnsignedAttribute("id") == (unsigned)speaker->dialogIndex) { - - /* - * Handle any quest tags - */ - - if ((oxml = exml->FirstChildElement("quest"))) { - std::string qname; - - while (oxml) { - if (!(qname = oxml->StrAttribute("assign")).empty()) - player->qh.assign(qname,"None",(std::string)oxml->GetText()); - else if (!(qname = oxml->StrAttribute("check")).empty()) { - if (player->qh.hasQuest(qname) && player->qh.finish(qname)) { - goto CONT; - }else{ - oldidx = speaker->dialogIndex; - speaker->dialogIndex = oxml->UnsignedAttribute("fail"); - return commonAIFunc(speaker); - } - } - - oxml = oxml->NextSiblingElement(); - } - } - -CONT: - - /* - * Handle any 'give' requests. - */ - - if ((oxml = exml->FirstChildElement("give"))) { - while(oxml) { - player->inv->addItem(oxml->Attribute("id"),oxml->UnsignedAttribute("count")); - oxml = oxml->NextSiblingElement(); - } - } - - /* - * Handle any 'take' requests. - */ - - if ((oxml = exml->FirstChildElement("take"))) { - while(oxml) { - player->inv->takeItem(oxml->Attribute("id"),oxml->UnsignedAttribute("count")); - oxml = oxml->NextSiblingElement(); - } + std::cout << speaker->dialogIndex << '\n'; + do { + if (std::string("text") == exml->Name() && exml->UnsignedAttribute("id") == (unsigned)speaker->dialogIndex) + break; + } while ((exml = exml->NextSiblingElement())); + + // handle quest tags + if ((oxml = exml->FirstChildElement("quest"))) { + std::string qname; + + // iterate through all quest tags + do { + // assign quest + if (!(qname = oxml->StrAttribute("assign")).empty()) + player->qh.assign(qname, "None", std::string(oxml->GetText())); // TODO add descriptions + + // check / finish quest + else if (!(qname = oxml->StrAttribute("check")).empty()) { + if (player->qh.hasQuest(qname) && player->qh.finish(qname)) { + // QuestHandler::finish() did all the work.. + break; + } else { + // run error dialog + oldidx = speaker->dialogIndex; + speaker->dialogIndex = oxml->UnsignedAttribute("fail"); + return commonAIFunc(speaker); } + } + } while((oxml = oxml->NextSiblingElement())); + } - /* - * Handle 'go to' thingy - */ - - if ((oxml = exml->FirstChildElement("gotox"))) - speaker->targetx = atoi(oxml->GetText()); - - /* - * Handle dialog options. - */ - - if ((oxml = exml->FirstChildElement("option"))) { - - /* - * Convert the list of options into a single colon-separated string. - */ - - std::string optstr; + // handle give tags + if ((oxml = exml->FirstChildElement("give"))) { + do player->inv->addItem(oxml->Attribute("id"), oxml->UnsignedAttribute("count")); + while ((oxml = oxml->NextSiblingElement())); + } - while(oxml) { + // handle take tags + if ((oxml = exml->FirstChildElement("take"))) { + do player->inv->takeItem(oxml->Attribute("id"), oxml->UnsignedAttribute("count")); + while ((oxml = oxml->NextSiblingElement())); + } - /* - * Create a buffer big enough for the next option. - */ + // handle movement directs + if ((oxml = exml->FirstChildElement("gotox"))) + speaker->moveTo(std::stoi(oxml->GetText())); - optstr.append((std::string)":" + oxml->Attribute("text")); + // handle dialog options + if ((oxml = exml->FirstChildElement("option"))) { + std::string optstr; - /* - * Append the next option. - */ + // convert option strings to a colon-separated format + do { + // append the next option + optstr.append(std::string(":") + oxml->Attribute("text")); - dopt.push_back(oxml); + // save the associated XMLElement + dopt.push_back(oxml); + } while ((oxml = oxml->NextSiblingElement())); - oxml = oxml->NextSiblingElement(); - } + // run the dialog stuff + ui::dialogBox(speaker->name, optstr, false, exml->GetText() + 1); + ui::waitForDialog(); - /* - * Get the player's choice, then set the XMLElement to the option's block. - */ + if (ui::dialogOptChosen) + exml = dopt[ui::dialogOptChosen - 1]; - ui::dialogBox(speaker->name,optstr.c_str(),false,exml->GetText()+1); - ui::waitForDialog(); + dopt.clear(); + } - if (ui::dialogOptChosen) - exml = dopt[ui::dialogOptChosen-1]; + // optionless dialog + else { + ui::dialogBox(speaker->name, "", false, exml->GetText()); + ui::waitForDialog(); + } - while(!dopt.empty()) - dopt.pop_back(); - }else{ + // trigger other npcs if desired + if (!(name = exml->StrAttribute("call")).empty()) { + NPC *n = *std::find_if(std::begin(currentWorld->npc), std::end(currentWorld->npc), [name](NPC *npc) { + return (npc->name == name); + }); - /* - * No options - simply print the text. - */ + if (exml->QueryUnsignedAttribute("callid", &idx) == XML_NO_ERROR) { + n->dialogIndex = idx; + n->addAIFunc(false); + } + } - ui::dialogBox(speaker->name,NULL,false,exml->GetText()); - ui::waitForDialog(); - } + // handle potential following dialogs + if ((idx = exml->UnsignedAttribute("nextid"))) { + speaker->dialogIndex = idx; - /* - * Give another NPC dialog if requested. - */ - - if ((name = exml->Attribute("call"))) { - for(auto &n : currentWorld->npc) { - if (!strcmp(n->name,name)) { - if (exml->QueryUnsignedAttribute("callid",&idx) == XML_NO_ERROR) - n->dialogIndex = idx; - n->addAIFunc(commonAIFunc,false); - break; - } - } - } + // stop talking + if (exml->QueryBoolAttribute("stop", &stop) == XML_NO_ERROR && stop) { + speaker->dialogIndex = 9999; + return 0; + } - /* - * Handle the next dialog block if this one leads to another. - */ - - if (exml->QueryUnsignedAttribute("nextid",&idx) == XML_NO_ERROR) { - speaker->dialogIndex = idx; - - if (exml->QueryBoolAttribute("stop",&stop) == XML_NO_ERROR && stop) { - speaker->dialogIndex = 9999; - return 0; - }else if (exml->QueryBoolAttribute("pause",&stop) == XML_NO_ERROR && stop) { - //speaker->dialogIndex = 9999; - return 1; - }else return commonAIFunc(speaker); - }else{ - if (oldidx != 9999) { - speaker->dialogIndex = oldidx; - oldidx = 9999; - return 1; - }else{ - speaker->dialogIndex = 9999; - return 0; - } - } - //return 1; - } + // pause, allow player to click npc to continue + else if (exml->QueryBoolAttribute("pause", &stop) == XML_NO_ERROR && stop) { + return 1; } - exml = exml->NextSiblingElement(); + // instantly continue + else { + return commonAIFunc(speaker); + } + } - }while(exml); + // stop talking + else { + // error text? + if (oldidx != 9999) { + speaker->dialogIndex = oldidx; + oldidx = 9999; + return 1; + } else { + speaker->dialogIndex = 9999; + return 0; + } + } return 0; } -void commonPageFunc(Mob *callee) { +void commonPageFunc(Mob *callee) +{ ui::drawPage(callee->heyid); ui::waitForDialog(); callee->health = 0; @@ -235,7 +191,7 @@ void commonTriggerFunc(Mob *callee) { xml.LoadFile(currentXML.c_str()); exml = xml.FirstChildElement("Trigger"); - while(strcmp(exml->Attribute("id"),callee->heyid.c_str())) + while(exml->StrAttribute("id") != callee->heyid) exml = exml->NextSiblingElement(); player->vel.x = 0; @@ -284,16 +240,11 @@ void initEverything(void) { * Load the first file found as currentWorld. */ - for(unsigned int i=0;iname == name) return i->texloc.c_str(); } @@ -397,7 +398,7 @@ void Inventory::draw(void) { glEnd(); glDisable(GL_TEXTURE_2D); ui::setFontColor(255,255,255,((float)dfp[a]/(float)(range?range:1))*255); - ui::putStringCentered(r.end.x,r.end.y-(itemWide*.9),itemMap[items[a].id]->name.c_str()); + ui::putStringCentered(r.end.x,r.end.y-(itemWide*.9),itemMap[items[a].id]->name); ui::putText(r.end.x-(itemWide/2)+(itemWide*.85),r.end.y-(itemWide/2),"%d",items[a].count); ui::setFontColor(255,255,255,255); } @@ -518,8 +519,8 @@ void Inventory::draw(void) { if (highlight < items.size()) { ui::putStringCentered(player->loc.x + player->width / 2, player->loc.y + range * 0.75f, - itemMap[items[highlight].id]->name.c_str() - ); + itemMap[items[highlight].id]->name + ); } } if (!items.empty() && items.size() > sel && items[sel].count) diff --git a/src/ui.cpp b/src/ui.cpp index 59ea102..30d08a2 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -20,9 +20,7 @@ extern WorldWeather weather; * In the case of dialog, some NPC quests can be preloaded so that they aren't assigned until * the dialog box closes. Reference variables for that here. */ - -extern std::vector AIpreload; -extern std::vector AIpreaddr; +extern std::vector aipreload; /* * Pressing ESC or closing the window will set this to false. @@ -463,20 +461,19 @@ namespace ui { return putString(x, y, buf.get()); } - void dialogBox(const char *name, const char *opt, bool passive, const char *text, ...) { + void dialogBox(std::string name, std::string opt, bool passive, std::string text, ...) { va_list dialogArgs; std::unique_ptr printfbuf (new char[512]); textWrapLimit = 110; dialogPassive = passive; - // reset & add speaker prefix - dialogBoxText.clear(); - dialogBoxText = (std::string)name + ": "; + // add speaker prefix + dialogBoxText = name + ": "; // handle the formatted string - va_start(dialogArgs,text); - vsnprintf(printfbuf.get(),512,text,dialogArgs); + va_start(dialogArgs, text); + vsnprintf(printfbuf.get(), 512, text.c_str(), dialogArgs); va_end(dialogArgs); dialogBoxText += printfbuf.get(); @@ -485,12 +482,11 @@ namespace ui { dialogOptChosen = 0; - if (opt) { - std::string soptbuf = opt; - char *sopt = strtok(&soptbuf[0], ":"); + if (!opt.empty()) { + char *sopt = strtok(&opt[0], ":"); // cycle through options - while(sopt) { + while (sopt) { dialogOptText.push_back(std::make_pair((std::string)sopt, vec3 {0,0,0})); sopt = strtok(NULL,":"); } @@ -558,11 +554,11 @@ namespace ui { * Wait for a dialog box to be dismissed. */ - void waitForDialog (void) { + void waitForDialog(void) { while (dialogBoxExists); } - void waitForCover (void) { + void waitForCover(void) { while (fadeIntensity < 255) mainLoop(); fadeIntensity = 255; @@ -666,7 +662,7 @@ namespace ui { } if (fadeIntensity == 255 || dialogPassive) { setFontSize(24); - putStringCentered(offset.x,offset.y,rtext.c_str()); + putStringCentered(offset.x,offset.y,rtext); setFontSize(16); } }else if (dialogMerchant) { @@ -683,10 +679,10 @@ namespace ui { vec2 merchBase = {offset.x, offset.y + SCREEN_HEIGHT / 5}; - putStringCentered(merchBase.x + SCREEN_WIDTH / 10 - 20, merchBase.y + 40 + fontSize * 2, itemString1.c_str()); - putStringCentered(merchBase.x + SCREEN_WIDTH / 10 - 20, merchBase.y + 40 + fontSize , merchTrade.item[0].c_str()); - putStringCentered(merchBase.x - SCREEN_WIDTH / 10 , merchBase.y + 40 + fontSize * 2, itemString2.c_str()); - putStringCentered(merchBase.x - SCREEN_WIDTH / 10 , merchBase.y + 40 + fontSize , merchTrade.item[1].c_str()); + putStringCentered(merchBase.x + SCREEN_WIDTH / 10 - 20, merchBase.y + 40 + fontSize * 2, itemString1); + putStringCentered(merchBase.x + SCREEN_WIDTH / 10 - 20, merchBase.y + 40 + fontSize , merchTrade.item[0]); + putStringCentered(merchBase.x - SCREEN_WIDTH / 10 , merchBase.y + 40 + fontSize * 2, itemString2); + putStringCentered(merchBase.x - SCREEN_WIDTH / 10 , merchBase.y + 40 + fontSize , merchTrade.item[1]); putStringCentered(offset.x, merchBase.y + 60, "for"); glEnable(GL_TEXTURE_2D); @@ -797,7 +793,7 @@ namespace ui { putText(hub.x,hub.y,"Health: %u/%u",player->health>0?(unsigned)player->health:0, (unsigned)player->maxHealth ); - if (player->alive) { + if (player->isAlive()) { glColor3ub(150,0,0); hub.y-=fontSize*1.15; glRectf(hub.x, @@ -824,7 +820,7 @@ namespace ui { for(auto &c : player->qh.current) { hub.y -= fontSize * 1.15; - putStringCentered(hub.x,hub.y,c.title.c_str()); + putStringCentered(hub.x,hub.y,c.title); } hub.y = offset.y + 40*1.2; @@ -899,6 +895,7 @@ EXIT: //if (!dialogMerchant)closeBox(); dialogBoxExists = false; dialogMerchant = false; + dialogPassive = false; //DONE: @@ -1211,11 +1208,10 @@ EXIT: } // Flush preloaded AI functions if necessary - if (!dialogBoxExists && AIpreaddr.size()) { - while (!AIpreaddr.empty()) { - AIpreaddr.front()->addAIFunc(AIpreload.front(), false); - AIpreaddr.erase(AIpreaddr.begin()); - AIpreload.erase(AIpreload.begin()); + if (!dialogBoxExists) { + while (!aipreload.empty()) { + aipreload.front()->addAIFunc(false); + aipreload.erase(std::begin(aipreload)); } } } diff --git a/src/ui_action.cpp b/src/ui_action.cpp index a7bb36b..264100d 100644 --- a/src/ui_action.cpp +++ b/src/ui_action.cpp @@ -137,10 +137,8 @@ void actionAction(void) auto e = currentWorld->getNearInteractable(*player); - if (e->type == NPCT) { - if (!NPCp(e)->aiFunc.empty()) - e->interact(); - } + if (e->type == NPCT) + e->interact(); ACTION_EPILOUGE; } diff --git a/src/world.cpp b/src/world.cpp index 5893bfb..37d899d 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -190,9 +190,6 @@ deleteEntities(void) void World:: generate(unsigned int width) { - // iterator for `for` loops - std::vector::iterator wditer; - // see below for description/usage float geninc = 0; @@ -206,34 +203,24 @@ generate(unsigned int width) // prepare for generation worldData.front().groundHeight = GROUND_HEIGHT_INITIAL; - wditer = worldData.begin(); + auto wditer = std::begin(worldData) + GROUND_HILLINESS; // give every GROUND_HILLINESSth entry a groundHeight value - for (unsigned i = GROUND_HILLINESS; i < worldData.size(); i += GROUND_HILLINESS, wditer += GROUND_HILLINESS) - worldData[i].groundHeight = (*wditer).groundHeight + (randGet() % 8 - 4); + for (; wditer < std::end(worldData); wditer += GROUND_HILLINESS) + (*(wditer - GROUND_HILLINESS)).groundHeight = (*wditer).groundHeight + (randGet() % 8 - 4); // create slopes from the points that were just defined, populate the rest of the WorldData structure + for (wditer = std::begin(worldData) + 1; wditer < std::end(worldData); wditer++){ + auto w = &*(wditer); - for(wditer = worldData.begin() + 1; wditer != worldData.end(); wditer++) { - if ((*wditer).groundHeight && wditer + GROUND_HILLINESS < worldData.end()) - // wditer + GROUND_HILLINESS can go out of bounds (invalid read) - geninc = ((*(wditer + GROUND_HILLINESS)).groundHeight - (*wditer).groundHeight) / (float)GROUND_HILLINESS; - else { - (*wditer).groundHeight = (*(wditer - 1)).groundHeight + geninc; - - if ((*wditer).groundHeight < GROUND_HEIGHT_MINIMUM) - (*wditer).groundHeight = GROUND_HEIGHT_MINIMUM; - else if ((*wditer).groundHeight > GROUND_HEIGHT_MAXIMUM) - (*wditer).groundHeight = GROUND_HEIGHT_MAXIMUM; - } - - (*wditer).groundColor = randGet() % 32 / 8; - (*wditer).grassUnpressed = true; - (*wditer).grassHeight[0] = (randGet() % 16) / 3 + 2; - (*wditer).grassHeight[1] = (randGet() % 16) / 3 + 2; + if (w->groundHeight) + geninc = ((w + (int)GROUND_HILLINESS)->groundHeight - w->groundHeight) / GROUND_HILLINESS; - if((*wditer).groundHeight <= 0) - (*wditer).groundHeight = GROUND_HEIGHT_MINIMUM; + w->groundHeight = fmin(fmax((w - 1)->groundHeight + geninc, GROUND_HEIGHT_MINIMUM), GROUND_HEIGHT_MAXIMUM); + w->groundColor = randGet() % 32 / 8; + w->grassUnpressed = true; + w->grassHeight[0] = (randGet() % 16) / 3 + 2; + w->grassHeight[1] = (randGet() % 16) / 3 + 2; } // define x-coordinate of world's leftmost 'line' @@ -449,22 +436,21 @@ draw(Player *p) glBegin(GL_QUADS); //std::for_each(std::begin(worldData) + iStart, std::begin(worldData) + iEnd, [&](WorldData wd) { for (i = iStart; i < iEnd; i++) { - auto wd = worldData[i]; - if (wd.groundHeight <= 0) { - wd.groundHeight = GROUND_HEIGHT_MINIMUM - 1; + if (worldData[i].groundHeight <= 0) { + worldData[i].groundHeight = GROUND_HEIGHT_MINIMUM - 1; glColor4ub(0, 0, 0, 255); } else { safeSetColorA(150, 150, 150, 255); } - int ty = wd.groundHeight / 64 + wd.groundColor; - glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE , wd.groundHeight - GRASS_HEIGHT); - glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE , wd.groundHeight - GRASS_HEIGHT); + int ty = worldData[i].groundHeight / 64 + worldData[i].groundColor; + glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE , worldData[i].groundHeight - GRASS_HEIGHT); + glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE , worldData[i].groundHeight - GRASS_HEIGHT); glTexCoord2i(1, ty); glVertex2i(worldStart + i * HLINE + HLINE, 0); glTexCoord2i(0, ty); glVertex2i(worldStart + i * HLINE , 0); - if (wd.groundHeight == GROUND_HEIGHT_MINIMUM - 1) - wd.groundHeight = 0; + if (worldData[i].groundHeight == GROUND_HEIGHT_MINIMUM - 1) + worldData[i].groundHeight = 0; }//); glEnd(); @@ -558,10 +544,11 @@ singleDetect(Entity *e) int l; // kill dead entities - if (e->alive && e->health <= 0) { + if (!e->isAlive()) { + return; + } else if (e->health <= 0) { // die - e->alive = false; - e->health = 0; + e->die(); // delete the entity for (i = 0; i < entity.size(); i++) { @@ -599,15 +586,10 @@ singleDetect(Entity *e) } // collision / gravity: handle only living entities - if (e->alive) { + else { // forced movement gravity (sword hits) - if (e->forcedMove) { - if (e->vel.x > .0005 || e->vel.x < -.0005) - e->vel.x *= .6; - else - e->forcedMove = false; - } + e->handleHits(); if (e->subtype == MS_TRIGGER) return; @@ -877,7 +859,7 @@ void World::save(void){ for (auto &m : mob) { data.append(std::to_string((int)m->loc.x) + "\n"); data.append(std::to_string((int)m->loc.y) + "\n"); - data.append(std::to_string((int)m->alive) + "\n"); + data.append(std::to_string((int)m->isAlive()) + "\n"); } // wrap up @@ -898,9 +880,8 @@ void World::load(void){ for(auto &n : npc){ std::getline(iss,line); if(line == "dOnE")return; - if((n->dialogIndex = std::stoi(line)) != 9999) - n->addAIFunc(commonAIFunc,false); - else n->clearAIFunc(); + if ((n->dialogIndex = std::stoi(line)) != 9999) + n->addAIFunc(false); std::getline(iss,line); if(line == "dOnE")return; @@ -928,7 +909,8 @@ void World::load(void){ m->loc.y = std::stoi(line); std::getline(iss,line); if(line == "dOnE")return; - m->alive = std::stoi(line); + if (!std::stoi(line)) + m->die(); } while(std::getline(iss,line)){ @@ -1005,18 +987,15 @@ setStyle(std::string pre) // get folder prefix std::string prefix = pre.empty() ? "assets/style/classic/" : pre; - for_each(std::begin(buildPaths), std::end(buildPaths), [this, prefix](std::string s){ + for (s : buildPaths) sTexLoc.push_back(prefix + s); - }); prefix += "bg/"; - for_each(std::begin(bgPaths[0]), std::end(bgPaths[0]), [this, prefix](std::string s){ + for (s : bgPaths[0]) bgFiles.push_back(prefix + s); - }); - for_each(std::begin(bgPaths[1]), std::end(bgPaths[1]), [this, prefix](std::string s){ + for (s : bgPaths[1]) bgFilesIndoors.push_back(prefix + s); - }); } /** @@ -1107,7 +1086,7 @@ goWorldLeft(NPC *e) // check if entity is at world edge if(!toLeft.empty() && e->loc.x < worldStart + HLINE * 15.0f) { currentWorldToLeft->addNPC(e->loc.x,e->loc.y); - e->alive = false; + e->die(); currentWorldToLeft->npc.back()->loc.x = 0; currentWorldToLeft->npc.back()->loc.y = GROUND_HEIGHT_MAXIMUM; @@ -1125,45 +1104,50 @@ World *World:: goInsideStructure(Player *p) { World *tmp; - std::string current; if (inside.empty()) { - for (auto &b : build) { - if (p->loc.x > b->loc.x && p->loc.x + p->width < b->loc.x + b->width) { - if (b->inside.empty()) - return this; + auto d = std::find_if(std::begin(build), std::end(build), [p](const Structures *s) { + return ((p->loc.x > s->loc.x) && (p->loc.x + p->width < s->loc.x + s->width)); + }); + auto b = *d; - // +size cuts folder prefix - inside.push_back(currentXML.c_str() + xmlFolder.size()); - tmp = loadWorldFromXML(b->inside); + if ((d == std::end(build)) || b->inside.empty()) + return this; - // make the fade, as we let it fade back the worlds should be switched - ui::toggleBlackFast(); - ui::waitForCover(); - ui::toggleBlackFast(); + // +size cuts folder prefix + inside.push_back(¤tXML[xmlFolder.size()]); + tmp = loadWorldFromXML(b->inside); - glClearColor(0, 0, 0, 1); + // make the fade, as we let it fade back the worlds should be switched + ui::toggleBlackFast(); + ui::waitForCover(); + ui::toggleBlackFast(); + glClearColor(0, 0, 0, 1); - return tmp; - } - } + return tmp; } else { - current = currentXML.c_str() + xmlFolder.size(); + std::string current = ¤tXML[xmlFolder.size()]; tmp = loadWorldFromXML(inside.back()); - for (auto &b : tmp->build) { - if (current == b->inside) { - inside.pop_back(); + inside.clear(); - ui::toggleBlackFast(); - ui::waitForCover(); - p->loc.x = b->loc.x + (b->width / 2); - ui::toggleBlackFast(); + Structures *b; + for (auto &s : build) { + if (s->inside == current) { + b = s; + break; + } + } + //auto b = *std::find_if(std::begin(build), std::end(build), [&](const Structures *s) { + // return (s->inside == current); + //}); - glClearColor(1, 1, 1, 1); + ui::toggleBlackFast(); + ui::waitForCover(); + p->loc.x = b->loc.x + (b->width / 2); + ui::toggleBlackFast(); + glClearColor(1, 1, 1, 1); - return tmp; - } - } + return tmp; } return this; @@ -1173,18 +1157,15 @@ void World::addStructure(BUILD_SUB sub, float x,float y, std::string tex, std::s build.push_back(new Structures()); build.back()->inWorld = this; build.back()->textureLoc = tex; - build.back()->spawn(sub,x,y); - build.back()->inside = inside; - entity.push_back(build.back()); } Village *World:: addVillage(std::string name, World *world) { - village.emplace_back(name.c_str(), world); + village.emplace_back(name, world); return &village.back(); } @@ -1358,9 +1339,9 @@ singleDetect(Entity *e) unsigned int floornum = 0; float start, end; - if (!e->alive) + if (!e->isAlive()) return; - if (e->type == MOBT && Mobp(e)->subtype == MS_TRIGGER) + if (e->type == MOBT && e->subtype == MS_TRIGGER) return; for (; floornum < floor.size(); floornum++) { @@ -1527,10 +1508,10 @@ Arena::~Arena(void) { World *Arena::exitArena(Player *p) { World *tmp; - if (!mmob->alive && - p->loc.x + p->width / 2 > mob[0]->loc.x && - p->loc.x + p->width / 2 < mob[0]->loc.x + HLINE * 12) { - tmp = battleNest.front(); + if (!mmob->isAlive() && + p->loc.x + p->width / 2 > mob[0]->loc.x && + p->loc.x + p->width / 2 < mob[0]->loc.x + HLINE * 12) { + tmp = battleNest.front(); battleNest.erase(battleNest.begin()); inBattle = !battleNest.empty(); @@ -1541,7 +1522,7 @@ World *Arena::exitArena(Player *p) { battleNestLoc.pop_back(); mob.clear(); - mmob->alive = false; + mmob->die(); return tmp; }else{ @@ -1749,9 +1730,9 @@ loadWorldFromXMLNoSave(std::string path) { // dialog enabling dialog = false; - if (wxml->QueryBoolAttribute("hasDialog", &dialog) == XML_NO_ERROR && dialog) - tmp->npc.back()->addAIFunc(commonAIFunc, false); - else + if (wxml->QueryBoolAttribute("hasDialog", &dialog) == XML_NO_ERROR && dialog) { + tmp->npc.back()->addAIFunc(false); + } else tmp->npc.back()->dialogIndex = 9999; if (Indoor && wxml->QueryUnsignedAttribute("floor", &flooor) == XML_NO_ERROR) @@ -1855,8 +1836,6 @@ loadWorldFromXMLNoSave(std::string path) { sxml->IntAttribute("quantity1"), sxml->StrAttribute("item1"))); } else if (tag == "text") { //this is what the merchant says - std::cout << "text" << std::endl; - XMLElement *txml = sxml->FirstChildElement(); std::string textOption; @@ -1894,7 +1873,7 @@ loadWorldFromXMLNoSave(std::string path) { vil = vil->NextSiblingElement(); } - std::ifstream dat (((std::string)currentXML + ".dat").c_str()); + std::ifstream dat (((std::string)currentXML + ".dat").data()); if (dat.good()) { dat.close(); tmp->load(); @@ -1903,7 +1882,8 @@ loadWorldFromXMLNoSave(std::string path) { return tmp; } -Village::Village(const char *meme, World *w) { +Village::Village(std::string meme, World *w) +{ name = meme; start.x = w->getTheWidth() / 2.0f; end.x = -start.x; diff --git a/xcf/cat.xcf b/xcf/cat.xcf new file mode 100644 index 0000000..4d602bb Binary files /dev/null and b/xcf/cat.xcf differ -- cgit v1.2.3