diff --git a/Aquaria/Avatar.cpp b/Aquaria/Avatar.cpp index fba109f..4f574f3 100644 --- a/Aquaria/Avatar.cpp +++ b/Aquaria/Avatar.cpp @@ -5012,6 +5012,11 @@ void Avatar::setCollisionAvoidanceData(int range, float mod) _collisionAvoidMod = mod; } +Vector Avatar::getPositionForMap() const +{ + return warpInLocal.isZero() ? position : warpInLocal; +} + bool Avatar::canQuickSong() { return !isSinging() && !isEntityDead() && isInputEnabled() && quickSongCastDelay <= 0; diff --git a/Aquaria/Avatar.h b/Aquaria/Avatar.h index 184b20f..960021f 100644 --- a/Aquaria/Avatar.h +++ b/Aquaria/Avatar.h @@ -320,6 +320,7 @@ public: void setSeeMapMode(SeeMapMode mode) { _seeMapMode = mode; } SeeMapMode getSeeMapMode() const { return _seeMapMode; } + Vector getPositionForMap() const; // same as position, but special when inside a local warp int leaches; float shieldPoints; diff --git a/Aquaria/CMakeLists.txt b/Aquaria/CMakeLists.txt index 81513f9..437d195 100644 --- a/Aquaria/CMakeLists.txt +++ b/Aquaria/CMakeLists.txt @@ -105,6 +105,7 @@ SET(AQUARIA_SRCS Web.h WorldMap.h WorldMapRender.cpp + WorldMapRender.h WorldMapTiles.cpp ) diff --git a/Aquaria/Continuity.cpp b/Aquaria/Continuity.cpp index fa54051..a8cbf83 100644 --- a/Aquaria/Continuity.cpp +++ b/Aquaria/Continuity.cpp @@ -27,6 +27,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "ttvfs_stdio.h" #include "ReadXML.h" #include "Web.h" +#include "WorldMap.h" +#include "WorldMapRender.h" #include using namespace tinyxml2; @@ -2368,33 +2370,33 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, } doc.InsertEndChild(gems); - XMLElement *worldMap = doc.NewElement("WorldMap"); + XMLElement *wmap = doc.NewElement("WorldMap"); { std::ostringstream os; - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) + for (size_t i = 0; i < worldMap.worldMapTiles.size(); i++) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (tile->revealed) + const WorldMapTile& tile = worldMap.worldMapTiles[i]; + if (tile.revealed) { - os << tile->index << " "; + os << tile.index << " "; } } - worldMap->SetAttribute("b", os.str().c_str()); + wmap->SetAttribute("b", os.str().c_str()); if (game->worldMapRender) { std::ostringstream os; - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) + for (size_t i = 0; i < worldMap.worldMapTiles.size(); i++) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - os << tile->index << " "; - tile->dataToString(os); + const WorldMapTile& tile = worldMap.worldMapTiles[i]; + os << tile.index << " "; + tile.dataToString(os); os << " "; } - worldMap->SetAttribute("va", os.str().c_str()); + wmap->SetAttribute("va", os.str().c_str()); } } - doc.InsertEndChild(worldMap); + doc.InsertEndChild(wmap); XMLElement *vox = doc.NewElement("VO"); { @@ -2895,24 +2897,24 @@ bool Continuity::loadFile(int slot) } } - XMLElement *worldMap = doc.FirstChildElement("WorldMap"); - if (worldMap) + XMLElement *wmap = doc.FirstChildElement("WorldMap"); + if (wmap) { - if (worldMap->Attribute("b")) + if (wmap->Attribute("b")) { - std::string s = worldMap->Attribute("b"); + std::string s = wmap->Attribute("b"); std::istringstream is(s); int idx; while (is >> idx) { - dsq->continuity.worldMap.revealMapIndex(idx); + worldMap.revealMapIndex(idx); } } - if (worldMap->Attribute("va") && dsq->continuity.worldMap.getNumWorldMapTiles()) + if (const char *va = wmap->Attribute("va")) { - std::istringstream is(worldMap->Attribute("va")); + SimpleIStringStream is(va, SimpleIStringStream::REUSE); WorldMapTile dummy; @@ -2922,7 +2924,7 @@ bool Continuity::loadFile(int slot) while (is >> idx) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(idx); + WorldMapTile *tile = worldMap.getWorldMapTileByIndex(idx); if (!tile) { @@ -3343,24 +3345,28 @@ void Continuity::removeGemData(GemData *gemData) } } -GemData *Continuity::pickupGem(std::string name, bool effects) + + +GemData *Continuity::pickupGem(const std::string& name, bool effects) { GemData g; g.name = name; g.mapName = game->sceneName; - int sz = gems.size(); - //HACK: (hacky) using effects to determine the starting position of the gem - if (!effects) + // HACK: First gem silently picked up ever with this texture needs special treatment + bool isPlayerGem = !effects && gems.empty() && !nocasecmp(name, "Naija-Token"); + if(isPlayerGem) { - g.pos = game->worldMapRender->getAvatarWorldMapPosition() + Vector(sz*16-64, -64); + g.global = false; // the player is always on a map + g.blink = true; } - else + g.isplayer = isPlayerGem; + + Vector avatarPos = game->avatar->getPositionForMap(); + if(!game->worldMapRender->getWorldToPlayerTile(g.pos, avatarPos, g.global)) { - if (!gems.empty()) - g.pos = game->worldMapRender->getAvatarWorldMapPosition(); - else - g.pos = Vector(0,0); + debugLog("pickupgem failed, no worldmap tile for current map"); + return NULL; } gems.push_back(g); @@ -3372,21 +3378,32 @@ GemData *Continuity::pickupGem(std::string name, bool effects) GemGet *gg = new GemGet(g.name); game->addRenderObject(gg, LR_MINIMAP); - - if (!getFlag("tokenHint")) { setFlag("tokenHint", 1); game->setControlHint(stringbank.get(4), false, false, false, 8); } - - } // return the last one return &gems.back(); } +void Continuity::setCurrentMap(const std::string& mapname) +{ + worldMap.revealMap(mapname); + + for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++) + { + GemData& g = *i; + if(g.isplayer) + { + g.mapName = mapname; + game->worldMapRender->updateGem(&g); + } + } +} + void Continuity::entityDied(Entity *eDead) { if (statsAndAchievements) diff --git a/Aquaria/Continuity.h b/Aquaria/Continuity.h index 2bce6d7..8783c25 100644 --- a/Aquaria/Continuity.h +++ b/Aquaria/Continuity.h @@ -162,9 +162,11 @@ public: typedef std::list Beacons; Beacons beacons; - GemData *pickupGem(std::string name, bool effects = true); + GemData *pickupGem(const std::string& name, bool effects = true); void removeGemData(GemData *gemData); + void setCurrentMap(const std::string& mapname); + typedef std::vector VoiceOversPlayed; VoiceOversPlayed voiceOversPlayed; diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index 08e6430..1b2cca5 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -48,6 +48,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Ingredient.h" #include "Beam.h" #include "Hair.h" +#include "WorldMapRender.h" #ifdef BBGE_USE_GLM @@ -2183,11 +2184,28 @@ void Game::createGradient() } } -bool Game::isInGameMenu() +bool Game::isInGameMenu() const { return themenu->isInGameMenu(); } +bool Game::isOnWorldMap() const +{ + return worldMapRender->isOn(); +} + +void Game::toggleWorldMap(bool on) +{ + if (dsq->continuity.gems.empty()) + dsq->continuity.pickupGem("Naija-Token", false); + worldMapRender->toggle(on); +} + +void Game::toggleWorldMap() +{ + toggleWorldMap(!isOnWorldMap()); +} + bool Game::isValidTarget(Entity *e, Entity *me) { return (e != me && e->isNormalLayer() && e->isPresent() && e->getEntityType() == ET_ENEMY && e->isAvatarAttackTarget()); @@ -2443,14 +2461,6 @@ void Game::action(int id, int state, int source, InputDevice device) } } -void Game::toggleWorldMap() -{ - if (worldMapRender) - { - worldMapRender->toggle(!worldMapRender->isOn()); - } -} - void Game::applyState() { bool verbose = true; @@ -2762,7 +2772,7 @@ void Game::applyState() // ----------------- SCENE IS LOADED BELOW HERE ------------------- - dsq->continuity.worldMap.revealMap(sceneName); + dsq->continuity.setCurrentMap(sceneName); if (verbose) debugLog("Adding Avatar"); addRenderObject(avatar, LR_ENTITIES); @@ -2794,9 +2804,9 @@ void Game::applyState() timerText->followCamera = 1; addRenderObject(timerText, LR_MINIMAP); - worldMapRender = 0; - - worldMapRender = new WorldMapRender; + worldMapRender = new WorldMapRender(dsq->continuity.worldMap); + worldMapRender->init(); + worldMapRender->setCurrentMap(sceneName.c_str()); addRenderObject(worldMapRender, LR_WORLDMAP); sceneToLoad=""; @@ -4251,6 +4261,11 @@ bool Game::isControlHint() return controlHint_bg->alpha.x != 0; } +WorldMapTileContainer* Game::getCurrentWorldMapTile() const +{ + return worldMapRender ? worldMapRender->getCurrentTile() : NULL; +} + bool Game::trace(Vector start, Vector target) { /* diff --git a/Aquaria/Game.h b/Aquaria/Game.h index 283b8e7..3b75514 100644 --- a/Aquaria/Game.h +++ b/Aquaria/Game.h @@ -65,6 +65,7 @@ class ToolTip; class Ingredient; class ManaBall; class Beam; +class WorldMapTileContainer; #include "Path.h" @@ -137,8 +138,6 @@ public: void clearGrid(int v = 0); void clearDynamicGrid(unsigned char maskbyte = OT_MASK_BLACK); - void toggleWorldMap(); - void action(int id, int state, int source, InputDevice device); InGameMenu *getInGameMenu() { return themenu; } @@ -254,7 +253,10 @@ public: SceneEditor sceneEditor; bool isSceneEditorActive() {return sceneEditor.isOn();} - bool isInGameMenu(); + bool isInGameMenu() const; + bool isOnWorldMap() const; + void toggleWorldMap(bool on); + void toggleWorldMap(); // on or off typedef std::vector EntityTypeList; EntityTypeList entityTypeList; @@ -313,6 +315,7 @@ public: int getNumberOfEntitiesNamed(const std::string &name); MiniMapRender *miniMapRender; WorldMapRender *worldMapRender; + WorldMapTileContainer *getCurrentWorldMapTile() const; bool loadingScene; bool doScreenTrans; diff --git a/Aquaria/GameStructs.h b/Aquaria/GameStructs.h index de9c736..897e87c 100644 --- a/Aquaria/GameStructs.h +++ b/Aquaria/GameStructs.h @@ -21,13 +21,15 @@ struct EmoteData struct GemData { - GemData() { canMove=false; blink = false; } + GemData() { canMove=false; blink = false; global = false; isplayer = false; } std::string name; std::string userString; std::string mapName; Vector pos; bool canMove; bool blink; // not saved + bool global; // local gems use their parent container's coordinate system, global gems are placed directly on the map screen + bool isplayer; }; struct BeaconData diff --git a/Aquaria/GridRender.h b/Aquaria/GridRender.h index b3fd9eb..06ecd44 100644 --- a/Aquaria/GridRender.h +++ b/Aquaria/GridRender.h @@ -23,14 +23,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "GameEnums.h" #include "GameStructs.h" -#include "../BBGE/Quad.h" +#include "Quad.h" #include "ActionMapper.h" #include "VertexBuffer.h" -class GemMover; + struct MinimapIcon; -struct WorldMapTile; -struct GemData; class AquariaMenuItem; class BitmapText; @@ -93,43 +91,6 @@ public: static bool setMaxHealthMarkerTex(const std::string& name); }; -class WorldMapRender : public RenderObject, public ActionMapper -{ -public: - WorldMapRender(); - void destroy(); - void toggle(bool on); - bool isOn(); - Vector getAvatarWorldMapPosition(); - Vector getWorldToTile(WorldMapTile *tile, Vector position, bool fromCenter, bool tilePos); - void setProperTileColor(WorldMapTile *tile); - void action(int id, int state, int source, InputDevice device); - GemMover* addGem(GemData *gemData); - void bindInput(); - void createGemHint(const std::string &gfx); - void addAllGems(); - void fixGems(); - void removeGem(GemMover *gemMover); - void onToggleHelpScreen(); - bool isCursorOffHud(); - -protected: - Quad *addHintQuad1, *addHintQuad2; - AquariaMenuItem *helpButton; - float doubleClickTimer; - float inputDelay; - BitmapText *areaLabel, *areaLabel2, *areaLabel3; - WorldMapTile *originalActiveTile; - void setVis(WorldMapTile *tile); - void clearVis(WorldMapTile *tile); - bool on; - void onUpdate(float dt); - unsigned char *savedTexData; - bool mb; - Vector lastMousePosition; // See FIXME in WorldMapRender.cpp --achurch - void updateEditor(); -}; - class PathRender : public RenderObject { public: diff --git a/Aquaria/MiniMapRender.cpp b/Aquaria/MiniMapRender.cpp index 8a70f42..f27893d 100644 --- a/Aquaria/MiniMapRender.cpp +++ b/Aquaria/MiniMapRender.cpp @@ -376,7 +376,7 @@ void MiniMapRender::onUpdate(float dt) _isCursorIn = false; if (alpha.x == 1) { - if (!game->isInGameMenu() && (!game->isPaused() || (game->isPaused() && game->worldMapRender->isOn()))) + if (!game->isInGameMenu() && (!game->isPaused() || (game->isPaused() && game->isOnWorldMap()))) { if (isCursorInButtons()) { @@ -397,7 +397,7 @@ void MiniMapRender::onUpdate(float dt) bool btn=false; - if (!game->worldMapRender->isOn()) + if (!game->isOnWorldMap()) { for (size_t i = 0; i < buttons.size(); i++) { @@ -421,20 +421,16 @@ void MiniMapRender::onUpdate(float dt) if (!btn && !radarHide && (!dsq->mod.isActive() || dsq->mod.hasWorldMap())) { - if (game->worldMapRender->isOn()) + if (game->isOnWorldMap()) { - game->worldMapRender->toggle(false); + game->toggleWorldMap(false); clickEffect(1); } else { if (doubleClickDelay > 0 && !core->isStateJumpPending()) { - - if (dsq->continuity.gems.empty()) - dsq->continuity.pickupGem("Naija-Token"); - - game->worldMapRender->toggle(true); + game->toggleWorldMap(true); clickEffect(0); diff --git a/Aquaria/SceneEditor.cpp b/Aquaria/SceneEditor.cpp index 73c985c..e809a67 100644 --- a/Aquaria/SceneEditor.cpp +++ b/Aquaria/SceneEditor.cpp @@ -2357,7 +2357,7 @@ void SceneEditor::toggle(bool on) if (core->getNestedMains() > 1) return; if (game->isInGameMenu()) return; if (!on && editType == ET_SELECTENTITY) return; - if (game->worldMapRender && game->worldMapRender->isOn()) return; + if (game->isOnWorldMap()) return; this->on = on; autoSaveTimer = 0; diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 7b51bae..c1cf8e5 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -61,6 +61,7 @@ extern "C" { #include "Shader.h" #include "ActionMapper.h" #include "QuadGrid.h" +#include "WorldMapRender.h" #include "MathFunctions.h" @@ -8855,16 +8856,15 @@ luaFunc(setGemPosition) Vector pos(lua_tonumber(L, 2), lua_tonumber(L, 3)); bool result = false; - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(mapname); - if(tile) + WorldMapTileContainer *tc = game->worldMapRender->getTileByName(mapname.c_str()); + if(tc) { - pos = game->worldMapRender->getWorldToTile(tile, pos, true, true); if(gemId < dsq->continuity.gems.size()) { Continuity::Gems::iterator it = dsq->continuity.gems.begin(); std::advance(it, gemId); GemData& gem = *it; - gem.pos = pos; + gem.pos = gem.global ? tc->worldPosToMapPos(pos) : tc->worldPosToTilePos(pos); gem.mapName = mapname; result = true; } @@ -8925,9 +8925,7 @@ luaFunc(removeGem) { Continuity::Gems::iterator it = dsq->continuity.gems.begin(); std::advance(it, gemId); - dsq->continuity.removeGemData(&(*it)); - if(game->worldMapRender->isOn()) - game->worldMapRender->fixGems(); + game->worldMapRender->removeGem(&(*it)); } luaReturnNil(); } diff --git a/Aquaria/StatsAndAchievements.cpp b/Aquaria/StatsAndAchievements.cpp index 164856b..ce1f569 100644 --- a/Aquaria/StatsAndAchievements.cpp +++ b/Aquaria/StatsAndAchievements.cpp @@ -439,10 +439,10 @@ void StatsAndAchievements::EvaluateAchievement( Achievement &achievement ) { // check world map data somehow bool hasAllMap = true; - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) + for (size_t i = 0; i < dsq->continuity.worldMap.worldMapTiles.size(); i++) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (!tile->revealed && (nocasecmp(tile->name, "thirteenlair") != 0)) { + const WorldMapTile& tile = dsq->continuity.worldMap.worldMapTiles[i]; + if (!tile.revealed && (nocasecmp(tile.name, "thirteenlair") != 0)) { hasAllMap = false; break; diff --git a/Aquaria/WorldMap.h b/Aquaria/WorldMap.h index 238d39c..f35a2ae 100644 --- a/Aquaria/WorldMap.h +++ b/Aquaria/WorldMap.h @@ -4,6 +4,10 @@ #include #include "Vector.h" #include "GameEnums.h" +#include "Image.h" +#include "Refcounted.h" +#include "Texture.h" +#include "SimpleIStringStream.h" #define MAPVIS_SUBDIV 64 @@ -15,21 +19,23 @@ struct WorldMapTile ~WorldMapTile(); void markVisited(int left, int top, int right, int bottom); - void dataToString(std::ostringstream &os); - void stringToData(std::istringstream &is); - const unsigned char *getData() const {return data;} + void dataToString(std::ostringstream &os) const; + void stringToData(SimpleIStringStream &is); + + ImageData generateAlphaImage(size_t w, size_t h); // free() data when no longer needed + bool updateDiscoveredTex(); std::string name; Vector gridPos; float scale, scale2; - bool revealed, prerevealed; + bool revealed, prerevealed, dirty; int layer, index; int stringIndex; - Quad *q; -protected: - unsigned char *data; + Array2d visited; + + CountedPtr originalTex, generatedTex; }; struct WorldMap @@ -37,12 +43,8 @@ struct WorldMap WorldMap(); void load(); void save(); - void hideMap(); void revealMap(const std::string &name); WorldMapTile *getWorldMapTile(const std::string &name); - size_t getNumWorldMapTiles(); - WorldMapTile *getWorldMapTile(size_t index); - WorldMapTile *getWorldMapTileByIndex(int index); void revealMapIndex(int index); diff --git a/Aquaria/WorldMapRender.cpp b/Aquaria/WorldMapRender.cpp index fd4986e..d033895 100644 --- a/Aquaria/WorldMapRender.cpp +++ b/Aquaria/WorldMapRender.cpp @@ -18,6 +18,7 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ +#include "WorldMapRender.h" #include "DSQ.h" #include "Game.h" #include "Avatar.h" @@ -43,28 +44,19 @@ namespace WorldMapRenderNamespace // overlook things on the edge of the screen while moving.) const float visitedFraction = 0.8f; - enum VisMethod - { - VIS_VERTEX = 0, // Uses the RenderObject tile grid (RenderObject::setSegs()) to display visited areas - VIS_WRITE = 1 // Uses render-to-texture instead - }; - - const VisMethod visMethod = VIS_VERTEX; - - Quad *activeQuad=0; - float xMin, yMin, xMax, yMax; - float zoomMin = 0.2f; - float zoomMax = 3.0f; - const float exteriorZoomMax = 3.0f; - const float interiorZoomMax = 3.0f; + const float zoomMin = 0.2f; + const float zoomMax = 3.0f; + + // Where does this come from? + // This has been always there, the map data were made with it, and it needs to be there + // to make everything look right. + // The scale factor recorded in the world map data is just off by a factor of 4, + // this fixes it and also documents the places where this factor needs to be used. + const float tileScaleFactor = 0.25f; bool editorActive=false; - - Quad *tophud=0; - - Gradient *underlay = 0; } using namespace WorldMapRenderNamespace; @@ -73,8 +65,6 @@ class GemMover; GemMover *mover=0; -WorldMapTile *activeTile=0; - const float beaconSpawnBitTime = 0.05f; @@ -243,33 +233,30 @@ class GemMover : public Quad public: GemMover(GemData *gemData) : Quad(), gemData(gemData) { - setTexture("Gems/" + gemData->name); - position = gemData->pos; followCamera = 1; - blink = false; blinkTimer = 0; alphaMod = 0.66f; - canMove = gemData->canMove; - - - - std::string useString = gemData->userString; - - text = new TTFText(&dsq->fontArialSmall); text->offset = Vector(0, 4); - text->setText(useString); text->setAlign(ALIGN_CENTER); textBG = new RoundedRect(); - textBG->setWidthHeight(text->getActualWidth() + 20, 25, 10); textBG->alpha = 0; textBG->followCamera = 1; game->addRenderObject(textBG, LR_WORLDMAPHUD); textBG->addChild(text, PM_POINTER); + refresh(); + } + + void refresh() + { + setTexture("gems/" + gemData->name); + position = gemData->pos; + text->setText(gemData->userString); + textBG->setWidthHeight(text->getActualWidth() + 20, 25, 10); } void destroy() @@ -284,18 +271,10 @@ public: GemData *getGemData() { return gemData; } - void setBlink(bool blink) - { - this->blink = blink; - - - - } - bool canMove; + inline bool canMove() const { return gemData->canMove; } protected: float blinkTimer; - bool blink; GemData *gemData; TTFText *text; @@ -323,7 +302,7 @@ protected: Vector wp = getWorldPosition(); - if (blink) + if (gemData->blink) { blinkTimer += dt; if (blinkTimer > blinkPeriod) @@ -337,7 +316,7 @@ protected: } } - if (canMove) + if (canMove()) { if (mover == 0) { @@ -345,9 +324,6 @@ protected: { core->sound->playSfx("Gem-Move"); mover = this; - - - } } else if (mover == this) @@ -358,9 +334,6 @@ protected: { mover = 0; core->sound->playSfx("Gem-Place"); - - - gemData->pos = position; } } @@ -370,220 +343,59 @@ protected: if (textBG) { textBG->position = getWorldPosition() + Vector(0, -20); - } - if ((core->mouse.position - wp).isLength2DIn(GEM_GRAB)) - { - - - if (!gemData->userString.empty()) - textBG->show(); - } - else - { - - if (textBG->alpha == 1) - textBG->hide(); + if ((core->mouse.position - wp).isLength2DIn(GEM_GRAB)) + { + if (!gemData->userString.empty()) + textBG->show(); + } + else + { + if (textBG->alpha == 1) + textBG->hide(); + } } } }; -typedef std::list GemMovers; +typedef std::vector GemMovers; GemMovers gemMovers; typedef std::vector BeaconRenders; BeaconRenders beaconRenders; -std::vector quads; - -void WorldMapRender::setProperTileColor(WorldMapTile *tile) +void WorldMapRender::setProperTileColor(WorldMapTileContainer& wt) { - if (tile) + const WorldMapTile& t = wt.tile; + if(selectedTile != &wt) { - if (tile->q) - { - tile->q->alphaMod = tile->revealed ? 0.5f : 0.0f; + wt.q.alphaMod = (t.revealed || t.prerevealed) ? 0.5f : 0.0f; - if (activeTile && (tile->layer != activeTile->layer || (tile->layer > 0 && activeTile != tile))) - tile->q->alphaMod *= 0.5f; + if (selectedTile && t.layer != selectedTile->tile.layer) + wt.q.alphaMod *= 0.5f; - tile->q->color = Vector(0.7f, 0.8f, 1); - } - else - { - debugLog("no Q!"); - } - } -} - - -static void tileDataToVis(WorldMapTile *tile, Array2d& vis) -{ - const unsigned char *data = tile->getData(); - - if (data != 0) - { - const float a = tile->prerevealed ? 0.4f : baseMapSegAlpha; - const size_t rowSize = MAPVIS_SUBDIV/8; - for (size_t y = 0; y < MAPVIS_SUBDIV; y++, data += rowSize) - { - for (size_t x = 0; x < MAPVIS_SUBDIV; x += 8) - { - unsigned char dataByte = data[x/8]; - for (size_t x2 = 0; x2 < 8; x2++) - { - vis(x+x2,y).z = (dataByte & (1 << x2)) ? visibleMapSegAlpha : a; - } - } - } + wt.q.color = Vector(0.7f, 0.8f, 1); } else { - const float a = tile->prerevealed ? 0.4f : baseMapSegAlpha; - Vector *gp = vis.data(); - const size_t n = vis.linearsize(); - for(size_t i = 0; i < n; ++i) - gp[i].z = a; + wt.q.color = Vector(1,1,1); + wt.q.alphaMod = 1; } } -// Returns a copy of the original texture data. -static unsigned char *tileDataToAlpha(WorldMapTile *tile) -{ - const unsigned char *data = tile->getData(); - const unsigned int ab = int(baseMapSegAlpha * (1<<8) + 0.5f); - const unsigned int av = int(visibleMapSegAlpha * (1<<8) + 0.5f); - - const unsigned int texWidth = tile->q->texture->width; - const unsigned int texHeight = tile->q->texture->height; - if (texWidth % MAPVIS_SUBDIV != 0 || texHeight % MAPVIS_SUBDIV != 0) - { - std::ostringstream os; - os << "Texture size " << texWidth << "x" << texHeight - << " not a multiple of MAPVIS_SUBDIV " << MAPVIS_SUBDIV - << ", can't edit"; - debugLog(os.str()); - return 0; - } - const unsigned int scaleX = texWidth / MAPVIS_SUBDIV; - const unsigned int scaleY = texHeight / MAPVIS_SUBDIV; - - unsigned char *savedTexData = new unsigned char[texWidth * texHeight * 4]; - tile->q->texture->readRGBA(savedTexData); - - unsigned char *texData = new unsigned char[texWidth * texHeight * 4]; - memcpy(texData, savedTexData, texWidth * texHeight * 4); - - if (data != 0) - { - const unsigned int rowSize = MAPVIS_SUBDIV/8; - for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++, data += rowSize) - { - unsigned char *texOut = &texData[(y*scaleY) * texWidth * 4]; - for (unsigned int x = 0; x < MAPVIS_SUBDIV; x += 8) - { - unsigned char dataByte = data[x/8]; - for (unsigned int x2 = 0; x2 < 8; x2++, texOut += scaleX*4) - { - const bool visited = (dataByte & (1 << x2)) != 0; - const unsigned int alphaMod = visited ? av : ab; - for (unsigned int pixelY = 0; pixelY < scaleY; pixelY++) - { - unsigned char *ptr = &texOut[pixelY * texWidth * 4]; - for (unsigned int pixelX = 0; pixelX < scaleX; pixelX++, ptr += 4) - { - if (ptr[3] == 0) - continue; - ptr[3] = (ptr[3] * alphaMod + 128) >> 8; - } - } - } - } - } - } - else - { - unsigned char *texOut = texData; - for (unsigned int y = 0; y < texHeight; y++) - { - for (unsigned int x = 0; x < texWidth; x++, texOut += 4) - { - texOut[3] = (texOut[3] * ab + 128) >> 8; - } - } - } - - tile->q->texture->writeRGBA(0, 0, texWidth, texHeight, texData); - delete[] texData; - - return savedTexData; -} - -static void resetTileAlpha(WorldMapTile *tile, const unsigned char *savedTexData) -{ - tile->q->texture->writeRGBA(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData); -} - - - -void WorldMapRender::setVis(WorldMapTile *tile) -{ - if (!tile) return; - - - tile->q->color = Vector(1,1,1); - tile->q->alphaMod = 1; - - if (visMethod == VIS_VERTEX) - { - DynamicRenderGrid *g = tile->q->setSegs(MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0, 0, 0, 0, 2.0, 1); - if(g) - { - g->gridType = GRID_UNDEFINED; - g->setDrawOrder(GRID_DRAW_WORLDMAP); - tileDataToVis(tile, g->array2d()); - } - } - else if (visMethod == VIS_WRITE) - { - assert(!savedTexData); - savedTexData = tileDataToAlpha(tile); - } -} - -void WorldMapRender::clearVis(WorldMapTile *tile) -{ - if (!tile) return; - if (visMethod == VIS_VERTEX) - { - if (tile->q) - tile->q->deleteGrid(); - } - else if (visMethod == VIS_WRITE) - { - if (savedTexData) - { - resetTileAlpha(tile, savedTexData); - delete[] savedTexData; - savedTexData = 0; - } - } -} - - -WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper() +WorldMapRender::WorldMapRender(WorldMap& wm) : RenderObject(), ActionMapper() + , worldmap(wm) { doubleClickTimer = 0; inputDelay = 0; editorActive=false; mb = false; - activeQuad=0; - - originalActiveTile = activeTile = 0; - + playerTile = NULL; + selectedTile = NULL; areaLabel = 0; on = false; + wasEditorSaveDown = false; alpha = 0; scale = Vector(1, 1); @@ -591,68 +403,8 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper() cull = false; position = Vector(400,300); - activeTile = 0; - activeQuad = 0; - lastMousePosition = core->mouse.position; - savedTexData = 0; - - - - const size_t num = dsq->continuity.worldMap.getNumWorldMapTiles(); - std::string n = game->sceneName; - stringToUpper(n); - std::vector textodo(num); - std::vector texs(num, NULL); - textodo.reserve(num); - for (size_t i = 0; i < num; i++) - { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (tile) - { - if (tile->name == n) - activeTile = tile; - textodo[i] = "gui/worldmap/" + tile->name; - } - } - - if(num) - dsq->texmgr.loadBatch(&texs[0], &textodo[0], num); - - for (size_t i = 0; i < num; i++) - { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (tile) - { - Vector pos(tile->gridPos.x, tile->gridPos.y); - - Quad *q = new Quad; - q->setTexturePointer(texs[i]); - q->position = pos; - q->alphaMod = 0; - - tile->q = q; - - q->setWidthHeight(q->getWidth()*tile->scale, q->getHeight()*tile->scale); - q->scale = Vector(0.25f*tile->scale2, 0.25f*tile->scale2); - - if (tile == activeTile) - activeQuad = q; - - setVis(tile); - - setProperTileColor(tile); - - if(activeQuad == q) - { - activeTile->q->color = Vector(1,1,1); - activeTile->q->alphaMod = 1; - } - - addChild(q, PM_POINTER); - } - } shareAlphaWithChildren = 1; bindInput(); @@ -696,13 +448,6 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper() areaLabel3->alpha = 0; game->addRenderObject(areaLabel3, LR_WORLDMAPHUD); - if (activeTile) - { - areaLabel2->setText(stringbank.get(activeTile->stringIndex)); - } - - originalActiveTile = activeTile; - bindInput(); underlay = new Gradient; @@ -736,6 +481,54 @@ WorldMapRender::WorldMapRender() : RenderObject(), ActionMapper() game->addRenderObject(helpButton, LR_WORLDMAPHUD); } +WorldMapRender::~WorldMapRender() +{ +} + +void WorldMapRender::init() +{ + debugLog("WorldMapRender: init..."); + for(size_t i = 0; i < tiles.size(); ++i) + { + tiles[i]->destroy(); + delete tiles[i]; + } + tiles.clear(); + + const size_t num = worldmap.worldMapTiles.size(); + + std::vector textodo(num); + std::vector texs(num, NULL); + textodo.reserve(num); + tiles.reserve(num); + for (size_t i = 0; i < num; i++) + { + WorldMapTile& tile = worldmap.worldMapTiles[i]; + textodo[i] = "gui/worldmap/" + tile.name; + WorldMapTileContainer *tc = new WorldMapTileContainer(tile); + tiles.push_back(tc); + addChild(tc, PM_POINTER); + } + + if(num) + dsq->texmgr.loadBatch(&texs[0], &textodo[0], num); + + for (size_t i = 0; i < num; i++) + { + WorldMapTileContainer& tc = *tiles[i]; + WorldMapTile& t = tc.tile; + + tc.position = t.gridPos; + t.originalTex = texs[i]; + + t.dirty = true; // force refresh to init texture, width, height + tc.refresh(); + + setProperTileColor(tc); + } + debugLog("WorldMapRender: init done"); +} + void WorldMapRender::onToggleHelpScreen() { game->toggleHelpScreen(); @@ -763,28 +556,45 @@ void WorldMapRender::bindInput() } } -void WorldMapRender::destroy() -{ - - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) - { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - clearVis(tile); - } - - RenderObject::destroy(); - delete[] savedTexData; -} - bool WorldMapRender::isCursorOffHud() { - if (helpButton && helpButton->isCursorInMenuItem()) + if (helpButton->isCursorInMenuItem()) { return false; } return true; } +void WorldMapRender::updateAllTilesColor() +{ + for (size_t i = 0; i < tiles.size(); i++) + setProperTileColor(*tiles[i]); +} + +bool WorldMapRender::getWorldToPlayerTile(Vector& dst, const Vector& pos, bool global) const +{ + if(!playerTile) + return false; + + dst = global + ? playerTile->worldPosToMapPos(pos) + : playerTile->worldPosToTilePos(pos); + return true; +} + +WorldMapTileContainer* WorldMapRender::getTileByName(const char* name) const +{ + for(size_t i = 0; i < tiles.size(); ++i) + if(!nocasecmp(tiles[i]->tile.name.c_str(), name)) + return tiles[i]; + return NULL; +} + +WorldMapTileContainer *WorldMapRender::setCurrentMap(const char* mapname) +{ + return ((playerTile = getTileByName(mapname))); +} + void WorldMapRender::onUpdate(float dt) { if (AquariaGuiElement::currentGuiInputLevel > 0) return; @@ -792,38 +602,20 @@ void WorldMapRender::onUpdate(float dt) RenderObject::onUpdate(dt); ActionMapper::onUpdate(dt); - if (areaLabel) - areaLabel->alpha.x = this->alpha.x; - - if (areaLabel2) - areaLabel2->alpha.x = this->alpha.x; - - if (areaLabel3) - areaLabel3->alpha.x = this->alpha.x; - - if (tophud) - tophud->alpha.x = this->alpha.x; + areaLabel->alpha.x = this->alpha.x; + areaLabel2->alpha.x = this->alpha.x; + areaLabel3->alpha.x = this->alpha.x; + tophud->alpha.x = this->alpha.x; const float mmWidth = game->miniMapRender->getMiniMapWidth(); const float mmHeight = game->miniMapRender->getMiniMapHeight(); - if (addHintQuad1) - addHintQuad1->position = game->miniMapRender->position + Vector(-mmWidth*3/22, -mmHeight/2-10); - if (addHintQuad2) - addHintQuad2->position = game->miniMapRender->position + Vector(mmWidth*3/22, -mmHeight/2-10); + addHintQuad1->position = game->miniMapRender->position + Vector(-mmWidth*3/22, -mmHeight/2-10); + addHintQuad2->position = game->miniMapRender->position + Vector(mmWidth*3/22, -mmHeight/2-10); - int offset = 26; - if (helpButton) - helpButton->position = Vector(core->getVirtualWidth()-core->getVirtualOffX()-offset, offset); + const int offset = 26; + helpButton->position = Vector(core->getVirtualWidth()-core->getVirtualOffX()-offset, offset); - if (alpha.x > 0) - { - - if (originalActiveTile && !gemMovers.empty()) - { - gemMovers.back()->position = getAvatarWorldMapPosition(); - } - } if (doubleClickTimer > 0) { @@ -832,8 +624,31 @@ void WorldMapRender::onUpdate(float dt) doubleClickTimer = 0; } + for(size_t i = 0; i < tiles.size(); ++i) + tiles[i]->q.alpha.x = alpha.x; + if (isOn()) { + if(!selectedTile) + selectedTile = playerTile; + + if(playerTile && !gemMovers.empty()) + { + Vector playerPos = game->avatar->getPositionForMap(); + Vector playerMapPos = playerTile->worldPosToTilePos(playerPos); + for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++) + { + GemMover *gm = *i; + GemData *g = gm->getGemData(); + if(g->isplayer) + { + assert(!g->global); + g->pos = playerMapPos; + gm->position = playerMapPos; + } + } + } + if (inputDelay > 0) { inputDelay -= dt; @@ -841,24 +656,23 @@ void WorldMapRender::onUpdate(float dt) } else { - WorldMapTile *selectedTile = 0; + WorldMapTileContainer *hoverTile = 0; float sd=-1,d=0; - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) + for (size_t i = 0; i < tiles.size(); i++) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (tile && tile != activeTile) + WorldMapTileContainer *tc = tiles[i]; + if (tc != selectedTile) { - if (tile->revealed || tile->prerevealed) + if (tc->tile.revealed || tc->tile.prerevealed) { - Quad *q = tile->q; - if (q) + if(tc->q.isCoordinateInsideWorld(core->mouse.position)) { - d = (q->getWorldPosition() - core->mouse.position).getSquaredLength2D(); + d = (tc->getWorldPosition() - core->mouse.position).getSquaredLength2D(); - if (q->isCoordinateInsideWorld(core->mouse.position) && (sd < 0 || d < sd)) + if (sd < 0 || d < sd) { sd = d; - selectedTile = tile; + hoverTile = tc; } } } @@ -867,42 +681,27 @@ void WorldMapRender::onUpdate(float dt) if (!editorActive) { - if (activeTile) + if (hoverTile) { - areaLabel3->setText(stringbank.get(activeTile->stringIndex)); - } + areaLabel->setText(stringbank.get(hoverTile->tile.stringIndex)); - if (selectedTile) - { - areaLabel->setText(stringbank.get(selectedTile->stringIndex)); - - if (activeTile && !mover && !dsq->isNested() && isCursorOffHud()) + if (selectedTile && !mover && !dsq->isNested() && isCursorOffHud()) { if (!core->mouse.buttons.left && mb) { - if ((activeTile != selectedTile) && selectedTile->q) + if (selectedTile != hoverTile) { - debugLog("selectedTile: " + selectedTile->name); + debugLog("selectedTile: " + hoverTile->tile.name); - activeTile = selectedTile; - activeQuad = activeTile->q; + selectedTile = hoverTile; - if (activeQuad) - { - dsq->clickRingEffect(activeQuad->getWorldPosition(), 0); - dsq->sound->playSfx("bubble-lid"); - dsq->sound->playSfx("menuselect"); - } + dsq->clickRingEffect(hoverTile->getWorldPosition(), 0); - int num = dsq->continuity.worldMap.getNumWorldMapTiles(); - for (int i = 0; i < num; i++) - { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - setProperTileColor(tile); - } + dsq->sound->playSfx("bubble-lid"); + dsq->sound->playSfx("menuselect"); - setVis(selectedTile); + updateAllTilesColor(); } mb = false; @@ -921,6 +720,11 @@ void WorldMapRender::onUpdate(float dt) { areaLabel->setText(""); } + + if (selectedTile) + { + areaLabel3->setText(stringbank.get(selectedTile->tile.stringIndex)); + } } } @@ -1013,15 +817,6 @@ void WorldMapRender::onUpdate(float dt) } } - if (activeTile && activeTile->layer == 1) - { - zoomMax = interiorZoomMax; - } - else - { - zoomMax = exteriorZoomMax; - } - float scrollAmount = 0.2f; if (core->mouse.scrollWheelChange) @@ -1059,8 +854,9 @@ void WorldMapRender::onUpdate(float dt) { if (editorActive) { - if (activeTile && activeQuad) + if (selectedTile) { + WorldMapTile& t = selectedTile->tile; float amt = dt*4; float a2 = dt*0.1f; @@ -1069,18 +865,18 @@ void WorldMapRender::onUpdate(float dt) if (core->getCtrlState()) a2 *= 10.0f; if (core->getKeyState(KEY_UP)) - activeTile->scale2 += -a2; + t.scale2 += -a2; if (core->getKeyState(KEY_DOWN)) - activeTile->scale2 += a2; + t.scale2 += a2; } else if (core->getAltState()) { if (core->getCtrlState()) a2 *= 10.0f; if (core->getKeyState(KEY_UP)) - activeTile->scale += -a2; + t.scale += -a2; if (core->getKeyState(KEY_DOWN)) - activeTile->scale += a2; + t.scale += a2; } else { @@ -1089,25 +885,27 @@ void WorldMapRender::onUpdate(float dt) amt *= 50; } if (core->getKeyState(KEY_LEFT)) - activeTile->gridPos += Vector(-amt, 0); + t.gridPos += Vector(-amt, 0); if (core->getKeyState(KEY_RIGHT)) - activeTile->gridPos += Vector(amt, 0); + t.gridPos += Vector(amt, 0); if (core->getKeyState(KEY_UP)) - activeTile->gridPos += Vector(0, -amt); + t.gridPos += Vector(0, -amt); if (core->getKeyState(KEY_DOWN)) - activeTile->gridPos += Vector(0, amt); + t.gridPos += Vector(0, amt); } - if (core->getKeyState(KEY_F2)) + bool isf2 = core->getKeyState(KEY_F2); + if (isf2 && !wasEditorSaveDown) { dsq->continuity.worldMap.save(); } + wasEditorSaveDown = isf2; - activeQuad->position = activeTile->gridPos; - activeQuad->scale = Vector(0.25f*activeTile->scale2, 0.25f*activeTile->scale2); - if(activeQuad->texture) + //selectedTile->position = t.gridPos; + //activeQuad->scale = Vector(0.25f*activeTile->scale2, 0.25f*activeTile->scale2); + /*if(activeQuad->texture) activeQuad->setWidthHeight(activeQuad->texture->width*activeTile->scale, // FG: HACK force resize proper - activeQuad->texture->height*activeTile->scale); + activeQuad->texture->height*activeTile->scale);*/ } updateEditor(); } @@ -1115,9 +913,7 @@ void WorldMapRender::onUpdate(float dt) } else { - if (!dsq->isInCutscene() && game->avatar && activeTile - && !game->sceneEditor.isOn() - ) + if (!dsq->isInCutscene() && game->avatar && playerTile && !game->sceneEditor.isOn()) { const float screenWidth = core->getVirtualWidth() * core->invGlobalScale; const float screenHeight = core->getVirtualHeight() * core->invGlobalScale; @@ -1135,64 +931,13 @@ void WorldMapRender::onUpdate(float dt) const int y0 = int(tl.y * MAPVIS_SUBDIV); const int x1 = int(br.x * MAPVIS_SUBDIV); const int y1 = int(br.y * MAPVIS_SUBDIV); - activeTile->markVisited(x0, y0, x1, y1); - if (activeQuad) - { - if (visMethod == VIS_VERTEX) - { - for (int x = x0; x <= x1; x++) - { - for (int y = y0; y <= y1; y++) - { - activeQuad->setDrawGridAlpha(x, y, visibleMapSegAlpha); - } - } - } - else if (visMethod == VIS_WRITE) - { - // Do nothing -- we regenerate the tile on opening the map. - } - } + playerTile->tile.markVisited(x0, y0, x1, y1); } } lastMousePosition = core->mouse.position; } -Vector WorldMapRender::getAvatarWorldMapPosition() -{ - Vector p; - if (originalActiveTile && game && game->avatar) - { - Vector p = game->avatar->position; - if (!game->avatar->warpInLocal.isZero()) - { - p = game->avatar->warpInLocal; - } - return getWorldToTile(originalActiveTile, p, true, true); - } - return p; -} - -Vector WorldMapRender::getWorldToTile(WorldMapTile *tile, Vector position, bool fromCenter, bool tilePos) -{ - if(!tile->q) - return Vector(); - const float sizew = (float)tile->q->texture->width; - const float halfw = sizew / 2.0f; - const float sizeh = (float)tile->q->texture->height; - const float halfh = sizeh / 2.0f; - Vector p; - p = Vector((position.x/TILE_SIZE) / (sizew*tile->scale), (position.y/TILE_SIZE) / (sizeh*tile->scale)); - p.x *= sizew*tile->scale*0.25f*tile->scale2; - p.y *= sizeh*tile->scale*0.25f*tile->scale2; - if (fromCenter) - p -= Vector((halfw*tile->scale)*(0.25f*tile->scale2), (halfh*tile->scale)*(0.25f*tile->scale2)); - if (tilePos) - p += tile->gridPos; - return p; -} - bool WorldMapRender::isOn() { return this->on; @@ -1201,42 +946,87 @@ bool WorldMapRender::isOn() GemMover *WorldMapRender::addGem(GemData *gemData) { GemMover *g = new GemMover(gemData); - addChild(g, PM_POINTER); + WorldMapTileContainer *tc = NULL; + if(!gemData->global && !gemData->mapName.empty()) + tc = getTileByName(gemData->mapName.c_str()); + + // tile-local gems also get added as a child to their tile + if(tc) + tc->addGem(g); + else + addChild(g, PM_POINTER); gemMovers.push_back(g); g->update(0); return g; } +void WorldMapRender::updateGem(const GemData* gemData) +{ + GemMover *m = getGem(gemData); + if(!m) + return; + + if(!gemData->global) + { + WorldMapTileContainer *src = getTileWithGem(gemData); + if(src) + { + if(nocasecmp(gemData->mapName, src->tile.name)) + { + WorldMapTileContainer *dst = getTileByName(gemData->mapName.c_str()); + if(dst) + { + GemMover *gm = src->removeGem(gemData); + if(gm) + dst->addGem(gm); + } + } + } + } + + m->refresh(); +} + void WorldMapRender::removeGem(GemMover *gem) { dsq->continuity.removeGemData(gem->getGemData()); - fixGems(); + for(size_t i = 0; i < gemMovers.size(); ++i) + if(gemMovers[i] == gem) + { + gemMovers[i] = gemMovers.back(); + gemMovers.pop_back(); + break; + } + WorldMapTileContainer *tc = getTileWithGem(gem->getGemData()); + if(tc) + tc->removeGem(gem); + gem->safeKill(); } -void WorldMapRender::fixGems() +void WorldMapRender::removeGem(const GemData* gemData) { - for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++) - { - removeChild(*i); - (*i)->destroy(); - delete *i; - } - gemMovers.clear(); - addAllGems(); + if(GemMover *gm = getGem(gemData)) + removeGem(gm); } -void WorldMapRender::addAllGems() +GemMover* WorldMapRender::getGem(const GemData* gemData) const { - size_t c = 0; - for (Continuity::Gems::reverse_iterator i = dsq->continuity.gems.rbegin(); i != dsq->continuity.gems.rend(); i++) + for(size_t i = 0; i < gemMovers.size(); ++i) + if(gemMovers[i]->getGemData() == gemData) + return gemMovers[i]; + return NULL; +} + +WorldMapTileContainer* WorldMapRender::getTileWithGem(const GemData* gemData) const +{ + WorldMapTileContainer *src = NULL; + for(size_t i = 0; i < tiles.size(); ++i) { - GemMover *g = addGem(&(*i)); - if (c == dsq->continuity.gems.size()-1 || i->blink) - g->setBlink(true); - else - g->setBlink(false); - c++; + WorldMapTileContainer *tc = tiles[i]; + if(tc->getGem(gemData)) + return tc; } + return NULL; } void WorldMapRender::toggle(bool turnON) @@ -1278,44 +1068,50 @@ void WorldMapRender::toggle(bool turnON) core->sound->playSfx("menu-open"); - originalActiveTile = activeTile; - - if (activeTile) + if (playerTile) { - internalOffset = -activeTile->gridPos; - if (activeTile->layer == 1) + selectedTile = playerTile; + + areaLabel2->setText(stringbank.get(playerTile->tile.stringIndex)); // FIXME: and if playerTile == NULL? + + internalOffset = -playerTile->tile.gridPos; + if (playerTile->layer == 1) scale = Vector(1.5,1.5); else scale = Vector(1,1); - if (visMethod == VIS_WRITE) - { - // Texture isn't updated while moving, so force an update here - clearVis(activeTile); - setVis(activeTile); - } } + for(size_t i = 0; i < tiles.size(); ++i) + tiles[i]->refresh(); + xMin = xMax = -internalOffset.x; yMin = yMax = -internalOffset.y; - for (size_t i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++) + for (size_t i = 0; i < tiles.size(); ++i) { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - if (tile && (tile->revealed || tile->prerevealed) && tile->q) + WorldMapTileContainer *tc = tiles[i]; + const WorldMapTile& t = tc->tile; + if (t.revealed || t.prerevealed) { - Quad *q = tile->q; - const float width = q->getWidth() * q->scale.x; - const float height = q->getHeight() * q->scale.y; - if (xMin > tile->gridPos.x - width/2) - xMin = tile->gridPos.x - width/2; - if (xMax < tile->gridPos.x + width/2) - xMax = tile->gridPos.x + width/2; - if (yMin > tile->gridPos.y - height/2) - yMin = tile->gridPos.y - height/2; - if (yMax < tile->gridPos.y + height/2) - yMax = tile->gridPos.y + height/2; + Quad& q = tc->q; + const float width = q.getWidth() * q.scale.x; + const float height = q.getHeight() * q.scale.y; + const float tx = t.gridPos.x; + const float ty = t.gridPos.y; + const float w2 = width * 0.5f; + const float h2 = height * 0.5f; + if (xMin > tx - w2) + xMin = tx - w2; + if (xMax < tx + w2) + xMax = tx + w2; + if (yMin > ty - h2) + yMin = ty - h2; + if (yMax < ty + h2) + yMax = ty + h2; } } + updateAllTilesColor(); + alpha.interpolateTo(1, 0.2f); @@ -1326,7 +1122,9 @@ void WorldMapRender::toggle(bool turnON) addHintQuad2->alpha.interpolateTo(1.0f, 0.2f); helpButton->alpha.interpolateTo(1.0f, 0.2f); - addAllGems(); + assert(gemMovers.empty()); + for (Continuity::Gems::iterator i = dsq->continuity.gems.begin(); i != dsq->continuity.gems.end(); i++) + addGem(&(*i)); for (Continuity::Beacons::reverse_iterator i = dsq->continuity.beacons.rbegin(); i != dsq->continuity.beacons.rend(); i++) { @@ -1346,31 +1144,8 @@ void WorldMapRender::toggle(bool turnON) { inputDelay = 0.5; - if (originalActiveTile && activeTile) - { - activeTile = originalActiveTile; - activeQuad = activeTile->q; - } - - int num = dsq->continuity.worldMap.getNumWorldMapTiles(); - for (int i = 0; i < num; i++) - { - WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i); - setProperTileColor(tile); - } - - // again to set the correct color - // lame, don't do that - //setVis(activeTile); - - // just set the color - if (activeTile && activeTile->q) - { - activeTile->q->color = Vector(1,1,1); - activeTile->q->alphaMod = 1; - } - - + for (size_t i = 0; i < tiles.size(); i++) + tiles[i]->gems.clear(); core->sound->playSfx("Menu-Close"); @@ -1402,16 +1177,25 @@ void WorldMapRender::toggle(bool turnON) void WorldMapRender::createGemHint(const std::string &gfx) { + if(!playerTile) + { + sound->playSfx("denied"); + debugLog("WorldMapRender::createGemHint can't create gem, no player tile"); + return; + } std::string useString = dsq->getUserInputString(stringbank.get(860), "", true); if (!useString.empty()) { doubleClickTimer = 0; GemData *g = dsq->continuity.pickupGem(gfx, false); - g->canMove = 1; - g->pos = getAvatarWorldMapPosition(); - g->userString = useString; - addGem(g); - fixGems(); + if(g) + { + g->canMove = 1; + g->global = true; + g->pos = playerTile->worldPosToMapPos(game->avatar->getPositionForMap()); + g->userString = useString; + addGem(g); + } } } @@ -1419,11 +1203,12 @@ void WorldMapRender::updateEditor() { std::ostringstream os; os << "EDITING..."; - if(activeTile) + if(selectedTile) { - os << " (" << activeTile->name << ")" << std::endl; - os << "x=" << activeTile->gridPos.x << "; y=" << activeTile->gridPos.y << std::endl; - os << "scale=" << activeTile->scale << "; scale2=" << activeTile->scale2; + const WorldMapTile& t = selectedTile->tile; + os << " (" << t.name << ")" << std::endl; + os << "x=" << t.gridPos.x << "; y=" << t.gridPos.y << std::endl; + os << "scale=" << t.scale << "; scale2=" << t.scale2; } areaLabel->setText(os.str()); } @@ -1446,6 +1231,9 @@ void WorldMapRender::action (int id, int state, int source, InputDevice device) { updateEditor(); } + + for(size_t i = 0; i < tiles.size(); ++i) + tiles[i]->q.renderBorder = editorActive; } } @@ -1467,16 +1255,141 @@ void WorldMapRender::action (int id, int state, int source, InputDevice device) { for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++) { - if ((*i)->canMove && (core->mouse.position - (*i)->getWorldPosition()).isLength2DIn(GEM_GRAB)) + if ((*i)->canMove() && (core->mouse.position - (*i)->getWorldPosition()).isLength2DIn(GEM_GRAB)) { removeGem(*i); break; } } } - } - - } } + + + + +WorldMapTileContainer::WorldMapTileContainer(WorldMapTile& tile) + : tile(tile) +{ + addChild(&q, PM_STATIC); + q.borderAlpha = 0.7f; + q.renderBorderColor = Vector(1, 0.5f, 0.5f); + assert(tile.generatedTex); + q.setTexturePointer(tile.generatedTex); +} + +WorldMapTileContainer::~WorldMapTileContainer() +{ +} + +void WorldMapTileContainer::refresh() +{ + if(tile.dirty) + { + bool usegen = !tile.prerevealed && tile.updateDiscoveredTex(); + q.setTexturePointer(usegen ? tile.generatedTex : tile.originalTex); // updates width, height + tile.dirty = false; + } +} + +void WorldMapTileContainer::removeGems() +{ + for(size_t i = 0; i < gems.size(); ++i) + removeChild(gems[i]); + gems.clear(); +} + +void WorldMapTileContainer::addGem(GemMover* gem) +{ + addChild(gem, PM_POINTER); + gems.push_back(gem); +} + +bool WorldMapTileContainer::removeGem(GemMover* gem) +{ + for(size_t i = 0; i < gems.size(); ++i) + if(gems[i] == gem) + { + gems[i] = gems.back(); + gems.pop_back(); + removeChild(gem); + return true; + } + return false; +} + +GemMover *WorldMapTileContainer::removeGem(const GemData* gem) +{ + for(size_t i = 0; i < gems.size(); ++i) + { + GemMover *gm = gems[i]; + if(gm->getGemData() == gem) + { + gems[i] = gems.back(); + gems.pop_back(); + removeChild(gm); + return gm; + } + } + return NULL; +} + +GemMover* WorldMapTileContainer::getGem(const GemData* gemData) const +{ + for(size_t i = 0; i < gems.size(); ++i) + if(gems[i]->getGemData() == gemData) + return gems[i]; + return NULL; +} + +void WorldMapTileContainer::updateGems() +{ + const float invscale = 1.0f / this->scale.x; + for(size_t i = 0; i < gems.size(); ++i) + { + Quad *q = gems[i]; + // Make sure the gems always have the same size no matter the container's scale + q->scale.x = invscale; + q->scale.y = invscale; + } +} + + + +Vector WorldMapTileContainer::worldPosToTilePos(const Vector& position) const +{ + // Notes: TILE_SIZE in the world is 1 pixel in the map template png. + // scale is intended to adjust for a texture with a lower resolution than the map template, + // eg. if a 1024x1024 png was resized to 512x512 the scale is used to compensate; + // and scale2 is used to scale the entirety of the map tile with gems and everything on it + const float sizew = (float)q.texture->width; + const float sizeh = (float)q.texture->height; + const float invsz = 1.0f / TILE_SIZE; + + const float f = tileScaleFactor * tile.scale2; // scale2 is off the same way as scale + const float g = f * tile.scale * 0.5f; // half the texture size + Vector p = position * invsz * f; + p -= Vector(sizew*g, sizeh*g); + return p; +} + +Vector WorldMapTileContainer::worldPosToMapPos(const Vector& p) const +{ + return worldPosToTilePos(p) + tile.gridPos; +} + +void WorldMapTileContainer::onUpdate(float dt) +{ + position = tile.gridPos; + scale.x = tile.scale2; + scale.y = tile.scale2; + + q.scale.x = tile.scale * tileScaleFactor; + q.scale.y = tile.scale * tileScaleFactor; + q.renderQuad = tile.revealed || tile.prerevealed; + + updateGems(); + + RenderObject::onUpdate(dt); +} diff --git a/Aquaria/WorldMapRender.h b/Aquaria/WorldMapRender.h new file mode 100644 index 0000000..f5e7f1f --- /dev/null +++ b/Aquaria/WorldMapRender.h @@ -0,0 +1,90 @@ +#ifndef WORLDMAPRENDER_H +#define WORLDMAPRENDER_H + +#include "Quad.h" +#include "ActionMapper.h" + +class GemMover; +struct WorldMapTile; +struct GemData; +class BitmapText; +class AquariaMenuItem; +struct WorldMap; +class Gradient; + + +// This is used for properly positioning the tile and gems on top of it. +// Affected by scale2 -- gems also move with scale2. +class WorldMapTileContainer : public RenderObject +{ +public: + WorldMapTileContainer(WorldMapTile& tile); + virtual ~WorldMapTileContainer(); + + virtual void onUpdate(float dt) OVERRIDE; + + void refresh(); // Called whenever we need to prepare for rendering + + void removeGems(); + void addGem(GemMover *gem); + bool removeGem(GemMover *gem); + GemMover *removeGem(const GemData *gem); + GemMover *getGem(const GemData *gemData) const; + void updateGems(); + + Vector worldPosToTilePos(const Vector& p) const; + Vector worldPosToMapPos(const Vector& p) const; + + WorldMapTile& tile; + Quad q; // The actual world map tile, additionally affected by scale (NOT scale2), and always at (0, 0) + std::vector gems; +}; + +class WorldMapRender : public RenderObject, public ActionMapper +{ +public: + WorldMapRender(WorldMap& wm); + virtual ~WorldMapRender(); + void init(); + void toggle(bool on); + bool isOn(); + void setProperTileColor(WorldMapTileContainer& wt); + void action(int id, int state, int source, InputDevice device); + GemMover* addGem(GemData *gemData); + void updateGem(const GemData *gemData); + void removeGem(GemMover *gemMover); + void removeGem(const GemData *gemData); + GemMover *getGem(const GemData *gemData) const; + WorldMapTileContainer *getTileWithGem(const GemData *gemData) const; + void bindInput(); + void createGemHint(const std::string &gfx); + void onToggleHelpScreen(); + bool isCursorOffHud(); + void updateAllTilesColor(); + WorldMapTileContainer *getCurrentTile() { return playerTile; } + bool getWorldToPlayerTile(Vector& dst, const Vector& pos, bool global) const; + WorldMapTileContainer *getTileByName(const char *name) const; + WorldMapTileContainer * setCurrentMap(const char *mapname); + +protected: + Quad *addHintQuad1, *addHintQuad2; + AquariaMenuItem *helpButton; + float doubleClickTimer; + float inputDelay; + BitmapText *areaLabel, *areaLabel2, *areaLabel3; + WorldMapTileContainer *playerTile; // tile where the player is located + WorldMapTileContainer *selectedTile; // starts out == playerTile but changed on selection + bool on; + void onUpdate(float dt); + bool mb, wasEditorSaveDown; + Vector lastMousePosition; // See FIXME in WorldMapRender.cpp --achurch + void updateEditor(); + std::vector tiles; + WorldMap& worldmap; + Quad *tophud; + Gradient *underlay; +}; + + + +#endif diff --git a/Aquaria/WorldMapTiles.cpp b/Aquaria/WorldMapTiles.cpp index 11d3bc4..a6ba209 100644 --- a/Aquaria/WorldMapTiles.cpp +++ b/Aquaria/WorldMapTiles.cpp @@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "DSQ.h" #include "ttvfs_stdio.h" #include +#include "stb_image_resize.h" #if MAPVIS_SUBDIV % 8 != 0 #error MAPVIS_SUBDIV must be a multiple of 8 @@ -39,23 +40,19 @@ WorldMapTile::WorldMapTile() scale = scale2 = 1; layer = 0; index = -1; - data = 0; - q = 0; stringIndex = 0; + dirty = true; + generatedTex = new Texture; } WorldMapTile::~WorldMapTile() { - delete[] data; } void WorldMapTile::markVisited(int left, int top, int right, int bottom) { - if (data == 0) - { - data = new unsigned char[dataSize]; - memset(data, 0, dataSize); - } + if(visited.empty()) + visited.init(rowSize, MAPVIS_SUBDIV); if (left < 0) left = 0; @@ -66,19 +63,32 @@ void WorldMapTile::markVisited(int left, int top, int right, int bottom) if (bottom > MAPVIS_SUBDIV - 1) bottom = MAPVIS_SUBDIV - 1; + // Accumulate bits that were changed + unsigned changed = 0; + for (int y = top; y <= bottom; y++) { + unsigned char *row = visited.row(y); for (int x = left; x <= right; x++) { - data[y*rowSize + x/8] |= 1 << (x%8); + unsigned char& val = row[x/8]; + unsigned char add = 1 << (x%8); + changed |= (val ^ add) & add; + val |= add; } } + + if(changed) + { + //debugLog("worldmap[" + name + "] visited something new"); + dirty = true; // Only mark as dirty if we've uncovered something new + } } // Convert the data array to string format for saving. -void WorldMapTile::dataToString(std::ostringstream &os) +void WorldMapTile::dataToString(std::ostringstream &os) const { - if (!data) + if (visited.empty()) { os << "0 0"; return; @@ -88,6 +98,7 @@ void WorldMapTile::dataToString(std::ostringstream &os) char *outbuf = new char[((dataSize+2)/3)*4 + 1]; char *ptr = outbuf; + const unsigned char * const data = visited.data(); unsigned int i; for (i = 0; i+3 <= dataSize; i += 3, ptr += 4) @@ -121,18 +132,21 @@ void WorldMapTile::dataToString(std::ostringstream &os) unsigned int count = 0; std::ostringstream tempStream; - unsigned char *ptr = data; - for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++, ptr += rowSize) + + for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++) { + const unsigned char *row = visited.row(y); for (unsigned int x = 0; x < MAPVIS_SUBDIV; x += 8) { - unsigned char dataByte = ptr[x/8]; - for (unsigned int x2 = 0; x2 < 8; x2++) + if(unsigned char dataByte = row[x/8]) { - if (dataByte & (1 << x2)) + for (unsigned int x2 = 0; x2 < 8; x2++) { - tempStream << " " << (x+x2) << " " << y; - count++; + if (dataByte & (1 << x2)) + { + tempStream << " " << (x+x2) << " " << y; + count++; + } } } } @@ -144,10 +158,10 @@ void WorldMapTile::dataToString(std::ostringstream &os) } // Parse a string from a save file and store in the data array. -void WorldMapTile::stringToData(std::istringstream &is) +void WorldMapTile::stringToData(SimpleIStringStream &is) { - delete[] data; - data = 0; + dirty = true; + visited.init(rowSize, MAPVIS_SUBDIV); int subdiv; std::string countOrType; @@ -182,15 +196,13 @@ void WorldMapTile::stringToData(std::istringstream &is) return; } - data = new unsigned char[dataSize]; - memset(data, 0, dataSize); if (countOrType == "b") // Raw bitmap (base64-encoded) { std::string encodedData = ""; is >> encodedData; const char *in = encodedData.c_str(); - unsigned char *out = data; - unsigned char * const top = data + dataSize; + unsigned char *out = visited.data(); + unsigned char * const top = out + dataSize; while (in[0] != 0 && in[1] != 0 && out < top) { unsigned char ch0, ch1, ch2, ch3; @@ -222,6 +234,8 @@ void WorldMapTile::stringToData(std::istringstream &is) } else // List of coordinate pairs { + visited.fill(0); + int count = 0; std::istringstream is2(countOrType); is2 >> count; @@ -231,11 +245,88 @@ void WorldMapTile::stringToData(std::istringstream &is) int x, y; is >> x >> y; if (x >= 0 && x < MAPVIS_SUBDIV && y >= 0 && y < MAPVIS_SUBDIV) - data[y*rowSize + x/8] |= 1 << (x%8); + visited(x/8, y) |= 1 << (x%8); } } } +ImageData WorldMapTile::generateAlphaImage(size_t w, size_t h) +{ + ImageData ret = {0}; + if(visited.empty()) + return ret; + + // stbir can't deal with 1-bit images, so let's make a 1-channel black or white image first + Array2d tmp(MAPVIS_SUBDIV, MAPVIS_SUBDIV); + unsigned char *dst = tmp.data(); + for(size_t y = 0; y < MAPVIS_SUBDIV; ++y) + { + const unsigned char * const src = visited.row(y); + for(size_t x = 0; x < MAPVIS_SUBDIV; x += 8) + { + unsigned c = src[x/8]; + for(size_t bit = 0; bit < 8; ++bit) + *dst++ = (c & (1 << bit)) ? 0xff : 0; + } + } + + std::ostringstream os; + os << "worldmap[" << name << "] Generate alpha from vis data, resize to (" << w << ", " << h << ")"; + debugLog(os.str()); + + ret.pixels = (unsigned char*)malloc(w * h); + if(ret.pixels) + { + ret.w = w; + ret.h = h; + ret.channels = 1; + + stbir_resize_uint8_generic( + tmp.data(), MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0, + ret.pixels, w, h, 0, + 1, STBIR_ALPHA_CHANNEL_NONE, 0, + STBIR_EDGE_CLAMP, STBIR_FILTER_TRIANGLE, STBIR_COLORSPACE_LINEAR, NULL + ); + } + + return ret; +} + +// If there was a shader to use one texture for colors and another for alpha this wouldn't be needed +// TODO: If we ever get this far, do this with shaders. Software resize sucks so bad... +bool WorldMapTile::updateDiscoveredTex() +{ + int w, h; + size_t sz; + const unsigned char * texbuf = originalTex->getBufferAndSize(&w, &h, &sz); + std::vector tmp(sz); + { + const ImageData aimg = generateAlphaImage(w, h); + if(!aimg.pixels) + return false; + const unsigned char *ap = aimg.pixels; + unsigned char *dst = &tmp[0]; + for(size_t i = 0; i < sz; i += 4) + { + *dst++ = *texbuf++; + *dst++ = *texbuf++; + *dst++ = *texbuf++; + *dst++ = (*texbuf++ * *ap++) >> 8; // inaccurate fixed-point multiplication; ends up 0xfe as the highest possible value but whatev + } + free(aimg.pixels); + } + + ImageData up; + up.pixels = &tmp[0]; + up.channels = 4; + up.w = w; + up.h = h; + + generatedTex->upload(up, true); + return true; +} + + WorldMap::WorldMap() { @@ -338,26 +429,3 @@ WorldMapTile *WorldMap::getWorldMapTileByIndex(int index) } return 0; } - - - -void WorldMap::hideMap() -{ - for (size_t i = 0; i < worldMapTiles.size(); i++) - { - worldMapTiles[i].revealed = false; - } -} - -size_t WorldMap::getNumWorldMapTiles() -{ - return worldMapTiles.size(); -} - -WorldMapTile *WorldMap::getWorldMapTile(size_t index) -{ - if (index >= worldMapTiles.size()) return 0; - - return &worldMapTiles[index]; -} - diff --git a/BBGE/Texture.cpp b/BBGE/Texture.cpp index 6f05c66..f8fdd20 100644 --- a/BBGE/Texture.cpp +++ b/BBGE/Texture.cpp @@ -100,11 +100,7 @@ void Texture::unload() glDeleteTextures(1, &gltexid); gltexid = 0; } - if(_pixbuf) - { - free(_pixbuf); - _pixbuf = NULL; - } + _freePixbuf(); } size_t Texture::sizeBytes() const @@ -112,6 +108,26 @@ size_t Texture::sizeBytes() const return size_t(width) * size_t(height) * 4; } +bool Texture::uploadAndKeep(ImageData& img, bool mipmap) +{ + bool ok = upload(img, mipmap); // this also clears pixbuf + if(ok) + { + _pixbuf = img.pixels; + img.pixels = NULL; + } + return ok; +} + +void Texture::_freePixbuf() +{ + if(_pixbuf) + { + free(_pixbuf); + _pixbuf = NULL; + } +} + void Texture::apply() const { glBindTexture(GL_TEXTURE_2D, gltexid); @@ -223,6 +239,8 @@ bool Texture::upload(const ImageData& img, bool mipmap) width = img.w; height = img.h; + _mipmap = mipmap; + _freePixbuf(); return true; } diff --git a/BBGE/Texture.h b/BBGE/Texture.h index 1e2348e..cf43716 100644 --- a/BBGE/Texture.h +++ b/BBGE/Texture.h @@ -70,10 +70,12 @@ public: std::string name, filename; bool upload(const ImageData& img, bool mipmap); + bool uploadAndKeep(ImageData& img, bool mipmap); bool success; protected: + void _freePixbuf(); int ow, oh; bool _mipmap;