diff --git a/Aquaria/Continuity.cpp b/Aquaria/Continuity.cpp index a8cbf83..ff54e5f 100644 --- a/Aquaria/Continuity.cpp +++ b/Aquaria/Continuity.cpp @@ -2275,6 +2275,19 @@ void Continuity::upgradeHealth() a->heal(maxHealth - a->health); } +static Vector gemGlobalPos(const GemData& g) +{ + if(g.global || g.mapName.empty()) + return g.pos; + + // Legacy gem storage stores global position only, so tile-local coords need conversion + const WorldMapTileContainer *tc = game->worldMapRender->getTileByName(g.mapName.c_str()); + if(!tc) + return g.pos; + + return tc->worldPosToMapPos(g.pos); +} + void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, int scrShotWidth, int scrShotHeight) { refreshAvatarData(game->avatar); @@ -2326,7 +2339,8 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, os << this->gems.size() << " "; for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++) { - os << (*i).name << " " << (*i).pos.x << " " << (*i).pos.y << " "; + Vector gpos = gemGlobalPos(*i); + os << (*i).name << " " << gpos.x << " " << gpos.y << " "; os << (*i).canMove << " "; hasUserString = !(*i).userString.empty(); @@ -2345,13 +2359,15 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, os << this->gems.size() << " "; for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++) { + Vector gpos = gemGlobalPos(*i); + os << (*i).name << " "; bool hasMapName = !(*i).mapName.empty(); os << hasMapName << " "; if(hasMapName) os << (*i).mapName << " "; // warning: this will fail to load if the map name contains whitespace - os << (*i).pos.x << " " << (*i).pos.y << " "; + os << gpos.x << " " << gpos.y << " "; os << (*i).canMove << " "; bool hasUserString = !(*i).userString.empty(); @@ -2362,9 +2378,29 @@ void Continuity::saveFile(int slot, Vector position, unsigned char *scrShotData, gems->SetAttribute("d", os.str().c_str()); } - // newest format; is aware if tile-relative position + // newest format; is aware of tile-relative position + for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++) { + const GemData& g = *i; + XMLElement *gx = doc.NewElement("Gem"); + gems->InsertEndChild(gx); + gx->SetAttribute("name", g.name.c_str()); + gx->SetAttribute("x", g.pos.x); // always store position as-is + gx->SetAttribute("y", g.pos.y); + gx->SetAttribute("id", g.id); + gx->SetAttribute("global", (unsigned)g.global); + + if(g.blink) + gx->SetAttribute("blink", (unsigned)g.blink); + if(g.canMove) + gx->SetAttribute("canMove", (unsigned)g.canMove); + if(!g.mapName.empty()) + gx->SetAttribute("mapName", g.mapName.c_str()); + if(!g.userString.empty()) + gx->SetAttribute("userString", g.userString.c_str()); + if(g.isPlayer) + gx->SetAttribute("isPlayer", (unsigned)g.isPlayer); } } @@ -2768,10 +2804,49 @@ bool Continuity::loadFile(int slot) XMLElement *gems = doc.FirstChildElement("Gems"); if (gems) { - if (gems->Attribute("a")) + bool needGemFix = true; + // New gem storage. All other stores are ignored if a tag is present, + // since the old format is stored as well for backwards compat. + // Old versions will not load this and instead use one of the fallbacks below + XMLElement *gem = gems->FirstChildElement("Gem"); + if(gem) { - std::string s = gems->Attribute("a"); - std::istringstream is(s); + needGemFix = false; + do + { + GemData g; + g.blink = gem->IntAttribute("blink"); + g.canMove = gem->IntAttribute("canMove"); + g.global = true; + if(const char *mapname = gem->Attribute("mapName")) + { + g.mapName = mapname; + g.global = false; + } + if(gem->Attribute("global")) + g.global = gem->IntAttribute("global"); + g.isPlayer = gem->IntAttribute("isPlayer"); + if(const char *name = gem->Attribute("name")) + g.name = name; + g.pos.x = gem->FloatAttribute("x"); + g.pos.y = gem->FloatAttribute("y"); + if(const char *user = gem->Attribute("userString")) + g.userString = user; + if(gem->Attribute("id")) + g.id = gem->IntAttribute("id"); + + this->gems.push_back(g); + + gem = gem->NextSiblingElement("Gem"); + } + while(gem); + + } + // ---- Legacy formats ----- + // [name x y]... + else if (const char *attr = gems->Attribute("a")) + { + SimpleIStringStream is(attr, SimpleIStringStream::REUSE); GemData g; while (is >> g.name) { @@ -2779,10 +2854,10 @@ bool Continuity::loadFile(int slot) this->gems.push_back(g); } } - else if (gems->Attribute("b")) + // [name x y canMove hasUserString (userString)]... + else if (const char *attr = gems->Attribute("b")) { - std::string s = gems->Attribute("b"); - std::istringstream is(s); + SimpleIStringStream is(attr, SimpleIStringStream::REUSE); GemData g; bool hasUserString = false; while (is >> g.name) @@ -2804,11 +2879,11 @@ bool Continuity::loadFile(int slot) this->gems.push_back(g); } } - // num [name mapX mapY canMove hasUserString (userString)] - else if (gems->Attribute("c")) + // This is the format used by the original PC release + // num [name mapX mapY canMove hasUserString (userString)]... + else if (const char *attr = gems->Attribute("c")) { - std::string s = gems->Attribute("c"); - std::istringstream is(s); + SimpleIStringStream is(attr, SimpleIStringStream::REUSE); int num = 0; is >> num; @@ -2816,11 +2891,6 @@ bool Continuity::loadFile(int slot) bool hasUserString = false; GemData g; - std::ostringstream os; - os << "continuity num: [" << num << "]" << std::endl; - os << "data: [" << s << "]" << std::endl; - debugLog(os.str()); - for (int i = 0; i < num; i++) { g.pos = Vector(0,0,0); @@ -2847,11 +2917,11 @@ bool Continuity::loadFile(int slot) debugLog(os.str()); } } + // This is the format used by the android version // num [name hasMapName (mapName) mapX mapY canMove hasUserString (userString)] - else if (gems->Attribute("d")) + else if (const char *attr = gems->Attribute("d")) { - std::string s = gems->Attribute("d"); - std::istringstream is(s); + SimpleIStringStream is(attr, SimpleIStringStream::REUSE); int num = 0; is >> num; @@ -2860,11 +2930,6 @@ bool Continuity::loadFile(int slot) bool hasMapName = false; GemData g; - std::ostringstream os; - os << "continuity num: [" << num << "]" << std::endl; - os << "data: [" << s << "]" << std::endl; - debugLog(os.str()); - for (int i = 0; i < num; i++) { g.pos = Vector(0,0,0); @@ -2880,7 +2945,7 @@ bool Continuity::loadFile(int slot) if(hasMapName) is >> g.mapName; - is >> g.pos.x >> g.pos.y; + is >> g.pos.x >> g.pos.y; // FIXME: check whether this is local coords or global coords is >> g.canMove; is >> hasUserString; @@ -2895,6 +2960,25 @@ bool Continuity::loadFile(int slot) debugLog(os.str()); } } + + // Slightly shoddy fix because we need to figure out which gem is the player gem but we don't know + if(needGemFix) + { + debugLog("Apply legacy gem fix"); + bool doPlayerGem = true; + for(Gems::iterator it = this->gems.begin(); it != this->gems.end(); ++it) + { + GemData& g = *it; + g.global = g.mapName.empty(); + if(doPlayerGem && !nocasecmp(g.name.c_str(), "Naija-Token")) // First gem with the special texture name is the player gem + { + g.isPlayer = true; + g.blink = true; + g.global = false; + //g.mapName = ... // Don't need to set this now, it'll be set once map name is applied + } + } + } } XMLElement *wmap = doc.FirstChildElement("WorldMap"); @@ -2917,11 +3001,8 @@ bool Continuity::loadFile(int slot) SimpleIStringStream is(va, SimpleIStringStream::REUSE); WorldMapTile dummy; - int idx; - - while (is >> idx) { WorldMapTile *tile = worldMap.getWorldMapTileByIndex(idx); @@ -3357,17 +3438,12 @@ GemData *Continuity::pickupGem(const std::string& name, bool effects) bool isPlayerGem = !effects && gems.empty() && !nocasecmp(name, "Naija-Token"); if(isPlayerGem) { - g.global = false; // the player is always on a map g.blink = true; + g.isPlayer = true; } - g.isplayer = isPlayerGem; - Vector avatarPos = game->avatar->getPositionForMap(); - if(!game->worldMapRender->getWorldToPlayerTile(g.pos, avatarPos, g.global)) - { - debugLog("pickupgem failed, no worldmap tile for current map"); - return NULL; - } + // local gem, use position directly + g.pos = game->avatar->getPositionForMap(); gems.push_back(g); @@ -3396,10 +3472,10 @@ void Continuity::setCurrentMap(const std::string& mapname) for (Gems::iterator i = this->gems.begin(); i != this->gems.end(); i++) { GemData& g = *i; - if(g.isplayer) + if(g.isPlayer) { g.mapName = mapname; - game->worldMapRender->updateGem(&g); + game->worldMapRender->refreshGem(&g); } } } diff --git a/Aquaria/GameStructs.h b/Aquaria/GameStructs.h index 897e87c..240be99 100644 --- a/Aquaria/GameStructs.h +++ b/Aquaria/GameStructs.h @@ -21,15 +21,16 @@ struct EmoteData struct GemData { - GemData() { canMove=false; blink = false; global = false; isplayer = false; } + GemData() { canMove=false; blink = false; global = false; isPlayer = false; id = -1; } std::string name; std::string userString; std::string mapName; Vector pos; bool canMove; - bool blink; // not saved + bool blink; // not saved on older versions bool global; // local gems use their parent container's coordinate system, global gems are placed directly on the map screen - bool isplayer; + bool isPlayer; + int id; // used to identify a gem via scripts }; struct BeaconData diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index c1cf8e5..46838fb 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -8864,8 +8864,9 @@ luaFunc(setGemPosition) Continuity::Gems::iterator it = dsq->continuity.gems.begin(); std::advance(it, gemId); GemData& gem = *it; - gem.pos = gem.global ? tc->worldPosToMapPos(pos) : tc->worldPosToTilePos(pos); + gem.pos = gem.global ? tc->worldPosToMapPos(pos) : pos; gem.mapName = mapname; + game->worldMapRender->refreshGem(&gem); result = true; } else @@ -8891,6 +8892,7 @@ luaFunc(setGemName) std::advance(it, gemId); GemData& gem = *it; gem.name = getString(L, 2); + game->worldMapRender->refreshGem(&gem); result = true; } else diff --git a/Aquaria/WorldMapRender.cpp b/Aquaria/WorldMapRender.cpp index 26c6e29..d82405d 100644 --- a/Aquaria/WorldMapRender.cpp +++ b/Aquaria/WorldMapRender.cpp @@ -257,9 +257,13 @@ public: void refresh() { setTexture("gems/" + gemData->name); - position = gemData->pos; + + WorldMapTileContainer *tc = dynamic_cast(parent); + position = tc ? tc->worldPosToTilePos(gemData->pos) : gemData->pos; text->setText(gemData->userString); textBG->setWidthHeight(text->getActualWidth() + 20, 25, 10); + + this->update(0); } void destroy() @@ -604,17 +608,6 @@ void WorldMapRender::updateAllTilesColor() 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) @@ -676,16 +669,16 @@ void WorldMapRender::onUpdate(float dt) if(playerTile && !gemMovers.empty()) { Vector playerPos = game->avatar->getPositionForMap(); - Vector playerMapPos = playerTile->worldPosToTilePos(playerPos); + Vector playerTilePos = playerTile->worldPosToTilePos(playerPos); for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++) { GemMover *gm = *i; GemData *g = gm->getGemData(); - if(g->isplayer) + if(g->isPlayer) // Don't want to call refresh() every frame, so just update position here { assert(!g->global); - g->pos = playerMapPos; - gm->position = playerMapPos; + g->pos = playerPos; + gm->position = playerTilePos; } } } @@ -994,11 +987,11 @@ GemMover *WorldMapRender::addGem(GemData *gemData) else addChild(g, PM_POINTER); gemMovers.push_back(g); - g->update(0); + g->refresh(); return g; } -void WorldMapRender::updateGem(const GemData* gemData) +void WorldMapRender::refreshGem(const GemData* gemData) { GemMover *m = getGem(gemData); if(!m) diff --git a/Aquaria/WorldMapRender.h b/Aquaria/WorldMapRender.h index e911ddb..9da9559 100644 --- a/Aquaria/WorldMapRender.h +++ b/Aquaria/WorldMapRender.h @@ -53,7 +53,7 @@ public: void setProperTileColor(WorldMapTileContainer& wt); void action(int id, int state, int source, InputDevice device); GemMover* addGem(GemData *gemData); - void updateGem(const GemData *gemData); + void refreshGem(const GemData *gemData); void removeGem(GemMover *gemMover); void removeGem(const GemData *gemData); GemMover *getGem(const GemData *gemData) const; @@ -64,7 +64,6 @@ public: 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);