1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2024-12-04 08:55:51 +00:00
Aquaria/Aquaria/WorldMapRender.cpp

1556 lines
34 KiB
C++
Raw Normal View History

/*
Copyright (C) 2007, 2010 - Bit-Blot
This file is part of Aquaria.
Aquaria is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
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 "DSQ.h"
#include "Game.h"
#include "Avatar.h"
#include "GridRender.h"
#include "Gradient.h"
#include "TTFFont.h"
#include "RoundedRect.h"
#define GEM_GRAB 10
namespace WorldMapRenderNamespace
{
const float WORLDMAP_UNDERLAY_ALPHA = 0.8;
float baseMapSegAlpha = 0.4;
float visibleMapSegAlpha = 0.8;
const float blinkPeriod = 0.2;
// Fraction of the screen width and height we consider "visited".
// (We don't mark the entire screen "visited" because the player may
// overlook things on the edge of the screen while moving.)
const float visitedFraction = 0.8;
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;
WorldMapRevealMethod revMethod = REVEAL_DEFAULT;
std::vector<Quad *> tiles;
Quad *activeQuad=0, *lastActiveQuad=0, *originalActiveQuad=0;
Quad *lastVisQuad=0, *visQuad=0;
WorldMapTile *lastVisTile=0;
float xMin, yMin, xMax, yMax;
float zoomMin = 0.2;
float zoomMax = 3.0;
const float exteriorZoomMax = 3.0;
const float interiorZoomMax = 3.0;
bool editorActive=false;
Quad *tophud=0;
2016-05-05 17:40:28 +00:00
Gradient *underlay = 0;
}
using namespace WorldMapRenderNamespace;
std::vector <Quad*> grid;
class GemMover;
GemMover *mover=0;
WorldMapTile *activeTile=0;
const float beaconSpawnBitTime = 0.05;
void WorldMapRender::setRevealMethod(WorldMapRevealMethod m)
{
switch(m)
{
case REVEAL_PARTIAL:
revMethod = REVEAL_PARTIAL;
baseMapSegAlpha = 0;
break;
default:
revMethod = REVEAL_DEFAULT;
baseMapSegAlpha = 0.4;
}
}
class WorldMapBoundQuad : public Quad
{
public:
WorldMapBoundQuad(const Vector &posToUse)
{
position = posToUse;
truePosition = posToUse;
followCamera = 1;
}
void render()
{
setProperPosition();
Quad::render();
}
protected:
Vector truePosition;
void setProperPosition()
{
Vector wp = parent->getWorldCollidePosition(truePosition);
Vector diff = wp - core->center;
float w2 = core->getVirtualWidth()/2;
float h2 = core->getVirtualHeight()/2;
if (diff.x < -w2)
wp.x = core->center.x - w2;
if (diff.x > w2)
wp.x = core->center.x + w2;
if (diff.y < -h2)
wp.y = core->center.y - h2;
if (diff.y > h2)
wp.y = core->center.y + h2;
Vector move = wp - getWorldPosition();
// FIXME: This is a quick HACK to get the current world map
// scale factor so we can set the position properly, without
// having to play with multiple levels of parent pointers or
// anything like that. (If we don't scale the move vector
// properly, the dots overshoot at high zoom or don't go far
// enough at low zoom and we can end up with a weird "disco"
// effect -- see icculus bug 4542.)
const float x0 = getWorldPosition().x;
position.x += 1;
const float x1 = getWorldPosition().x;
position.x -= 1;
position += move / (x1-x0);
}
};
class BeaconRender : public Quad
{
public:
BeaconRender(BeaconData *beaconData) : Quad(), beaconData(beaconData)
{
renderQuad = false;
2016-05-05 17:40:28 +00:00
setTexture("gui/minimap/ripple");
position = beaconData->pos;
truePosition = beaconData->pos;
followCamera = 1;
2016-05-05 17:40:28 +00:00
alpha = 0.5;
color = beaconData->color;
2016-05-05 17:40:28 +00:00
spawnBitTimer = 0;
}
2016-05-05 17:40:28 +00:00
Vector truePosition;
ParticleEffect *pe;
2016-05-05 17:40:28 +00:00
float spawnBitTimer;
2016-05-05 17:40:28 +00:00
void render()
{
2016-05-05 17:40:28 +00:00
Quad::render();
}
2016-05-05 17:40:28 +00:00
protected:
BeaconData *beaconData;
2016-05-05 17:40:28 +00:00
void setProperPosition()
{
Vector wp = parent->getWorldCollidePosition(truePosition);
Vector diff = wp - core->center;
float w2 = core->getVirtualWidth()/2;
float h2 = core->getVirtualHeight()/2;
if (diff.x < -w2)
wp.x = core->center.x - w2;
if (diff.x > w2)
wp.x = core->center.x + w2;
if (diff.y < -h2)
wp.y = core->center.y - h2;
if (diff.y > h2)
wp.y = core->center.y + h2;
Vector move = wp - getWorldPosition();
position += move;
}
2016-05-05 17:40:28 +00:00
void onUpdate(float dt)
{
Quad::onUpdate(dt);
2016-05-05 17:40:28 +00:00
if (!dsq->game->worldMapRender->isOn()) return;
const int lenRange = 125;
const float pscale = 0.7;
float leftOver = dt;
while (leftOver)
{
spawnBitTimer -= leftOver;
if (spawnBitTimer <= 0)
{
leftOver = -spawnBitTimer;
spawnBitTimer = beaconSpawnBitTime;
float r = (rand()%100)/100.0f;
float radius = r * 2*PI;
float len = (rand()%lenRange);
int x = sinf(radius)*len;
int y = cosf(radius)*len;
2016-05-05 17:40:28 +00:00
float t = 0.75;
WorldMapBoundQuad *q = new WorldMapBoundQuad(Vector(x, y, 0));
q->setTexture("particles/glow");
q->alpha.ensureData();
q->alpha.data->path.addPathNode(0.0, 0.0);
q->alpha.data->path.addPathNode(1.0, 0.5);
q->alpha.data->path.addPathNode(0.0, 1.0);
q->alpha.startPath(0.5);
q->alphaMod = 0.5;
q->color = color;
q->scale = Vector(pscale, pscale);
2016-05-05 17:40:28 +00:00
q->setLife(1);
q->setDecayRate(1.0f/t);
2016-05-05 17:40:28 +00:00
q->setBlendType(BLEND_ADD);
addChild(q, PM_POINTER);
2016-05-05 17:40:28 +00:00
}
else
{
leftOver = 0;
}
}
}
};
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.66;
canMove = gemData->canMove;
2016-05-05 17:40:28 +00:00
std::string useString = gemData->userString;
2016-05-05 17:40:28 +00:00
text = new TTFText(&dsq->fontArialSmall);
text->offset = Vector(0, 4);
text->setText(useString);
text->setAlign(ALIGN_CENTER);
textBG = new RoundedRect();
2016-05-05 17:40:28 +00:00
textBG->setWidthHeight(text->getActualWidth() + 20, 25, 10);
textBG->alpha = 0;
textBG->followCamera = 1;
game->addRenderObject(textBG, LR_WORLDMAPHUD);
textBG->addChild(text, PM_POINTER);
2016-05-05 17:40:28 +00:00
}
void destroy()
{
if (textBG)
{
textBG->safeKill();
textBG = 0;
}
Quad::destroy();
}
GemData *getGemData() { return gemData; }
void setBlink(bool blink)
{
this->blink = blink;
2016-05-05 17:40:28 +00:00
}
bool canMove;
protected:
2016-05-05 17:40:28 +00:00
float blinkTimer;
bool blink;
GemData *gemData;
2016-05-05 17:40:28 +00:00
TTFText *text;
RoundedRect *textBG;
void onUpdate(float dt)
{
Quad::onUpdate(dt);
Vector sz = parent->scale;
if (sz.x < zoomMin)
sz.x = sz.y = zoomMin;
if (sz.x > zoomMax)
sz.x = sz.y = zoomMax;
2016-05-05 17:40:28 +00:00
if (sz.x > 1.0f)
{
scale.x = (1.0f/sz.x);
scale.y = (1.0f/sz.y);
}
else
{
scale = Vector(1,1);
}
Vector wp = getWorldPosition();
if (blink)
{
blinkTimer += dt;
if (blinkTimer > blinkPeriod)
{
if (alphaMod == 0)
alphaMod = 1;
else
alphaMod = 0;
blinkTimer = 0;
}
}
if (canMove)
{
if (mover == 0)
{
if (core->mouse.buttons.left && (core->mouse.position - wp).isLength2DIn(GEM_GRAB))
{
core->sound->playSfx("Gem-Move");
mover = this;
2016-05-05 17:40:28 +00:00
}
}
else if (mover == this)
{
2016-05-05 17:40:28 +00:00
position += (core->mouse.position - wp)/parent->scale.x;
if (!core->mouse.buttons.left)
{
mover = 0;
core->sound->playSfx("Gem-Place");
2016-05-05 17:40:28 +00:00
gemData->pos = position;
}
}
}
if (textBG)
{
textBG->position = getWorldPosition() + Vector(0, -20);
}
if ((core->mouse.position - wp).isLength2DIn(GEM_GRAB))
{
2016-05-05 17:40:28 +00:00
if (!gemData->userString.empty())
textBG->show();
}
else
{
2016-05-05 17:40:28 +00:00
if (textBG->alpha == 1)
textBG->hide();
}
}
};
typedef std::list <GemMover*> GemMovers;
GemMovers gemMovers;
typedef std::list <BeaconRender*> BeaconRenders;
BeaconRenders beaconRenders;
std::vector<Quad*> quads;
void WorldMapRender::setProperTileColor(WorldMapTile *tile)
{
if (tile)
{
if (tile->q)
{
if (!tile->revealed)
tile->q->alphaMod = 0;
if (tile->revealed)
tile->q->alphaMod = 0.5;
if (activeTile && (tile->layer != activeTile->layer || (tile->layer > 0 && activeTile != tile)))
tile->q->alphaMod *= 0.5;
tile->q->color = Vector(0.7, 0.8, 1);
}
else
{
debugLog("no Q!");
}
}
}
static void tileDataToVis(WorldMapTile *tile, Vector **vis)
{
const unsigned char *data = tile->getData();
if (data != 0)
{
const float a = tile->prerevealed ? 0.4f : baseMapSegAlpha;
const unsigned int rowSize = MAPVIS_SUBDIV/8;
for (unsigned int y = 0; y < MAPVIS_SUBDIV; y++, data += rowSize)
{
for (unsigned int x = 0; x < MAPVIS_SUBDIV; x += 8)
{
unsigned char dataByte = data[x/8];
for (unsigned int x2 = 0; x2 < 8; x2++)
{
vis[x+x2][y].z = (dataByte & (1 << x2)) ? visibleMapSegAlpha : a;
}
}
}
}
else
{
const float a = tile->prerevealed ? 0.4f : baseMapSegAlpha;
for (int x = 0; x < MAPVIS_SUBDIV; x++)
{
for (int y = 0; y < MAPVIS_SUBDIV; y++)
{
vis[x][y].z = a;
}
}
return;
}
}
// 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->read(0, 0, texWidth, texHeight, 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->write(0, 0, texWidth, texHeight, texData);
delete[] texData;
return savedTexData;
}
static void resetTileAlpha(WorldMapTile *tile, const unsigned char *savedTexData)
{
tile->q->texture->write(0, 0, tile->q->texture->width, tile->q->texture->height, savedTexData);
}
void WorldMapRender::setVis(WorldMapTile *tile)
{
if (!tile) return;
2016-05-05 17:40:28 +00:00
tile->q->color = Vector(1,1,1);
tile->q->alphaMod = 1;
2016-05-05 17:40:28 +00:00
if (visMethod == VIS_VERTEX)
{
tile->q->setSegs(MAPVIS_SUBDIV, MAPVIS_SUBDIV, 0, 0, 0, 0, 2.0, 1);
tileDataToVis(tile, tile->q->getDrawGrid());
}
else if (visMethod == VIS_WRITE)
{
savedTexData = tileDataToAlpha(tile);
}
lastVisQuad = tile->q;
lastVisTile = 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()
{
doubleClickTimer = 0;
inputDelay = 0;
editorActive=false;
mb = false;
activeQuad=0;
lastActiveQuad=0;
originalActiveQuad=0;
lastVisQuad=0;
visQuad=0;
lastVisTile=0;
originalActiveTile = activeTile = 0;
areaLabel = 0;
on = false;
alpha = 0;
scale = Vector(1, 1);
followCamera = 1;
cull = false;
position = Vector(400,300);
activeTile = 0;
activeQuad = 0;
lastMousePosition = core->mouse.position;
bg = 0;
savedTexData = 0;
int num = dsq->continuity.worldMap.getNumWorldMapTiles();
std::string n = dsq->game->sceneName;
stringToUpper(n);
for (int i = 0; i < num; i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile)
{
if (tile->name == n)
{
activeTile = tile;
break;
}
}
}
tiles.clear();
for (int 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;
std::string tn = "Gui/WorldMap/" + tile->name;
q->setTexture(tn);
q->position = pos;
q->alphaMod = 0;
tile->q = q;
2016-05-05 17:40:28 +00:00
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;
if (revMethod == REVEAL_PARTIAL || activeQuad == q)
{
setVis(tile);
}
setProperTileColor(tile);
if(activeQuad == q)
{
activeTile->q->color = Vector(1,1,1);
activeTile->q->alphaMod = 1;
}
2016-05-05 17:40:28 +00:00
addChild(q, PM_POINTER);
tiles.push_back(q);
}
}
shareAlphaWithChildren = 1;
bindInput();
tophud = new Quad("gui/worldmap-ui", Vector(400,64));
tophud->followCamera = 1;
tophud->alpha = 0;
dsq->game->addRenderObject(tophud, LR_WORLDMAPHUD);
2016-05-05 17:40:28 +00:00
float aly = 26, aly2 = 18;
float sz = 0.6;
//hover
areaLabel = new BitmapText(&dsq->smallFont);
areaLabel->scale = Vector(sz,sz);
2016-05-05 17:40:28 +00:00
areaLabel->setAlign(ALIGN_CENTER);
areaLabel->followCamera = 1;
areaLabel->position = Vector(150,aly);
dsq->game->addRenderObject(areaLabel, LR_WORLDMAPHUD);
areaLabel->alpha = 0;
//in
areaLabel2 = new BitmapText(&dsq->smallFont);
2016-05-05 17:40:28 +00:00
areaLabel2->scale = Vector(sz,sz);
areaLabel2->followCamera = 1;
areaLabel2->setAlign(ALIGN_CENTER);
areaLabel2->position = Vector(400,aly2);
dsq->game->addRenderObject(areaLabel2, LR_WORLDMAPHUD);
areaLabel2->alpha = 0;
//select
areaLabel3 = new BitmapText(&dsq->smallFont);
areaLabel3->scale = Vector(sz,sz);
2016-05-05 17:40:28 +00:00
areaLabel3->followCamera = 1;
areaLabel3->setAlign(ALIGN_CENTER);
areaLabel3->position = Vector(650, aly);
areaLabel3->alpha = 0;
dsq->game->addRenderObject(areaLabel3, LR_WORLDMAPHUD);
if (activeTile)
{
areaLabel2->setText(dsq->continuity.stringBank.get(activeTile->stringIndex));
}
originalActiveTile = activeTile;
bindInput();
2016-05-05 17:40:28 +00:00
underlay = new Gradient;
2016-05-05 17:40:28 +00:00
underlay->makeVertical(Vector(0.25,0.25,0.5), Vector(0,0,0.25));
underlay->position = Vector(400,300);
underlay->autoWidth = AUTO_VIRTUALWIDTH;
underlay->autoHeight = AUTO_VIRTUALHEIGHT;
underlay->followCamera = 1;
underlay->alpha = 0;
dsq->game->addRenderObject(underlay, LR_HUDUNDERLAY);
addHintQuad1 = new Quad("gems/pyramidyellow", Vector(0,0));
addHintQuad1->followCamera = 1;
addHintQuad1->alpha = 0;
dsq->game->addRenderObject(addHintQuad1, LR_WORLDMAPHUD);
addHintQuad2 = new Quad("gems/pyramidpurple", Vector(0,0));
addHintQuad2->followCamera = 1;
addHintQuad2->alpha = 0;
dsq->game->addRenderObject(addHintQuad2, LR_WORLDMAPHUD);
2016-05-05 17:40:28 +00:00
helpButton = new AquariaMenuItem;
helpButton->event.setActionMapperCallback(this, ACTION_TOGGLEHELPSCREEN, 0);
helpButton->useQuad("gui/icon-help");
helpButton->useGlow("particles/glow", 40, 40);
helpButton->useSound("Click");
helpButton->alpha = 0;
2016-05-05 17:40:28 +00:00
dsq->game->addRenderObject(helpButton, LR_WORLDMAPHUD);
}
void WorldMapRender::onToggleHelpScreen()
{
game->toggleHelpScreen();
}
void WorldMapRender::bindInput()
{
clearActions();
clearCreatedEvents();
addAction(ACTION_TOGGLEWORLDMAPEDITOR, KEY_TAB, -1);
for(size_t i = 0; i < dsq->user.control.actionSets.size(); ++i)
{
const ActionSet& as = dsq->user.control.actionSets[i];
int sourceID = (int)i;
as.importAction(this, "PrimaryAction", ACTION_PRIMARY, sourceID);
as.importAction(this, "SecondaryAction", ACTION_SECONDARY, sourceID);
as.importAction(this, "SwimLeft", ACTION_SWIMLEFT, sourceID);
as.importAction(this, "SwimRight", ACTION_SWIMRIGHT, sourceID);
as.importAction(this, "SwimUp", ACTION_SWIMUP, sourceID);
as.importAction(this, "SwimDown", ACTION_SWIMDOWN, sourceID);
}
}
void WorldMapRender::destroy()
{
2016-05-05 17:40:28 +00:00
for (int 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())
{
return false;
}
return true;
}
void WorldMapRender::onUpdate(float dt)
{
if (AquariaGuiElement::currentGuiInputLevel > 0) return;
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;
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);
int offset = 26;
if (helpButton)
helpButton->position = Vector(core->getVirtualWidth()-core->getVirtualOffX()-offset, offset);
if (alpha.x > 0)
{
2016-05-05 17:40:28 +00:00
if (originalActiveTile && !gemMovers.empty())
{
gemMovers.back()->position = getAvatarWorldMapPosition();
}
}
if (doubleClickTimer > 0)
{
doubleClickTimer -= dt;
if (doubleClickTimer < 0)
doubleClickTimer = 0;
}
if (isOn())
{
if (inputDelay > 0)
{
inputDelay -= dt;
if (inputDelay < 0) inputDelay = 0;
}
else
{
WorldMapTile *selectedTile = 0;
int sd=-1,d=0;
for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile && tile != activeTile)
{
if (tile->revealed || tile->prerevealed)
{
Quad *q = tile->q;
if (q)
{
d = (q->getWorldPosition() - core->mouse.position).getSquaredLength2D();
2016-05-05 17:40:28 +00:00
if (q->isCoordinateInsideWorld(core->mouse.position) && (sd == -1 || d < sd))
{
sd = d;
selectedTile = tile;
break;
}
}
}
}
}
if (!editorActive)
{
if (activeTile)
{
areaLabel3->setText(dsq->continuity.stringBank.get(activeTile->stringIndex));
}
2016-05-05 17:40:28 +00:00
if (selectedTile)
{
areaLabel->setText(dsq->continuity.stringBank.get(selectedTile->stringIndex));
if (activeTile && !mover && !dsq->isNested() && isCursorOffHud())
{
if (!core->mouse.buttons.left && mb)
{
if ((activeTile != selectedTile) && selectedTile->q)
{
if(revMethod == REVEAL_DEFAULT)
clearVis(activeTile);
activeTile = selectedTile;
activeQuad = activeTile->q;
2016-05-05 17:40:28 +00:00
if (activeQuad)
{
dsq->clickRingEffect(activeQuad->getWorldPosition(), 0);
dsq->sound->playSfx("bubble-lid");
dsq->sound->playSfx("menuselect");
}
int num = dsq->continuity.worldMap.getNumWorldMapTiles();
for (int i = 0; i < num; i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
setProperTileColor(tile);
}
setVis(selectedTile);
}
mb = false;
}
else if (core->mouse.buttons.left && !mb)
{
mb = true;
}
}
else
{
mb = false;
}
}
else
{
areaLabel->setText("");
}
}
}
if (!core->mouse.buttons.left && mb)
mb = false;
if (core->mouse.buttons.middle || core->mouse.buttons.right)
{
// FIXME: For some reason, not all mouse movement events reach
// this handler (at least under Linux/SDL), so when moving the
// mouse quickly, the world map scrolling tends to lag behind.
// We work around this by keeping our own "last position" vector
// and calculating the mouse movement from that. --achurch
Vector mouseChange = core->mouse.position - lastMousePosition;
internalOffset += mouseChange / scale.x;
}
if(!editorActive)
2011-11-20 14:44:17 +00:00
{
float scrollSpeed = 2.0f;
float amt = (400*dt)/scale.x;
if (isActing(ACTION_SWIMLEFT))
2011-11-20 14:44:17 +00:00
{
internalOffset += Vector(amt, 0);
2011-11-20 14:44:17 +00:00
}
if (isActing(ACTION_SWIMRIGHT))
2011-11-20 14:44:17 +00:00
{
internalOffset += Vector(-amt, 0);
2011-11-20 14:44:17 +00:00
}
if (isActing(ACTION_SWIMDOWN))
2011-11-20 14:44:17 +00:00
{
if (core->getShiftState())
{
scale.stop();
scale -= Vector(scrollSpeed*dt, scrollSpeed*dt);
}
else
{
internalOffset += Vector(0, -amt);
}
2011-11-20 14:44:17 +00:00
}
if (isActing(ACTION_SWIMUP))
2011-11-20 14:44:17 +00:00
{
if (core->getShiftState())
{
scale.stop();
scale += Vector(scrollSpeed*dt, scrollSpeed*dt);
}
else
{
internalOffset += Vector(0, amt);
}
2011-11-20 14:44:17 +00:00
}
if (core->joystickEnabled)
2011-11-20 14:44:17 +00:00
{
if (isActing(ACTION_SECONDARY))
{
Vector jpos;
for(size_t i = 0; i < core->getNumJoysticks(); ++i)
if(Joystick *j = core->getJoystick(i))
if(j && j->isEnabled())
if(fabsf(j->position.y) > 0.6f)
{
jpos = j->position;
break;
}
if (jpos.y >= 0.6f)
scale.interpolateTo(scale / 1.2f, 0.1f);
else if (jpos.y <= -0.6f)
scale.interpolateTo(scale * 1.2f, 0.1f);
}
else
{
Vector jpos;
for(size_t i = 0; i < core->getNumJoysticks(); ++i)
if(Joystick *j = core->getJoystick(i))
if(j && j->isEnabled())
if(!j->position.isZero())
{
jpos = j->position;
break;
}
// The negative multiplier is deliberate -- it makes the
// map scroll as though the joystick was controlling the
// cursor (which is fixed in the center of the screen).
internalOffset += jpos * (-400*dt / scale.x);
}
2011-11-20 14:44:17 +00:00
}
}
if (activeTile && activeTile->layer == 1)
{
zoomMax = interiorZoomMax;
}
else
{
zoomMax = exteriorZoomMax;
}
2016-05-05 17:40:28 +00:00
float scrollAmount = 0.2;
if (core->mouse.scrollWheelChange)
{
Vector target = scale;
int changeLeft = core->mouse.scrollWheelChange;
for (; changeLeft > 0; changeLeft--)
target *= 1 + scrollAmount;
for (; changeLeft < 0; changeLeft++)
target /= 1 + scrollAmount;
scale.interpolateTo(target, 0.1);
}
if (scale.x < zoomMin)
{
scale.stop();
scale.x = scale.y = zoomMin;
}
if (scale.x > zoomMax)
{
scale.stop();
scale.x = scale.y = zoomMax;
}
if (-internalOffset.x < xMin - 300/scale.x)
internalOffset.x = -(xMin - 300/scale.x);
else if (-internalOffset.x > xMax + 300/scale.x)
internalOffset.x = -(xMax + 300/scale.x);
if (-internalOffset.y < yMin - 225/scale.x)
internalOffset.y = -(yMin - 225/scale.x);
else if (-internalOffset.y > yMax + 150/scale.x)
internalOffset.y = -(yMax + 150/scale.x);
if (dsq->canOpenEditor())
{
if (editorActive)
{
if (activeTile && activeQuad)
{
float amt = dt*4;
float a2 = dt*0.1f;
if (core->getShiftState())
{
if (core->getCtrlState())
a2 *= 10.0f;
if (core->getKeyState(KEY_UP))
activeTile->scale2 += -a2;
if (core->getKeyState(KEY_DOWN))
activeTile->scale2 += a2;
}
else if (core->getAltState())
{
if (core->getCtrlState())
a2 *= 10.0f;
if (core->getKeyState(KEY_UP))
activeTile->scale += -a2;
if (core->getKeyState(KEY_DOWN))
activeTile->scale += a2;
}
else
{
if (core->getCtrlState())
{
amt *= 50;
}
if (core->getKeyState(KEY_LEFT))
activeTile->gridPos += Vector(-amt, 0);
if (core->getKeyState(KEY_RIGHT))
activeTile->gridPos += Vector(amt, 0);
if (core->getKeyState(KEY_UP))
activeTile->gridPos += Vector(0, -amt);
if (core->getKeyState(KEY_DOWN))
activeTile->gridPos += Vector(0, amt);
}
if (core->getKeyState(KEY_F2))
{
dsq->continuity.worldMap.save();
}
activeQuad->position = activeTile->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);
}
updateEditor();
}
}
}
else
{
if (!dsq->isInCutscene() && dsq->game->avatar && activeTile
#ifdef AQUARIA_BUILD_SCENEEDITOR
&& !dsq->game->sceneEditor.isOn()
#endif
)
{
const float screenWidth = core->getVirtualWidth() * core->invGlobalScale;
const float screenHeight = core->getVirtualHeight() * core->invGlobalScale;
Vector camera = core->cameraPos;
camera.x += screenWidth/2;
camera.y += screenHeight/2;
const float visWidth = screenWidth * visitedFraction;
const float visHeight = screenHeight * visitedFraction;
Vector tl, br;
tl.x = (camera.x - visWidth/2 ) / dsq->game->cameraMax.x;
tl.y = (camera.y - visHeight/2) / dsq->game->cameraMax.y;
br.x = (camera.x + visWidth/2 ) / dsq->game->cameraMax.x;
br.y = (camera.y + visHeight/2) / dsq->game->cameraMax.y;
const int x0 = int(tl.x * MAPVIS_SUBDIV);
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.
}
}
}
}
lastMousePosition = core->mouse.position;
}
Vector WorldMapRender::getAvatarWorldMapPosition()
{
Vector p;
if (originalActiveTile && dsq->game && dsq->game->avatar)
{
Vector p = dsq->game->avatar->position;
if (!dsq->game->avatar->warpInLocal.isZero())
{
p = dsq->game->avatar->warpInLocal;
}
return getWorldToTile(originalActiveTile, p, true, true);
}
return p;
}
Vector WorldMapRender::getWorldToTile(WorldMapTile *tile, Vector position, bool fromCenter, bool tilePos)
{
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;
}
void WorldMapRender::onRender()
{
RenderObject::onRender();
}
bool WorldMapRender::isOn()
{
return this->on;
}
GemMover *WorldMapRender::addGem(GemData *gemData)
{
GemMover *g = new GemMover(gemData);
addChild(g, PM_POINTER);
gemMovers.push_back(g);
g->update(0);
return g;
}
void WorldMapRender::removeGem(GemMover *gem)
{
dsq->continuity.removeGemData(gem->getGemData());
fixGems();
}
void WorldMapRender::fixGems()
{
for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++)
{
removeChild(*i);
(*i)->destroy();
delete *i;
}
gemMovers.clear();
addAllGems();
}
void WorldMapRender::addAllGems()
{
int c = 0;
for (Continuity::Gems::reverse_iterator i = dsq->continuity.gems.rbegin(); i != dsq->continuity.gems.rend(); i++)
{
GemMover *g = addGem(&(*i));
if (c == dsq->continuity.gems.size()-1 || i->blink)
g->setBlink(true);
else
g->setBlink(false);
c++;
}
}
void WorldMapRender::toggle(bool turnON)
{
if (AquariaGuiElement::currentGuiInputLevel > 0) return;
if (dsq->game->miniMapRender->isRadarHide()) return;
if (alpha.isInterpolating()) return;
if (dsq->mod.isActive() && !dsq->mod.hasWorldMap()) return;
2016-05-05 17:40:28 +00:00
if (dsq->isNested()) return;
if (!dsq->game->avatar) return;
if (turnON && dsq->game->avatar->isSinging()) return;
if (dsq->game->isInGameMenu()) return;
if (!dsq->game->isActive()) return;
2016-05-05 17:40:28 +00:00
if (turnON && dsq->game->isPaused()) return;
if (!this->on && !dsq->game->avatar->isInputEnabled()) return;
const SeeMapMode mapmode = dsq->game->avatar->getSeeMapMode();
if (mapmode == SEE_MAP_NEVER
|| (mapmode == SEE_MAP_DEFAULT && dsq->game->avatar->isInDarkness() && dsq->continuity.form != FORM_SUN))
{
core->sound->playSfx("denied");
return;
}
mb = false;
this->on = turnON;
if (on)
{
restoreVel = dsq->game->avatar->vel;
dsq->game->avatar->vel = Vector(0,0,0);
2016-05-05 17:40:28 +00:00
dsq->game->togglePause(true);
core->sound->playSfx("menu-open");
originalActiveTile = activeTile;
if (activeTile)
{
internalOffset = -activeTile->gridPos;
if (activeTile->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);
}
}
xMin = xMax = -internalOffset.x;
yMin = yMax = -internalOffset.y;
for (int i = 0; i < dsq->continuity.worldMap.getNumWorldMapTiles(); i++)
{
WorldMapTile *tile = dsq->continuity.worldMap.getWorldMapTile(i);
if (tile && (tile->revealed || tile->prerevealed) && tile->q)
{
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;
}
}
if (bg)
bg->alpha.interpolateTo(1, 0.2);
alpha.interpolateTo(1, 0.2);
2016-05-05 17:40:28 +00:00
underlay->alpha.interpolateTo(WORLDMAP_UNDERLAY_ALPHA, 0.2);
addHintQuad1->alpha.interpolateTo(1.0, 0.2);
addHintQuad2->alpha.interpolateTo(1.0, 0.2);
helpButton->alpha.interpolateTo(1.0, 0.2);
2016-05-05 17:40:28 +00:00
addAllGems();
2016-05-05 17:40:28 +00:00
for (Continuity::Beacons::reverse_iterator i = dsq->continuity.beacons.rbegin(); i != dsq->continuity.beacons.rend(); i++)
{
if ((*i).on)
{
BeaconRender *b = new BeaconRender(&(*i));
2016-05-05 17:40:28 +00:00
addChild(b, PM_POINTER);
beaconRenders.push_back(b);
}
}
inputDelay = 0.5;
}
else if (!on)
{
inputDelay = 0.5;
if (originalActiveTile && activeTile)
{
if (activeTile != originalActiveTile)
{
if(revMethod == REVEAL_DEFAULT)
{
clearVis(activeTile);
setVis(originalActiveTile);
}
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->color = Vector(1,1,1);
activeTile->q->alphaMod = 1;
}
core->sound->playSfx("Menu-Close");
if (bg)
bg->alpha.interpolateTo(0, 0.2);
alpha.interpolateTo(0, 0.2);
dsq->game->togglePause(false);
2016-05-05 17:40:28 +00:00
underlay->alpha.interpolateTo(0, 0.2);
addHintQuad1->alpha.interpolateTo(0, 0.2);
addHintQuad2->alpha.interpolateTo(0, 0.2);
helpButton->alpha.interpolateTo(0, 0.2);
for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++)
{
2016-05-05 17:40:28 +00:00
(*i)->safeKill();
}
gemMovers.clear();
2016-05-05 17:40:28 +00:00
for (BeaconRenders::iterator i = beaconRenders.begin(); i != beaconRenders.end(); i++)
{
2016-05-05 17:40:28 +00:00
(*i)->safeKill();
}
beaconRenders.clear();
dsq->game->avatar->vel = restoreVel;
}
}
void WorldMapRender::createGemHint(const std::string &gfx)
{
std::string useString = dsq->getUserInputString(dsq->continuity.stringBank.get(860), "", true);
if (!useString.empty())
{
doubleClickTimer = 0;
GemData *g = dsq->continuity.pickupGem(gfx, false);
g->canMove = 1;
2016-05-05 17:40:28 +00:00
g->pos = getAvatarWorldMapPosition();
g->userString = useString;
addGem(g);
fixGems();
}
}
void WorldMapRender::updateEditor()
{
std::ostringstream os;
os << "EDITING... ";
if(activeTile)
{
os << "x=" << activeTile->gridPos.x << "; y=" << activeTile->gridPos.y << std::endl;
os << "scale=" << activeTile->scale << "; scale2=" << activeTile->scale2;
}
areaLabel->setText(os.str());
}
void WorldMapRender::action (int id, int state, int source)
{
if (isOn())
{
if (id == ACTION_TOGGLEHELPSCREEN && !state)
{
onToggleHelpScreen();
}
if (id == ACTION_TOGGLEWORLDMAPEDITOR && !state)
{
if (dsq->canOpenEditor())
{
editorActive = !editorActive;
if (editorActive)
{
updateEditor();
}
}
}
if (id == ACTION_PRIMARY && state)
{
if (addHintQuad1->isCoordinateInRadius(core->mouse.position, 10))
{
createGemHint("pyramidyellow");
}
if (addHintQuad2->isCoordinateInRadius(core->mouse.position, 10))
{
createGemHint("pyramidpurple");
}
}
if (id == ACTION_SECONDARY && !state)
{
if (!mover)
{
for (GemMovers::iterator i = gemMovers.begin(); i != gemMovers.end(); i++)
{
if ((*i)->canMove && (core->mouse.position - (*i)->getWorldPosition()).isLength2DIn(GEM_GRAB))
{
removeGem(*i);
break;
}
}
}
}
2016-05-05 17:40:28 +00:00
}
}