diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/entities.cpp | 16 | ||||
-rw-r--r-- | src/inventory.cpp | 73 | ||||
-rw-r--r-- | src/ui.cpp | 131 | ||||
-rw-r--r-- | src/ui_action.cpp | 146 | ||||
-rw-r--r-- | src/world.cpp | 1296 |
5 files changed, 893 insertions, 769 deletions
diff --git a/src/entities.cpp b/src/entities.cpp index c52ff4e..96fe0e6 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 } 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 224ee12..c6b28d9 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -440,32 +440,22 @@ 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???"<<std::endl; - if (!mouseSel) { + // setup? mouseStart.x = ui::mouse.x - offset.x; - std::cout << "Setting highlight" << std::endl; highlight = sel; - std::cout << "Setting thing" << std::endl; thing = sel; - std::cout << "Setting mouseSel" << std::endl; - mouseSel=true; - std::cout << "Done" << std::endl; - }else{ - std::cout << "Is mousex greater than the start" << std::endl; - if ((ui::mouse.x - offset.x) >= mouseStart.x) { - std::cout << "Thing" << std::endl; + mouseSel = true; + } else { + if((ui::mouse.x - offset.x) >= mouseStart.x){ 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)) { + if(highlight>numSlot-1)highlight=numSlot-1; + if(SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)){ sel = highlight; mouseSel=false; invHover=false; @@ -474,9 +464,11 @@ 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 (SDL_GetMouseState(NULL, NULL) & SDL_BUTTON(SDL_BUTTON_LEFT)) { + 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; invHover=false; @@ -484,17 +476,15 @@ 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)); @@ -503,18 +493,16 @@ void Inventory::draw(void) { glVertex2i(r.end.x-(itemWide/2), r.end.y+(itemWide/2)); glEnd(); - std::cout << "Draw items," << a << std::endl; - if (!items.empty() && a < numSlot && items[a].count) { - std::cout << "drawing" << 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, 1.0f); + glColor4f(1.0f, 1.0f, 1.0f, a == highlight ? 0.8f : 0.2f); glBegin(GL_QUADS); - if (itemMap[items[a].id]->height > itemMap[items[a].id]->width) { - 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)); - glTexCoord2i(0,0);glVertex2i(r.end.x-((itemWide/2)*((float)itemMap[items[a].id]->width/(float)itemMap[items[a].id]->height)), r.end.y+(itemWide/2)); + if(itemMap[items[a].id]->height > itemMap[items[a].id]->width){ + 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)); + glTexCoord2i(0,0);glVertex2i(r.end.x-((itemWide/2)*((float)itemMap[items[a].id]->width/(float)itemMap[items[a].id]->height)),r.end.y+(itemWide/2)); }else{ glTexCoord2i(0,1);glVertex2i(r.end.x-(itemWide/2),r.end.y-(itemWide/2)*((float)itemMap[items[a].id]->height/(float)itemMap[items[a].id]->width)); glTexCoord2i(1,1);glVertex2i(r.end.x+(itemWide/2),r.end.y-(itemWide/2)*((float)itemMap[items[a].id]->height/(float)itemMap[items[a].id]->width)); @@ -523,15 +511,16 @@ void Inventory::draw(void) { } glEnd(); glDisable(GL_TEXTURE_2D); - - if (highlight == a) { - std::cout << "Shitting" << std::endl; - std::cout << itemMap[items[a].id]->name << std::endl; - ui::putStringCentered(player->loc.x+player->width/2, player->loc.y + range*.75,itemMap[items[a].id]->name); - } } 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() + ); + } } if (!items.empty() && items.size() > sel && items[sel].count) itemDraw(player,items[sel].id); @@ -615,11 +615,30 @@ namespace ui { pageTexReady = true; } - void draw(void) { + 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) { + 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) { 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; @@ -1067,31 +1065,42 @@ EXIT: break; case SDLK_LSHIFT: if (debug) { - Mix_PlayChannel(1,sanic,-1); + 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; + 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; } + + // handle world switches? if (tmp != currentWorld) { - std::swap(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..a7bb36b --- /dev/null +++ b/src/ui_action.cpp @@ -0,0 +1,146 @@ +#include <ui_action.hpp> + +#define ACTION_PROLOUGE { actioning = true; } +#define ACTION_EPILOUGE { actioning = false; actionHover = 0; } + +extern World *currentWorld; +extern Player *player; +extern bool inBattle; + +static std::vector<std::pair<std::string, vec3>> 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<void (*)(void)> actionFunc = { + actionAttack, + actionAction, + nullptr, +}; + +static bool actionToggle = false, actioning = 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) { + 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) { + while(actioning); + + if (!actionHover) { + make = false; + return; + } + + if (actionFunc[actionHover - 1] != nullptr) + std::thread(actionFunc[actionHover - 1]).detach(); + + actionHover = 0; + } 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); + make = false; + } + } +} + +void actionAttack(void) +{ + ACTION_PROLOUGE; + + 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); + } + + 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 be8ba94..5893bfb 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -1,54 +1,74 @@ +#include <world.hpp> + +/* ---------------------------------------------------------------------------- +** Includes section +** --------------------------------------------------------------------------*/ + +// standard library headers #include <algorithm> #include <sstream> -#include <world.hpp> +// local game headers #include <ui.hpp> +// local library headers #include <tinyxml2.h> 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<std::string> inside; // tracks indoor worlds +// externally referenced in entities.cpp +const float PLAYER_SPEED_CONSTANT = 0.150f; +const float GRAVITY_CONSTANT = 0.001f; -std::vector<World *> battleNest; // tracks arenas -std::vector<vec2> 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<std::string> inside; + +// keeps track of information of worlds the player has left to enter arenas +static std::vector<World *> battleNest; +static std::vector<vec2> 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,99 +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; +/* ---------------------------------------------------------------------------- +** Functions section +** --------------------------------------------------------------------------*/ - case WorldBGType::WoodHouse: - bgTex = new Texturec(bgFilesIndoors); - break; - - 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 - mob.clear(); + 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(); } @@ -184,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<WorldData>::iterator wditer; - // see below for description + // see below for description/usage float geninc = 0; // check for valid width @@ -237,7 +201,7 @@ generate(unsigned int width) UserError("Invalid world dimensions"); // allocate space for world - worldData = std::vector<WorldData> (width + GROUND_HILLINESS, WorldData { false, {0,0}, 0, 0 }); + worldData = std::vector<WorldData> (width + GROUND_HILLINESS, WorldData { false, {0, 0}, 0, 0 }); lineCount = worldData.size(); // prepare for generation @@ -245,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 @@ -254,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) + if((*wditer).groundHeight <= 0) (*wditer).groundHeight = GROUND_HEIGHT_MINIMUM; - } // define x-coordinate of world's leftmost 'line' @@ -285,107 +248,17 @@ 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; @@ -398,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); @@ -541,14 +385,7 @@ void World::draw(Player *p) { b->draw(); } - // draw light elements? - - glEnable(GL_TEXTURE_2D); - - glActiveTexture(GL_TEXTURE0); - bgTex->bindNext(); - - for(auto &l : light) { + for (auto &l : light) { if (l.belongsTo) { l.loc.x = l.following->loc.x + SCREEN_WIDTH/2; l.loc.y = l.following->loc.y; @@ -557,11 +394,16 @@ void World::draw(Player *p) { 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<GLfloat[]> pointArrayBuf = std::make_unique<GLfloat[]> (2 * (light.size())); auto pointArray = pointArrayBuf.get(); GLfloat flameArray[64]; @@ -595,214 +437,171 @@ 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; + 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(); } for (auto &m : mob) - m.draw(); + m->draw(); 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;j<build.size();j++) { - if (build[j]==e) { - delete build[j]; - build.erase(build.begin()+j); - break; - } - } - break; - case NPCT: + build.erase(std::find(std::begin(build), std::end(build), e)); + break; + case NPCT: killed = "NPC"; - for(j=0;j<npc.size();j++) { - if (npc[j]==e) { - delete npc[j]; - npc.erase(npc.begin()+j); - break; - } - } + npc.erase(std::find(std::begin(npc), std::end(npc), e)); break; case MOBT: killed = "mob"; - /*for(j=0;j<mob.size();j++) { - if (mob[j]==e) { - delete mob[j]; - mob.erase(mob.begin()+j); - break; - } - }*/ + // TODO break; case OBJECTT: killed = "object"; - for (auto o = std::begin(object); o != std::end(object); o++) { - if (&(*o) == e) { - object.erase(o); - break; - } - } + object.erase(std::find(std::begin(object), std::end(object), *Objectp(e))); break; default: break; } + std::cout << "Killed a " << killed << "..." << std::endl; - entity.erase(entity.begin()+i); + entity.erase(entity.begin() + i); return; } } + + // exit on player death std::cout << "RIP " << e->name << "." << 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; @@ -810,25 +609,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; } @@ -837,26 +632,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; } } } @@ -868,7 +660,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) { @@ -883,16 +674,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); } @@ -910,11 +694,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 @@ -926,12 +708,10 @@ detect(Player *p) { 255, 0, 0 }, // RGB color 400 // duration (ms) ); - particles.back().gravity = false; particles.back().behind = true; } break; - default: break; } @@ -939,140 +719,345 @@ 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; +/** + * 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; - build.back()->spawn(sub,x,y); + // handle high-ness + if (p->loc.y > 5000) + UserError("Too high for me m8."); - build.back()->inside = inside; + // 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(build.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()); + + 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); + } + } + } + + // handle music fades + if (!Mix_PlayingMusic()) + Mix_FadeInMusic(bgmObj, -1, 2000); } -Village *World:: -addVillage(std::string name, World *world) +/** + * Get's the world's width in pixels. + */ +int World:: +getTheWidth(void) const { - village.emplace_back(name.c_str(), world); - return &village.back(); + return (worldStart * -2); } -void World::addMob(int t,float x,float y) { - mob.emplace_back(t); - mob.back().spawn(x,y); +/** + * Get a pointer to the most recently created light. + * Meant to be non-constant. + */ +Light *World:: +getLastLight(void) +{ + return &light.back(); +} - entity.push_back(&mob.back()); +/** + * Get a pointer to the most recently created mob. + * Meant to be non-constant. + */ +Mob *World:: +getLastMob(void) +{ + return 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; +/** + * Get the interactable entity that is closest to the entity provided. + */ +Entity *World:: +getNearInteractable(Entity &e) +{ + 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)); + }); - entity.push_back(&mob.back()); + return n == std::end(entity) ? nullptr : *n; } -void World::addNPC(float x,float y) { - npc.push_back(new NPC()); - npc.back()->spawn(x,y); +/** + * Get the file path for the `index`th building. + */ +std::string World:: +getSTextureLocation(unsigned int index) const +{ + return index < sTexLoc.size() ? sTexLoc[ index ] : ""; +} - entity.push_back(npc.back()); +/** + * 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 build[index]->loc; } -void World::addMerchant(float x, float y) { - merchant.push_back(new Merchant()); - merchant.back()->spawn(x,y); +/** + * 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); - npc.push_back(merchant.back()); - entity.push_back(npc.back()); + 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::addObject(std::string in, std::string p, float x, float y) { - object.emplace_back(in, p); - object.back().spawn(x, y); +void World::load(void){ + std::string save, data, line; + const char *filedata; - entity.push_back(&object.back()); + 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:: -addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d) +bgmPlay(World *prev) const { - particles.emplace_back(x, y, w, h, vx, vy, color, d); - particles.back().canMove = true; + 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:: -addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d, unsigned char flags) +setBGM(std::string path) { - 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); + 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:: -addLight(vec2 loc, Color color) +setBackground(WorldBGType bgt) { - if (light.size() < 64) - light.push_back(Light(loc, color, 1)); + // 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; + } } -Light *World:: -getLastLight(void) +/** + * 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) { - return &light.back(); -} + // get folder prefix + std::string prefix = pre.empty() ? "assets/style/classic/" : pre; -Mob *World:: -getLastMob(void) -{ - return &mob.back(); -} + for_each(std::begin(buildPaths), std::end(buildPaths), [this, prefix](std::string s){ + sTexLoc.push_back(prefix + s); + }); -std::string World:: -getSTextureLocation(unsigned int index) const -{ - if (index > sTexLoc.size()) - return ""; + prefix += "bg/"; - return sTexLoc[ index ]; + 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) { @@ -1080,7 +1065,6 @@ goWorldLeft(Player *p) // check if player is at world edge if (!toLeft.empty() && p->loc.x < worldStart + HLINE * 15.0f) { - // load world (`toLeft` conditional confirms existance) tmp = loadWorldFromPtr(currentWorldToLeft); @@ -1094,24 +1078,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) { @@ -1129,6 +1098,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) { @@ -1137,21 +1129,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; } @@ -1165,12 +1156,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; } @@ -1180,6 +1169,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) { @@ -1193,7 +1264,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; @@ -1212,95 +1285,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 "<<save<<" ..."<<std::endl; - - 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"); - } - - 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; @@ -1530,8 +1514,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 +1528,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()); @@ -1842,9 +1826,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 @@ -1900,15 +1882,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(); |