diff options
Diffstat (limited to 'src/world.cpp')
-rw-r--r-- | src/world.cpp | 1244 |
1 files changed, 196 insertions, 1048 deletions
diff --git a/src/world.cpp b/src/world.cpp index ef9969f..2772160 100644 --- a/src/world.cpp +++ b/src/world.cpp @@ -47,12 +47,6 @@ void makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions_Ju ** 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 std::string xmlFolder; // particle mutex @@ -73,9 +67,6 @@ constexpr const unsigned int GRASS_HEIGHT = 4; // the path of the currently loaded XML file, externally referenced in places std::string currentXML; -// keeps track of information of worlds the player has left to enter arenas -static std::vector<WorldSwitchInfo> arenaNest; - // pathnames of images for world themes using StyleList = std::array<std::string, 9>; @@ -120,88 +111,29 @@ XMLDocument currentXMLDoc; ** Functions section ** --------------------------------------------------------------------------*/ -/** - * Creates a world object. - * Note that all this does is nullify a pointer... - */ -World::World(bool indoor) - : m_Indoor(indoor), lineCount(0), worldStart(0) -{ -} - -/** - * The world destructor. - * This will free objects used by the world itself, then free the vectors of - * entity-related objects. - */ -World:: -~World(void) -{ - deleteEntities(); -} - -/** - * The entity vector destroyer. - * This function will free all memory used by all entities, and then empty the - * vectors they were stored in. - */ -template<class T> -void clearPointerVector(T &vec) -{ - while (!vec.empty()) { - delete vec.back(); - vec.pop_back(); - } -} - -void World:: -deleteEntities(void) -{ - // free particles - particles.clear(); - // clear light array - light.clear(); - // free villages - village.clear(); - // clear entity array - clearPointerVector(entity); -} - -/** - * Generates a world of the specified width. - * This will mainly populate the WorldData array, preparing most of the world - * object for usage. - */ -void World:: -generate(int width) +void WorldSystem::generate(unsigned int width) { float geninc = 0; - // check for valid width - if (width <= 0) - UserError("Invalid world dimensions"); - // allocate space for world - worldData = std::vector<WorldData> (width + GROUND_HILLINESS, WorldData { false, {0, 0}, 0, 0 }); - lineCount = worldData.size(); + world.data = std::vector<WorldData> (width + GROUND_HILLINESS, WorldData { false, {0, 0}, 0, 0 }); // prepare for generation - worldData.front().groundHeight = GROUND_HEIGHT_INITIAL; - auto wditer = std::begin(worldData) + GROUND_HILLINESS; - - if (m_Indoor) { - for(wditer = std::begin(worldData); wditer < std::end(worldData); wditer++) { - auto w = &*(wditer); - w->groundHeight = GROUND_HEIGHT_MINIMUM + 5; - w->groundColor = 4; + world.data[0].groundHeight = GROUND_HEIGHT_INITIAL; + auto wditer = std::begin(world.data) + GROUND_HILLINESS; + + if (world.indoor) { + for (auto &l : world.data) { + l.groundHeight = GROUND_HEIGHT_MINIMUM + 5; + l.groundColor = 4; } } else { // give every GROUND_HILLINESSth entry a groundHeight value - for (; wditer < std::end(worldData); wditer += GROUND_HILLINESS) + for (; wditer < std::end(world.data); wditer += GROUND_HILLINESS) wditer[-static_cast<int>(GROUND_HILLINESS)].groundHeight = wditer[0].groundHeight + (randGet() % 8 - 4); // create slopes from the points that were just defined, populate the rest of the WorldData structure - for (wditer = std::begin(worldData) + 1; wditer < std::end(worldData); wditer++){ + for (wditer = std::begin(world.data) + 1; wditer < std::end(world.data); wditer++){ auto w = &*(wditer); if (w->groundHeight != 0) @@ -216,585 +148,22 @@ generate(int width) } // define x-coordinate of world's leftmost 'line' - worldStart = (width - GROUND_HILLINESS) * game::HLINE / 2 * -1; - - // create empty star array, should be filled here as well... - star = std::vector<vec2> (100, vec2 { 0, 400 }); - for (auto &s : star) { - s.x = (randGet() % (static_cast<int>(-worldStart) * 2)) + worldStart; - s.y = (randGet() % game::SCREEN_HEIGHT) + 100; - } + world.startX = (width - GROUND_HILLINESS) * game::HLINE / 2 * -1; } static Color ambient; -void World::draw(Player *p) -{ - auto HLINE = game::HLINE; - - uint ls = light.size(); - - GLfloat *lightCoords = new GLfloat[ls * 4]; - GLfloat *lightColors = new GLfloat[ls * 4]; - - uint lpIndex = 0; - uint lcIndex = 0; - - static bool ambientUpdaterStarted = false; - if (!ambientUpdaterStarted) { - ambientUpdaterStarted = true; - std::thread([&](void) { - while (true) { - float v = 75 * sin((game::time::getTickCount() + (DAY_CYCLE / 2)) / (DAY_CYCLE / PI)); - float rg = std::clamp(.5f + (-v / 100.0f), 0.01f, .9f); - float b = std::clamp(.5f + (-v / 80.0f), 0.03f, .9f); - - ambient = Color(rg, rg, b, 1.0f); - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } - }).detach(); - } - - for (uint i = 0; i < ls; i++) { - auto &l = light[i]; - if (l.belongsTo) { - l.loc.x = l.following->loc.x; - l.loc.y = l.following->loc.y; - } - if (l.flame) { - l.fireFlicker = 0.9f + ((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 { - l.fireFlicker = 1; - } - - lightCoords[lpIndex++] = l.loc.x; - lightCoords[lpIndex++] = l.loc.y; - lightCoords[lpIndex++] = 0.0; - lightCoords[lpIndex++] = l.radius; - - lightColors[lcIndex++] = l.color.red; - lightColors[lcIndex++] = l.color.green; - lightColors[lcIndex++] = l.color.blue; - lightColors[lcIndex++] = 1.0; - } - - Render::worldShader.use(); - - glUniform4fv(Render::worldShader.uniform[WU_light], ls, lightCoords); - glUniform4fv(Render::worldShader.uniform[WU_light_color], ls, lightColors); - glUniform1i(Render::worldShader.uniform[WU_light_size], ls); - - Render::worldShader.unuse(); - - for (auto &e :entity) - e->draw(); - - // flatten grass under the player if the player is on the ground - if (p->ground) { - int pOffset = (p->loc.x + p->width / 2 - worldStart) / HLINE; - - for (unsigned int i = 0; i < worldData.size(); i++) - worldData[i].grassUnpressed = !(i < static_cast<unsigned int>(pOffset + 6) && i > static_cast<unsigned int>(pOffset - 6)); - } else { - for (auto &wd : worldData) - wd.grassUnpressed = true; - } - - // draw the player - //p->draw(); - - // draw particles like a MASTAH - glBindTexture(GL_TEXTURE_2D, colorIndex); - glUniform1i(Render::worldShader.uniform[WU_texture], 0); - Render::worldShader.use(); - - glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, .8); - - Render::worldShader.enable(); - - partMutex.lock(); - uint ps = particles.size(); - uint pss = ps * 6 * 5; - uint pc = 0; - - std::vector<GLfloat> partVec(pss); - auto *pIndex = &partVec[0]; - for (uint i = 0; i < ps; i++) { - pc += 30; - if (pc > pss) { - // TODO resize the vector or something better than breaking - break; - } - particles[i].draw(pIndex); - } - partMutex.unlock(); - - glVertexAttribPointer(Render::worldShader.coord, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &partVec[0]); - glVertexAttribPointer(Render::worldShader.tex, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), &partVec[3]); - glDrawArrays(GL_TRIANGLES, 0, ps * 6); - - Render::worldShader.disable(); - - glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.0); - - Render::worldShader.unuse(); -} - -/** - * Get's the world's width in pixels. - */ -int World:: -getTheWidth(void) const -{ - return (worldStart * -2); -} - -float World:: -getWorldStart(void) const -{ - return static_cast<float>(worldStart); -} - -/** - * 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) -{ - for (auto e = entity.rbegin(); e != entity.rend(); ++e) { - if ((*e)->type == MOBT) - return dynamic_cast<Mob *>(*e); - } - - return nullptr; -} - -/** - * 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)); - }); - - return n == std::end(entity) ? nullptr : *n; -} - -Mob* World:: -getNearMob(Entity &e) -{ - auto n = std::find_if(std::begin(entity), std::end(entity), [&](Entity *a) { - return (a->type == MOBT && e.isNear(a) && (e.left ? (a->loc.x < e.loc.x + e.width / 2) : (a->loc.x + a->width > e.loc.x + e.width / 2))); - }); - - return (n == std::end(entity)) ? nullptr : dynamic_cast<Mob *>(*n); -} - - -/** - * Get the file path for the `index`th building. - */ -std::string World:: -getSTextureLocation(unsigned int index) const -{ - 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) { - for (auto e = entity.rbegin(); e != entity.rend(); ++e) { - if ((*e)->type == STRUCTURET) - return (*e)->loc; - } - - return vec2 {0, 0}; - } - - int nth = 0; - for (const auto &e : entity) { - if (e->type == STRUCTURET) { - if (index == nth) - return e->loc; - else - ++nth; - } - } - - return vec2 {0, 0}; -} - -/** - * Saves world data to a file. - */ -void World::save(const std::string& s) +bool WorldSystem::save(const std::string& s) { - for (const auto &e : entity) + (void)s; + /*for (const auto &e : entity) e->saveToXML(); - currentXMLDoc.SaveFile((s.empty() ? currentXML : xmlFolder + s).c_str(), false); -} - -/** - * 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) -{ - bgType = bgt; -} - -/** - * 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; - styleFolder = prefix + "bg/"; - - for (const auto &s : buildPaths) - sTexLoc.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. - */ -WorldSwitchInfo World::goWorldLeft(Player *p) -{ - World *tmp; - // check if player is at world edge - if (!toLeft.empty() && p->loc.x < worldStart + HLINES(15)) { - // load world (`toLeft` conditional confirms existance) - tmp = loadWorldFromPtr(currentWorldToLeft); - - // return pointer and new player coords - return std::make_pair(tmp, vec2 {tmp->worldStart + tmp->getTheWidth() - (float)HLINES(15), - tmp->worldData[tmp->lineCount - 1].groundHeight}); - } - - return std::make_pair(this, vec2 {0, 0}); -} - -/** - * Attempts to go to the right world, returning either that world or itself. - */ -WorldSwitchInfo World::goWorldRight(Player *p) -{ - World *tmp; - if (!toRight.empty() && p->loc.x + p->width > -worldStart - HLINES(15)) { - tmp = loadWorldFromPtr(currentWorldToRight); - return std::make_pair(tmp, vec2 {tmp->worldStart + (float)HLINES(15.0), GROUND_HEIGHT_MINIMUM} ); - } - - return std::make_pair(this, vec2 {0, 0}); -} - -void World::adoptNPC(NPC *e) -{ - entity.push_back(e); -} - -void World::adoptMob(Mob* e) -{ - entity.push_back(e); -} - -/** - * 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 + HLINES(15)) { - currentWorldToLeft->adoptNPC(e); - - entity.erase(std::find(std::begin(entity), std::end(entity), e)); - - e->loc.x = currentWorldToLeft->worldStart + currentWorldToLeft->getTheWidth() - HLINES(15); - e->loc.y = GROUND_HEIGHT_MAXIMUM; - ++e->outnabout; - - return true; - } - - return false; -} - -bool World::goWorldRight(NPC *e) -{ - if (!toRight.empty() && e->loc.x + e->width > -worldStart - HLINES(15)) { - currentWorldToRight->adoptNPC(e); - - entity.erase(std::find(std::begin(entity), std::end(entity), e)); - - e->loc.x = currentWorldToRight->worldStart + HLINES(15); - e->loc.y = GROUND_HEIGHT_MINIMUM; - --e->outnabout; - - return true; - } + currentXMLDoc.SaveFile((s.empty() ? currentXML : xmlFolder + s).c_str(), false);*/ return false; } -/** - * Attempts to enter a building that the player is standing in front of. - */ -WorldSwitchInfo World::goInsideStructure(Player *p) -{ - World *tmp; - static std::string outdoorData, outdoorName; - - // enter a building - if (outdoorName.empty()) { - auto d = std::find_if(std::begin(entity), std::end(entity), [p](const Entity *s) { - return ((p->loc.x > s->loc.x) && (p->loc.x + p->width < s->loc.x + s->width)); - }); - - if ((d == std::end(entity)) || dynamic_cast<Structures *>(*d)->inside.empty()) - return std::make_pair(this, vec2 {0, 0}); - - outdoorData = currentXMLRaw; - outdoorName = currentXML; - currentXML = xmlFolder + dynamic_cast<Structures *>(*d)->inside; - const char *buf = readFile(currentXML.c_str()); - currentXMLRaw = buf; - delete[] buf; - - tmp = dynamic_cast<Structures *>(*d)->insideWorld; - tmp->houseTex = dynamic_cast<Structures *>(*d)->insideTex; - - return std::make_pair(tmp, vec2 {0, 100}); - } - - // exit the building - else { - std::string current = ¤tXML[xmlFolder.size()]; - currentXML = outdoorName; - currentXMLRaw = outdoorData; - outdoorName.clear(); - outdoorData.clear(); - - /*tmp = dynamic_cast<IndoorWorld *>(currentWorld)->outside; //loadWorldFromXML(inside.back()); - - Structures *b = nullptr; - for (auto &s : tmp->entity) { - if (s->type == STRUCTURET && dynamic_cast<Structures *>(s)->inside == current) { - b = dynamic_cast<Structures *>(s); - break; - } - } - - if (b == nullptr)*/ - return std::make_pair(currentWorld, vec2 {0, 100}); - - //return std::make_pair(tmp, vec2 {b->loc.x + (b->width / 2), 0}); - } - - return std::make_pair(this, vec2 {0, 0}); -} - -void World:: -addStructure(Structures *s) -{ - entityPending.push_back(s); -} - -Village *World:: -addVillage(std::string name, World *world) -{ - village.emplace_back(name, world); - return &village.back(); -} - -void World::addMob(Mob *m, vec2 coord) -{ - m->spawn(coord.x, coord.y); - - entityPending.push_back(m); -} - -void World:: -addNPC(NPC *n) -{ - entityPending.push_back(n); -} - -void World:: -addMerchant(float x, float y, bool housed) -{ - Merchant *tmp = new Merchant(); - - tmp->spawn(x, y); - - if (housed) { - tmp->inside = dynamic_cast<Structures *>(*std::find_if(entity.rbegin(), entity.rend(), [&](Entity *e){ return (e->type == STRUCTURET); })); - tmp->z = tmp->inside->z + 0.1f; - } - - entityPending.push_back(tmp); -} - -void World:: -addObject(std::string in, std::string p, float x, float y) -{ - Object *tmp = new Object(in, p); - tmp->spawn(x, y); - - entityPending.push_back(tmp); -} - -void World:: -addParticle(float x, float y, float w, float h, float vx, float vy, Color color, int d) -{ - particles.push_back(Particles(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.push_back(Particles(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, float radius, Color color) -{ - if (light.size() < 128) - light.emplace_back(loc, radius, color); -} - -void World:: -addHole(unsigned int start, unsigned int end) -{ - end = fmin(worldData.size(), end); - - for (unsigned int i = start; i < end; i++) - worldData[i].groundHeight = 0; -} - -void World:: -addHill(const ivec2 peak, const unsigned int width) -{ - int start = peak.x - width / 2, - end = start + width, - offset = 0; - const float thing = peak.y - worldData[std::clamp(start, 0, static_cast<int>(lineCount))].groundHeight; - const float period = PI / width; - - if (start < 0) { - offset = -start; - start = 0; - } - - end = fmin(worldData.size(), end); - - for (int i = start; i < end; i++) { - worldData[i].groundHeight += thing * sin((i - start + offset) * period); - if (worldData[i].groundHeight > peak.y) - worldData[i].groundHeight = peak.y; - } -} - -Arena::Arena(void) -{ - generate(800); - addMob(new Door(), vec2 {100, 100}); - mmob = nullptr; -} - -Arena::~Arena(void) -{ - if (mmob != nullptr) - mmob->die(); - deleteEntities(); -} - -void Arena::fight(World *leave, const Player *p, Mob *m) -{ - inBattle = true; - - entity.push_back((mmob = m)); - mmob->aggressive = false; - - arenaNest.emplace_back(leave, p->loc); -} - -WorldSwitchInfo Arena::exitArena(Player *p) -{ - if (!mmob->isAlive() && - p->loc.x + p->width / 2 > mmob->loc.x && - p->loc.x + p->width / 2 < mmob->loc.x + HLINES(12)) { - auto ret = arenaNest.back(); - arenaNest.pop_back(); - inBattle = !(arenaNest.empty()); - - return ret; - } - - return std::make_pair(this, vec2 {0, 0}); -} - -static bool loadedLeft = false; +/*static bool loadedLeft = false; static bool loadedRight = false; World *loadWorldFromXML(std::string path) { @@ -823,10 +192,6 @@ World *loadWorldFromPtr(World *ptr) return ptr; } -/** - * Loads a world from the given XML file. - */ - World * loadWorldFromXMLNoTakeover(std::string path) { @@ -835,7 +200,110 @@ loadWorldFromXMLNoTakeover(std::string path) loadedLeft = false, loadedRight = false; return ret; } +*/ + +void WorldSystem::load(const std::string& file) +{ + std::string xmlRaw; + std::string xmlPath; + + // check for empty file name + if (file.empty()) + return; + + // load file data to string + xmlPath = xmlFolder + file; + auto xmlRawData = readFile(xmlPath.c_str()); + xmlRaw = xmlRawData; + delete[] xmlRawData; + + // let tinyxml parse the file + if (xmlDoc.Parse(xmlRaw.data()) != XML_NO_ERROR) + UserError("XML Error: Failed to parse file (not your fault though..?)"); + + // look for an opening world tag + auto wxml = xmlDoc.FirstChildElement("World"); + if (wxml != nullptr) { + wxml = wxml->FirstChildElement(); + world.indoor = false; + } else { + wxml = xmlDoc.FirstChildElement("IndoorWorld"); + if (wxml != nullptr) { + wxml = wxml->FirstChildElement(); + world.indoor = true; + } else { + UserError("XML Error: Cannot find a <World> or <IndoorWorld> tag in " + xmlPath); + } + } + + // iterate through tags + while (wxml) { + std::string tagName = wxml->Name(); + + // style tag + if (tagName == "style") { + world.styleFolder = wxml->StrAttribute("folder"); + + unsigned int styleNo; + if (wxml->QueryUnsignedAttribute("background", &styleNo) != XML_NO_ERROR) + UserError("XML Error: No background given in <style> in " + xmlPath); + + world.style = static_cast<WorldBGType>(styleNo); + world.bgm = wxml->StrAttribute("bgm"); + + bgFiles.clear(); + + const auto& files = bgPaths[(int)world.style]; + + for (const auto& f : files) + bgFiles.push_back(world.styleFolder + "bg/" + f); + + bgTex = TextureIterator(bgFiles); + } + + // world generation + else if (tagName == "generation") { + generate(wxml->UnsignedAttribute("width") / game::HLINE); + } + + // indoor stuff + else if (tagName == "house") { + if (!world.indoor) + UserError("<house> can only be used inside <IndoorWorld>"); + + world.indoorWidth = wxml->FloatAttribute("width"); + world.indoorTex = Texture::loadTexture(wxml->Attribute("texture")); + } + + // weather tag + else if (tagName == "weather") { + setWeather(wxml->GetText()); + } + + // link tags + else if (tagName == "link") { + auto linkTo = wxml->Attribute("left"); + if (linkTo != nullptr) { + world.toLeft = linkTo; + } else { + linkTo = wxml->Attribute("right"); + if (linkTo != nullptr) + world.toRight = linkTo; + else + UserError("<link> doesn't handle left or right... huh"); + } + } + + // time setting + else if (tagName == "time") { + game::time::setTickCount(std::stoi(wxml->GetText())); + } + + wxml = wxml->NextSiblingElement(); + } +} +/* World * loadWorldFromXMLNoSave(std::string path) { XMLDocument *_currentXMLDoc; @@ -852,45 +320,6 @@ loadWorldFromXMLNoSave(std::string path) { const char *ptr; std::string name, sptr; - // no file? -> no world - if (path.empty()) - return nullptr; - - _currentXML = xmlFolder + path; - const char *worthless = readFile(_currentXML.c_str()); - _currentXMLRaw = worthless; - delete[] worthless; - - // create a temporary XMLDocument if this isn't the main world - if (!loadedLeft && !loadedRight) - _currentXMLDoc = ¤tXMLDoc; - else - _currentXMLDoc = new XMLDocument(); - - // parse the file - if (_currentXMLDoc->Parse(_currentXMLRaw.data()) != XML_NO_ERROR) - UserError("XML Error: Failed to parse file (not your fault though..?)"); - - // attempt to load a <World> tag - if ((wxml = _currentXMLDoc->FirstChildElement("World"))) { - wxml = wxml->FirstChildElement(); - vil = _currentXMLDoc->FirstChildElement("World")->FirstChildElement("village"); - tmp = new World(); - Indoor = false; - } - - // attempt to load an <IndoorWorld> tag - else if ((wxml = _currentXMLDoc->FirstChildElement("IndoorWorld"))) { - wxml = wxml->FirstChildElement(); - vil = NULL; - tmp = new World(true); - Indoor = true; - } - - // error: can't load a world... - else - UserError("XML Error: Cannot find a <World> or <IndoorWorld> tag in " + _currentXML + "!"); - // iterate through world tags while (wxml) { newEntity = nullptr; @@ -940,38 +369,6 @@ loadWorldFromXMLNoSave(std::string path) { } - // style tags - else if (name == "style") { - // set style folder - tmp->setStyle(wxml->StrAttribute("folder")); - - // set background folder - unsigned int bgt; - if (wxml->QueryUnsignedAttribute("background", &bgt) != XML_NO_ERROR) - UserError("XML Error: No background given in <style> in " + _currentXML + "!"); - tmp->setBackground(static_cast<WorldBGType>(bgt)); - - // set BGM file - tmp->bgm = wxml->StrAttribute("bgm"); - } - - // world generation (for outdoor areas) - else if (name == "generation") { - tmp->generate(wxml->UnsignedAttribute("width") / game::HLINE); - } - - else if (name == "house") { - if (Indoor) - tmp->HouseWidth = wxml->FloatAttribute("width"); - else - UserError("<house> can only be used with indoor worlds"); - } - - // weather tags - else if (name == "weather") { - game::engine.getSystem<WorldSystem>()->setWeather(wxml->GetText()); - } - // set spawn x for player else if (name == "spawnx" && !(loadedLeft | loadedRight)) { player->loc.x = std::stoi(wxml->GetText()); @@ -1009,11 +406,6 @@ loadWorldFromXMLNoSave(std::string path) { tmp->addHill(ivec2 { wxml->IntAttribute("peakx"), wxml->IntAttribute("peaky") }, wxml->UnsignedAttribute("width")); } - // time setting - else if (name == "time" && !(loadedLeft | loadedRight)) { - game::time::setTickCount(std::stoi(wxml->GetText())); - } - if (newEntity != nullptr) { //bool alive = true; //if (wxml->QueryBoolAttribute("alive", &alive) != XML_NO_ERROR || alive) { @@ -1053,9 +445,7 @@ loadWorldFromXMLNoSave(std::string path) { while(vil) { name = vil->Name(); - /** - * READS DATA ABOUT STRUCTURE CONTAINED IN VILLAGE - */ + //READS DATA ABOUT STRUCTURE CONTAINED IN VILLAGE if (name == "structure") { s = new Structures(); @@ -1147,17 +537,7 @@ loadWorldFromXMLNoSave(std::string path) { } return tmp; -} - -Village::Village(std::string meme, World *w) - : name(meme) -{ - start.x = w->getTheWidth() / 2.0f; - end.x = -start.x; - in = false; -} - - +}*/ WorldSystem::WorldSystem(void) : weather(WorldWeather::None), bgmObj(nullptr) {} @@ -1169,68 +549,6 @@ WorldSystem::~WorldSystem(void) Mix_FreeMusic(bgmObj); } -void WorldSystem::update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) -{ - (void)en; - (void)ev; - (void)dt; - - // fade in music if not playing - if (bgmObj != nullptr && !Mix_PlayingMusic()) - Mix_FadeInMusic(bgmObj, -1, 2000); - - // update player coords - player->loc.y += player->vel.y * dt; - player->loc.x += (player->vel.x * player->speed) * dt; - - // update entity coords - for (auto &e : world->entity) { - // dont let structures move? - if (e->type != STRUCTURET && e->canMove) { - e->loc.x += e->vel.x * dt; - e->loc.y += e->vel.y * dt; - - // 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 * dt; - } - } - - partMutex.lock(); - // iterate through particles - world->particles.remove_if([](const Particles &part) { - return part.duration <= 0; - }); - - for (auto &pa : world->particles) { - if (pa.canMove) { // causes overhead - pa.loc.y += pa.vel.y * dt; - pa.loc.x += pa.vel.x * dt; - - if (pa.stu != nullptr) { - if (pa.loc.x >= pa.stu->loc.x && pa.loc.x <= pa.stu->loc.x + pa.stu->width && - pa.loc.y <= pa.stu->loc.y + pa.stu->height * 0.25f) - pa.duration = 0; - } - } - } - partMutex.unlock(); - - // add entities if need be - auto& entityPending = world->entityPending; - - if (!entityPending.empty()) { - while (entityPending.size() > 0) { - world->entity.push_back(entityPending.back()); - entityPending.pop_back(); - } - } - - // run detect stuff - detect(dt); -} - void WorldSystem::render(void) { const auto SCREEN_WIDTH = game::SCREEN_WIDTH; @@ -1241,18 +559,32 @@ void WorldSystem::render(void) static_cast<int>(SCREEN_WIDTH) / 2, static_cast<int>(SCREEN_HEIGHT) / 2 }; - auto& worldData = currentWorld->worldData; - auto& star = currentWorld->star; - auto worldStart = currentWorld->worldStart; - int iStart, iEnd, pOffset; // world width in pixels - int width = worldData.size() * HLINE; + int width = world.data.size() * HLINE; // used for alpha values of background textures int alpha; + + static bool ambientUpdaterStarted = false; + if (!ambientUpdaterStarted) { + ambientUpdaterStarted = true; + std::thread([&](void) { + while (true) { + float v = 75 * sin((game::time::getTickCount() + (DAY_CYCLE / 2)) / (DAY_CYCLE / PI)); + float rg = std::clamp(.5f + (-v / 100.0f), 0.01f, .9f); + float b = std::clamp(.5f + (-v / 80.0f), 0.03f, .9f); + + ambient = Color(rg, rg, b, 1.0f); + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } + }).detach(); + } + + switch (weather) { case WorldWeather::Snowy: alpha = 150; @@ -1348,7 +680,7 @@ void WorldSystem::render(void) makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions(0, fron_tex_coord, tex_coord, 6); // TODO make stars dynamic - static GLuint starTex = Texture::loadTexture("assets/style/classic/bg/star.png"); + /*static GLuint starTex = Texture::loadTexture("assets/style/classic/bg/star.png"); const static float stardim = 24; GLfloat star_coord[star.size() * 5 * 6 + 1]; GLfloat *si = &star_coord[0]; @@ -1374,7 +706,7 @@ void WorldSystem::render(void) glUniform4f(Render::worldShader.uniform[WU_tex_color], 1.0, 1.0, 1.0, 1.3 - static_cast<float>(alpha)/255.0f); makeWorldDrawingSimplerEvenThoughAndyDoesntThinkWeCanMakeItIntoFunctions(5 * sizeof(GLfloat), &star_coord[0], &star_coord[3], star.size() * 6); - } + }*/ Render::worldShader.disable(); @@ -1389,7 +721,7 @@ void WorldSystem::render(void) bgTex++; dim2 mountainDim = bgTex.getTextureDim(); auto xcoord = width / 2 * -1 + offset.x * 0.85f; - for (unsigned int i = 0; i <= worldData.size() * HLINE / mountainDim.x; i++) { + for (int i = 0; i <= width / mountainDim.x; i++) { bg_items.emplace_back(mountainDim.x * i + xcoord, GROUND_HEIGHT_MINIMUM, 8.0f); bg_items.emplace_back(mountainDim.x * (i + 1) + xcoord, GROUND_HEIGHT_MINIMUM, 8.0f); bg_items.emplace_back(mountainDim.x * (i + 1) + xcoord, GROUND_HEIGHT_MINIMUM + mountainDim.y, 8.0f); @@ -1420,18 +752,20 @@ void WorldSystem::render(void) bg_items.clear(); bg_tex.clear(); - if (world->isIndoor() && i == 3) { - glBindTexture(GL_TEXTURE_2D, world->houseTex); + if (world.indoor && i == 3) { + glBindTexture(GL_TEXTURE_2D, world.indoorTex); - bg_items.emplace_back(worldStart, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); - bg_items.emplace_back(worldStart + world->HouseWidth, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); - bg_items.emplace_back(worldStart + world->HouseWidth, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); + const auto& startx = world.startX; - bg_items.emplace_back(worldStart + world->HouseWidth, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); - bg_items.emplace_back(worldStart, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); - bg_items.emplace_back(worldStart, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); + bg_items.emplace_back(startx, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); + bg_items.emplace_back(startx + world.indoorWidth, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); + bg_items.emplace_back(startx + world.indoorWidth, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); + + bg_items.emplace_back(startx + world.indoorWidth, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); + bg_items.emplace_back(startx, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); + bg_items.emplace_back(startx, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); } else { - for (int j = worldStart; j <= -worldStart; j += dim.x) { + for (int j = world.startX; j <= -world.startX; j += dim.x) { bg_items.emplace_back(j + xcoord, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); bg_items.emplace_back(j + dim.x + xcoord, GROUND_HEIGHT_MINIMUM, 7-(i*.1)); bg_items.emplace_back(j + dim.x + xcoord, GROUND_HEIGHT_MINIMUM + dim.y, 7-(i*.1)); @@ -1462,38 +796,38 @@ void WorldSystem::render(void) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // get the line that the player is currently standing on - pOffset = (offset.x + player->width / 2 - worldStart) / HLINE; + pOffset = 200;//(offset.x + player->width / 2 - worldStart) / HLINE; // only draw world within player vision iStart = std::clamp(static_cast<int>(pOffset - (SCREEN_WIDTH / 2 / HLINE) - GROUND_HILLINESS), - 0, static_cast<int>(world->lineCount)); + 0, static_cast<int>(world.data.size())); iEnd = std::clamp(static_cast<int>(pOffset + (SCREEN_WIDTH / 2 / HLINE)), - 0, static_cast<int>(world->lineCount)); + 0, static_cast<int>(world.data.size())); // draw the dirt bgTex++; std::vector<std::pair<vec2,vec3>> c; for (int i = iStart; i < iEnd; i++) { - if (worldData[i].groundHeight <= 0) { // TODO holes (andy) - worldData[i].groundHeight = GROUND_HEIGHT_MINIMUM - 1; + if (world.data[i].groundHeight <= 0) { // TODO holes (andy) + world.data[i].groundHeight = GROUND_HEIGHT_MINIMUM - 1; glColor4ub(0, 0, 0, 255); } else { safeSetColorA(150, 150, 150, 255); } - int ty = worldData[i].groundHeight / 64 + worldData[i].groundColor; + int ty = world.data[i].groundHeight / 64 + world.data[i].groundColor; - c.push_back(std::make_pair(vec2(0, 0), vec3(worldStart + HLINES(i), worldData[i].groundHeight - GRASS_HEIGHT, -4.0f))); - c.push_back(std::make_pair(vec2(1, 0), vec3(worldStart + HLINES(i) + HLINE, worldData[i].groundHeight - GRASS_HEIGHT, -4.0f))); - c.push_back(std::make_pair(vec2(1, ty),vec3(worldStart + HLINES(i) + HLINE, 0, -4.0f))); + c.push_back(std::make_pair(vec2(0, 0), vec3(world.startX + HLINES(i), world.data[i].groundHeight - GRASS_HEIGHT, -4.0f))); + c.push_back(std::make_pair(vec2(1, 0), vec3(world.startX + HLINES(i) + HLINE, world.data[i].groundHeight - GRASS_HEIGHT, -4.0f))); + c.push_back(std::make_pair(vec2(1, ty),vec3(world.startX + HLINES(i) + HLINE, 0, -4.0f))); - c.push_back(std::make_pair(vec2(1, ty),vec3(worldStart + HLINES(i) + HLINE, 0, -4.0f))); - c.push_back(std::make_pair(vec2(0, ty),vec3(worldStart + HLINES(i), 0, -4.0f))); - c.push_back(std::make_pair(vec2(0, 0), vec3(worldStart + HLINES(i), worldData[i].groundHeight - GRASS_HEIGHT, -4.0f))); + c.push_back(std::make_pair(vec2(1, ty),vec3(world.startX + HLINES(i) + HLINE, 0, -4.0f))); + c.push_back(std::make_pair(vec2(0, ty),vec3(world.startX + HLINES(i), 0, -4.0f))); + c.push_back(std::make_pair(vec2(0, 0), vec3(world.startX + HLINES(i), world.data[i].groundHeight - GRASS_HEIGHT, -4.0f))); - if (worldData[i].groundHeight == GROUND_HEIGHT_MINIMUM - 1) - worldData[i].groundHeight = 0; + if (world.data[i].groundHeight == GROUND_HEIGHT_MINIMUM - 1) + world.data[i].groundHeight = 0; } std::vector<GLfloat> dirtc; @@ -1520,7 +854,7 @@ void WorldSystem::render(void) Render::worldShader.disable(); Render::worldShader.unuse(); - if (!world->isIndoor()) { + if (!world.indoor) { bgTex++; safeSetColorA(255, 255, 255, 255); @@ -1529,7 +863,7 @@ void WorldSystem::render(void) std::vector<GLfloat> grasst; for (int i = iStart; i < iEnd; i++) { - auto wd = worldData[i]; + auto wd = world.data[i]; auto gh = wd.grassHeight; // flatten the grass if the player is standing on it. @@ -1540,6 +874,8 @@ void WorldSystem::render(void) // actually draw the grass. if (wd.groundHeight) { + const auto& worldStart = world.startX; + c.push_back(std::make_pair(vec2(0, 0),vec3(worldStart + HLINES(i) , wd.groundHeight + gh[0], -3))); c.push_back(std::make_pair(vec2(1, 0),vec3(worldStart + HLINES(i) + HLINE / 2, wd.groundHeight + gh[0], -3))); c.push_back(std::make_pair(vec2(1, 1),vec3(worldStart + HLINES(i) + HLINE / 2, wd.groundHeight - GRASS_HEIGHT, -3))); @@ -1583,39 +919,24 @@ void WorldSystem::render(void) Render::worldShader.use(); static const GLuint rug = Texture::genColor(Color {255, 0, 0}); glBindTexture(GL_TEXTURE_2D, rug); - vec2 ll = vec2 {worldStart, GROUND_HEIGHT_MINIMUM}; - Render::drawRect(ll, vec2 {ll.x + world->HouseWidth, ll.y + 4}, -3); + vec2 ll = vec2 {world.startX, GROUND_HEIGHT_MINIMUM}; + Render::drawRect(ll, vec2 {ll.x + world.indoorWidth, ll.y + 4}, -3); Render::worldShader.unuse(); } - player->draw(); + //player->draw(); } -void WorldSystem::setWorld(World *w) -{ - world = w; - - bgFiles.clear(); - - const auto& files = bgPaths[(int)w->bgType]; - - for (const auto& f : files) - bgFiles.push_back(w->styleFolder + f); - - bgTex = TextureIterator(bgFiles); -} - - void WorldSystem::receive(const BGMToggleEvent &bte) { - if (bte.world == nullptr || bgmObjFile != bte.file) { + if (bte.world == nullptr || world.bgm != bte.file) { Mix_FadeOutMusic(800); if (bgmObj != nullptr) Mix_FreeMusic(bgmObj); - bgmObjFile = bte.file; - bgmObj = Mix_LoadMUS(bgmObjFile.c_str()); + //worldBgmFile = bte.file; + bgmObj = Mix_LoadMUS(world.bgm.c_str()); Mix_PlayMusic(bgmObj, -1); } } @@ -1632,118 +953,21 @@ void WorldSystem::setWeather(const std::string &s) weather = WorldWeather::None; } -void WorldSystem::enterWorld(World *w) -{ - if (w != nullptr) { - outside = world; - world = w; - } -} - -void WorldSystem::leaveWorld(void) -{ - world = currentWorld = outside; -} - -void WorldSystem::singleDetect(Entity *e, entityx::TimeDelta dt) +void WorldSystem::update(entityx::EntityManager &en, entityx::EventManager &ev, entityx::TimeDelta dt) { - std::string killed; - unsigned int i; - int l; - - auto& worldData = world->worldData; - - if (e == nullptr || !(e->isAlive())) - return; - - // kill dead entities - if (e->health <= 0) { - // die - e->die(); - if (inBattle && e->type == MOBT) - Mobp(e)->onDeath(); - - // delete the entity - for (i = 0; i < world->entity.size(); i++) { - if (world->entity[i] == e) { - switch (e->type) { - case STRUCTURET: - killed = " structure"; - break; - case NPCT: - killed = "n NPC"; - break; - case MOBT: - killed = " mob"; - break; - case OBJECTT: - killed = "n object"; - break; - default: - break; - } - - std::cout << "Killed a" << killed << "...\n"; - world->entity.erase(world->entity.begin() + i); - return; - } - } - - // exit on player death - std::cout << "RIP " << e->name << ".\n"; - exit(0); - } - - // collision / gravity: handle only living entities - else { - - // forced movement gravity (sword hits) - e->handleHits(); - - // calculate the line that this entity is currently standing on - l = std::clamp(static_cast<int>((e->loc.x + e->width / 2 - world->worldStart) / game::HLINE), - 0, - static_cast<int>(world->lineCount)); - - // if the entity is under the world/line, pop it back to the surface - if (e->loc.y < worldData[l].groundHeight) { - int dir = e->vel.x < 0 ? -1 : 1; - if (l + (dir * 2) < static_cast<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[l].groundHeight - 0.001f * dt; - e->ground = true; - e->vel.y = 0; - } - - } + (void)en; + (void)ev; + (void)dt; - // handle gravity if the entity is above the line - else { - if (e->type == STRUCTURET) { - e->loc.y = worldData[l].groundHeight; - e->vel.y = 0; - e->ground = true; - return; - } else if (e->vel.y > -2) { - e->vel.y -= GRAVITY_CONSTANT * dt; - } - } + // fade in music if not playing + if (bgmObj != nullptr && !Mix_PlayingMusic()) + Mix_FadeInMusic(bgmObj, -1, 2000); - // insure that the entity doesn't fall off either edge of the world. - if (e->loc.x < world->worldStart) { - e->vel.x = 0; - e->loc.x = world->worldStart + HLINES(0.5f); - } else if (e->loc.x + e->width + game::HLINE > -((int)world->worldStart)) { - e->vel.x = 0; - e->loc.x = -((int)world->worldStart) - e->width - game::HLINE; - } - } + // run detect stuff + detect(dt); } -void WorldSystem::detect2(entityx::TimeDelta dt) +void WorldSystem::detect(entityx::TimeDelta dt) { game::entities.each<Position, Direction, Health, Solid>( [&](entityx::Entity e, Position &loc, Direction &vel, Health &health, Solid &dim) { @@ -1754,12 +978,12 @@ void WorldSystem::detect2(entityx::TimeDelta dt) UserError("die mofo"); // get the line the entity is on - int line = std::clamp(static_cast<int>((loc.x + dim.width / 2 - world->worldStart) / game::HLINE), + int line = std::clamp(static_cast<int>((loc.x + dim.width / 2 - world.startX) / game::HLINE), 0, - static_cast<int>(world->lineCount)); + static_cast<int>(world.data.size())); // make sure entity is above ground - auto& data = world->worldData; + const auto& data = world.data; if (loc.y < data[line].groundHeight) { int dir = vel.x < 0 ? -1 : 1; if (line + dir * 2 < static_cast<int>(data.size()) && @@ -1779,90 +1003,14 @@ void WorldSystem::detect2(entityx::TimeDelta dt) } // insure that the entity doesn't fall off either edge of the world. - if (loc.x < world->worldStart) { + if (loc.x < world.startX) { vel.x = 0; - loc.x = world->worldStart + HLINES(0.5f); - } else if (loc.x + dim.width + game::HLINE > -((int)world->worldStart)) { + loc.x = world.startX + HLINES(0.5f); + } else if (loc.x + dim.width + game::HLINE > -((int)world.startX)) { vel.x = 0; - loc.x = -((int)world->worldStart) - dim.width - game::HLINE; + loc.x = -((int)world.startX) - dim.width - game::HLINE; } }); } -void WorldSystem::detect(entityx::TimeDelta dt) -{ - int l; - - // handle the player - singleDetect(player, dt); - - // handle other entities - for (auto &e : world->entity) - singleDetect(e, dt); - - partMutex.lock(); - // handle particles - for (auto &part : world->particles) { - // get particle's current world line - l = std::clamp(static_cast<int>((part.loc.x + part.width / 2 - world->worldStart) / game::HLINE), - 0, - static_cast<int>(world->lineCount - 1)); - part.update(GRAVITY_CONSTANT, world->worldData[l].groundHeight); - } - - // handle particle creation - for (auto &e : world->entity) { - if (e->type == STRUCTURET) { - auto b = dynamic_cast<Structures *>(e); - switch (b->bsubtype) { - case FOUNTAIN: - for (unsigned int r = (randGet() % 25) + 11; r--;) { - world->addParticle(randGet() % HLINES(3) + b->loc.x + b->width / 2, // x - b->loc.y + b->height, // y - HLINES(1.25), // width - HLINES(1.25), // height - randGet() % 7 * .01 * (randGet() % 2 == 0 ? -1 : 1), // vel.x - randGet() % 1 ? (8 + randGet() % 6) * .05 : (4 + randGet() % 6) * .05,// vel.y - { 0, 0, 255 }, // RGB color - 2500 // duration (ms) - ); - world->particles.back().fountain = true; - world->particles.back().stu = b; - } - break; - case FIRE_PIT: - for(unsigned int r = (randGet() % 20) + 11; r--;) { - world->addParticle(randGet() % (int)(b->width / 2) + b->loc.x + b->width / 4, // x - b->loc.y + HLINES(3), // y - game::HLINE, // width - game::HLINE, // height - randGet() % 3 * .01 * (randGet() % 2 == 0 ? -1 : 1), // vel.x - (4 + randGet() % 6) * .005, // vel.y - { 255, 0, 0 }, // RGB color - 400 // duration (ms) - ); - world->particles.back().gravity = false; - world->particles.back().behind = true; - world->particles.back().stu = b; - } - break; - default: - break; - } - } - } - partMutex.unlock(); - - // draws the village welcome message if the player enters the village bounds - for (auto &v : world->village) { - if (player->loc.x > v.start.x && player->loc.x < v.end.x) { - if (!v.in) { - ui::passiveImportantText(5000, "Welcome to %s", v.name.c_str()); - v.in = true; - } - } else { - v.in = false; - } - } -} |