1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-11-25 09:44:02 +00:00

improvements to gem storage

gems from old saves are still compatible but won't be associated to map tiles
This commit is contained in:
fgenesis 2024-11-16 02:21:41 +01:00
parent 4a71c5e12f
commit de80af5612
5 changed files with 135 additions and 64 deletions

View file

@ -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 <Gem ...> 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);
}
}
}

View file

@ -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

View file

@ -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

View file

@ -257,9 +257,13 @@ public:
void refresh()
{
setTexture("gems/" + gemData->name);
position = gemData->pos;
WorldMapTileContainer *tc = dynamic_cast<WorldMapTileContainer*>(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)

View file

@ -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);