From 941c1965002f114c6612dad6fc3f35d48261baff Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Sat, 16 Apr 2016 13:55:31 -0400 Subject: action menu? --- include/common.hpp | 2 + include/ui.hpp | 3 + include/ui_action.hpp | 21 +++++++ include/ui_menu.hpp | 11 ++-- include/world.hpp | 95 +++++++------------------------ main.cpp | 70 +++-------------------- src/entities.cpp | 16 ++---- src/inventory.cpp | 57 ++++++++----------- src/ui.cpp | 133 ++++++++++++++++++++++++-------------------- src/ui_action.cpp | 142 +++++++++++++++++++++++++++++++++++++++++++++++ src/world.cpp | 92 +++++++++++++++++++----------- xml/playerSpawnHill1.xml | 2 +- 12 files changed, 367 insertions(+), 277 deletions(-) create mode 100644 include/ui_action.hpp create mode 100644 src/ui_action.cpp diff --git a/include/common.hpp b/include/common.hpp index 65ac47b..a7ad6ec 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -160,6 +160,8 @@ extern std::mutex mtx; * */ +#define HLINES(n) (HLINE * n) + extern unsigned int HLINE; extern float VOLUME_MASTER; diff --git a/include/ui.hpp b/include/ui.hpp index 5281d31..5ddf165 100644 --- a/include/ui.hpp +++ b/include/ui.hpp @@ -11,7 +11,9 @@ #include #include + #include +#include #include #include @@ -109,6 +111,7 @@ namespace ui { * limited until a right click is given, closing the box. */ + void drawBox(vec2 c1, vec2 c2); void dialogBox(const char *name,const char *opt,bool passive,const char *text,...); void merchantBox(const char *name,Trade trade,const char *opt,bool passive,const char *text,...); void merchantBox(); diff --git a/include/ui_action.hpp b/include/ui_action.hpp new file mode 100644 index 0000000..a275ab3 --- /dev/null +++ b/include/ui_action.hpp @@ -0,0 +1,21 @@ +#ifndef ACTION_H_ +#define ACTION_H_ + +#include +#include + +namespace ui { + namespace action { + extern bool make; + + // enables the action ui + void enable(void); + // disables the action ui + void disable(void); + + // draws the action ui + void draw(vec2 loc); + } +} + +#endif // ACTION_H_ diff --git a/include/ui_menu.hpp b/include/ui_menu.hpp index bfeecba..621676a 100644 --- a/include/ui_menu.hpp +++ b/include/ui_menu.hpp @@ -3,6 +3,7 @@ #include #include +#include typedef void (*menuFunc)(void); @@ -13,7 +14,7 @@ struct menuItem { vec2 loc; dim2 dim; Color color; - + const char *text; menuFunc func; } button; @@ -21,11 +22,11 @@ struct menuItem { vec2 loc; dim2 dim; Color color; - + float minValue; float maxValue; float sliderLoc; - + const char *text; float *var; } slider; @@ -36,7 +37,7 @@ class Menu { public: std::vector items; Menu *child, *parent; - + ~Menu() { // TODO you CANNOT delete null pointers! /*child = NULL; @@ -55,7 +56,7 @@ namespace ui { menuItem createChildButton(vec2 l, dim2 d, Color c, const char* t); menuItem createParentButton(vec2 l, dim2 d, Color c, const char* t); menuItem createSlider(vec2 l, dim2 d, Color c, float min, float max, const char* t, float* v); - + void draw(void); } } diff --git a/include/world.hpp b/include/world.hpp index bee2bcf..5608acc 100644 --- a/include/world.hpp +++ b/include/world.hpp @@ -134,7 +134,6 @@ public: vec2 start; vec2 end; bool in; - std::vector build; Village(const char *meme, World *w); ~Village(void){} @@ -251,16 +250,19 @@ protected: std::vector sTexLoc; - std::vector light; - std::vector village; - std::vector particles; - std::vector object; - std::vector mob; + std::vector light; + std::vector village; + std::vector particles; + std::vector object; + std::vector mob; + std::vector build; public: Light *getLastLight(void); Mob *getLastMob(void); + Entity *getNearInteractable(Entity e); + vec2 getStructurePos(int index); std::string getSTextureLocation(unsigned int index) const; @@ -280,20 +282,9 @@ public: * world. */ - std::vector entity; - - /** - * A vector of all NPCs in this world. - */ - - std::vector npc; - std::vector merchant; - - /** - * A vector of all Structures in this world. - */ - - std::vector build; + std::vector entity; + std::vector npc; + std::vector merchant; /** * NULLifies pointers and allocates necessary memory. This should be @@ -309,63 +300,17 @@ public: virtual ~World(void); - /** - * Adds a structure to the world, with the specified subtype and - * coordinates. `inside` is a file name for the IndoorWorld XML file that - * this structure will lead to; if NULL the player won't be able to enter - * the structure. - */ - - void addStructure(BUILD_SUB subtype,float x,float y, std::string tex, std::string inside); + void addLight(vec2 xy, Color color); + void addMerchant(float x, float y, bool housed); + void addMob(int type,float x,float y); + void addMob(int t,float x,float y,void (*hey)(Mob *)); + void addNPC(float x,float y); + void addObject(std::string in, std::string pickupDialog, float x, float y); + void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d); + void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags); + void addStructure(BUILD_SUB subtype,float x,float y, std::string tex, std::string inside); Village *addVillage(std::string name, World *world); - /** - * Adds a Mob to the world with the specified type and coordinates. - */ - - void addMob(int type,float x,float y); - - /** - * Adds a Mob to the world with a handler function that can be called by - * certain mobs to trigger events. - */ - - void addMob(int t,float x,float y,void (*hey)(Mob *)); - - /** - * Adds an NPC to the world with the specified coordinates. - */ - - void addNPC(float x,float y); - - /** - * Adds a Merchant to the world at the specified coordinates. - */ - - void addMerchant(float x, float y); - - /** - * Adds an object to the world with the specified item id and coordinates. - * If `pickupDialog` is not NULL, that string will display in a dialog box - * upon object interaction. - */ - - void addObject(std::string in, std::string pickupDialog, float x, float y); - - /** - * Adds a particle to the world with the specified coordinates, dimensions, - * velocity, color and duration (time to live). - */ - - void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d); - void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags); - - /** - * Adds a light to the world with the specified coordinates and color. - */ - - void addLight(vec2 xy, Color color); - /** * Updates the coordinates of everything in the world that has coordinates * and a velocity. The provided delta time is used for smoother updating. diff --git a/main.cpp b/main.cpp index 2be34ca..3037c77 100644 --- a/main.cpp +++ b/main.cpp @@ -3,14 +3,6 @@ * The main game loop contains all of the global variables the game uses, and it runs the main game loop, the render loop, and the logic loop that control all of the entities. */ -/* - * Standard library includes - */ - -#include -#include -#include - #include using namespace tinyxml2; @@ -18,52 +10,24 @@ using namespace tinyxml2; * Game includes */ -#include #include +#include +#include #include #include -#include - -/** - * The window object returned by SDL when we create the main window. - */ +// SDL's window object SDL_Window *window = NULL; -/** - * Determines when the game should exit. This variable is set to true right - * before the main loop is entered, once set to false the game will exit/ - * free resources. - */ - +// main loop runs based on this variable's value bool gameRunning; -/** - * TODO - */ - -GLuint invUI; - -/** - * Contains an angle based off of the player's location and the mouse's - * location. - */ - -float handAngle; - -/** - * Contains a pointer to the world that the player is currently in. All render/ - * logic operations have to go access members of this object in order to work. - */ - +// world objects for the current world and the two that are adjacent World *currentWorld = NULL, *currentWorldToLeft = NULL, *currentWorldToRight = NULL; -/** - * The player object. - */ - +// the player object Player *player; /** @@ -397,7 +361,6 @@ int main(int argc, char *argv[]){ * Load sprites used in the inventory menu. See src/inventory.cpp */ - invUI = Texture::loadTexture("assets/invUI.png" ); mouseTex = Texture::loadTexture("assets/mouse.png"); initInventorySprites(); @@ -616,22 +579,6 @@ void render() { currentWorld->draw(player); - /* - * Calculate the player's hand angle. - */ - - handAngle = atan((ui::mouse.y - (player->loc.y + player->height/2)) / (ui::mouse.x - player->loc.x + player->width/2))*180/PI; - if(ui::mouse.x < player->loc.x){ - if(handAngle <= 0) - handAngle+=180; - if(ui::mouse.y < player->loc.y + player->height/2){ - handAngle+=180; - } - } - - if(ui::mouse.x > player->loc.x && ui::mouse.y < player->loc.y+player->height/2 && handAngle <= 0) - handAngle = 360 + handAngle; - /* * Draw the player's inventory. */ @@ -669,7 +616,7 @@ void render() { ui::putText(offset.x-SCREEN_WIDTH/2, (offset.y+SCREEN_HEIGHT/2)-ui::fontSize, - "FPS: %d\nG:%d\nRes: %ux%u\nE: %d\nPOS: (x)%+.2f\n (y)%+.2f\nTc: %u\nHA: %+.2f\nVol: %f\nWea: %s", + "fps: %d\ngrounded:%d\nresolution: %ux%u\nentity cnt: %d\nloc: (%+.2f, %+.2f)\nticks: %u\nvolume: %f\nweather: %s", fps, player->ground, SCREEN_WIDTH, // Window dimensions @@ -678,7 +625,6 @@ void render() { player->loc.x, // The player's x coordinate debugY, // The player's y coordinate tickCount, - handAngle, VOLUME_MASTER, getWorldWeatherStr(weather).c_str() ); @@ -792,6 +738,8 @@ void logic(){ } else e->near = false; } else if (e->type == MOBT) { + e->near = player->isNear(*e); + switch (e->subtype) { case MS_RABBIT: case MS_BIRD: diff --git a/src/entities.cpp b/src/entities.cpp index 02be6fe..daf4cc9 100644 --- a/src/entities.cpp +++ b/src/entities.cpp @@ -114,7 +114,10 @@ void Entity::spawn(float x, float y){ //spawns the entity you pass to it based o } name = new char[32]; - getRandomName(this); + if (type == MOBT) + name[0] = '\0'; + else + getRandomName(this); followee = NULL; } @@ -382,14 +385,7 @@ void Entity::draw(void){ //draws the entities } break; case STRUCTURET: - for(auto &strt : currentWorld->build){ - if(this == strt){ - glActiveTexture(GL_TEXTURE0); - tex->bind(0); - break; - } - } - break; + /* fall through */ default: glActiveTexture(GL_TEXTURE0); tex->bind(0); @@ -414,7 +410,7 @@ NOPE: glDisable(GL_TEXTURE_2D); glMatrixMode(GL_MODELVIEW); glPopMatrix(); - if (near) + if (near && type != MOBT) ui::putStringCentered(loc.x+width/2,loc.y-ui::fontSize-HLINE/2,name); if (health != maxHealth) { glColor3ub(150,0,0); glRectf(loc.x, loc.y + height, loc.x + width, loc.y + height + HLINE * 2); diff --git a/src/inventory.cpp b/src/inventory.cpp index 1b2d378..84f8871 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -425,31 +425,21 @@ void Inventory::draw(void){ a++; } C("Done drawing standard inv"); - }else if(invHover){ + } else if (invHover) { static unsigned int highlight = 0; static unsigned int thing = 0; - std::cout<<"Inventory2???"<= mouseStart.x){ - std::cout << "Thing" << std::endl; thing = (ui::mouse.x - offset.x - mouseStart.x)/80; - std::cout << "Highlight" << std::endl; highlight=sel+thing; - std::cout << "Highlight Check" << std::endl; if(highlight>numSlot-1)highlight=numSlot-1; - std::cout << "Left Click" << std::endl; if(SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)){ sel = highlight; mouseSel=false; @@ -459,8 +449,10 @@ void Inventory::draw(void){ } if((ui::mouse.x - offset.x) < mouseStart.x){ thing = (mouseStart.x - (ui::mouse.x - offset.x))/80; - if((int)sel-(int)thing<0)highlight=0; - else highlight=sel-thing; + if ((int)sel - (int)thing < 0) + highlight = 0; + else + highlight = sel - thing; if(SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)){ sel = highlight; mouseSel=false; @@ -469,17 +461,16 @@ void Inventory::draw(void){ } } } - std::cout << "Rays" << std::endl; - for(auto &r : iray){ - std::cout << "Setting angle" << std::endl; - angle=180-(angleB*a) - angleB/2.0f; - std::cout << "Currcourd" << std::endl; + + a = 0; + for (auto &r : iray) { + angle = 180 - (angleB * a) - angleB / 2.0f; + curCoord[a].x += float(range) * cos(angle*PI/180); curCoord[a].y += float(range) * sin(angle*PI/180); - std::cout << "Ray.end" << std::endl; r.end = curCoord[a]; - std::cout << "Draw" << std::endl; + // square drawing glColor4f(0.0f, 0.0f, 0.0f, a == highlight ? 0.5f : 0.1f); glBegin(GL_QUADS); glVertex2i(r.end.x-(itemWide/2), r.end.y-(itemWide/2)); @@ -488,17 +479,12 @@ void Inventory::draw(void){ glVertex2i(r.end.x-(itemWide/2), r.end.y+(itemWide/2)); glEnd(); - std::cout << "Draw items" << std::endl; - if(!items.empty() && a < items.size() && items[a].count){ - std::cout << "Jamie" << std::endl; + if (a < items.size() && items[a].count) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, itemtex[items[a].id]); glColor4f(1.0f, 1.0f, 1.0f, a == highlight ? 0.8f : 0.2f); - std::cout << "Done Binding" << std::endl; glBegin(GL_QUADS); - std::cout << "jdjdjd" << std::endl; if(itemMap[items[a].id]->height > itemMap[items[a].id]->width){ - std::cout << "map" << std::endl; glTexCoord2i(0,1);glVertex2i(r.end.x-((itemWide/2)*((float)itemMap[items[a].id]->width/(float)itemMap[items[a].id]->height)),r.end.y-(itemWide/2)); glTexCoord2i(1,1);glVertex2i(r.end.x+((itemWide/2)*((float)itemMap[items[a].id]->width/(float)itemMap[items[a].id]->height)),r.end.y-(itemWide/2)); glTexCoord2i(1,0);glVertex2i(r.end.x+((itemWide/2)*((float)itemMap[items[a].id]->width/(float)itemMap[items[a].id]->height)),r.end.y+(itemWide/2)); @@ -511,11 +497,16 @@ void Inventory::draw(void){ } glEnd(); glDisable(GL_TEXTURE_2D); - std::cout << "Adding a" << std::endl; - a++; } + a++; + } + + 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() + ); } - ui::putStringCentered(player->loc.x+player->width/2, player->loc.y + range*.75,itemMap[items[highlight].id]->name.c_str()); } if(!items.empty() && items.size() > sel && items[sel].count) diff --git a/src/ui.cpp b/src/ui.cpp index a78686c..f6f7b94 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -615,11 +615,30 @@ namespace ui { pageTexReady = true; } + void drawBox(vec2 c1, vec2 c2) { + // draw black body + glColor3ub(0, 0, 0); + glRectf(c1.x, c1.y, c2.x, c2.y); + + // draw white border + glColor3ub(255, 255, 255); + glBegin(GL_LINE_STRIP); + glVertex2i(c1.x , c1.y); + glVertex2i(c2.x + 1, c1.y); + glVertex2i(c2.x + 1, c2.y); + glVertex2i(c1.x - 1, c2.y); + glVertex2i(c1.x , c1.y); + glEnd(); + } + void draw(void){ unsigned char i; float x,y,tmp; std::string rtext; + // will return if not toggled + action::draw(vec2 {player->loc.x + player->width / 2, player->loc.y + player->height + HLINE}); + if (pageTexReady) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, pageTex); @@ -654,38 +673,24 @@ namespace ui { x=offset.x-SCREEN_WIDTH/6; y=(offset.y+SCREEN_HEIGHT/2)-HLINE*8; - // draw the box border - glColor3ub(255,255,255); - glBegin(GL_LINE_STRIP); - glVertex2f(x-1 ,y+1); - glVertex2f(x+1+(SCREEN_WIDTH/3),y+1); - glVertex2f(x+1+(SCREEN_WIDTH/3),y-1-SCREEN_HEIGHT*.6); - glVertex2f(x-1,y-1-SCREEN_HEIGHT*.6); - glVertex2f(x - 1,y+1); - glEnd(); - - // draw the box - glColor3ub(0,0,0); - glRectf(x,y,x+SCREEN_WIDTH/3,y-SCREEN_HEIGHT*.6); + drawBox(vec2 {x, y}, vec2 {x + SCREEN_WIDTH / 3, y - SCREEN_HEIGHT * 0.6f}); // draw typeOut'd text putString(x + HLINE, y - fontSize - HLINE, (rtext = typeOut(dialogBoxText))); - std::string itemString1 = std::to_string(merchTrade.quantity[0]); - itemString1 += "x"; - - std::string itemString2 = std::to_string(merchTrade.quantity[1]); - itemString2 += "x"; + std::string itemString1 = std::to_string(merchTrade.quantity[0]) + "x", + itemString2 = std::to_string(merchTrade.quantity[1]) + "x"; - putStringCentered(offset.x - (SCREEN_WIDTH / 10) + 20, offset.y + (SCREEN_HEIGHT / 5) + 40 + (fontSize*2), itemString1.c_str()); - putStringCentered(offset.x - (SCREEN_WIDTH / 10) + 20, offset.y + (SCREEN_HEIGHT / 5) + 40 + fontSize, merchTrade.item[0].c_str()); + vec2 merchBase = {offset.x, offset.y + SCREEN_HEIGHT / 5}; - putStringCentered(offset.x + (SCREEN_WIDTH / 10) - 20, offset.y + (SCREEN_HEIGHT / 5) + 40 + (fontSize*2), itemString2.c_str()); - putStringCentered(offset.x + (SCREEN_WIDTH / 10) - 20, offset.y + (SCREEN_HEIGHT / 5) + 40 + fontSize, merchTrade.item[1].c_str()); - - putStringCentered(offset.x,offset.y + (SCREEN_HEIGHT / 5) + 60, "for"); + 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(offset.x, merchBase.y + 60, "for"); glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, getItemTexture(merchTrade.item[0])); glBegin(GL_QUADS); glTexCoord2d(0,1);glVertex2f(offset.x - (SCREEN_WIDTH / 10) ,offset.y + (SCREEN_HEIGHT/5)); @@ -701,6 +706,7 @@ namespace ui { glTexCoord2d(1,0);glVertex2f(offset.x + (SCREEN_WIDTH / 10) ,offset.y + (SCREEN_HEIGHT/5) + 40); glTexCoord2d(0,0);glVertex2f(offset.x + (SCREEN_WIDTH / 10) - 40,offset.y + (SCREEN_HEIGHT/5) + 40); glEnd(); + glDisable(GL_TEXTURE_2D); merchArrowLoc[0].x = offset.x - (SCREEN_WIDTH / 8.5) - 16; @@ -748,24 +754,12 @@ namespace ui { } setFontColor(255, 255, 255); - }else{ //normal dialog box + } else { //normal dialog box - x=offset.x-SCREEN_WIDTH/2+HLINE*8; - y=(offset.y+SCREEN_HEIGHT/2)-HLINE*8; - - // draw white border - glColor3ub(255, 255, 255); + x = offset.x - SCREEN_WIDTH / 2 + HLINE * 8; + y = offset.y + SCREEN_HEIGHT / 2 - HLINE * 8; - glBegin(GL_LINE_STRIP); - glVertex2i(x-1 ,y+1); - glVertex2i(x+1+SCREEN_WIDTH-HLINE*16,y+1); - glVertex2i(x+1+SCREEN_WIDTH-HLINE*16,y-1-SCREEN_HEIGHT/4); - glVertex2i(x-1 ,y-1-SCREEN_HEIGHT/4); - glVertex2i(x-1 ,y+1); - glEnd(); - - glColor3ub(0,0,0); - glRectf(x,y,x+SCREEN_WIDTH-HLINE*16,y-SCREEN_HEIGHT/4); + drawBox(vec2 {x, y}, vec2 {x + SCREEN_WIDTH - HLINE * 16, y - SCREEN_HEIGHT / 4}); rtext = typeOut(dialogBoxText); @@ -956,16 +950,22 @@ EXIT: // mouse clicks case SDL_MOUSEBUTTONDOWN: - // right click advances dialog - if ((e.button.button & SDL_BUTTON_RIGHT) && (dialogBoxExists | pageTexReady)) - dialogAdvance(); + // run actions? + if ((action::make = e.button.button & SDL_BUTTON_RIGHT)) + /*player->inv->invHover =*/ edown = false; - // left click uses item - if ((e.button.button & SDL_BUTTON_LEFT) && !dialogBoxExists) - player->inv->usingi = true; + if (dialogBoxExists || pageTexReady) { + // right click advances dialog + if ((e.button.button & SDL_BUTTON_RIGHT)) + dialogAdvance(); + } else { + // left click uses item + if (e.button.button & SDL_BUTTON_LEFT) + player->inv->usingi = true; + } if(mouse.x > player->loc.x && mouse.x < player->loc.x + player->width && - mouse.y > player->loc.y && mouse.y < player->loc.y + player->height) { + mouse.y > player->loc.y && mouse.y < player->loc.y + player->height) { player->vel.y = .05; fr = mouse; ig = player; @@ -1054,8 +1054,6 @@ EXIT: } } break; - case SDLK_s: - break; case SDLK_w: if (inBattle) { tmp = currentWorld; @@ -1066,32 +1064,43 @@ EXIT: currentWorld = tmp; break; case SDLK_LSHIFT: - if(debug){ - Mix_PlayChannel(1,sanic,-1); + if (debug) { + Mix_PlayChannel(1, sanic, -1); player->speed = 4.0f; - }else + } else player->speed = 2.0f; break; case SDLK_LCTRL: player->speed = .5; break; case SDLK_e: - edown=true; - if(!heyOhLetsGo){ + edown = true; + + // start hover counter? + if (!heyOhLetsGo) { heyOhLetsGo = loops; player->inv->mouseSel = false; } - if(loops - heyOhLetsGo >= 2 && !(player->inv->invOpen) && !(player->inv->selected)) - player->inv->invHover=true; + + // run hover thing + if (loops - heyOhLetsGo >= 2 && !(player->inv->invOpen) && !(player->inv->selected)) { + player->inv->invHover = true; + + // enable action ui + action::enable(); + } + break; default: break; } - if(tmp != currentWorld){ - std::swap(tmp,currentWorld); + + // handle world switches? + if (tmp != currentWorld) { + std::swap(tmp, currentWorld); toggleBlackFast(); waitForCover(); - std::swap(tmp,currentWorld); + std::swap(tmp, currentWorld); toggleBlackFast(); } } @@ -1149,6 +1158,10 @@ EXIT: else player->inv->selected = false; player->inv->mouseSel = false; } + + // disable action ui + action::disable(); + heyOhLetsGo = 0; break; case SDLK_l: @@ -1165,7 +1178,7 @@ EXIT: else { currentWorld->addStructure(FIRE_PIT, player->loc.x, player->loc.y, "", ""); currentWorld->addLight({player->loc.x + SCREEN_WIDTH/2, player->loc.y},{1.0f,1.0f,1.0f}); - currentWorld->getLastLight()->follow(currentWorld->build.back()); + //currentWorld->getLastLight()->follow(currentWorld->build.back()); currentWorld->getLastLight()->makeFlame(); } break; diff --git a/src/ui_action.cpp b/src/ui_action.cpp new file mode 100644 index 0000000..66fea19 --- /dev/null +++ b/src/ui_action.cpp @@ -0,0 +1,142 @@ +#include + +extern World *currentWorld; +extern Player *player; +extern bool inBattle; + +static std::vector> actionText = { + {"Attack", vec3 {0, 0, 0}}, + {"Action", vec3 {0, 0, 0}}, + {"Umm" , vec3 {0, 0, 0}} +}; + +void actionAttack(void); +void actionAction(void); + +static std::vector actionFunc = { + actionAttack, + actionAction, + nullptr, +}; + +static bool actionToggle = false; +static unsigned int actionHover = 0; +static Entity *nearEntity = nullptr, *lastEntity = nullptr; + +namespace ui { + namespace action { + bool make = false; + + // enables action ui + void enable(void) { + actionToggle = true; + } + + // disables action ui + void disable(void) { + actionToggle = false; + + if (lastEntity != nullptr) + lastEntity->canMove = true; + } + + // draws the action ui + void draw(vec2 loc) { + static bool second = false; + unsigned int i = 1; + float y = loc.y; + + if (!actionToggle) + return; + + nearEntity = currentWorld->getNearInteractable(*player); + + if (nearEntity == nullptr) { + if (lastEntity != nullptr) { + lastEntity->canMove = true; + lastEntity = nullptr; + } + return; + } else if (nearEntity != lastEntity) { + if (lastEntity != nullptr) + lastEntity->canMove = true; + lastEntity = nearEntity;; + } + + if (make) { + if (!actionHover) { + make = false; + return; + } + + if (!second) + second = true; + else { + second = false; + return; + } + + if (actionFunc[actionHover - 1] != nullptr) + std::thread(actionFunc[actionHover - 1]).detach(); + + actionHover = 0; + make = false; + return; + } else { + nearEntity->canMove = false; + ui::drawBox(vec2 {loc.x - HLINES(11), loc.y}, vec2 {loc.x + HLINES(12), loc.y + actionText.size() * HLINES(8)}); + + for (auto &s : actionText) { + s.second.z = ui::putStringCentered((s.second.x = loc.x), (s.second.y = (y += fontSize * 1.15f)), s.first) / 2; + + if (ui::mouse.x > s.second.x - s.second.z && ui::mouse.x < s.second.x + s.second.z && + ui::mouse.y > s.second.y && ui::mouse.y < s.second.y + ui::fontSize) { + actionHover = i; + ui::setFontColor(255, 100, 100, 255); + ui::putStringCentered(s.second.x, s.second.y, s.first); + ui::setFontColor(255, 255, 255, 255); + } + i++; + } + + ui::putStringCentered(loc.x, y + fontSize * 1.2f, nearEntity->name); + } + + if (i == actionText.size()) + actionHover = 0; + + ui::setFontColor(255, 255, 255, 255); + } + } +} + +void actionAttack(void) +{ + auto m = currentWorld->getNearInteractable(*player); + + if (m->type == MOBT) { + if (!inBattle && m != nullptr) { + Arena *a = new Arena(currentWorld, player, Mobp(m)); + a->setStyle(""); + a->setBackground(WorldBGType::Forest); + a->setBGM("assets/music/embark.wav"); + + ui::toggleWhiteFast(); + ui::waitForCover(); + currentWorld = a; + ui::toggleWhiteFast(); + } + } else { + ui::dialogBox(player->name, "", false, "%s doesn't appear to be in the mood for fighting...", m->name); + } +} + +void actionAction(void) +{ + auto e = currentWorld->getNearInteractable(*player); + + if (e->type == NPCT) { + if (!NPCp(e)->aiFunc.empty()) + e->interact(); + } +} diff --git a/src/world.cpp b/src/world.cpp index 434f619..df6c556 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -168,7 +168,10 @@ void World:: deleteEntities(void) { // free mobs - mob.clear(); + while(!mob.empty()){ + delete mob.back(); + mob.pop_back(); + } merchant.clear(); while(!npc.empty()){ @@ -696,7 +699,7 @@ void World::draw(Player *p){ } for (auto &m : mob) - m.draw(); + m->draw(); for (auto &o : object) o.draw(); @@ -969,18 +972,18 @@ addVillage(std::string name, World *world) } void World::addMob(int t,float x,float y){ - mob.emplace_back(t); - mob.back().spawn(x,y); + mob.push_back(new Mob(t)); + mob.back()->spawn(x,y); - entity.push_back(&mob.back()); + entity.push_back(mob.back()); } void World::addMob(int t,float x,float y,void (*hey)(Mob *)){ - mob.emplace_back(t); - mob.back().spawn(x,y); - mob.back().hey = hey; + mob.push_back(new Mob(t)); + mob.back()->spawn(x,y); + mob.back()->hey = hey; - entity.push_back(&mob.back()); + entity.push_back(mob.back()); } void World::addNPC(float x,float y){ @@ -990,10 +993,13 @@ void World::addNPC(float x,float y){ entity.push_back(npc.back()); } -void World::addMerchant(float x, float y){ +void World::addMerchant(float x, float y, bool housed){ merchant.push_back(new Merchant()); merchant.back()->spawn(x,y); + if (housed) + merchant.back()->inside = build.back(); + npc.push_back(merchant.back()); entity.push_back(npc.back()); } @@ -1037,7 +1043,20 @@ getLastLight(void) Mob *World:: getLastMob(void) { - return &mob.back(); + return mob.back(); +} + +Entity *World:: +getNearInteractable(Entity e) +{ + for (auto &n : entity) { + if (n->type == MOBT || n->type == NPCT || n->type == MERCHT) { + if (e.isNear(*n) && (e.left ? n->loc.x < e.loc.x : n->loc.x > e.loc.x)) + return n; + } + } + + return nullptr; } std::string World:: @@ -1049,6 +1068,17 @@ getSTextureLocation(unsigned int index) const return sTexLoc[ index ]; } +vec2 World:: +getStructurePos(int index) +{ + if (index < 0) + return build.back()->loc; + else if ((unsigned)index >= build.size()) + return vec2{0, 0}; + + return build[index]->loc; +} + std::string World:: setToLeft(std::string file) { @@ -1193,7 +1223,9 @@ addHole(unsigned int start, unsigned int end) void World:: addHill(const ivec2 peak, const unsigned int width) { - int start = peak.x - width / 2, end = start + width, offset = 0; + int start = peak.x - width / 2, + end = start + width, + offset = 0; const float thing = peak.y - worldData[start].groundHeight; const float period = PI / width; @@ -1238,9 +1270,9 @@ 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->loc.x) + "\n"); + data.append(std::to_string((int)m->loc.y) + "\n"); + data.append(std::to_string((int)m->alive) + "\n"); } data.append("dOnE\0"); @@ -1284,13 +1316,13 @@ void World::load(void){ for(auto &m : mob){ std::getline(iss,line); if(line == "dOnE")return; - m.loc.x = std::stoi(line); + m->loc.x = std::stoi(line); std::getline(iss,line); if(line == "dOnE")return; - m.loc.y = std::stoi(line); + m->loc.y = std::stoi(line); std::getline(iss,line); if(line == "dOnE")return; - m.alive = std::stoi(line); + m->alive = std::stoi(line); } while(std::getline(iss,line)){ @@ -1530,8 +1562,8 @@ Arena::Arena(World *leave,Player *p,Mob *m){ mmob = m; mmob->aggressive = false; - mob.push_back(*m); - entity.push_back(&mob.back()); + mob.push_back(m); + entity.push_back(mob.back()); battleNest.push_back(leave); battleNestLoc.push_back(p->loc); @@ -1544,8 +1576,8 @@ 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) { + 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()); @@ -1843,9 +1875,7 @@ loadWorldFromXMLNoSave(std::string path) { vil->StrAttribute("texture"), vil->StrAttribute("inside") ); - tmp->addMerchant(0, 100); - - tmp->merchant.back()->inside = tmp->build.back(); + tmp->addMerchant(0, 100, true); } // handle traders @@ -1880,15 +1910,13 @@ loadWorldFromXMLNoSave(std::string path) { } } - vptr->build.push_back(tmp->build.back()); + float buildx = tmp->getStructurePos(-1).x; - if(vptr->build.back()->loc.x < vptr->start.x){ - vptr->start.x = vptr->build.back()->loc.x; - } + if (buildx < vptr->start.x) + vptr->start.x = buildx; - if(vptr->build.back()->loc.x + vptr->build.back()->width > vptr->end.x){ - vptr->end.x = vptr->build.back()->loc.x + vptr->build.back()->width; - } + if (buildx > vptr->end.x) + vptr->end.x = buildx; //go to the next element in the village block vil = vil->NextSiblingElement(); diff --git a/xml/playerSpawnHill1.xml b/xml/playerSpawnHill1.xml index 53f5ad2..622a4e6 100644 --- a/xml/playerSpawnHill1.xml +++ b/xml/playerSpawnHill1.xml @@ -7,7 +7,7 @@ - + -- cgit v1.2.3 From e04fae7b72b424e5019429a071ad2de9edf4df1f Mon Sep 17 00:00:00 2001 From: Clyne Sullivan Date: Thu, 21 Apr 2016 18:44:47 -0400 Subject: more world rewriting --- Makefile | 2 +- include/common.hpp | 9 +- include/entities.hpp | 48 +- include/world.hpp | 410 +++++----------- src/ui_action.cpp | 28 +- src/world.cpp | 1293 ++++++++++++++++++++++++-------------------------- 6 files changed, 818 insertions(+), 972 deletions(-) diff --git a/Makefile b/Makefile index 6abc13f..dd1267f 100644 --- a/Makefile +++ b/Makefile @@ -12,7 +12,7 @@ ifeq ($(TARGET_OS),win32) -lSDL2main -lSDL2 -lSDL2_image -lSDL2_mixer -lfreetype endif -CXXFLAGS = -m$(TARGET_BITS) -std=c++1z +CXXFLAGS = -m$(TARGET_BITS) -std=c++17 CXXINC = -Iinclude -Iinclude/freetype CXXWARN = -Wall -Wextra -Werror -pedantic-errors diff --git a/include/common.hpp b/include/common.hpp index a7ad6ec..83350d3 100644 --- a/include/common.hpp +++ b/include/common.hpp @@ -86,10 +86,15 @@ typedef struct { int y; } ivec2; -typedef struct { +struct _vec2 { float x; float y; -} vec2; + + bool operator==(const _vec2 &v) { + return (x == v.x) && (y == v.y); + } +}; +typedef struct _vec2 vec2; typedef struct { float x; diff --git a/include/entities.hpp b/include/entities.hpp index 6cd5731..f38a5fe 100644 --- a/include/entities.hpp +++ b/include/entities.hpp @@ -13,7 +13,6 @@ #define NPCp(n) ((NPC *)n) #define Structurep(n) ((Structures *)n) #define Mobp(n) ((Mob *)n) -#define Objectp(n) ((Object *)n) #define PLAYER_INV_SIZE 43 // The size of the player's inventory #define NPC_INV_SIZE 3 // Size of an NPC's inventory @@ -275,7 +274,54 @@ public: void reloadTexture(void); void interact(void); + + bool operator==(const Object &o) { + return !strcmp(name, o.name) && (loc == o.loc); + } +}; + +/** + * The light structure, used to store light coordinates and color. + */ + +class Light{ +public: + vec2 loc; /**< Light location */ + Color color; /**< Light color */ + float radius; /**< Light radius */ + + bool belongsTo; + Entity *following; + + bool flame; + float fireFlicker; + vec2 fireLoc; + + Light(vec2 l, Color c, float r){ + loc = l; + color = c; + radius = r; + + belongsTo = false; + following = nullptr; + + flame = false; + } + + void makeFlame(void){ + flame = true; + } + + void follow(Entity *f){ + following=f; + belongsTo = true; + } }; + +constexpr Object *Objectp(Entity *e) { + return (Object *)e; +} + #endif // ENTITIES_H /** diff --git a/include/world.hpp b/include/world.hpp index 5608acc..dcd047b 100644 --- a/include/world.hpp +++ b/include/world.hpp @@ -1,43 +1,29 @@ -/** @file world.h - * @brief The world system. - * - * This file contains the classes and variables necessary to create an in-game - * world. - */ - +/* ---------------------------------------------------------------------------- +** The world stuffs. +** +** This file contains the classes and variables necessary to create an in-game +** world... "and stuffs". +** --------------------------------------------------------------------------*/ #ifndef WORLD_H #define WORLD_H +/* ---------------------------------------------------------------------------- +** Includes section +** --------------------------------------------------------------------------*/ + +// 'local' game includes #include #include -#define GROUND_HEIGHT_INITIAL 80 -#define GROUND_HEIGHT_MINIMUM 60 -#define GROUND_HEIGHT_MAXIMUM 110 -#define GROUND_HILLINESS 10 - -#define PLAYER_SPEED_CONSTANT 0.15f - -/** - * Gravity thing - */ - -#define GRAVITY_CONSTANT 0.001f - -/** - * Defines how many game ticks it takes for a day to elapse. - */ - -#define DAY_CYCLE 12000 - -#define Indoorp(x) ((IndoorWorld *)x) +/* ---------------------------------------------------------------------------- +** Structures section +** --------------------------------------------------------------------------*/ /** * The background type enum. * This enum contains all different possibilities for world backgrounds; used * in World::setBackground() to select the appropriate images. */ - enum class WorldBGType : unsigned char { Forest, /**< A forest theme. */ WoodHouse /**< An indoor wooden house theme. */ @@ -48,7 +34,6 @@ enum class WorldBGType : unsigned char { * This enum contains every type of weather currently implemented in the game. * Weather is set by the world somewhere. */ - enum class WorldWeather : unsigned char { Sunny = 0, /**< Sunny/daytime */ Dark, /**< Nighttime */ @@ -61,7 +46,6 @@ enum class WorldWeather : unsigned char { * This structure is used to store the world's ground, stored in vertical * lines. Dirt color and grass properties are also kept track of here. */ - typedef struct { bool grassUnpressed; float grassHeight[2]; @@ -69,70 +53,36 @@ typedef struct { unsigned char groundColor; } WorldData; -/** - * A value used by World::draw() for shading, ranges from -50 to 50 depending - * on the current time of day. - */ +/* ---------------------------------------------------------------------------- +** Variables section +** --------------------------------------------------------------------------*/ +// affects brightness of world elements when drawn extern int worldShade; -/** - * The path to the currently loaded XML file. - */ - +// the path to the currently loaded XML file. extern std::string currentXML; -// prototype so Village can reference it -class World; - -/** - * The light structure, used to store light coordinates and color. - */ - -class Light{ -public: - vec2 loc; /**< Light location */ - Color color; /**< Light color */ - float radius; /**< Light radius */ - - bool belongsTo; - Entity *following; - - bool flame; - float fireFlicker; - vec2 fireLoc; - - Light(vec2 l, Color c, float r){ - loc = l; - color = c; - radius = r; +// defines how many game ticks it takes for a day to elapse +extern const unsigned int DAY_CYCLE; - belongsTo = false; - following = nullptr; +// velocity of player when moved by user +extern const float PLAYER_SPEED_CONSTANT; - flame = false; - } - - void makeFlame(void){ - flame = true; - } - - void follow(Entity *f){ - following=f; - belongsTo = true; - } -}; +// maximum pull of gravity in one game tick +extern const float GRAVITY_CONSTANT; +/* ---------------------------------------------------------------------------- +** Classes / function prototypes section +** --------------------------------------------------------------------------*/ /** * The village class, used to group structures into villages. */ - class Village { public: std::string name; - vec2 start; - vec2 end; + vec2 start, end; bool in; Village(const char *meme, World *w); @@ -142,277 +92,160 @@ public: /** * The world class. This class does everything a world should do. */ - class World { protected: - /** - * The line array. - * - * This array is created through 'new' in World::generate(), with an amount - * of elements provided by the function. - */ - - std::vector worldData; - - /** - * Starting x coordinate. - * - * This x value is the point at which line[0] should reside, can be used to - * calculate the width of the world. - */ - - int worldStart; - - /** - * Handle physics for a single entity. - * - * This function handles gravity and death for an entity. The public version - * of this, World::detect(), handles all entities in the world as well as - * the player. World::singleDetect() should never be used outside of - * World::detect(), which is why it is declared private. - */ - - virtual void singleDetect(Entity *e); - - /** - * Empties all entity vectors. - * - * Each entity vector is iterated through, calling delete for each entry. - * Once all specific vectors are cleared, the general entity vector is - * emptied of the pointers to those other vectors. This function should only - * be called in World's destructor, as there shouldn't be another reason to - * call this function. - */ - - void deleteEntities(void); - - /** - * Number of lines in the world. - * - * While this number is helpful for knowing the world's width, it is kept - * private for security reasons. To compensate for this, - * World::getTheWidth() is provided (see below). - */ + // an array of all the world's ground data + std::vector worldData; + // the size of `worldData` unsigned int lineCount; - /** - * An array of star coordinates. - */ - - std::vector star; - - /** - * The Texturec object that holds the background sprites for this world. - */ + // the left-most (negative) coordinate of the worldStart + int worldStart; + // holds / handles textures for background elements Texturec *bgTex; - /** - * Defines the set of background images that should be used for this world. - */ - + // defines what type of background is being used WorldBGType bgType; - /** - * The Mix_Music object that holds the background soundtrack for the world. - */ - + // an SDL_mixer object for the world's BGM Mix_Music *bgmObj; - /** - * The file path of the song wished to be loaded by bgmObj. - */ - + // the pathname of the loaded BGM std::string bgm; - std::vector bgFiles; - std::vector bgFilesIndoors; - - /** - * The filename of the XML file for the world to the left; NULL if no world - * is present. - */ - + // XML file names of worlds to the left and right, empty if nonexistant std::string toLeft; - - /** - * The filename of the XML file for the world to the right; NULL if no world - * is present. - */ - std::string toRight; - /** - * Vector of all building textures for the current world style - */ - + // structure texture file paths std::vector sTexLoc; + // TODO + std::vector bgFiles; + std::vector bgFilesIndoors; + + // an array of star coordinates + std::vector star; + + // entity vectors std::vector light; - std::vector village; - std::vector particles; - std::vector object; std::vector mob; + std::vector object; + std::vector particles; std::vector build; + std::vector village; -public: - - Light *getLastLight(void); - Mob *getLastMob(void); - Entity *getNearInteractable(Entity e); - vec2 getStructurePos(int index); - - std::string getSTextureLocation(unsigned int index) const; - - /** - * These handle accessing/modifying pathnames for worlds linked to the left - * and right of this one. - */ - - std::string setToLeft(std::string file); - std::string setToRight(std::string file); + // handles death, gravity, etc. for a single entity + virtual void singleDetect(Entity *e); - std::string getToLeft(void) const; - std::string getToRight(void) const; + // frees entities and clears vectors that contain them + void deleteEntities(void); - /** - * A vector of pointers to every NPC, Structure, Mob, and Object in this - * world. - */ +public: + // entity vectors that need to be public because we're based std::vector entity; - std::vector npc; - std::vector merchant; - /** - * NULLifies pointers and allocates necessary memory. This should be - * followed by some combination of setBackground(), setBGM(), or - * generate(). - */ + std::vector npc; + std::vector merchant; + // the world constructor, prepares variables World(void); - /** - * Frees resources taken by the world. - */ - + // destructor, frees used memory virtual ~World(void); - void addLight(vec2 xy, Color color); - void addMerchant(float x, float y, bool housed); - void addMob(int type,float x,float y); - void addMob(int t,float x,float y,void (*hey)(Mob *)); - void addNPC(float x,float y); - void addObject(std::string in, std::string pickupDialog, float x, float y); - void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d); - void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags); - void addStructure(BUILD_SUB subtype,float x,float y, std::string tex, std::string inside); - Village *addVillage(std::string name, World *world); + // generates a world of the specified width + void generate(unsigned int width); - /** - * Updates the coordinates of everything in the world that has coordinates - * and a velocity. The provided delta time is used for smoother updating. - */ + // draws everything to the screen + virtual void draw(Player *p); - void update(Player *p, unsigned int delta); + // handles collisions/death of player and all entities + void detect(Player *p); - /** - * Generate a world of the provided width. Worlds are drawn centered on the - * y-axis, so the reachable coordinates on the world would be from negative - * half-width to positive half-width. - */ + // updates entities, moving them and such + void update(Player *p, unsigned int delta); - void generate(unsigned int width); + // gets the world's width in TODO + int getTheWidth(void) const; - /** - * Sets the background theme, collecting the required textures into a - * Texturec object. - */ + // gets a pointer to the most recently added light + Light *getLastLight(void); - void setBackground(WorldBGType bgt); + // gets a pointer to the most recently added mob + Mob *getLastMob(void); - /** - * Sets the background music for the world, required for the world to be - * playable. - */ + // gets the nearest interactable entity to the given one + Entity *getNearInteractable(Entity &e); - void setBGM(std::string path); + // gets the coordinates of the `index`th structure + vec2 getStructurePos(int index); - /** - * Sets the worlds style folder - */ + // gets the texture path of the `index`th structure + std::string getSTextureLocation(unsigned int index) const; - void setStyle(std::string pre); + // saves the world's data to an XML file + void save(void); - /** - * Plays/stops this world's BGM. If `prev` is not NULL, that world's BGM - * will be faded out followed by the fading in of this world's BGM. - */ + // attempts to load world data from an XML file + void load(void); + // plays/pauses the world's music, according to if a new world is being entered void bgmPlay(World *prev) const; - /** - * Draw the world and entities based on the player's coordinates. - */ - - virtual void draw(Player *p); + // sets and loads the specified BGM + void setBGM(std::string path); - /** - * Handles collision between the entities and the world, as well as entity - * death. - */ + // sets the world's background theme + void setBackground(WorldBGType bgt); - void detect(Player *p); + // sets the folder to collect entity textures from + void setStyle(std::string pre); - /** - * Attempts to let the player enter the left-linked world specified by - * `toLeft`. Returns the world to the left if the movement is possible, - * otherwise returns this world. - */ + // sets / gets pathnames of XML files for worlds to the left and right + std::string setToLeft(std::string file); + std::string setToRight(std::string file); + std::string getToLeft(void) const; + std::string getToRight(void) const; + // attempts to enter the left/right adjacent world, returning either that world or this World *goWorldLeft(Player *p); - bool goWorldLeft(NPC *e); - - /** - * Attempts to let the player enter the right-linked world specified by - * `toRight`. Returns the world to the right if the movement is possible, - * otherwise returns this world. - */ - World *goWorldRight(Player *p); - /** - * This function looks for any structure the player is standing in front of - * that also have an inside world. Returns the inside world if those - * conditions are met, otherwise returns this world. - */ + // attempts to move an NPC to the left adjacent world, returning true on success + bool goWorldLeft(NPC *e); + // attempts to enter a structure that the player would be standing in front of World *goInsideStructure(Player *p); - /** - * Adds a hole between the specified y coordinates. If the player falls in - * this hole the game will exit. - */ - + // adds a hole at the specified start and end x-coordinates void addHole(unsigned int start,unsigned int end); - /** - * Adds a hill to the world, given the peak's coordinates and how wide the - * hill can be. - */ - + // adds a hill that peaks at the given coordinate and is `width` HLINEs wide void addHill(ivec2 peak, unsigned int width); - /** - * Gets the world's width. - */ + // functions to add entities to the world + void addLight(vec2 xy, Color color); - int getTheWidth(void) const; + void addMerchant(float x, float y, bool housed); - void save(void); - void load(void); + void addMob(int type, float x, float y); + void addMob(int type, float x, float y, void (*hey)(Mob *)); + + void addNPC(float x, float y); + + void addObject(std::string in, std::string pickupDialog, float x, float y); + + void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int dur); + void addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int dur, unsigned char flags); + + void addStructure(BUILD_SUB subtype, float x, float y, std::string tex, std::string inside); + + Village *addVillage(std::string name, World *world); }; /* @@ -502,4 +335,9 @@ World *loadWorldFromXMLNoSave(std::string path); World *loadWorldFromPtr(World *ptr); +constexpr IndoorWorld *Indoorp(World *w) +{ + return (IndoorWorld *)w; +} + #endif // WORLD_H diff --git a/src/ui_action.cpp b/src/ui_action.cpp index 66fea19..a7bb36b 100644 --- a/src/ui_action.cpp +++ b/src/ui_action.cpp @@ -1,5 +1,8 @@ #include +#define ACTION_PROLOUGE { actioning = true; } +#define ACTION_EPILOUGE { actioning = false; actionHover = 0; } + extern World *currentWorld; extern Player *player; extern bool inBattle; @@ -19,7 +22,7 @@ static std::vector actionFunc = { nullptr, }; -static bool actionToggle = false; +static bool actionToggle = false, actioning = false; static unsigned int actionHover = 0; static Entity *nearEntity = nullptr, *lastEntity = nullptr; @@ -42,7 +45,6 @@ namespace ui { // draws the action ui void draw(vec2 loc) { - static bool second = false; unsigned int i = 1; float y = loc.y; @@ -60,28 +62,21 @@ namespace ui { } else if (nearEntity != lastEntity) { if (lastEntity != nullptr) lastEntity->canMove = true; - lastEntity = nearEntity;; + lastEntity = nearEntity; } if (make) { + while(actioning); + if (!actionHover) { make = false; return; } - if (!second) - second = true; - else { - second = false; - return; - } - if (actionFunc[actionHover - 1] != nullptr) std::thread(actionFunc[actionHover - 1]).detach(); actionHover = 0; - make = false; - return; } else { nearEntity->canMove = false; ui::drawBox(vec2 {loc.x - HLINES(11), loc.y}, vec2 {loc.x + HLINES(12), loc.y + actionText.size() * HLINES(8)}); @@ -106,12 +101,15 @@ namespace ui { actionHover = 0; ui::setFontColor(255, 255, 255, 255); + make = false; } } } void actionAttack(void) { + ACTION_PROLOUGE; + auto m = currentWorld->getNearInteractable(*player); if (m->type == MOBT) { @@ -129,14 +127,20 @@ void actionAttack(void) } else { ui::dialogBox(player->name, "", false, "%s doesn't appear to be in the mood for fighting...", m->name); } + + ACTION_EPILOUGE; } void actionAction(void) { + ACTION_PROLOUGE; + auto e = currentWorld->getNearInteractable(*player); if (e->type == NPCT) { if (!NPCp(e)->aiFunc.empty()) e->interact(); } + + ACTION_EPILOUGE; } diff --git a/src/world.cpp b/src/world.cpp index df6c556..baf3080 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -1,54 +1,74 @@ +#include + +/* ---------------------------------------------------------------------------- +** Includes section +** --------------------------------------------------------------------------*/ + +// standard library headers #include #include -#include +// local game headers #include +// local library headers #include using namespace tinyxml2; -/** - * Defines how many HLINEs tall a blade of grass can be. - */ +/* ---------------------------------------------------------------------------- +** Macros section +** --------------------------------------------------------------------------*/ -#define GRASS_HEIGHT 4 - -/** - * Defines the height of the floor in an IndoorWorld. - */ +// defines grass height in HLINEs +#define GRASS_HEIGHT 4 +// indoor world constants #define INDOOR_FLOOR_THICKNESS 50 #define INDOOR_FLOOR_HEIGHTT 400 -extern Player *player; // main.cpp? -extern World *currentWorld; // main.cpp -extern World *currentWorldToLeft; // main.cpp -extern World *currentWorldToRight; // main.cpp -extern int commonAIFunc(NPC *); // entities.cpp -extern void commonTriggerFunc(Mob *); // gameplay.cpp -extern void commonPageFunc(Mob *); // gameplay.cpp -extern bool inBattle; +/* ---------------------------------------------------------------------------- +** Variables section +** --------------------------------------------------------------------------*/ +// external variables +extern Player *player; // main.cpp? +extern World *currentWorld; // main.cpp +extern World *currentWorldToLeft; // main.cpp +extern World *currentWorldToRight; // main.cpp +extern bool inBattle; // ui.cpp? extern unsigned int tickCount; // main.cpp +extern std::string xmlFolder; -extern std::string xmlFolder; +// externally referenced in main.cpp +const unsigned int DAY_CYCLE = 12000; int worldShade = 0; -std::string currentXML; - -std::vector inside; // tracks indoor worlds +// externally referenced in entities.cpp +const float PLAYER_SPEED_CONSTANT = 0.150f; +const float GRAVITY_CONSTANT = 0.001f; -std::vector battleNest; // tracks arenas -std::vector battleNestLoc; // keeps arena locations +// ground-generating constants +static const float GROUND_HEIGHT_INITIAL = 80.0f; +static const float GROUND_HEIGHT_MINIMUM = 60.0f; +static const float GROUND_HEIGHT_MAXIMUM = 110.0f; +static const float GROUND_HILLINESS = 10.0f; -/** - * Contains the current weather, used in many other places/files. - */ +// the path of the currently loaded XML file, externally referenced in places +std::string currentXML; +// contains the current world's weather, extern'd in ui.cpp, main.cpp, ..? WorldWeather weather = WorldWeather::Sunny; -const std::string bgPaths[2][9]={ +// keeps track of pathnames of XML file'd worlds the player has left to enter structures +std::vector inside; + +// keeps track of information of worlds the player has left to enter arenas +static std::vector battleNest; +static std::vector battleNestLoc; + +// pathnames of images for world themes +static const std::string bgPaths[][9] = { {"bg.png", // Daytime background "bgn.png", // Nighttime background "bgFarMountain.png", // Furthest layer @@ -68,7 +88,8 @@ const std::string bgPaths[2][9]={ "bgWoodTile.png"} }; -const std::string buildPaths[] = { +// pathnames of structure textures +static const std::string buildPaths[] = { "townhall.png", "house1.png", "house2.png", @@ -79,102 +100,66 @@ const std::string buildPaths[] = { "brazzier.png" }; -/** - * Constants used for layer drawing in World::draw(), releated to transparency. - */ - -const float bgDraw[4][3]={ +// alpha-related values used for world drawing? nobody knows... +static const float bgDraw[4][3]={ { 100, 240, 0.6 }, { 150, 250, 0.4 }, { 200, 255, 0.25 }, { 255, 255, 0.1 } }; -/** - * Sets the desired theme for the world's background. - * - * The images chosen for the background layers are selected depending on the - * world's background type. - */ - -void World:: -setBackground(WorldBGType bgt) -{ - // load textures with a limit check - switch ((bgType = bgt)) { - case WorldBGType::Forest: - bgTex = new Texturec(bgFiles); - break; - - case WorldBGType::WoodHouse: - bgTex = new Texturec(bgFilesIndoors); - break; +/* ---------------------------------------------------------------------------- +** Functions section +** --------------------------------------------------------------------------*/ - default: - UserError("Invalid world background type"); - break; - } -} +// externs +extern int commonAIFunc(NPC *); // gameplay.cpp +extern void commonTriggerFunc(Mob *); // gameplay.cpp +extern void commonPageFunc(Mob *); // gameplay.cpp /** - * Sets the world's style. - * - * The world's style will determine what sprites are used for things like\ - * generic structures. + * Creates a world object. + * Note that all this does is nullify a pointer... */ - -void World:: -setStyle(std::string pre) +World:: +World(void) { - unsigned int i; - - // get folder prefix - std::string prefix = pre.empty() ? "assets/style/classic/" : pre; - - for (i = 0; i < arrAmt(buildPaths); i++) - sTexLoc.push_back(prefix + buildPaths[i]); - - prefix += "bg/"; - - for (i = 0; i < arrAmt(bgPaths[0]); i++) - bgFiles.push_back(prefix + bgPaths[0][i]); - - for (i = 0; i < arrAmt(bgPaths[1]); i++) - bgFilesIndoors.push_back(prefix + bgPaths[1][i]); + bgmObj = nullptr; } /** - * Creates a world object. - * - * Note that all this does is nullify pointers, to prevent as much disaster as - * possible. Functions like setBGM(), setStyle() and generate() should be called - * before the World is actually put into use. + * The world destructor. + * This will free objects used by the world itself, then free the vectors of + * entity-related objects. */ - World:: -World(void) +~World(void) { - bgmObj = NULL; + // SDL2_mixer's object + if (bgmObj != nullptr) + Mix_FreeMusic(bgmObj); + + delete bgTex; + deleteEntities(); } /** * The entity vector destroyer. - * * This function will free all memory used by all entities, and then empty the * vectors they were stored in. */ - void World:: deleteEntities(void) { // free mobs - while(!mob.empty()){ + while (!mob.empty()) { delete mob.back(); mob.pop_back(); } - merchant.clear(); - while(!npc.empty()){ + // free npcs + merchant.clear(); // TODO + while (!npc.empty()) { delete npc.back(); npc.pop_back(); } @@ -187,52 +172,28 @@ deleteEntities(void) // free objects object.clear(); - - // clear entity array - entity.clear(); - // free particles particles.clear(); - // clear light array light.clear(); - // free villages village.clear(); -} - -/** - * The world destructor. - * - * This will free objects used by the world itself, then free the vectors of - * entity-related objects. - */ - -World:: -~World(void) -{ - // sdl2_mixer's object - if(bgmObj) - Mix_FreeMusic(bgmObj); - - delete bgTex; - deleteEntities(); + // clear entity array + entity.clear(); } /** * Generates a world of the specified width. - * - * This will mainly populate the WorldData array, mostly preparing the World + * This will mainly populate the WorldData array, preparing most of the world * object for usage. */ - void World:: generate(unsigned int width) { // iterator for `for` loops std::vector::iterator wditer; - // see below for description + // see below for description/usage float geninc = 0; // check for valid width @@ -240,7 +201,7 @@ generate(unsigned int width) UserError("Invalid world dimensions"); // allocate space for world - worldData = std::vector (width + GROUND_HILLINESS, WorldData { false, {0,0}, 0, 0 }); + worldData = std::vector (width + GROUND_HILLINESS, WorldData { false, {0, 0}, 0, 0 }); lineCount = worldData.size(); // prepare for generation @@ -248,7 +209,7 @@ generate(unsigned int width) wditer = worldData.begin(); // give every GROUND_HILLINESSth entry a groundHeight value - for(unsigned int i = GROUND_HILLINESS; i < worldData.size(); i += GROUND_HILLINESS, wditer += GROUND_HILLINESS) + for (unsigned i = GROUND_HILLINESS; i < worldData.size(); i += GROUND_HILLINESS, wditer += GROUND_HILLINESS) worldData[i].groundHeight = (*wditer).groundHeight + (randGet() % 8 - 4); // create slopes from the points that were just defined, populate the rest of the WorldData structure @@ -257,23 +218,22 @@ generate(unsigned int width) 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 + 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; - // bound checks - if ((*wditer).groundHeight < GROUND_HEIGHT_MINIMUM) - (*wditer).groundHeight = GROUND_HEIGHT_MINIMUM; - else if ((*wditer).groundHeight > GROUND_HEIGHT_MAXIMUM) - (*wditer).groundHeight = GROUND_HEIGHT_MAXIMUM; - if((*wditer).groundHeight <= 0) (*wditer).groundHeight = GROUND_HEIGHT_MINIMUM; - } // define x-coordinate of world's leftmost 'line' @@ -287,108 +247,18 @@ generate(unsigned int width) } } -/** - * Updates all entity and player coordinates with their velocities. - * - * Also handles music fading, although that could probably be placed elsewhere. - */ - -void World:: -update(Player *p, unsigned int delta) -{ - // update player coords - p->loc.y += p->vel.y * delta; - p->loc.x +=(p->vel.x * p->speed) * delta; - - if (p->loc.y > 5000) - UserError("Too high for me m8."); - - // update entity coords - for (auto &e : entity) { - - // dont let structures move? - if (e->type != STRUCTURET && e->canMove) { - e->loc.x += e->vel.x * delta; - e->loc.y += e->vel.y * delta; - - // update boolean directions - if (e->vel.x < 0) - e->left = true; - else if (e->vel.x > 0) - e->left = false; - } else if (e->vel.y < 0) - e->loc.y += e->vel.y * delta; - } - // iterate through particles - particles.erase(std::remove_if(particles.begin(), particles.end(), [&delta](Particles &part){return part.kill(delta);}), particles.end()); - for (auto part = particles.begin(); part != particles.end(); part++) { - if ((*part).canMove) { - (*part).loc.y += (*part).vel.y * delta; - (*part).loc.x += (*part).vel.x * delta; - - for (auto &b : build) { - if (b->bsubtype == FOUNTAIN) { - if ((*part).loc.x >= b->loc.x && (*part).loc.x <= b->loc.x + b->width) { - if ((*part).loc.y <= b->loc.y + b->height * .25) - particles.erase(part); - - } - } - } - } - } - - // handle music fades - if (ui::dialogImportant) { - //Mix_FadeOutMusic(2000); - } else if(!Mix_PlayingMusic()) - Mix_FadeInMusic(bgmObj,-1,2000); -} - -/** - * Set the world's BGM. - * - * This will load a sound file to be played while the player is in this world. - * If no file is found, no music should play. - */ - -void World:: -setBGM(std::string path) -{ - if(!path.empty()) - bgmObj = Mix_LoadMUS((bgm = path).c_str()); -} - -/** - * Toggle play/stop of the background music. - * - * If new music is to be played a crossfade will occur, otherwise... uhm. - */ - -void World:: -bgmPlay(World *prev) const -{ - if (prev) { - if (bgm != prev->bgm) { - // new world, new music - Mix_FadeOutMusic(800); - Mix_PlayMusic(bgmObj, -1); - } - } else { - // first call - Mix_FadeOutMusic(800); - Mix_PlayMusic(bgmObj, -1); - } -} - /** * The world draw function. - * * This function will draw the background layers, entities, and player to the * screen. */ +void World:: +draw(Player *p) +{ + const ivec2 backgroundOffset = ivec2 { + (int)(SCREEN_WIDTH / 2), (int)(SCREEN_HEIGHT / 2) + }; -void World::draw(Player *p){ // iterators int i, iStart, iEnd; @@ -401,134 +271,105 @@ void World::draw(Player *p){ // world width in pixels int width = worldData.size() * HLINE; + // used for alpha values of background textures + int alpha; + // shade value for GLSL - float shadeAmbient = -worldShade / 50.0f + 0.5f; // -0.5f to 1.5f - if (shadeAmbient < 0) - shadeAmbient = 0; - else if (shadeAmbient > 0.9f) - shadeAmbient = 1; + float shadeAmbient = fmax(0, -worldShade / 50.0f + 0.5f); // 0 to 1.5f - /* - * Draw background images. - */ + if (shadeAmbient > 0.9f) + shadeAmbient = 1.0f; + // draw background images. glEnable(GL_TEXTURE_2D); // the sunny wallpaper is faded with the night depending on tickCount - bgTex->bind(0); - int alpha; - switch(weather) { - case WorldWeather::Snowy: - alpha = 150; - break; - case WorldWeather::Rain: - alpha = 0; - break; + switch (weather) { + case WorldWeather::Snowy : alpha = 150; break; + case WorldWeather::Rain : alpha = 0; break; default: alpha = 255 - worldShade * 4; break; } - safeSetColorA(255, 255, 255, alpha); + safeSetColorA(255, 255, 255, alpha); glBegin(GL_QUADS); - glTexCoord2i(0, 0); glVertex2i(offset.x - SCREEN_WIDTH/2-5, offset.y + SCREEN_HEIGHT/2); - glTexCoord2i(1, 0); glVertex2i(offset.x + SCREEN_WIDTH/2+5, offset.y + SCREEN_HEIGHT/2); - glTexCoord2i(1, 1); glVertex2i(offset.x + SCREEN_WIDTH/2+5, offset.y - SCREEN_HEIGHT/2); - glTexCoord2i(0, 1); glVertex2i(offset.x - SCREEN_WIDTH/2-5, offset.y - SCREEN_HEIGHT/2); + glTexCoord2i(0, 0); glVertex2i(offset.x - backgroundOffset.x - 5, offset.y + backgroundOffset.y); + glTexCoord2i(1, 0); glVertex2i(offset.x + backgroundOffset.x + 5, offset.y + backgroundOffset.y); + glTexCoord2i(1, 1); glVertex2i(offset.x + backgroundOffset.x + 5, offset.y - backgroundOffset.y); + glTexCoord2i(0, 1); glVertex2i(offset.x - backgroundOffset.x - 5, offset.y - backgroundOffset.y); glEnd(); bgTex->bindNext(); safeSetColorA(255, 255, 255, !alpha ? 255 : worldShade * 4); - glBegin(GL_QUADS); - glTexCoord2i(0, 0); glVertex2i(offset.x - SCREEN_WIDTH/2-5, offset.y + SCREEN_HEIGHT/2); - glTexCoord2i(1, 0); glVertex2i(offset.x + SCREEN_WIDTH/2+5, offset.y + SCREEN_HEIGHT/2); - glTexCoord2i(1, 1); glVertex2i(offset.x + SCREEN_WIDTH/2+5, offset.y - SCREEN_HEIGHT/2); - glTexCoord2i(0, 1); glVertex2i(offset.x - SCREEN_WIDTH/2-5, offset.y - SCREEN_HEIGHT/2); + glTexCoord2i(0, 0); glVertex2i(offset.x - backgroundOffset.x - 5, offset.y + backgroundOffset.y); + glTexCoord2i(1, 0); glVertex2i(offset.x + backgroundOffset.x + 5, offset.y + backgroundOffset.y); + glTexCoord2i(1, 1); glVertex2i(offset.x + backgroundOffset.x + 5, offset.y - backgroundOffset.y); + glTexCoord2i(0, 1); glVertex2i(offset.x - backgroundOffset.x - 5, offset.y - backgroundOffset.y); glEnd(); glDisable(GL_TEXTURE_2D); // draw the stars if the time deems it appropriate - - //if ((((weather == WorldWeather::Dark) & (tickCount % DAY_CYCLE)) < DAY_CYCLE / 2) || - // (((weather == WorldWeather::Sunny) & (tickCount % DAY_CYCLE)) > DAY_CYCLE * .75)){ if (worldShade > 0) { - safeSetColorA(255, 255, 255, 255 - (getRand() % 30 - 15)); - for (i = 0; i < 100; i++) { - glRectf(star[i].x + offset.x * .9, - star[i].y, - star[i].x + offset.x * .9 + HLINE, - star[i].y + HLINE - ); - } + auto xcoord = offset.x * 0.9f; + for (auto &s : star) + glRectf(s.x + xcoord, s.y, s.x + xcoord + HLINE, s.y + HLINE); } // draw remaining background items - glEnable(GL_TEXTURE_2D); bgTex->bindNext(); safeSetColorA(150 + shadeBackground * 2, 150 + shadeBackground * 2, 150 + shadeBackground * 2, 255); - - glBegin(GL_QUADS); + glBegin(GL_QUADS); { + auto xcoord = width / 2 * -1 + offset.x * 0.85f; for (i = 0; i <= (int)(worldData.size() * HLINE / 1920); i++) { - glTexCoord2i(0, 1); glVertex2i(width / 2 * -1 + (1920 * i) + offset.x * .85, GROUND_HEIGHT_MINIMUM); - glTexCoord2i(1, 1); glVertex2i(width / 2 * -1 + (1920 * (i + 1)) + offset.x * .85, GROUND_HEIGHT_MINIMUM); - glTexCoord2i(1, 0); glVertex2i(width / 2 * -1 + (1920 * (i + 1)) + offset.x * .85, GROUND_HEIGHT_MINIMUM + 1080); - glTexCoord2i(0, 0); glVertex2i(width / 2 * -1 + (1920 * i) + offset.x * .85, GROUND_HEIGHT_MINIMUM + 1080); + glTexCoord2i(0, 1); glVertex2i(1920 * i + xcoord, GROUND_HEIGHT_MINIMUM); + glTexCoord2i(1, 1); glVertex2i(1920 * (i + 1) + xcoord, GROUND_HEIGHT_MINIMUM); + glTexCoord2i(1, 0); glVertex2i(1920 * (i + 1) + xcoord, GROUND_HEIGHT_MINIMUM + 1080); + glTexCoord2i(0, 0); glVertex2i(1920 * i + xcoord, GROUND_HEIGHT_MINIMUM + 1080); } - glEnd(); + } glEnd(); for (i = 0; i < 4; i++) { bgTex->bindNext(); - safeSetColorA(bgDraw[i][0] + shadeBackground * 2, bgDraw[i][0] + shadeBackground * 2, bgDraw[i][0] + shadeBackground * 2, bgDraw[i][1]); - - glBegin(GL_QUADS); - for(int j = worldStart; j <= -worldStart; j += 600){ - glTexCoord2i(0, 1); glVertex2i(j + offset.x * bgDraw[i][2], GROUND_HEIGHT_MINIMUM); - glTexCoord2i(1, 1); glVertex2i((j + 600) + offset.x * bgDraw[i][2], GROUND_HEIGHT_MINIMUM); - glTexCoord2i(1, 0); glVertex2i((j + 600) + offset.x * bgDraw[i][2], GROUND_HEIGHT_MINIMUM + 400); - glTexCoord2i(0, 0); glVertex2i(j + offset.x * bgDraw[i][2], GROUND_HEIGHT_MINIMUM + 400); + safeSetColorA(bgDraw[i][0] + shadeBackground * 2, + bgDraw[i][0] + shadeBackground * 2, + bgDraw[i][0] + shadeBackground * 2, + bgDraw[i][1] + ); + glBegin(GL_QUADS); { + auto xcoord = offset.x * bgDraw[i][2]; + for (int j = worldStart; j <= -worldStart; j += 600) { + glTexCoord2i(0, 1); glVertex2i(j + xcoord, GROUND_HEIGHT_MINIMUM); + glTexCoord2i(1, 1); glVertex2i(j + 600 + xcoord, GROUND_HEIGHT_MINIMUM); + glTexCoord2i(1, 0); glVertex2i(j + 600 + xcoord, GROUND_HEIGHT_MINIMUM + 400); + glTexCoord2i(0, 0); glVertex2i(j + xcoord, GROUND_HEIGHT_MINIMUM + 400); } - glEnd(); + } glEnd(); } glDisable(GL_TEXTURE_2D); - // draw black under backgrounds - + // draw black under backgrounds (y-coordinate) glColor3ub(0, 0, 0); glRectf(worldStart, GROUND_HEIGHT_MINIMUM, -worldStart, 0); - pOffset = (offset.x + p->width / 2 - worldStart) / HLINE; - - /* - * Prepare for world ground drawing. - */ - - // only draw world within player vision - - if ((iStart = pOffset - (SCREEN_WIDTH / 2 / HLINE) - GROUND_HILLINESS) < 0) - iStart = 0; - - if ((iEnd = pOffset + (SCREEN_WIDTH / 2 / HLINE) + GROUND_HILLINESS + HLINE) > (int)worldData.size()) - iEnd = worldData.size(); - else if (iEnd < GROUND_HILLINESS) - iEnd = GROUND_HILLINESS; - // draw particles and buildings - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorIndex); - glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0); glUseProgram(shaderProgram); - std::for_each(particles.begin(), particles.end(), [](Particles part) { if (part.behind) part.draw(); }); + std::for_each(std::begin(particles), std::end(particles), [](Particles &p) { + if (p.behind) + p.draw(); + }); glUseProgram(0); @@ -544,27 +385,26 @@ void World::draw(Player *p){ b->draw(); } - // draw light elements? - - glEnable(GL_TEXTURE_2D); - - glActiveTexture(GL_TEXTURE0); - bgTex->bindNext(); - - for(auto &l : light){ - if(l.belongsTo){ + for (auto &l : light) { + if (l.belongsTo) { l.loc.x = l.following->loc.x + SCREEN_WIDTH/2; l.loc.y = l.following->loc.y; } - if(l.flame){ + + if (l.flame) { l.fireFlicker = .9+((rand()%2)/10.0f); l.fireLoc.x = l.loc.x + (rand()%2-1)*3; l.fireLoc.y = l.loc.y + (rand()%2-1)*3; - }else{ + } else { l.fireFlicker = 1.0f; } } + // draw light elements + glEnable(GL_TEXTURE_2D); + glActiveTexture(GL_TEXTURE0); + bgTex->bindNext(); + std::unique_ptr pointArrayBuf = std::make_unique (2 * (light.size())); auto pointArray = pointArrayBuf.get(); GLfloat flameArray[64]; @@ -598,101 +438,90 @@ void World::draw(Player *p){ glUniform1fv(glGetUniformLocation(shaderProgram,"fireFlicker"), light.size(),flameArray); } - /* - * Draw the dirt. - */ - - glBegin(GL_QUADS); + // get the line that the player is currently standing on + pOffset = (offset.x + p->width / 2 - worldStart) / HLINE; - // faulty - /*glTexCoord2i(0 ,0);glVertex2i(pOffset - (SCREEN_WIDTH / 1.5),0); - glTexCoord2i(64,0);glVertex2i(pOffset + (SCREEN_WIDTH / 1.5),0); - glTexCoord2i(64,1);glVertex2i(pOffset + (SCREEN_WIDTH / 1.5),GROUND_HEIGHT_MINIMUM); - glTexCoord2i(0 ,1);glVertex2i(pOffset - (SCREEN_WIDTH / 1.5),GROUND_HEIGHT_MINIMUM);*/ + // only draw world within player vision + iStart = (int)fmax(pOffset - (SCREEN_WIDTH / 2 / HLINE) - GROUND_HILLINESS, 0); + iEnd = (int)fmin(pOffset + (SCREEN_WIDTH / 2 / HLINE) + GROUND_HILLINESS + HLINE, worldData.size()); + iEnd = (int)fmax(iEnd, GROUND_HILLINESS); + // draw the dirt + glBegin(GL_QUADS); + //std::for_each(std::begin(worldData) + iStart, std::begin(worldData) + iEnd, [&](WorldData wd) { for (i = iStart; i < iEnd; i++) { - if (worldData[i].groundHeight <= 0) { - worldData[i].groundHeight = GROUND_HEIGHT_MINIMUM - 1; + auto wd = worldData[i]; + if (wd.groundHeight <= 0) { + wd.groundHeight = GROUND_HEIGHT_MINIMUM - 1; glColor4ub(0, 0, 0, 255); - } else + } else { safeSetColorA(150, 150, 150, 255); + } - 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, (int)(worldData[i].groundHeight / 64) + worldData[i].groundColor); glVertex2i(worldStart + i * HLINE + HLINE, 0); - glTexCoord2i(0, (int)(worldData[i].groundHeight / 64) + worldData[i].groundColor); glVertex2i(worldStart + i * HLINE , 0); - - if (worldData[i].groundHeight == GROUND_HEIGHT_MINIMUM - 1) - worldData[i].groundHeight = 0; - } + 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); + 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; + }//); glEnd(); glUseProgram(0); glDisable(GL_TEXTURE_2D); - /* - * Draw the grass/the top of the ground. - */ - + // draw the grass glEnable(GL_TEXTURE_2D); - glActiveTexture(GL_TEXTURE0); bgTex->bindNext(); - glUseProgram(shaderProgram); glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0); + safeSetColorA(255, 255, 255, 255); - float cgh[2]; - for (i = iStart; i < iEnd - GROUND_HILLINESS; i++) { - - // load the current line's grass values - if (worldData[i].groundHeight) - memcpy(cgh, worldData[i].grassHeight, 2 * sizeof(float)); - else - memset(cgh, 0 , 2 * sizeof(float)); + for (i = iStart; i < iEnd; i++) { + auto wd = worldData[i]; + auto gh = wd.grassHeight; // flatten the grass if the player is standing on it. - if(!worldData[i].grassUnpressed){ - cgh[0] /= 4; - cgh[1] /= 4; + if (!worldData[i].grassUnpressed) { + gh[0] /= 4; + gh[1] /= 4; } // actually draw the grass. - - safeSetColorA(255, 255, 255, 255); - - glBegin(GL_QUADS); - glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE , worldData[i].groundHeight + cgh[0]); - glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE / 2, worldData[i].groundHeight + cgh[0]); - glTexCoord2i(1, 1); glVertex2i(worldStart + i * HLINE + HLINE / 2, worldData[i].groundHeight - GRASS_HEIGHT); - glTexCoord2i(0, 1); glVertex2i(worldStart + i * HLINE , worldData[i].groundHeight - GRASS_HEIGHT); - glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE + HLINE / 2, worldData[i].groundHeight + cgh[1]); - glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE , worldData[i].groundHeight + cgh[1]); - glTexCoord2i(1, 1); glVertex2i(worldStart + i * HLINE + HLINE , worldData[i].groundHeight - GRASS_HEIGHT); - glTexCoord2i(0, 1); glVertex2i(worldStart + i * HLINE + HLINE / 2, worldData[i].groundHeight - GRASS_HEIGHT); - glEnd(); + if (wd.groundHeight) { + glBegin(GL_QUADS); + glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE , wd.groundHeight + gh[0]); + glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE / 2, wd.groundHeight + gh[0]); + glTexCoord2i(1, 1); glVertex2i(worldStart + i * HLINE + HLINE / 2, wd.groundHeight - GRASS_HEIGHT); + glTexCoord2i(0, 1); glVertex2i(worldStart + i * HLINE , wd.groundHeight - GRASS_HEIGHT); + glTexCoord2i(0, 0); glVertex2i(worldStart + i * HLINE + HLINE / 2, wd.groundHeight + gh[1]); + glTexCoord2i(1, 0); glVertex2i(worldStart + i * HLINE + HLINE , wd.groundHeight + gh[1]); + glTexCoord2i(1, 1); glVertex2i(worldStart + i * HLINE + HLINE , wd.groundHeight - GRASS_HEIGHT); + glTexCoord2i(0, 1); glVertex2i(worldStart + i * HLINE + HLINE / 2, wd.groundHeight - GRASS_HEIGHT); + glEnd(); + } } glUseProgram(0); glDisable(GL_TEXTURE_2D); - /* - * Draw remaining entities. - */ - - + // draw particles glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, colorIndex); - glUniform1i(glGetUniformLocation(shaderProgram, "sampler"), 0); glUseProgram(shaderProgram); - std::for_each(particles.begin(), particles.end(), [](Particles part) { if (!part.behind) part.draw(); }); + for (auto &p : particles) { + if (!p.behind) + p.draw(); + } glUseProgram(0); + // draw remaining entities for (auto &n : npc) { if (n->type != MERCHT) n->draw(); @@ -704,108 +533,76 @@ void World::draw(Player *p){ for (auto &o : object) o.draw(); - /* - * Handle grass-squishing. - */ - - // calculate the line that the player is on - int ph = (p->loc.x + p->width / 2 - worldStart) / HLINE; - // flatten grass under the player if the player is on the ground if (p->ground) { - for (i = 0; i < (int)(worldData.size() - GROUND_HILLINESS); i++) - worldData[i].grassUnpressed = !(i < ph + 6 && i > ph - 6); + for (i = 0; i < (int)worldData.size(); i++) + worldData[i].grassUnpressed = !(i < pOffset + 6 && i > pOffset - 6); } else { - for (i = 0; i < (int)(worldData.size() - GROUND_HILLINESS); i++) + for (i = 0; i < (int)worldData.size(); i++) worldData[i].grassUnpressed = true; } - /* - * Draw the player. - */ - + // draw the player p->draw(); } /** * Handles physics and such for a single entity. - * * This function is kept private, as World::detect() should be used instead to * handle stuffs for all entities at once. */ - void World:: singleDetect(Entity *e) { std::string killed; - unsigned int i,j; + unsigned int i; int l; - /* - * Kill any dead entities. - */ - + // kill dead entities if (e->alive && e->health <= 0) { + // die e->alive = false; e->health = 0; + + // delete the entity for (i = 0; i < entity.size(); i++) { if (entity[i] == e){ switch (e->type) { case STRUCTURET: killed = "structure"; - for(j=0;jname << "." << std::endl; exit(0); } - // handle only living entities + // collision / gravity: handle only living entities if (e->alive) { - // forced movement gravity + // forced movement gravity (sword hits) if (e->forcedMove) { if (e->vel.x > .0005 || e->vel.x < -.0005) e->vel.x *= .6; @@ -813,25 +610,21 @@ singleDetect(Entity *e) e->forcedMove = false; } - if (e->type == MOBT && Mobp(e)->subtype == MS_TRIGGER) + if (e->subtype == MS_TRIGGER) return; // calculate the line that this entity is currently standing on - l = (e->loc.x + e->width / 2 - worldStart) / HLINE; - if (l < 0) - l = 0; - i = l; - if (i > lineCount - 1) - i = lineCount - 1; + l = (int)fmax((e->loc.x + e->width / 2 - worldStart) / HLINE, 0); + l = (int)fmin(l, lineCount - 1); // if the entity is under the world/line, pop it back to the surface - if (e->loc.y < worldData[i].groundHeight) { + if (e->loc.y < worldData[l].groundHeight) { int dir = e->vel.x < 0 ? -1 : 1; - if (i + (dir * 2) < worldData.size() && worldData[i + (dir * 2)].groundHeight - 30 > worldData[i + dir].groundHeight) { - e->loc.x -= (PLAYER_SPEED_CONSTANT + 2.7) * e->speed * 2 * dir; + if (l + (dir * 2) < (int)worldData.size() && worldData[l + (dir * 2)].groundHeight - 30 > worldData[l + dir].groundHeight) { + e->loc.x -= (PLAYER_SPEED_CONSTANT + 2.7f) * e->speed * 2 * dir; e->vel.x = 0; } else { - e->loc.y = worldData[i].groundHeight - .001 * deltaTime; + e->loc.y = worldData[l].groundHeight - 0.001f * deltaTime; e->ground = true; e->vel.y = 0; } @@ -840,26 +633,23 @@ singleDetect(Entity *e) // handle gravity if the entity is above the line else { - if (e->type == STRUCTURET) { - e->loc.y = worldData[i].groundHeight; + e->loc.y = worldData[l].groundHeight; e->vel.y = 0; e->ground = true; return; - } else if (e->vel.y > -2) + } else if (e->vel.y > -2) { e->vel.y -= GRAVITY_CONSTANT * deltaTime; + } } - /* - * Insure that the entity doesn't fall off either edge of the world. - */ - - if(e->loc.x < worldStart){ // Left bound - e->vel.x=0; - e->loc.x=(float)worldStart + HLINE / 2; - }else if(e->loc.x + e->width + HLINE > worldStart + worldStart * -2){ // Right bound - e->vel.x=0; - e->loc.x=worldStart + worldStart * -2 - e->width - HLINE; + // insure that the entity doesn't fall off either edge of the world. + if (e->loc.x < worldStart) { + e->vel.x = 0; + e->loc.x = worldStart + HLINE / 2; + } else if (e->loc.x + e->width + HLINE > worldStart + worldStart * -2) { + e->vel.x = 0; + e->loc.x = worldStart + worldStart * -2 - e->width - HLINE; } } } @@ -871,7 +661,6 @@ singleDetect(Entity *e) * currently in a vector of this world. Particles and village entrance/exiting * are also handled here. */ - void World:: detect(Player *p) { @@ -886,16 +675,9 @@ detect(Player *p) // handle particles for (auto &part : particles) { - // get particle's current world line - l = (part.loc.x + part.width / 2 - worldStart) / HLINE; - - if (l < 0) - l = 0; - - if (l > (int)(lineCount - 1)) - l = lineCount - 1; - + l = (int)fmax((part.loc.x + part.width / 2 - worldStart) / HLINE, 0); + l = (int)fmin(lineCount - 1, l); part.update(GRAVITY_CONSTANT, worldData[l].groundHeight); } @@ -913,11 +695,9 @@ detect(Player *p) { 0, 0, 255 }, // RGB color 2500 // duration (ms) ); - particles.back().fountain = true; } break; - case FIRE_PIT: for(unsigned int r = (randGet() % 20) + 11; r--;) { addParticle(randGet() % (int)(b->width / 2) + b->loc.x + b->width / 4, // x @@ -929,12 +709,10 @@ detect(Player *p) { 255, 0, 0 }, // RGB color 400 // duration (ms) ); - particles.back().gravity = false; particles.back().behind = true; } break; - default: break; } @@ -942,175 +720,352 @@ detect(Player *p) // draws the village welcome message if the player enters the village bounds for (auto &v : village) { - if (p->loc.x > v.start.x && p->loc.x < v.end.x) { - if (!v.in) { - ui::passiveImportantText(5000, "Welcome to %s", v.name.c_str()); - v.in = true; - } - } else + if (p->loc.x > v.start.x && p->loc.x < v.end.x && !v.in) { + ui::passiveImportantText(5000, "Welcome to %s", v.name.c_str()); + v.in = true; + } else { v.in = false; + } } } -void World::addStructure(BUILD_SUB sub, float x,float y, std::string tex, std::string inside){ - 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) +/** + * Updates all entity and player coordinates with their velocities. + * Also handles music fading, although that could probably be placed elsewhere. + */ +void World:: +update(Player *p, unsigned int delta) { - village.emplace_back(name.c_str(), world); - return &village.back(); -} - -void World::addMob(int t,float x,float y){ - mob.push_back(new Mob(t)); - mob.back()->spawn(x,y); - - entity.push_back(mob.back()); -} - -void World::addMob(int t,float x,float y,void (*hey)(Mob *)){ - mob.push_back(new Mob(t)); - mob.back()->spawn(x,y); - mob.back()->hey = hey; - - entity.push_back(mob.back()); -} - -void World::addNPC(float x,float y){ - npc.push_back(new NPC()); - npc.back()->spawn(x,y); - - entity.push_back(npc.back()); -} - -void World::addMerchant(float x, float y, bool housed){ - merchant.push_back(new Merchant()); - merchant.back()->spawn(x,y); - - if (housed) - merchant.back()->inside = build.back(); + // update player coords + p->loc.y += p->vel.y * delta; + p->loc.x +=(p->vel.x * p->speed) * delta; - npc.push_back(merchant.back()); - entity.push_back(npc.back()); -} + // handle high-ness + if (p->loc.y > 5000) + UserError("Too high for me m8."); -void World::addObject(std::string in, std::string p, float x, float y){ - object.emplace_back(in, p); - object.back().spawn(x, y); + // update entity coords + for (auto &e : entity) { + // dont let structures move? + if (e->type != STRUCTURET && e->canMove) { + e->loc.x += e->vel.x * delta; + e->loc.y += e->vel.y * delta; - entity.push_back(&object.back()); -} + // update boolean directions + e->left = e->vel.x ? (e->vel.x < 0) : e->left; + } else if (e->vel.y < 0) { + e->loc.y += e->vel.y * delta; + } + } + // iterate through particles + particles.erase(std::remove_if(particles.begin(), particles.end(), [&delta](Particles &part){ + return part.kill(delta); + }), + particles.end()); -void World:: -addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d) -{ - particles.emplace_back(x, y, w, h, vx, vy, color, d); - particles.back().canMove = true; -} + for (auto part = particles.begin(); part != particles.end(); part++) { + auto pa = *part; + + if (pa.canMove) { + (*part).loc.y += pa.vel.y * delta; + (*part).loc.x += pa.vel.x * delta; + + if (std::any_of(std::begin(build), std::end(build), [pa](Structures *s) { + return (s->bsubtype == FOUNTAIN) && + (pa.loc.x >= s->loc.x) && (pa.loc.x <= s->loc.x + s->width) && + (pa.loc.y <= s->loc.y + s->height * 0.25f); + })) { + particles.erase(part); + } + } + } -void World:: -addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags) -{ - particles.emplace_back(x, y, w, h, vx, vy, color, d); - particles.back().canMove = true; - particles.back().gravity = flags & (1 << 0); - particles.back().bounce = flags & (1 << 1); + // handle music fades + if (!Mix_PlayingMusic()) + Mix_FadeInMusic(bgmObj, -1, 2000); } -void World:: -addLight(vec2 loc, Color color) +/** + * Get's the world's width in pixels. + */ +int World:: +getTheWidth(void) const { - if (light.size() < 64) - light.push_back(Light(loc, color, 1)); + return (worldStart * -2); } +/** + * Get a pointer to the most recently created light. + * Meant to be non-constant. + */ Light *World:: getLastLight(void) { return &light.back(); } +/** + * Get a pointer to the most recently created mob. + * Meant to be non-constant. + */ Mob *World:: getLastMob(void) { return mob.back(); } +/** + * Get the interactable entity that is closest to the entity provided. + */ Entity *World:: -getNearInteractable(Entity e) +getNearInteractable(Entity &e) { - for (auto &n : entity) { - if (n->type == MOBT || n->type == NPCT || n->type == MERCHT) { - if (e.isNear(*n) && (e.left ? n->loc.x < e.loc.x : n->loc.x > e.loc.x)) - return n; - } - } + auto n = std::find_if(std::begin(entity), std::end(entity), [&](Entity *&a) { + return ((a->type == MOBT) || (a->type == NPCT) || a->type == MERCHT) && + e.isNear(*a) && (e.left ? (a->loc.x < e.loc.x) : (a->loc.x > e.loc.x)); + }); - return nullptr; + return n == std::end(entity) ? nullptr : *n; } +/** + * Get the file path for the `index`th building. + */ std::string World:: getSTextureLocation(unsigned int index) const { - if (index > sTexLoc.size()) - return ""; - - return sTexLoc[ index ]; + return index < sTexLoc.size() ? sTexLoc[ index ] : ""; } +/** + * Get the coordinates of the `index`th building, with -1 meaning the last building. + */ vec2 World:: getStructurePos(int index) { if (index < 0) return build.back()->loc; else if ((unsigned)index >= build.size()) - return vec2{0, 0}; + return vec2 {0, 0}; return build[index]->loc; } +/** + * Saves world data to a file. + */ +void World::save(void){ + std::string data; + std::string save = currentXML + ".dat"; + std::ofstream out (save, std::ios::out | std::ios::binary); + + std::cout << "Saving to " << save << " ..." << '\n'; + + // save npcs + for (auto &n : npc) { + data.append(std::to_string(n->dialogIndex) + "\n"); + data.append(std::to_string((int)n->loc.x) + "\n"); + data.append(std::to_string((int)n->loc.y) + "\n"); + } + + // save structures + for (auto &b : build) { + data.append(std::to_string((int)b->loc.x) + "\n"); + data.append(std::to_string((int)b->loc.y) + "\n"); + } + + // save mobs + 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"); + } + + // wrap up + data.append("dOnE\0"); + out.write(data.data(), data.size()); + out.close(); +} + +void World::load(void){ + std::string save, data, line; + const char *filedata; + + save = currentXML + ".dat"; + filedata = readFile(save.c_str()); + data = filedata; + std::istringstream iss (data); + + 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(); + + std::getline(iss,line); + if(line == "dOnE")return; + n->loc.x = std::stoi(line); + std::getline(iss,line); + if(line == "dOnE")return; + n->loc.y = std::stoi(line); + } + + for(auto &b : build){ + std::getline(iss,line); + if(line == "dOnE")return; + b->loc.x = std::stoi(line); + std::getline(iss,line); + if(line == "dOnE")return; + b->loc.y = std::stoi(line); + } + + for(auto &m : mob){ + std::getline(iss,line); + if(line == "dOnE")return; + m->loc.x = std::stoi(line); + std::getline(iss,line); + if(line == "dOnE")return; + m->loc.y = std::stoi(line); + std::getline(iss,line); + if(line == "dOnE")return; + m->alive = std::stoi(line); + } + + while(std::getline(iss,line)){ + if(line == "dOnE") + break; + } + + delete[] filedata; +} + +/** + * Toggle play/stop of the background music. + * If new music is to be played a crossfade will occur, otherwise... uhm. + */ +void World:: +bgmPlay(World *prev) const +{ + if (prev) { + if (bgm != prev->bgm) { + // new world, new music + Mix_FadeOutMusic(800); + Mix_PlayMusic(bgmObj, -1); + } + // sucks to be here + } else { + // first call + Mix_FadeOutMusic(800); + Mix_PlayMusic(bgmObj, -1); + } +} + +/** + * Set the world's BGM. + * This will load a sound file to be played while the player is in this world. + * If no file is found, no music should play. + */ +void World:: +setBGM(std::string path) +{ + if (!path.empty()) + bgmObj = Mix_LoadMUS((bgm = path).c_str()); +} + +/** + * Sets the desired theme for the world's background. + * The images chosen for the background layers are selected depending on the + * world's background type. + */ +void World:: +setBackground(WorldBGType bgt) +{ + // load textures with a limit check + switch ((bgType = bgt)) { + case WorldBGType::Forest: + bgTex = new Texturec(bgFiles); + break; + case WorldBGType::WoodHouse: + bgTex = new Texturec(bgFilesIndoors); + break; + default: + UserError("Invalid world background type"); + break; + } +} + +/** + * Sets the world's style. + * The world's style will determine what sprites are used for things like\ + * generic structures. + */ +void World:: +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){ + sTexLoc.push_back(prefix + s); + }); + + prefix += "bg/"; + + for_each(std::begin(bgPaths[0]), std::end(bgPaths[0]), [this, prefix](std::string s){ + bgFiles.push_back(prefix + s); + }); + for_each(std::begin(bgPaths[1]), std::end(bgPaths[1]), [this, prefix](std::string s){ + bgFilesIndoors.push_back(prefix + s); + }); +} + +/** + * Pretty self-explanatory. + */ std::string World:: setToLeft(std::string file) { return (toLeft = file); } +/** + * Pretty self-explanatory. + */ std::string World:: setToRight(std::string file) { return (toRight = file); } +/** + * Pretty self-explanatory. + */ std::string World:: getToLeft(void) const { return toLeft; } +/** + * Pretty self-explanatory. + */ std::string World:: getToRight(void) const { return toRight; } +/** + * Attempts to go to the left world, returning either that world or itself. + */ World *World:: goWorldLeft(Player *p) { World *tmp; // check if player is at world edge - if(!toLeft.empty() && p->loc.x < worldStart + HLINE * 15.0f) { - + if (!toLeft.empty() && p->loc.x < worldStart + HLINE * 15.0f) { // load world (`toLeft` conditional confirms existance) tmp = loadWorldFromPtr(currentWorldToLeft); @@ -1124,24 +1079,9 @@ goWorldLeft(Player *p) return this; } -bool World:: -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; - - currentWorldToLeft->npc.back()->loc.x = 0; - currentWorldToLeft->npc.back()->loc.y = GROUND_HEIGHT_MAXIMUM; - - return true; - } - - return false; -} - +/** + * Attempts to go to the right world, returning either that world or itself. + */ World *World:: goWorldRight(Player *p) { @@ -1159,6 +1099,29 @@ goWorldRight(Player *p) return this; } +/** + * Acts like goWorldLeft(), but takes an NPC; returning true on success. + */ +bool World:: +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; + + currentWorldToLeft->npc.back()->loc.x = 0; + currentWorldToLeft->npc.back()->loc.y = GROUND_HEIGHT_MAXIMUM; + + return true; + } + + return false; +} + +/** + * Attempts to enter a building that the player is standing in front of. + */ World *World:: goInsideStructure(Player *p) { @@ -1167,21 +1130,20 @@ goInsideStructure(Player *p) 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 (p->loc.x > b->loc.x && p->loc.x + p->width < b->loc.x + b->width) { if (b->inside.empty()) return this; + // +size cuts folder prefix inside.push_back(currentXML.c_str() + xmlFolder.size()); - tmp = loadWorldFromXML(b->inside); + // 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); + glClearColor(0, 0, 0, 1); return tmp; } @@ -1195,12 +1157,10 @@ goInsideStructure(Player *p) ui::toggleBlackFast(); ui::waitForCover(); - p->loc.x = b->loc.x + (b->width / 2); - ui::toggleBlackFast(); - glClearColor(1,1,1,1); + glClearColor(1, 1, 1, 1); return tmp; } @@ -1210,6 +1170,88 @@ goInsideStructure(Player *p) return this; } +void World::addStructure(BUILD_SUB sub, float x,float y, std::string tex, std::string inside){ + 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); + return &village.back(); +} + +void World::addMob(int t,float x,float y){ + mob.push_back(new Mob(t)); + mob.back()->spawn(x,y); + + entity.push_back(mob.back()); +} + +void World::addMob(int t,float x,float y,void (*hey)(Mob *)){ + mob.push_back(new Mob(t)); + mob.back()->spawn(x,y); + mob.back()->hey = hey; + + entity.push_back(mob.back()); +} + +void World::addNPC(float x,float y){ + npc.push_back(new NPC()); + npc.back()->spawn(x,y); + + entity.push_back(npc.back()); +} + +void World::addMerchant(float x, float y, bool housed){ + merchant.push_back(new Merchant()); + merchant.back()->spawn(x,y); + + if (housed) + merchant.back()->inside = build.back(); + + npc.push_back(merchant.back()); + entity.push_back(npc.back()); +} + +void World::addObject(std::string in, std::string p, float x, float y){ + object.emplace_back(in, p); + object.back().spawn(x, y); + + entity.push_back(&object.back()); +} + +void World:: +addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d) +{ + particles.emplace_back(x, y, w, h, vx, vy, color, d); + particles.back().canMove = true; +} + +void World:: +addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags) +{ + particles.emplace_back(x, y, w, h, vx, vy, color, d); + particles.back().canMove = true; + particles.back().gravity = flags & (1 << 0); + particles.back().bounce = flags & (1 << 1); +} + +void World:: +addLight(vec2 loc, Color color) +{ + if (light.size() < 64) + light.push_back(Light(loc, color, 1)); +} + void World:: addHole(unsigned int start, unsigned int end) { @@ -1244,95 +1286,6 @@ addHill(const ivec2 peak, const unsigned int width) } } -int World:: -getTheWidth(void) const -{ - return worldStart * -2; -} - -void World::save(void){ - std::string data; - - std::string save = (std::string)currentXML + ".dat"; - std::ofstream out (save,std::ios::out | std::ios::binary); - - std::cout<<"Saving to "<dialogIndex) + "\n"); - data.append(std::to_string((int)n->loc.x) + "\n"); - data.append(std::to_string((int)n->loc.y) + "\n"); - } - - for(auto &b : build){ - data.append(std::to_string((int)b->loc.x) + "\n"); - data.append(std::to_string((int)b->loc.y) + "\n"); - } - - 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("dOnE\0"); - out.write(data.c_str(),data.size()); - out.close(); -} - -void World::load(void){ - std::string save,data,line; - const char *filedata; - - save = std::string(currentXML + ".dat"); - filedata = readFile(save.c_str()); - data = filedata; - std::istringstream iss (data); - - 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(); - - std::getline(iss,line); - if(line == "dOnE")return; - n->loc.x = std::stoi(line); - std::getline(iss,line); - if(line == "dOnE")return; - n->loc.y = std::stoi(line); - } - - for(auto &b : build){ - std::getline(iss,line); - if(line == "dOnE")return; - b->loc.x = std::stoi(line); - std::getline(iss,line); - if(line == "dOnE")return; - b->loc.y = std::stoi(line); - } - - for(auto &m : mob){ - std::getline(iss,line); - if(line == "dOnE")return; - m->loc.x = std::stoi(line); - std::getline(iss,line); - if(line == "dOnE")return; - m->loc.y = std::stoi(line); - std::getline(iss,line); - if(line == "dOnE")return; - m->alive = std::stoi(line); - } - - while(std::getline(iss,line)){ - if(line == "dOnE") - break; - } - - delete[] filedata; -} - float getIndoorWorldFloorHeight(void) { return INDOOR_FLOOR_HEIGHTT + INDOOR_FLOOR_THICKNESS; -- cgit v1.2.3