1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-02-03 10:04:01 +00:00

Rework + optimize map tile storage & rendering, initial working draft.

class Element is completely gone.
(The files are still there but no longer compiled in. Will delete later)

Broken still:
- support for vertical flip
- the editor
- culling
This commit is contained in:
fgenesis 2023-07-14 00:19:33 +02:00
parent e8c405cd9e
commit bd5b2b3495
31 changed files with 1704 additions and 1422 deletions

View file

@ -6721,11 +6721,7 @@ void Avatar::onUpdate(float dt)
if(!core->particlesPaused && elementEffectMult > 0)
{
ElementUpdateList& elems = game->elementInteractionList;
for (ElementUpdateList::iterator it = elems.begin(); it != elems.end(); ++it)
{
(*it)->doInteraction(this, elementEffectMult, 16);
}
dsq->tilemgr.doTileInteraction(position, vel, elementEffectMult, 16);
}
}

View file

@ -22,9 +22,6 @@ SET(AQUARIA_SRCS
Damage.h
DSQ.cpp
DSQ.h
Element.cpp
Element.h
Elements.h
Emote.cpp
Entity.cpp
Entity.h
@ -95,6 +92,8 @@ SET(AQUARIA_SRCS
StringBank_gen.h
SubtitlePlayer.cpp
SubtitlePlayer.h
TileMgr.cpp
TileMgr.h
TileVector.h
ToolTip.cpp
ToolTip.h

View file

@ -3482,7 +3482,7 @@ void Continuity::reset()
knowsSong.clear();
loadSongBank();
loadEatBank();
dsq->loadElementEffects();
dsq->loadTileEffects();
form = FORM_NORMAL;
costume = "";
dsq->emote.load("data/naijaemote.txt");

View file

@ -209,9 +209,6 @@ DSQ::DSQ(const std::string& fileSystem, const std::string& extraDataDir)
avgFPS.resize(user.video.fpsSmoothing);
cursor = cursorGlow = 0;
for (int i = 0; i < 16; i++)
firstElementOnLayer[i] = 0;
}
DSQ::~DSQ()
@ -267,82 +264,6 @@ void DSQ::newGame()
game->transitionToScene("NaijaCave");
}
void DSQ::loadElementEffects()
{
bool found = false;
std::string fn;
if (mod.isActive())
{
fn = mod.getPath() + "elementeffects.txt";
if(exists(fn))
found = true;
}
if(!found)
fn = "data/elementeffects.txt";
InStream inFile(fn.c_str());
elementEffects.clear();
std::string line;
while (std::getline(inFile, line))
{
debugLog("Line: " + line);
std::istringstream is(line);
ElementEffect e;
int efxType = EFX_NONE;
int idx;
std::string type;
is >> idx >> type;
if (type == "EFX_SEGS")
{
efxType = EFX_SEGS;
is >> e.segsx >> e.segsy >> e.segs_dgox >> e.segs_dgoy >> e.segs_dgmx >> e.segs_dgmy >> e.segs_dgtm >> e.segs_dgo;
}
else if (type == "EFX_WAVY")
{
debugLog("loading wavy");
efxType = EFX_WAVY;
is >> e.segsy >> e.wavy_radius >> e.wavy_flip;
}
else if (type == "EFX_ALPHA")
{
efxType = EFX_ALPHA;
float to_x, time, loop, pingPong, ease;
int blend;
is >> blend >> e.alpha.x >> to_x >> time >> loop >> pingPong >> ease;
e.alpha.interpolateTo(to_x, time, loop, pingPong, ease);
e.blendType = blend < _BLEND_MAXSIZE ? (BlendType)blend : BLEND_DISABLED;
}
e.type = efxType;
elementEffects.push_back(e);
}
inFile.close();
}
ElementEffect DSQ::getElementEffectByIndex(size_t e)
{
if (e < elementEffects.size())
{
return elementEffects[e];
}
ElementEffect empty;
empty.type = EFX_NONE;
empty.alpha = 0;
empty.blendType = BLEND_DEFAULT;
empty.color = 0;
empty.segsx = empty.segsy = 0;
empty.segs_dgmx = empty.segs_dgmy = 0;
empty.segs_dgo = 0;
empty.segs_dgox = empty.segs_dgoy = 0;
empty.segs_dgtm = 0;
empty.wavy_flip = false;
empty.wavy_max = empty.wavy_min = 0;
empty.wavy_radius = 0;
return empty;
}
void DSQ::centerMessage(const std::string &text, float y, int type)
{
Vector pos(400,y);
@ -773,7 +694,7 @@ void DSQ::init()
PSIZEOF(RenderObject);
PSIZEOF(Quad);
PSIZEOF(CollideQuad);
PSIZEOF(Element);
PSIZEOF(TileData);
PSIZEOF(Shot);
PSIZEOF(Bone);
PSIZEOF(PauseQuad);
@ -3553,6 +3474,74 @@ bool DSQ::canOpenEditor() const
return isDeveloperKeys() || (mod.isActive() && !mod.isEditorBlocked());
}
void DSQ::loadTileEffects()
{
bool found = false;
std::string fn;
if (mod.isActive())
{
fn = mod.getPath() + "elementeffects.txt";
if(exists(fn))
found = true;
}
if(!found)
fn = "data/elementeffects.txt";
return tilemgr.loadTileEffects(fn.c_str());
}
bool DSQ::loadTileset(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen)
{
stringToLower(pack);
std::string fn;
if (mod.isActive())
fn = mod.getPath() + "tilesets/" + pack + ".txt";
else
fn = "data/tilesets/" + pack + ".txt";
if(!tilemgr.tileset.loadFile(fn.c_str(), usedIdx, usedIdxLen))
{
errorLog ("Could not load tileset [" + fn + "]");
return false;
}
// Aquarian alphabet letters
if(const CountedPtr<Texture> aqtex = getTexture("aquarian"))
{
const float cell = 64.0f/512.0f;
for (int i = 0; i < 27; i++)
{
ElementTemplate t;
t.idx = 1024+i;
t.tex = aqtex;
t.loaded = true;
int x = i,y=0;
while (x >= 6)
{
x -= 6;
y++;
}
t.tu1 = x*cell;
t.tv1 = y*cell;
t.tu2 = t.tu1 + cell;
t.tv2 = t.tv1 + cell;
t.tv2 = 1 - t.tv2;
t.tv1 = 1 - t.tv1;
std::swap(t.tv1,t.tv2);
t.w = 512*cell;
t.h = 512*cell;
tilemgr.tileset.elementTemplates.push_back(t);
}
}
return true;
}
bool DSQ::isQuitFlag()
{
return watchQuitFlag;
@ -3751,6 +3740,8 @@ void DSQ::onUpdate(float dt)
// This queries pressed keys and updates ActionMapper
Core::onUpdate(dt);
tilemgr.update(dt);
mod.update(dt);
@ -3912,7 +3903,8 @@ void DSQ::onUpdate(float dt)
os << sound->getVolumeString() << std::endl;
os << "runInBG: " << settings.runInBackground << " nested: " << getNestedMains() << std::endl;
os << globalResolutionScale.x << ", " << globalResolutionScale.y << std::endl;
os << "elemu: " << game->elementUpdateList.size() << " elemi: " << game->elementInteractionList.size() << std::endl;
TileStorage::Sizes tsz = tilemgr.getStats();
os << "Tiles: " << tsz.tiles << ", u: " << tsz.update << ", i: " << tsz.collide << std::endl;
os << "Lua mem: " << scriptInterface.gcGetStats() << " KB" << std::endl;
cmDebug->position = Vector(20 - virtualOffX,50);
@ -4093,20 +4085,6 @@ void DSQ::playVisualEffect(int vfx, Vector position, Entity *target)
}
}
void DSQ::addElement(Element *e)
{
elements.push_back(e);
if (e->bgLayer >= 0 && e->bgLayer < 16)
{
e->bgLayerNext = firstElementOnLayer[e->bgLayer];
firstElementOnLayer[e->bgLayer] = e;
}
else
{
e->bgLayerNext = 0;
}
}
void DSQ::modifyDt(float &dt)
{
if (isDeveloperKeys())
@ -4149,46 +4127,6 @@ void DSQ::modifyDt(float &dt)
dt *= gameSpeed.x;
}
void DSQ::removeElement(Element *element)
{
for (size_t i = 0; i < elements.size(); i++)
{
if (elements[i] == element)
{
removeElement(i);
break;
}
}
}
// only happens in editor, no need to optimize
void DSQ::removeElement(size_t idx)
{
ElementContainer copy = elements;
clearElements();
size_t i = 0;
for (i = 0; i < idx; i++)
{
addElement(copy[i]);
}
for (i = idx+1; i < copy.size(); i++)
{
addElement(copy[i]);
}
copy.clear();
if (!game->elementUpdateList.empty())
game->rebuildElementUpdateList();
}
void DSQ::clearElements()
{
elements.clear();
for (int i = 0; i < 16; i++)
firstElementOnLayer[i] = 0;
}
void DSQ::addEntity(Entity *entity)
{
size_t i;

View file

@ -22,12 +22,12 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define DSQ_H
#include "AquariaCompileConfig.h"
#include "../BBGE/Core.h"
#include "../BBGE/Quad.h"
#include "Element.h"
#include "../BBGE/BitmapFont.h"
#include "../BBGE/ScreenTransition.h"
#include "../BBGE/Precacher.h"
#include "Core.h"
#include "Quad.h"
#include "TileMgr.h"
#include "BitmapFont.h"
#include "ScreenTransition.h"
#include "Precacher.h"
#include "ScriptInterface.h"
#include "GameEnums.h"
#include "Mod.h"
@ -186,21 +186,6 @@ public:
int getEntityLayerToLayer(int layer);
void addElement(Element *e);
size_t getNumElements() const {return elements.size();}
Element *getElement(size_t idx) const {return elements[idx];}
Element *getFirstElementOnLayer(size_t layer) const {return layer>15 ? 0 : firstElementOnLayer[layer];}
void clearElements();
// Used only by scene editor:
void removeElement(size_t idx);
void removeElement(Element *e);
ElementContainer getElementsCopy() const {return elements;}
protected: // These should never be accessed from outside (use the functions above).
ElementContainer elements;
Element *firstElementOnLayer[16];
public:
void addEntity(Entity *entity);
void removeEntity(Entity *e);
void clearEntities();
@ -267,10 +252,9 @@ public:
bool isDeveloperKeys() const;
bool canOpenEditor() const;
void loadElementEffects();
ElementEffect getElementEffectByIndex(size_t e);
typedef std::vector<ElementEffect> ElementEffects;
ElementEffects elementEffects;
void loadTileEffects();
bool loadTileset(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen);
TileMgr tilemgr;
bool playedVoice(const std::string &file);

View file

@ -34,22 +34,6 @@ enum EFXType
EFX_MAX
};
enum ElementFlag
{
EF_NONE = 0,
EF_SOLID = 1,
EF_MOVABLE = 2,
EF_HURT = 3,
EF_SOLID2 = 4,
EF_SOLID3 = 5,
EF_MAX = 6
/*
EF_GLASS = 0x00000020,
EF_FORCEBREAK = 0x00000100,
EF_HURT = 0x00000200
*/
};
struct ElementEffectData
{
ElementEffectData();

View file

@ -1,28 +0,0 @@
/*
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.
*/
#ifndef __elements__
#define __elements__
#include "Element.h"
#endif

View file

@ -330,7 +330,7 @@ void Entity::setEntityProperty(EntityProperty ep, bool value)
entityProperties[ep] = value;
}
bool Entity::isEntityProperty(EntityProperty ep)
bool Entity::isEntityProperty(EntityProperty ep) const
{
return entityProperties[ep];
}
@ -2012,7 +2012,7 @@ void Entity::onEnterState(int action)
}
}
bool Entity::isPullable()
bool Entity::isPullable() const
{
return ((isEntityProperty(EP_MOVABLE)) || (frozenTimer > 0));
}
@ -2106,11 +2106,14 @@ void Entity::setInvincible(bool inv)
invincible = inv;
}
bool Entity::isInDarkness()
bool Entity::isInDarkness() const
{
for (Element *e = dsq->getFirstElementOnLayer(12); e; e = e->bgLayerNext)
const TileStorage& ts = dsq->tilemgr.tilestore[12];
const size_t n = ts.tiles.size();
for(size_t i = 0; i < n; ++i)
{
if (e->isCoordinateInside(position))
const TileData& t = ts.tiles[i];
if(t.isVisible() && t.isCoordinateInside(position.x, position.y))
return true;
}
return false;

View file

@ -117,9 +117,9 @@ public:
virtual void lightFlare(){}
virtual void sporesDropped(const Vector &pos, int type) {}
bool isPullable();
bool isPullable() const;
bool isInDarkness();
bool isInDarkness() const;
bool isPresent() const
{
@ -224,7 +224,7 @@ public:
float damageTime;
void setEntityProperty(EntityProperty ep, bool value=true);
bool isEntityProperty(EntityProperty ep);
bool isEntityProperty(EntityProperty ep) const;
virtual void song(SongType songType){}
bool updateCurrents(float dt);
void updateVel2(float dt, bool override=false);

View file

@ -25,6 +25,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "../BBGE/LensFlare.h"
#include "../BBGE/RoundedRect.h"
#include "../BBGE/SimpleIStringStream.h"
#include "TileRender.h"
#include "ttvfs_stdio.h"
#include "ReadXML.h"
@ -47,6 +48,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "Beam.h"
#include "Hair.h"
#ifdef BBGE_USE_GLM
#include "glm/glm.hpp"
#include "glm/gtx/transform.hpp"
@ -316,63 +318,6 @@ void Game::transitionToScene(std::string scene)
core->enqueueJumpState("Game", false);
}
ElementTemplate *Game::getElementTemplateByIdx(size_t idx)
{
return tileset.getByIdx(idx);
}
Element* Game::createElement(size_t idx, Vector position, size_t bgLayer, RenderObject *copy, ElementTemplate *et)
{
if (idx == -1) return 0;
if (!et)
et = this->getElementTemplateByIdx(idx);
Element *element = new Element();
if (et)
{
element->setTexturePointer(et->getTexture());
}
element->position = position;
element->position.z = -0.05f;
element->templateIdx = idx;
element->bgLayer = bgLayer;
if (et)
{
if (et->w != -1 && et->h != -1)
element->setWidthHeight(et->w, et->h);
}
if (et)
{
if (et->tu1 != 0 || et->tu2 != 0 || et->tv1 != 0 || et->tv2 != 0)
{
element->upperLeftTextureCoordinates = Vector(et->tu1, et->tv1);
element->lowerRightTextureCoordinates = Vector(et->tu2, et->tv2);
}
}
if (copy)
{
element->scale = copy->scale;
if (copy->isfh())
element->flipHorizontal();
if (copy->isfv())
element->flipVertical();
element->rotation = copy->rotation;
Quad *q = dynamic_cast<Quad*>(copy);
if (q)
{
element->repeatTextureToFill(q->isRepeatingTextureToFill());
}
}
addRenderObject(element, LR_ELEMENTS1+bgLayer);
dsq->addElement(element);
return element;
}
void Game::addObsRow(unsigned tx, unsigned ty, unsigned len)
{
ObsRow obsRow(tx, ty, len);
@ -386,12 +331,27 @@ void Game::clearObsRows()
void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
{
if (q->texture)
GridFiller f;
f.obs = obsType;
f.trim = trim;
f.fh = q->isfh();
f.position = q->position;
f.rotation = q->rotation.z;
f.scale = q->scale;
f.texture = q->texture.content();
f.width = q->width;
f.height = q->height;
fillGrid(f);
}
void Game::fillGrid(const GridFiller& gf)
{
if (gf.texture)
{
std::vector<TileVector> obs;
TileVector tpos(q->position);
int widthscale = q->getWidth()*q->scale.x;
int heightscale = q->getHeight()*q->scale.y;
TileVector tpos(gf.position);
int widthscale = int(gf.width*gf.scale.x);
int heightscale = int(gf.height*gf.scale.y);
int w2 = widthscale/2;
int h2 = heightscale/2;
w2/=TILE_SIZE;
@ -401,15 +361,15 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
int w = 0, h = 0;
size_t size = 0;
unsigned char *data = q->texture->getBufferAndSize(&w, &h, &size);
unsigned char *data = gf.texture->getBufferAndSize(&w, &h, &size);
if (!data)
{
debugLog("Failed to get buffer in Game::fillGridFromQuad()");
return;
}
int szx = TILE_SIZE/q->scale.x;
int szy = TILE_SIZE/q->scale.y;
int szx = TILE_SIZE/gf.scale.x;
int szy = TILE_SIZE/gf.scale.y;
if (szx < 1) szx = 1;
if (szy < 1) szy = 1;
@ -424,8 +384,8 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
{
// starting position =
// tx / scale.x
unsigned int px = int(tx/q->scale.x) + x;
unsigned int py = int(ty/q->scale.y) + y;
unsigned int px = int(tx/gf.scale.x) + x;
unsigned int py = int(ty/gf.scale.y) + y;
if (px < unsigned(w) && py < unsigned(h))
{
unsigned int p = (py*unsigned(w)*4) + (px*4) + 3; // position of alpha component
@ -453,7 +413,7 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
free(data);
if (trim)
if (gf.trim)
{
std::vector<TileVector> obsCopy;
obsCopy.swap(obs);
@ -492,8 +452,8 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
const float h2f = float(h2);
for (size_t i = 0; i < obs.size(); i++)
{
glm::mat4 transformMatrix = glm::rotate(q->rotation.z, glm::vec3(0.0f, 0.0f, 1.0f));
if(q->isfh())
glm::mat4 transformMatrix = glm::rotate(gf.rotation, glm::vec3(0.0f, 0.0f, 1.0f));
if(gf.fh)
transformMatrix *= glm::rotate(180.0f, glm::vec3(0.0f, 1.0f, 0.0f));
transformMatrix *= glm::translate(float(obs[i].x)-w2f, float(obs[i].y)-h2f, 0.0f);
@ -502,7 +462,7 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
TileVector tvec(tpos.x+w2+x, tpos.y+h2+y);
if (!isObstructed(tvec))
addGrid(tvec, obsType);
addGrid(tvec, gf.obs);
}
#else
glPushMatrix();
@ -511,8 +471,8 @@ void Game::fillGridFromQuad(Quad *q, ObsType obsType, bool trim)
{
glLoadIdentity();
glRotatef(q->rotation.z, 0, 0, 1);
if (q->isfh())
glRotatef(gf.rotation, 0, 0, 1);
if (gf.fh)
{
glRotatef(180, 0, 1, 0);
}
@ -585,16 +545,21 @@ void Game::reconstructGrid(bool force)
if (!force && isSceneEditorActive()) return;
clearGrid();
for (size_t i = 0; i < dsq->getNumElements(); i++)
std::vector<GridFiller> fillers;
dsq->tilemgr.exportGridFillers(fillers);
std::ostringstream os;
os << "ReconstructGrid using " << fillers.size() << " tiles";
debugLog(os.str());
for (size_t i = 0; i < fillers.size(); i++)
{
Element *e = dsq->getElement(i);
e->fillGrid();
fillGrid(fillers[i]);
}
ObsRow *o;
for (size_t i = 0; i < obsRows.size(); i++)
{
o = &obsRows[i];
const ObsRow *o = &obsRows[i];
for (unsigned tx = 0; tx < o->len; tx++)
{
setGrid(TileVector(o->tx + tx, o->ty), OT_BLACK);
@ -1612,109 +1577,107 @@ bool Game::loadSceneXML(std::string scene)
saveFile->InsertEndChild(newSF);
}
struct ElementDef
{
ElementDef(int lr)
: layer(lr), idx(0), x(0), y(0), rot(0), fh(0), fv(0), flags(0), efxIdx(-1), repeat(0)
, tag(0), sx(1), sy(1), rsx(1), rsy(1)
{}
int layer, idx, x, y, rot, fh, fv, flags, efxIdx, repeat, tag;
float sx, sy, rsx, rsy;
};
std::vector<ElementDef> elemsDefs;
elemsDefs.reserve(256);
std::vector<TileDef> tilesDefs;
tilesDefs.reserve(256);
XMLElement *simpleElements = doc.FirstChildElement("SE");
while (simpleElements)
{
const size_t defsBeginIdx = elemsDefs.size();
const int layer = atoi(simpleElements->Attribute("l"));
const size_t defsBeginIdx = tilesDefs.size();
if (const char *attr = simpleElements->Attribute("d"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(4); // legacy crap
TileDef d(4); // legacy crap
while (is >> d.idx)
{
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
const int layer = atoi(simpleElements->Attribute("l"));
if(layer < 0 || layer >= MAX_TILE_LAYERS)
{
errorLog("Save file specifies invalid layer in SE tag, ignoring (if you save the map now, you may lose data!)");
goto next_SE;
}
if (const char *attr = simpleElements->Attribute("e"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("f"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx;
d.sy = d.sx;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("g"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv;
d.sy = d.sx;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("h"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags;
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.ef;
d.sy = d.sx;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("i"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx;
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.ef >> d.efxIdx;
d.sy = d.sx;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("j"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
is >> d.x >> d.y >> d.rot >> d.sx >> d.fh >> d.fv >> d.ef >> d.efxIdx >> d.repeat;
d.sy = d.sx;
elemsDefs.push_back(d);
tilesDefs.push_back(d);
}
}
if (const char *attr = simpleElements->Attribute("k"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
ElementDef d(layer);
TileDef d(layer);
while(is >> d.idx)
{
is >> d.x >> d.y >> d.rot >> d.sx >> d.sy >> d.fh >> d.fv >> d.flags >> d.efxIdx >> d.repeat;
elemsDefs.push_back(d);
is >> d.x >> d.y >> d.rot >> d.sx >> d.sy >> d.fh >> d.fv >> d.ef >> d.efxIdx >> d.repeat;
tilesDefs.push_back(d);
}
}
@ -1723,9 +1686,9 @@ bool Game::loadSceneXML(std::string scene)
if (const char *attr = simpleElements->Attribute("repeatScale"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
for(size_t i = defsBeginIdx; i < tilesDefs.size(); ++i)
{
ElementDef& d = elemsDefs[i];
TileDef& d = tilesDefs[i];
if(d.repeat)
{
if(!(is >> d.rsx >> d.rsy))
@ -1736,76 +1699,64 @@ bool Game::loadSceneXML(std::string scene)
if (const char *attr = simpleElements->Attribute("tag"))
{
SimpleIStringStream is(attr, SimpleIStringStream::REUSE);
for(size_t i = defsBeginIdx; i < elemsDefs.size(); ++i)
for(size_t i = defsBeginIdx; i < tilesDefs.size(); ++i)
{
ElementDef& d = elemsDefs[i];
TileDef& d = tilesDefs[i];
if(!(is >> d.tag))
break;
}
}
next_SE:
simpleElements = simpleElements->NextSiblingElement("SE");
}
dsq->tilemgr.clearTiles();
if(fullTilesetReload)
{
fullTilesetReload = false;
tileset.clear();
dsq->tilemgr.tileset.clear();
// used by SceneEditor
// no elements exist right now -> textures will be cleared and reloaded
// no tiles exist right now -> textures will be cleared and reloaded
dsq->texmgr.clearUnused();
}
// figure out which textures in the tileset are used and preload those that are actually used
{
std::ostringstream os;
os << "Scene has " << elemsDefs.size() << " elements";
os << "Scene has " << tilesDefs.size() << " static tiles";
debugLog(os.str());
unsigned char usedIdx[1024] = {0};
for(size_t i = 0; i < elemsDefs.size(); ++i)
for(size_t i = 0; i < tilesDefs.size(); ++i)
{
unsigned idx = elemsDefs[i].idx;
TileDef& d = tilesDefs[i];
unsigned idx = d.idx;
if(idx < Countof(usedIdx))
usedIdx[idx] = 1;
// HACK: due to a renderer bug in old versions, we need to fix the rotation
// for horizontally flipped tiles on parallax layers to make maps look correct.
// See commit 4b52730be253dbfce9bea6f604c772a87da104e3
// bgLayer IDs (which are NOT LR_* constants):
// 0..8 are normal layers (keys 1-9)
// 9,10,11; 13,14,15 are parallax ones, 15 is closest, 9 is furthest away
// 12 is the dark layer
if(d.fh && d.layer >= 9 && d.layer <= 15 && d.layer != 12
&& dsq->renderObjectLayers[LR_ELEMENTS1 + d.layer].followCamera != 0)
{
d.rot = -d.rot;
}
}
loadElementTemplates(tilesetToLoad, &usedIdx[0], Countof(usedIdx));
dsq->loadTileset(tilesetToLoad, &usedIdx[0], Countof(usedIdx));
}
// Now that all SE tags have been processed, spawn them
for(size_t i = 0; i < elemsDefs.size(); ++i)
{
const ElementDef& d = elemsDefs[i];
Element *e = createElement(d.idx, Vector(d.x,d.y), d.layer);
e->elementFlag = (ElementFlag)d.flags;
if (d.fh)
e->flipHorizontal();
if (d.fv)
e->flipVertical();
e->scale = Vector(d.sx, d.sy);
e->rotation.z = d.rot;
e->repeatToFillScale.x = d.rsx;
e->repeatToFillScale.y = d.rsy;
e->setElementEffectByIndex(d.efxIdx);
if (d.repeat)
e->repeatTextureToFill(true); // also applies repeatToFillScale
e->setTag(d.tag);
// HACK: due to a renderer bug in old versions, we need to fix the rotation
// for horizontally flipped tiles on parallax layers to make maps look correct.
// See commit 4b52730be253dbfce9bea6f604c772a87da104e3
// bgLayer IDs (which are NOT LR_* constants):
// 0..8 are normal layers (keys 1-9)
// 9,10,11; 13,14,15 are parallax ones, 15 is closest, 9 is furthest away
// 12 is the dark layer
if(d.fh && d.layer >= 9 && d.layer <= 15 && d.layer != 12
&& dsq->renderObjectLayers[e->layer].followCamera != 0)
{
e->rotation.z = -e->rotation.z;
}
}
if(!tilesDefs.empty())
dsq->tilemgr.createTiles(&tilesDefs[0], tilesDefs.size());
this->reconstructGrid(true);
@ -1835,7 +1786,6 @@ bool Game::loadSceneXML(std::string scene)
spawnEntities(&toSpawn[0], toSpawn.size());
this->reconstructGrid(true);
rebuildElementUpdateList();
findMaxCameraValues();
@ -2058,49 +2008,71 @@ bool Game::saveScene(std::string scene)
saveFile.InsertEndChild(pathXml);
}
std::ostringstream simpleElements[LR_MAX];
std::ostringstream simpleElements_repeatScale[LR_MAX];
std::ostringstream simpleElements_tag[LR_MAX];
unsigned tagBitsUsed[LR_MAX] = { 0 };
for (size_t i = 0; i < dsq->getNumElements(); i++)
for(size_t lr = 0; lr < MAX_TILE_LAYERS; ++lr)
{
Element *e = dsq->getElement(i);
const TileStorage& ts = dsq->tilemgr.tilestore[lr];
std::ostringstream simpleElements;
std::ostringstream simpleElements_repeatScale;
std::ostringstream simpleElements_tag;
unsigned tagBitsUsed = 0;
float rot = e->rotation.z;
const size_t N = ts.tiles.size();
const bool isParallax = lr >= 9 && lr <= 15 && lr != 12
&& dsq->renderObjectLayers[LR_ELEMENTS1 + lr].followCamera != 0;
// HACK: Intentionally store the wrong rotation for parallax layers,
// to make loading a scene and the same hack there result in the correct value.
// This is to ensure compatibility with older game versions that have the renderer bug.
// See commit 4b52730be253dbfce9bea6f604c772a87da104e3
if(e->isfh() && e->bgLayer >= 9 && e->bgLayer <= 15 && e->bgLayer != 12
&& dsq->renderObjectLayers[e->layer].followCamera != 0)
for (size_t i = 0; i < N; i++)
{
rot = -rot;
TileDef d(lr, ts.tiles[i]);
// HACK: Intentionally store the wrong rotation for parallax layers,
// to make loading a scene and the same hack there result in the correct value.
// This is to ensure compatibility with older game versions that have the renderer bug.
// See commit 4b52730be253dbfce9bea6f604c772a87da104e3
if(isParallax && d.fh)
d.rot = -d.rot;
simpleElements
<< d.idx << " "
<< int(d.x) << " "
<< int(d.y) << " "
<< int(d.rot) << " "
<< d.sx << " "
<< d.sy << " "
<< int(d.fh) << " "
<< int(d.fv) << " "
<< d.ef << " "
<< d.efxIdx << " "
<< d.repeat << " ";
if(d.repeat)
{
simpleElements_repeatScale
<< d.rsx << " "
<< d.rsy << " ";
}
simpleElements_tag << d.tag << " ";
tagBitsUsed |= d.tag;
}
std::ostringstream& SE = simpleElements[e->bgLayer];
SE << e->templateIdx << " "
<< int(e->position.x) << " "
<< int(e->position.y) << " "
<< int(rot) << " "
<< e->scale.x << " "
<< e->scale.y << " "
<< int(e->isfh()) << " "
<< int(e->isfv()) << " "
<< e->elementFlag << " "
<< e->getElementEffectIndex()<< " "
<< e->isRepeatingTextureToFill() << " ";
if(e->isRepeatingTextureToFill())
std::string s = simpleElements.str();
if (!s.empty())
{
std::ostringstream& SE_rs = simpleElements_repeatScale[e->bgLayer];
SE_rs << e->repeatToFillScale.x << " "
<< e->repeatToFillScale.y << " ";
}
XMLElement *simpleElementsXML = saveFile.NewElement("SE");
simpleElementsXML->SetAttribute("k", s.c_str());
simpleElementsXML->SetAttribute("l", (unsigned)lr);
std::string str = simpleElements_repeatScale.str();
if(!str.empty())
simpleElementsXML->SetAttribute("repeatScale", str.c_str());
if(tagBitsUsed) // skip writing tags on layers where it's all zero (mainly to avoid putting a long string of 0's for border elements)
{
str = simpleElements_tag.str();
if(!str.empty())
simpleElementsXML->SetAttribute("tag", str.c_str());
}
simpleElements_tag[e->bgLayer] << e->tag << " ";
tagBitsUsed[e->bgLayer] |= e->tag;
saveFile.InsertEndChild(simpleElementsXML);
}
}
if (entitySaveData.size() > 0)
@ -2127,28 +2099,6 @@ bool Game::saveScene(std::string scene)
saveFile.InsertEndChild(entitiesNode);
}
for (int i = 0; i < LR_MAX; i++)
{
std::string s = simpleElements[i].str();
if (!s.empty())
{
XMLElement *simpleElementsXML = saveFile.NewElement("SE");
simpleElementsXML->SetAttribute("k", s.c_str());
simpleElementsXML->SetAttribute("l", i);
std::string str = simpleElements_repeatScale[i].str();
if(!str.empty())
simpleElementsXML->SetAttribute("repeatScale", str.c_str());
if(tagBitsUsed[i]) // skip writing tags on layers where it's all zero (mainly to avoid putting a long string of 0's for border elements)
{
str = simpleElements_tag[i].str();
if(!str.empty())
simpleElementsXML->SetAttribute("tag", str.c_str());
}
saveFile.InsertEndChild(simpleElementsXML);
}
}
bool result = saveFile.SaveFile(fn.c_str()) == XML_SUCCESS;
if (result)
debugLog("Successfully saved map: " + fn);
@ -2389,25 +2339,6 @@ int game_collideParticle(Vector pos)
return game->isObstructed(t);
}
void Game::rebuildElementUpdateList()
{
for (int i = LR_ELEMENTS1; i <= LR_ELEMENTS8; i++)
dsq->getRenderObjectLayer(i)->update = false;
elementUpdateList.clear();
elementInteractionList.clear();
for (size_t i = 0; i < dsq->getNumElements(); i++)
{
Element *e = dsq->getElement(i);
const int eeidx = e->getElementEffectIndex();
if (eeidx != -1 && e->layer >= LR_ELEMENTS1 && e->layer <= LR_ELEMENTS8)
elementUpdateList.push_back(e);
ElementEffect ee = dsq->getElementEffectByIndex(eeidx);
if(ee.type == EFX_WAVY)
elementInteractionList.push_back(e);
}
}
float Game::getTimer(float mod)
{
return timer*mod;
@ -2556,6 +2487,11 @@ void Game::applyState()
dsq->overlay->alpha = 1;
dsq->overlay->color = 0;
for (unsigned i = 0; i < MAX_TILE_LAYERS; ++i)
{
TileRender *tr = new TileRender(dsq->tilemgr.tilestore[i]);
addRenderObject(tr, LR_ELEMENTS1 + i);
}
for (int i = LR_ELEMENTS1; i <= LR_ELEMENTS12; i++) // LR_ELEMENTS13 is darkness, stop before that
{
@ -2607,7 +2543,7 @@ void Game::applyState()
StateObject::applyState();
dsq->clearEntities();
dsq->clearElements();
dsq->tilemgr.clearTiles();
damageSprite = new Quad;
{
@ -4422,12 +4358,6 @@ void Game::update(float dt)
StateObject::update(dt);
for (ElementUpdateList::iterator e = elementUpdateList.begin(); e != elementUpdateList.end(); e++)
{
(*e)->update(dt);
}
size_t i = 0;
for (i = 0; i < getNumPaths(); i++)
{
@ -4669,57 +4599,6 @@ void Game::snapCam()
warpCameraTo(*cameraFollow);
}
bool Game::loadElementTemplates(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen)
{
stringToLower(pack);
std::string fn;
if (dsq->mod.isActive())
fn = dsq->mod.getPath() + "tilesets/" + pack + ".txt";
else
fn = "data/tilesets/" + pack + ".txt";
if(!tileset.loadFile(fn.c_str(), usedIdx, usedIdxLen))
{
errorLog ("Could not load tileset [" + fn + "]");
return false;
}
// Aquarian alphabet letters
if(const CountedPtr<Texture> aqtex = dsq->getTexture("aquarian"))
{
const float cell = 64.0f/512.0f;
for (int i = 0; i < 27; i++)
{
ElementTemplate t;
t.idx = 1024+i;
t.tex = aqtex;
int x = i,y=0;
while (x >= 6)
{
x -= 6;
y++;
}
t.tu1 = x*cell;
t.tv1 = y*cell;
t.tu2 = t.tu1 + cell;
t.tv2 = t.tv1 + cell;
t.tv2 = 1 - t.tv2;
t.tv1 = 1 - t.tv1;
std::swap(t.tv1,t.tv2);
t.w = 512*cell;
t.h = 512*cell;
tileset.elementTemplates.push_back(t);
}
}
return true;
}
void Game::clearGrid(int v)
{
// ensure that grid is really a byte-array
@ -4789,9 +4668,6 @@ void Game::removeState()
core->particlesPaused = false;
elementUpdateList.clear();
elementInteractionList.clear();
dsq->setCursor(CURSOR_NORMAL);
dsq->darkLayer.toggle(0);
dsq->shakeCamera(0,0);
@ -4824,7 +4700,7 @@ void Game::removeState()
clearPaths();
StateObject::removeState();
dsq->clearElements();
dsq->tilemgr.clearTiles();
dsq->clearEntities();
avatar = 0;
sceneEditor.shutdown();

View file

@ -28,7 +28,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#include "ScriptedEntity.h"
#include "TileVector.h"
#include "SceneEditor.h"
#include "Tileset.h"
#include "TileMgr.h"
#include <tinyxml2.h>
using namespace tinyxml2;
@ -112,8 +112,6 @@ public:
int idx;
};
typedef std::vector<Element*> ElementUpdateList;
struct EntitySaveData
{
public:
@ -160,8 +158,6 @@ public:
// pass usedIdx == NULL to preload all textures from tileset
// pass usedIdx != NULL to preload only textures where usedIdx[i] != 0
bool loadElementTemplates(std::string pack, const unsigned char *usedIdx, size_t usedIdxLen);
Element* createElement(size_t etidx, Vector position, size_t bgLayer=0, RenderObject *copy=0, ElementTemplate *et=0);
void updateParticlePause();
@ -184,11 +180,8 @@ public:
void handleShotCollisionsSkeletal(Entity *e);
void handleShotCollisionsHair(Entity *e, int num = 0, float perc = 0);
Tileset tileset;
std::string sceneName, sceneDisplayName;
ElementTemplate *getElementTemplateByIdx(size_t idx);
bool saveScene(std::string scene);
void postInitEntities();
@ -306,6 +299,7 @@ public:
ObsType lastCollideTileType;
void fillGrid(const GridFiller& gf);
void fillGridFromQuad(Quad *q, ObsType ot=OT_INVISIBLEIN, bool trim=true);
bool isDamageTypeAvatar(DamageType dt);
@ -370,8 +364,6 @@ public:
void ensureLimit(Entity *e, int num, int state=0);
void rebuildElementUpdateList();
float getTimer(float mod=1);
float getHalfTimer(float mod=1);
@ -389,8 +381,6 @@ public:
std::string saveMusic;
GridRender *gridRender, *gridRender2, *gridRender3, *edgeRender, *gridRenderEnt, *gridRenderUser1, *gridRenderUser2;
void toggleGridRender();
ElementUpdateList elementUpdateList;
ElementUpdateList elementInteractionList;
bool invinciblity;

View file

@ -8,19 +8,6 @@
class Path;
struct ElementEffect
{
public:
int type;
int segsx, segsy;
float segs_dgox, segs_dgoy, segs_dgmx, segs_dgmy, segs_dgtm, segs_dgo;
float wavy_radius, wavy_min, wavy_max;
bool wavy_flip;
InterpolatedVector alpha;
InterpolatedVector color;
BlendType blendType;
};
struct EmoteData
{
EmoteData()

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,10 @@
class Element;
class Entity;
class Path;
class PathRender;
class TileStorage;
class MultiTileHelper;
struct EntityGroupEntity
{
@ -78,20 +82,14 @@ public:
void update(float dt);
void prevElement();
void nextElement();
void doPrevElement();
Element *cycleElementNext(Element *e);
Element *cycleElementPrev(Element *e);
void cyclePlacer(int direction);
void cycleSelectedTiles(int direction); // transmute selected to next/prev in tileset
void selectZero();
void selectEnd();
void placeElement();
void flipElementHorz();
void flipElementVert();
void deleteSelectedElement();
void deleteElement(int selectedIdx);
virtual void action(int id, int state, int source, InputDevice device);
void scaleElementUp();
void scaleElementDown();
void scaleElement1();
void placeAvatar();
void executeButtonID(int bid);
@ -117,7 +115,7 @@ public:
EditTypes editType;
EditorStates state;
Element *getElementAtCursor();
int getTileAtCursor(); // <0 when no tile, otherwise index
Entity *getEntityAtCursor();
void mouseButtonLeftUp();
@ -125,7 +123,6 @@ public:
void moveToBack();
void moveToFront();
int bgLayer;
Element *editingElement;
Entity *editingEntity;
Path *editingPath;
@ -133,21 +130,13 @@ public:
size_t selectedNode;
Path *getSelectedPath();
void changeDepth();
void updateEntitySaveData(Entity *editingEntity);
void moveLayer();
void moveElementToLayer(Element *e, int bgLayer);
void toggleElementRepeat();
bool multiSelecting;
Vector multiSelectPoint;
std::vector <Element*> selectedElements;
std::vector <size_t> selectedTiles; // indices
Vector groupCenter;
Vector getSelectedElementsCenter();
Quad dummy;
void updateSelectedElementPosition(Vector position);
void updateSelectedElementPosition(Vector rel);
int selectedEntityType;
SelectedEntity selectedEntity;
@ -203,8 +192,10 @@ protected:
void enterScaleState();
void enterRotateState();
void enterMoveState();
void enterAnyStateHelper(EditorStates newstate);
Vector oldPosition, oldRotation, oldScale, cursorOffset, oldRepeatScale;
float oldRotation;
Vector oldPosition, oldScale, cursorOffset, oldRepeatScale;
Entity *movingEntity;
@ -234,12 +225,20 @@ protected:
void mouseButtonLeft();
void mouseButtonRight();
void setActiveLayer(unsigned bglayer);
TileStorage& getCurrentLayerTiles();
void clearSelection();
MultiTileHelper *createMultiTileHelperFromSelection();
void destroyMultiTileHelper();
size_t curElement;
Quad *placer;
MultiTileHelper *multi;
DebugFont *text;
bool on;
InterpolatedVector oldGlobalScale;
PathRender *pathRender;
};
#endif // AQUARIA_SCENEEDITOR_H

View file

@ -5908,11 +5908,7 @@ luaFunc(entity_doElementInteraction)
if (!touchWidth)
touchWidth = 16;
ElementUpdateList& elems = game->elementInteractionList;
for (ElementUpdateList::iterator it = elems.begin(); it != elems.end(); ++it)
{
(*it)->doInteraction(e, mult, touchWidth);
}
dsq->tilemgr.doTileInteraction(e->position, e->vel, mult, touchWidth);
}
luaReturnNil();
}
@ -7640,44 +7636,59 @@ luaFunc(node_setElementsInLayerActive)
Path *p = path(L);
if (p)
{
int l = lua_tointeger(L, 2);
unsigned l = lua_tointeger(L, 2);
bool v = getBool(L, 3);
int tag = lua_tointeger(L, 3);
for (Element *e = dsq->getFirstElementOnLayer(l); e; e = e->bgLayerNext)
if(l < MAX_TILE_LAYERS)
{
if (e && (!tag || e->tag == tag) && p->isCoordinateInside(e->position))
TileStorage& ts = dsq->tilemgr.tilestore[l];
const size_t N = ts.tiles.size();
for(size_t i = 0; i < N; ++i)
{
e->setElementActive(v);
TileData& t = ts.tiles[i];
if((!tag || t.tag == tag) && p->isCoordinateInside(Vector(t.x, t.y)))
t.setVisible(v);
}
ts.refreshAll();
}
}
luaReturnNil();
}
static int pushElementData(lua_State *L, const Element *e)
static int pushTileData(lua_State *L, const TileData& t, unsigned layer)
{
lua_pushinteger(L, e->templateIdx);
lua_pushstring(L, e->texture->name.c_str());
lua_pushboolean(L, e->isElementActive());
lua_pushinteger(L, e->bgLayer);
lua_pushinteger(L, e->tag);
lua_pushinteger(L, t.et->idx);
lua_pushstring(L, t.et->tex->name.c_str());
lua_pushboolean(L, t.isVisible());
lua_pushinteger(L, layer);
lua_pushinteger(L, t.tag);
return 5;
}
// (layer, func)
luaFunc(refreshElementsOnLayerCallback)
{
const int layer = lua_tointeger(L, 1);
const unsigned layer = lua_tointeger(L, 1);
size_t done = 0;
for (Element *e = dsq->getFirstElementOnLayer(layer); e; e = e->bgLayerNext)
if(layer < MAX_TILE_LAYERS)
{
lua_pushvalue(L, 2); // the callback
int args = pushElementData(L, e);
lua_call(L, args, 1);
bool newon = lua_toboolean(L, -1);
lua_pop(L, 1);
e->setElementActive(newon);
++done;
TileStorage& ts = dsq->tilemgr.tilestore[layer];
const size_t N = ts.tiles.size();
for(size_t i = 0; i < N; ++i)
{
lua_pushvalue(L, 2); // the callback
TileData& t = ts.tiles[i];
int args = pushTileData(L, t, layer);
lua_call(L, args, 1);
bool newon = lua_toboolean(L, -1);
lua_pop(L, 1);
t.setVisible(newon);
++done;
}
if(done)
ts.refreshAll();
}
luaReturnInt(done);
}
@ -7686,23 +7697,31 @@ luaFunc(refreshElementsOnLayerCallback)
luaFunc(refreshElementsWithTagCallback)
{
const int tag = lua_tointeger(L, 1);
const size_t N = dsq->getNumElements();
size_t done = 0;
for(size_t i = 0; i < N; ++i)
size_t total = 0;
for(unsigned layer = 0; layer < MAX_TILE_LAYERS; ++layer)
{
Element *e = dsq->getElement(i);
if(e->tag == tag)
TileStorage& ts = dsq->tilemgr.tilestore[layer];
const size_t N = ts.tiles.size();
size_t done = 0;
for(size_t i = 0; i < N; ++i)
{
lua_pushvalue(L, 2); // the callback
int args = pushElementData(L, e);
lua_call(L, args, 1);
bool newon = lua_toboolean(L, -1);
lua_pop(L, 1);
e->setElementActive(newon);
++done;
TileData& t = ts.tiles[i];
if(t.tag == tag)
{
lua_pushvalue(L, 2); // the callback
int args = pushTileData(L, t, layer);
lua_call(L, args, 1);
bool newon = lua_toboolean(L, -1);
lua_pop(L, 1);
t.setVisible(newon);
++done;
}
}
if(done)
ts.refreshAll();
total += done;
}
luaReturnInt(done);
luaReturnInt(total);
}

278
Aquaria/TileMgr.cpp Normal file
View file

@ -0,0 +1,278 @@
#include "TileMgr.h"
#include <assert.h>
#include "Base.h"
#include "ttvfs_stdio.h"
static const unsigned s_tileFlags[] =
{
/* EF_NONE -> */ TILEFLAG_NONE,
/* EF_SOLID -> */ TILEFLAG_SOLID,
/* EF_MOVABLE-> */ TILEFLAG_NONE, /* unused */
/* EF_HURT -> */ TILEFLAG_SOLID | TILEFLAG_HURT,
/* EF_SOLID2 -> */ TILEFLAG_SOLID | TILEFLAG_SOLID_THICK,
/* EF_SOLID3 -> */ TILEFLAG_SOLID | TILEFLAG_SOLID_THICK | TILEFLAG_SOLID_IN
};
TileFlags TileMgr::GetTileFlags(ElementFlag ef)
{
//compile_assert(Countof(s_tileFlags) == EF_MAX);
unsigned tf = TILEFLAG_NONE;
if(unsigned(ef) < Countof(s_tileFlags))
tf = s_tileFlags[ef];
return (TileFlags)tf;
}
ElementFlag TileMgr::GetElementFlag(TileFlags tf)
{
unsigned ef = EF_NONE;
if(tf & TILEFLAG_SOLID)
{
if(tf & TILEFLAG_HURT)
ef = EF_HURT;
else if(tf & TILEFLAG_SOLID_IN)
ef = EF_SOLID3;
else if(tf & TILEFLAG_SOLID_THICK)
ef = EF_SOLID2;
else
ef = EF_SOLID;
}
return (ElementFlag)ef;
}
TileMgr::TileMgr()
{
}
TileMgr::~TileMgr()
{
destroy();
}
void TileMgr::update(float dt)
{
tileEffects.update(dt);
for(size_t i = 0; i < Countof(tilestore); ++i)
tilestore[i].update(dt);
}
void TileMgr::destroy()
{
clearTiles();
tileset.clear();
tileEffects.clear();
}
size_t TileMgr::getNumTiles() const
{
size_t num = 0;
for(size_t i = 0; i < Countof(tilestore); ++i)
num += tilestore[i].size();
return num;
}
TileStorage::Sizes TileMgr::getStats() const
{
TileStorage::Sizes tsz {};
for(size_t i = 0; i < Countof(tilestore); ++i)
{
TileStorage::Sizes sz = tilestore[i].stats();
tsz.tiles += sz.tiles;
tsz.collide += sz.collide;
tsz.update += sz.update;
}
return tsz;
}
void TileMgr::clearTiles()
{
for(size_t i = 0; i < Countof(tilestore); ++i)
tilestore[i].destroyAll();
}
void TileMgr::doTileInteraction(const Vector& pos, const Vector& vel, float mult, float touchWidth)
{
for(size_t i = 0; i < Countof(tilestore); ++i)
tilestore[i].doInteraction(pos, vel, mult, touchWidth);
}
TileData* TileMgr::createOneTile(unsigned tilesetID, unsigned layer, float x, float y, ElementFlag ef, int effidx)
{
TileData *t = _createTile(tilesetID, layer, x, y, ef, effidx);
if(t)
{
TileStorage& ts = tilestore[layer];
ts.refreshAll();
}
return t;
}
void TileMgr::createTiles(const TileDef* defs, size_t n)
{
char used[Countof(tilestore)] = {0};
for(size_t i = 0; i < n; ++i)
{
const TileDef& d = defs[i];
TileData *t = _createTile(d.idx, d.layer, d.x, d.y, (ElementFlag)d.ef, d.efxIdx);
if(t)
{
used[d.layer] = 1;
if(d.fh)
t->flags |= TILEFLAG_FH;
if(d.repeat)
t->flags |= TILEFLAG_REPEAT;
// FIXME: handle fv
t->rotation = d.rot;
t->texscaleX = d.rsx;
t->texscaleY = d.rsy;
t->tag = d.tag;
t->scalex = d.sx;
t->scaley = d.sy;
}
}
for(size_t i = 0; i < Countof(used); ++i)
if(used[i])
tilestore[i].refreshAll();
}
void TileMgr::exportGridFillers(std::vector<GridFiller>& fillers) const
{
for(size_t k = 0; k < Countof(tilestore); ++k)
{
const TileStorage& ts = tilestore[k];
const size_t N = ts.size();
for(size_t i = 0; i < N; ++i)
{
const TileData& t = ts.tiles[i];
if((t.flags & TILEFLAG_SOLID) && !(t.flags & TILEFLAG_HIDDEN) && t.et)
{
GridFiller gf;
gf.fh = !!(t.flags & TILEFLAG_FH);
gf.position = Vector(t.x, t.y);
gf.rotation = t.rotation;
gf.scale = Vector(t.scalex, t.scaley);
gf.texture = t.et->tex.content();
gf.width = t.et->w;
gf.height = t.et->h;
gf.trim = !(t.flags & TILEFLAG_SOLID_THICK);
if(t.flags & TILEFLAG_HURT)
gf.obs = OT_HURT;
else if(t.flags & TILEFLAG_SOLID_IN)
gf.obs = OT_INVISIBLEIN;
else
gf.obs = OT_INVISIBLE;
fillers.push_back(gf);
}
}
}
}
TileData* TileMgr::_createTile(unsigned tilesetID, unsigned layer, float x, float y, ElementFlag ef, int effidx)
{
if(layer >= Countof(tilestore))
return NULL;
TileData t;
t.x = x;
t.y = y;
t.rotation = 0;
t.texscaleX = 1;
t.texscaleY = 1;
t.scalex = 1;
t.scaley = 1;
t.beforeScaleOffsetX = 0;
t.beforeScaleOffsetY = 0;
t.flags = GetTileFlags(ef);
t.tag = 0;
t.et = tileset.getByIdx(tilesetID);
assert(t.et);
/* t.eff = */ tileEffects.assignEffect(t, effidx);
TileStorage& ts = tilestore[layer];
ts.tiles.push_back(t);
return &ts.tiles.back();
}
void TileMgr::loadTileEffects(const char *fn)
{
debugLog(fn);
InStream inFile(fn);
if(!inFile)
{
errorLog("TileMgr::loadTileEffects: Failed to open file");
return;
}
clearTiles();
tileEffects.clear();
std::string line;
while (std::getline(inFile, line))
{
debugLog("Line: " + line);
std::istringstream is(line);
TileEffectConfig e;
int efxType = EFX_NONE;
std::string type;
is >> e.index >> type;
if (type == "EFX_SEGS")
{
efxType = EFX_SEGS;
is >> e.u.segs.x >> e.u.segs.y >> e.u.segs.dgox >> e.u.segs.dgoy >> e.u.segs.dgmx >> e.u.segs.dgmy >> e.u.segs.dgtm >> e.u.segs.dgo;
}
else if (type == "EFX_WAVY")
{
debugLog("loading wavy");
efxType = EFX_WAVY;
is >> e.u.wavy.segsy >> e.u.wavy.radius >> e.u.wavy.flip;
}
else if (type == "EFX_ALPHA")
{
efxType = EFX_ALPHA;
int loop_unused, blend; // loop is unused because we always loop forever
is >> blend >> e.u.alpha.val0 >> e.u.alpha.val1 >> e.u.alpha.time >> loop_unused >> e.u.alpha.pingpong >> e.u.alpha.ease;
e.u.alpha.blend = blend < _BLEND_MAXSIZE ? (BlendType)blend : BLEND_DISABLED;
}
if(efxType != EFX_NONE)
{
e.type = (EFXType)efxType;
const size_t newsize = size_t(e.index) + 1;
if(tileEffects.configs.size() < newsize)
tileEffects.configs.resize(newsize);
tileEffects.configs[e.index] = e;
}
else
errorLog("elementeffects.txt: Error on this line:\n" + line);
}
inFile.close();
tileEffects.finalize();
}
TileDef::TileDef(unsigned lr)
: layer(lr), idx(0), x(0), y(0), rot(0), fh(0), fv(0), ef(0), efxIdx(-1), repeat(0)
, tag(0), sx(1), sy(1), rsx(1), rsy(1)
{
}
TileDef::TileDef(unsigned lr, const TileData& t)
: layer(lr), idx((unsigned)t.et->idx), x(t.x), y(t.y), rot(t.rotation)
, fh(!!(t.flags & TILEFLAG_FH))
, fv(false) // FIXME
, ef(TileMgr::GetElementFlag((TileFlags)t.flags))
, efxIdx(t.eff ? t.eff->efxidx : -1)
, repeat(!!(t.flags & TILEFLAG_REPEAT))
, tag(t.tag)
, sx(t.scalex), sy(t.scaley)
, rsx(t.texscaleX), rsy(t.texscaleY)
{
}

85
Aquaria/TileMgr.h Normal file
View file

@ -0,0 +1,85 @@
#ifndef TILEMGR_H
#define TILEMGR_H
#include "Tile.h"
#include "Tileset.h"
#include "GameEnums.h"
enum { MAX_TILE_LAYERS = 16 };
// Legacy!
// These are needed as-is when loading and saving maps.
// The values are stored in the map and can't be changed.
enum ElementFlag
{
EF_NONE = 0,
EF_SOLID = 1,
EF_MOVABLE = 2, // unused
EF_HURT = 3,
EF_SOLID2 = 4,
EF_SOLID3 = 5,
//EF_MAX = 6
};
// temporary struct to fill grid with
struct GridFiller
{
ObsType obs;
bool trim;
const Texture *texture;
Vector scale, position;
float width, height, rotation;
bool fh;
};
// struct with all tile properties for batch creation of tiles
struct TileDef
{
TileDef(unsigned lr);
TileDef(unsigned lr, const TileData& t);
unsigned layer, idx;
int x, y, rot, fh, fv, ef, efxIdx, repeat, tag;
float sx, sy, rsx, rsy;
};
class TileMgr
{
public:
TileMgr();
~TileMgr();
void update(float dt);
void destroy();
void loadTileEffects(const char *fn);
size_t getNumTiles() const;
TileStorage::Sizes getStats() const;
void clearTiles();
void doTileInteraction(const Vector& pos, const Vector& vel, float mult, float touchWidth);
// don't store the returned pointer anywhere! Use it to set extra things, but then drop it.
// It will become invalid when more tiles are added.
// This function is also quite ineffieient and is intended for editor use only!
TileData *createOneTile(unsigned tilesetID, unsigned layer, float x, float y, ElementFlag ef = EF_NONE, int effidx = -1);
void createTiles(const TileDef *defs, size_t n);
void exportGridFillers(std::vector<GridFiller>& fillers) const;
TileStorage tilestore[MAX_TILE_LAYERS];
TileEffectStorage tileEffects;
Tileset tileset;
static TileFlags GetTileFlags(ElementFlag ef);
static ElementFlag GetElementFlag(TileFlags tf);
private:
TileData *_createTile(unsigned tilesetID, unsigned layer, float x, float y, ElementFlag ef = EF_NONE, int effidx = -1);
TileMgr(const TileMgr&); // no-copy
};
#endif

View file

@ -97,6 +97,7 @@ RenderObject::RenderObject()
shareAlphaWithChildren = false;
shareColorWithChildren = false;
neverFollowCamera = false;
}
RenderObject::~RenderObject()

View file

@ -242,6 +242,7 @@ public:
bool _hidden;
bool _fv, _fh;
bool _markedForDelete;
bool neverFollowCamera;
unsigned char pm; // unsigned char to save space

View file

@ -229,6 +229,8 @@ void RenderObjectLayer::prepareRender()
toRender.push_back(NULL); // terminate
core->totalRenderObjectCount += n;
// TODO: set followCameraMult = (0,0) when followCamera == 0 ?
switch(followCameraLock)
{
default:

View file

@ -41,6 +41,8 @@ Vector RenderObject::getFollowCameraPosition(const Vector& v) const
{
assert(layer != LR_NONE);
assert(!parent); // this makes no sense when we're not a root object
if(neverFollowCamera)
return v;
const RenderObjectLayer &rl = core->renderObjectLayers[layer];
Vector M = rl.followCameraMult;
float F = followCamera;

View file

@ -47,7 +47,7 @@ Texture::~Texture()
unload();
}
void Texture::readRGBA(unsigned char *pixels)
void Texture::readRGBA(unsigned char *pixels) const
{
glPixelStorei(GL_PACK_ALIGNMENT, 1);
glBindTexture(GL_TEXTURE_2D, gltexid);
@ -207,7 +207,7 @@ bool Texture::upload(const ImageData& img, bool mipmap)
return true;
}
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam)
unsigned char * Texture::getBufferAndSize(int *wparam, int *hparam, size_t *sizeparam) const
{
const size_t bytes = size_t(width) * size_t(height) * 4;
unsigned char *data = (unsigned char*)malloc(bytes);

View file

@ -46,9 +46,9 @@ public:
int width, height;
void writeRGBA(int tx, int ty, int w, int h, const unsigned char *pixels);
void readRGBA(unsigned char *pixels);
void readRGBA(unsigned char *pixels) const;
unsigned char *getBufferAndSize(int *w, int *h, size_t *size); // returned memory must be free()'d
unsigned char *getBufferAndSize(int *w, int *h, size_t *size) const; // returned memory must be free()'d
std::string name, filename;
bool upload(const ImageData& img, bool mipmap);

View file

@ -269,24 +269,31 @@ Texture *TextureMgr::finalize(TexLoadTmp& tt)
return tex;
}
void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
size_t TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_t n, LoadMode mode, ProgressCallback cb, void *cbUD)
{
size_t doneCB = 0;
if(threads.empty())
{
size_t loaded = 0;
for(size_t i = 0; i < n; ++i)
{
Texture *tex = load(texnames[i], mode);
if(pdst)
pdst[i] = tex;
if(cb)
cb(++doneCB, cbUD);
loaded += !!tex;
}
return;
return loaded;
}
// Important that this is pre-allocated. We store pointers to elements and
// send them to threads, so this must never reallocate.
std::vector<TexLoadTmp> tmp(n);
size_t inprogress = 0, doneCB = 0;
size_t inprogress = 0;
size_t loaded = 0;
for(size_t i = 0; i < n; ++i)
{
TexLoadTmp& tt = tmp[i];
@ -301,6 +308,7 @@ void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_
pdst[i] = tt.curTex;
if(cb)
cb(++doneCB, cbUD);
++loaded;
continue;
}
@ -320,7 +328,9 @@ void TextureMgr::loadBatch(Texture * pdst[], const std::string texnames[], size_
pdst[tt.arrayidx] = tex;
if(cb)
cb(++doneCB, cbUD);
loaded += !!tex;
}
return loaded;
}
Texture* TextureMgr::load(const std::string& texname, LoadMode mode)

View file

@ -30,7 +30,7 @@ public:
OVERWRITE, // always overwrite
};
void loadBatch(Texture *pdst[], const std::string texnames[], size_t n, LoadMode mode = KEEP, ProgressCallback cb = 0, void *cbUD = 0);
size_t loadBatch(Texture *pdst[], const std::string texnames[], size_t n, LoadMode mode = KEEP, ProgressCallback cb = 0, void *cbUD = 0);
Texture *load(const std::string& texname, LoadMode mode);
void reloadAll(LoadMode mode);

View file

@ -2,9 +2,9 @@
#include "RenderGrid.h"
#include "Tileset.h"
#include "Base.h"
#include <algorithm>
TileStorage::TileStorage(const TileEffectStorage& eff)
: effstore(eff)
TileStorage::TileStorage()
{
}
@ -13,16 +13,31 @@ TileStorage::~TileStorage()
destroyAll();
}
void TileStorage::moveToFront(size_t idx)
TileStorage::Sizes TileStorage::stats() const
{
_moveToFront(idx);
refreshAll();
Sizes sz;
sz.tiles = tiles.size();
sz.update = indicesToUpdate.size();
sz.collide = indicesToCollide.size();
return sz;
}
void TileStorage::moveToBack(size_t idx)
void TileStorage::moveToFront(const size_t *indices, size_t n)
{
_moveToBack(idx);
refreshAll();
if(n)
{
_moveToFront(indices, n);
refreshAll();
}
}
void TileStorage::moveToBack(const size_t *indices, size_t n)
{
if(n)
{
_moveToBack(indices, n);
refreshAll();
}
}
void TileStorage::update(float dt)
@ -44,24 +59,58 @@ void TileStorage::doInteraction(const Vector& pos, const Vector& vel, float mult
}
}
void TileStorage::_moveToFront(size_t idx)
void TileStorage::_moveToFront(const size_t *indices, size_t n)
{
// move tile to front -> move it to the back of the list, to be rendered last aka on top of everything else
TileData tile = tiles[idx];
tiles.erase(tiles.begin() + idx);
tiles.push_back(tile);
if(n == 1)
{
TileData tile = tiles[*indices];
tiles.erase(tiles.begin() + *indices);
tiles.push_back(tile);
return;
}
_moveToPos(size(), indices, n);
}
void TileStorage::_moveToBack(size_t idx)
void TileStorage::_moveToBack(const size_t *indices, size_t n)
{
// move tile to back -> move it to the front of the list, to be rendered first aka underneath everything else
TileData tile = tiles[idx];
tiles.erase(tiles.begin() + idx);
tiles.insert(tiles.begin(), tile);
if(n == 1)
{
TileData tile = tiles[*indices];
tiles.erase(tiles.begin() + *indices);
tiles.insert(tiles.begin(), tile);
return;
}
_moveToPos(0, indices, n);
}
void TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t n)
void TileStorage::_moveToPos(size_t where, const size_t * indices, size_t n)
{
std::vector<size_t> tmp(indices, indices + n);
std::sort(tmp.begin(), tmp.end());
std::vector<TileData> tt(n);
// sorted indices -> preserve relative order of tiles
for(size_t i = 0; i < n; ++i)
tt[i] = tiles[tmp[i]];
// SORTED indices, erasing from the BACK -> we don't get a destructive index shift
for(size_t i = tmp.size(); i --> 0; )
tiles.erase(tiles.begin() + tmp[i]);
tiles.insert(tiles.begin() + where, tt.begin(), tt.end());
}
size_t TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t n)
{
const size_t firstNewIdx = other.tiles.size();
for(size_t i = 0; i < n; ++i)
other.tiles.push_back(tiles[indices[i]]);
@ -81,6 +130,7 @@ void TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t
refreshAll();
other.refreshAll();
return firstNewIdx;
}
static void dropAttachments(TileData& t)
@ -133,13 +183,51 @@ void TileStorage::setTag(unsigned tag, const size_t* indices, size_t n)
// don't need to refresh here
}
void TileStorage::setEffect(int idx, const size_t* indices, size_t n)
void TileStorage::setEffect(const TileEffectStorage& effstore, int idx, const size_t* indices, size_t n)
{
for(size_t i = 0; i < n; ++i)
effstore.assignEffect(tiles[indices[i]], idx);
refreshAll();
}
void TileStorage::changeFlags(unsigned flagsToSet, unsigned flagsToUnset, const size_t* indices, size_t n)
{
for(size_t i = 0; i < n; ++i)
{
unsigned& f = tiles[indices[i]].flags;
unsigned tmp = f & ~flagsToUnset;
f = tmp | flagsToSet;
}
}
size_t TileStorage::cloneSome(const TileEffectStorage& effstore, const size_t* indices, size_t n)
{
const size_t ret = tiles.size(); // new starting index of clone tiles
// cloning tiles is very simple, but owned pointers will be duplicated and need to be fixed up
const size_t N = ret + n;
tiles.resize(N);
for(size_t i = 0; i < n; ++i)
tiles[ret + i] = tiles[indices[i]];
// cleanup pointers
for(size_t i = ret; i < N; ++i) // loop only over newly added tiles
{
TileData& t = tiles[i];
if((t.flags & TILEFLAG_OWN_EFFDATA) && t.eff)
{
int efx = t.eff->efxidx;
t.eff = NULL; // not our pointer, just pretend it was never there
t.flags &= TILEFLAG_OWN_EFFDATA;
effstore.assignEffect(t, efx); // recreate effect properly
}
}
refreshAll();
return ret;
}
void TileStorage::refreshAll()
{
indicesToCollide.clear();
@ -149,13 +237,16 @@ void TileStorage::refreshAll()
for(size_t i = 0; i < n; ++i)
{
const TileData& t = tiles[i];
if(const TileEffectData *e = t.eff)
if(!(t.flags & TILEFLAG_HIDDEN))
{
if(t.flags & TILEFLAG_OWN_EFFDATA)
if(const TileEffectData *e = t.eff)
{
indicesToUpdate.push_back(i);
if(e->efxtype == EFX_WAVY)
indicesToCollide.push_back(i);
if(t.flags & TILEFLAG_OWN_EFFDATA)
{
indicesToUpdate.push_back(i);
if(e->efxtype == EFX_WAVY)
indicesToCollide.push_back(i);
}
}
}
}
@ -170,10 +261,14 @@ void TileStorage::clearSelection()
TileEffectData::TileEffectData(const TileEffectConfig& cfg)
: efxtype(cfg.type), efxidx(cfg.index)
, grid(NULL), blend(BLEND_DEFAULT)
, grid(NULL), alpha(1), blend(BLEND_DEFAULT)
{
switch(cfg.type)
{
case EFX_NONE:
assert(false);
break;
case EFX_WAVY:
{
float bity = 20; // FIXME
@ -184,7 +279,7 @@ TileEffectData::TileEffectData(const TileEffectConfig& cfg)
RenderGrid *g = new RenderGrid(2, cfg.u.wavy.segsy);
grid = g;
g->gridType = GRID_UNDEFINED; // by default it's GRID_WAVY, but that would reset during update
g->gridType = GRID_UNDEFINED; // we do the grid update manually
wavy.angleOffset = 0;
wavy.magnitude = 0;
@ -328,6 +423,15 @@ void TileEffectData::doInteraction(const TileData& t, const Vector& pos, const V
}
}
TileEffectStorage::TileEffectStorage()
{
}
TileEffectStorage::~TileEffectStorage()
{
clear();
}
void TileEffectStorage::assignEffect(TileData& t, int index) const
{
dropAttachments(t);
@ -343,6 +447,9 @@ void TileEffectStorage::assignEffect(TileData& t, int index) const
}
else if(idx < configs.size())
{
if(configs[idx].type == EFX_NONE)
return;
t.eff = new TileEffectData(configs[idx]);
t.flags |= TILEFLAG_OWN_EFFDATA;
}
@ -354,3 +461,49 @@ void TileEffectStorage::update(float dt)
if(TileEffectData *eff = prepared[i])
eff->update(dt, NULL);
}
void TileEffectStorage::clear()
{
clearPrepared();
configs.clear();
}
void TileEffectStorage::clearPrepared()
{
for(size_t i = 0; i < prepared.size(); ++i)
delete prepared[i];
prepared.clear();
}
void TileEffectStorage::finalize()
{
clearPrepared();
prepared.resize(configs.size(), (TileEffectData*)NULL);
for(size_t i = 0; i < configs.size(); ++i)
{
TileEffectConfig& c = configs[i];
c.index = unsigned(i); // just in case
// segs and alpha are independent of the tile they are applied to,
// so we can create shared instances of the effect.
if(c.type == EFX_SEGS || c.type == EFX_ALPHA)
prepared[i] = new TileEffectData(c);
}
}
bool TileData::isCoordinateInside(float cx, float cy, float minsize) const
{
float hw = fabsf(et->w * scalex)*0.5f;
float hh = fabsf(et->h * scaley)*0.5f;
if (hw < minsize)
hw = minsize;
if (hh < minsize)
hh = minsize;
return cx >= x - hw && cx <= x + hw
&& cy >= y - hh && cy <= y + hh;
}

View file

@ -28,8 +28,13 @@ Further observations:
And on map reload everything is back to the same value for each tile with the same effect and params.
So we can totally exclude the editor.
Assumptions:
- Most tiles that exist are going to be rendered
- Only few tiles have an effect attached
Gotaches:
- Keeping a pointer to a TileData is not safe.
- Tile indexes are not stable. Moving a tile changes the index it can be addressed with
*/
class ElementTemplate;
@ -39,12 +44,13 @@ class TileRender;
enum EFXType
{
EFX_NONE,
EFX_SEGS,
EFX_ALPHA,
EFX_WAVY
};
// static configuration for one effect type
// static configuration for one effect type. POD.
struct TileEffectConfig
{
public:
@ -85,9 +91,10 @@ enum TileFlags
TILEFLAG_SOLID_IN = 0x08, // instead of OT_INVISIBLE, generate OT_INVISIBLEIN
TILEFLAG_HURT = 0x10, // always generate OT_HURT
TILEFLAG_FH = 0x20, // flipped horizontally
TILEFLAG_OWN_EFFDATA = 0x40, // tile owns its TileEffectData, can modify & must delete
TILEFLAG_OWN_EFFDATA = 0x40, // tile owns its TileEffectData, can update, must delete
TILEFLAG_HIDDEN = 0x80, // don't render tile
TILEFLAG_SELECTED = 0x100
TILEFLAG_SELECTED = 0x100, // ephemeral: selected in editor
TILEFLAG_EDITOR_HIDDEN = 0x200 // tile is hidden for editor reasons. temporarily set when multi-selecting and moving. doesn't count as hidden externally and is only for rendering.
};
struct TileData;
@ -100,7 +107,7 @@ struct TileEffectData
void doInteraction(const TileData& t, const Vector& pos, const Vector& vel, float mult, float touchWidth);
const EFXType efxtype;
const unsigned efxidx; // index to ElementEffect
const unsigned efxidx; // index of TileEffect
RenderGrid *grid;
InterpolatedVector alpha;
BlendType blend;
@ -116,46 +123,70 @@ struct TileEffectData
void update(float dt);
};
Wavy wavy;
private:
TileEffectData(const TileEffectData&); // no-copy
};
// POD and as compact as possible
// POD and as compact as possible. Intended for rendering as quickly as possible.
// the idea is that these are linearly adjacent in memory in the order they are rendered,
// to maximize cache & prefetch efficiency
struct TileData
{
float x, y, rotation, texscale;
float scalex, scaley;
float beforeScaleOffsetX, beforeScaleOffsetY;
float x, y, scalex, scaley, texscaleX, texscaleY;
float beforeScaleOffsetX, beforeScaleOffsetY; // almost always 0. // TODO: this is nasty, ideally get rid of this
float rotation;
unsigned flags; // TileFlags
unsigned tag;
ElementTemplate *et; // texture, texcoords, etc is here
TileEffectData *eff;
unsigned tag; // FIXME: make this int
const ElementTemplate *et; // never NULL. texture, texcoords, etc is here. // TODO: maybe replace with unsigned tilesetID? but that's an extra indirection or two during rendering...
TileEffectData *eff; // mostly NULL
// helpers for external access
inline void setVisible(bool on) { if(on) flags &= ~TILEFLAG_HIDDEN; else flags |= TILEFLAG_HIDDEN; }
inline bool isVisible() const { return !(flags & TILEFLAG_HIDDEN); }
bool isCoordinateInside(float cx, float cy, float minsize = 0) const;
};
class TileEffectStorage
{
public:
TileEffectStorage();
~TileEffectStorage();
void finalize(); // first fill configs[], then call this
void assignEffect(TileData& t, int index) const;
void update(float dt);
void clear(); // do NOT call this while there are tiles that may reference one in prepared[]
std::vector<TileEffectData*> prepared;
std::vector<TileEffectConfig> configs;
private:
void clearPrepared();
std::vector<TileEffectData*> prepared;
TileEffectStorage(const TileEffectStorage&); // no-copy
};
class TileStorage
{
friend class TileRender;
public:
TileStorage(const TileEffectStorage& eff);
TileStorage();
~TileStorage();
void moveToFront(size_t idx);
void moveToBack(size_t idx);
void moveToOther(TileStorage& other, const size_t *indices, size_t n);
void moveToFront(const size_t *indices, size_t n);
void moveToBack(const size_t *indices, size_t n);
// returns starting index of new tiles. Since new tiles are always appended at the end,
// the new indices corresponding to the moved tiles are [retn .. retn+n)
size_t moveToOther(TileStorage& other, const size_t *indices, size_t n);
size_t cloneSome(const TileEffectStorage& effstore, const size_t *indices, size_t n);
void deleteSome(const size_t *indices, size_t n);
void setTag(unsigned tag, const size_t *indices, size_t n);
void setEffect(int idx, const size_t *indices, size_t n);
void setEffect(const TileEffectStorage& effstore, int idx, const size_t *indices, size_t n);
void changeFlags(unsigned flagsToSet, unsigned flagsToUnset, const size_t *indices, size_t n);
void update(float dt);
void doInteraction(const Vector& pos, const Vector& vel, float mult, float touchWidth);
@ -164,16 +195,27 @@ public:
void clearSelection();
struct Sizes
{
size_t tiles, update, collide;
};
Sizes stats() const;
size_t size() const { return tiles.size(); }
std::vector<TileData> tiles; // must call refreshAll() after changing this
private:
std::vector<TileData> tiles;
std::vector<size_t> indicesToUpdate;
std::vector<size_t> indicesToCollide;
const TileEffectStorage& effstore;
void _refreshTile(const TileData& t);
void _moveToFront(size_t idx);
void _moveToBack(size_t idx);
void _moveToFront(const size_t *indices, size_t n);
void _moveToBack(const size_t *indices, size_t n);
void _moveToPos(size_t where, const size_t *indices, size_t n);
TileStorage(const TileStorage&); // no-copy
};

View file

@ -9,6 +9,8 @@
TileRender::TileRender(const TileStorage& tiles)
: storage(tiles), renderBorders(false)
{
this->cull = false;
this->neverFollowCamera = true;
}
TileRender::~TileRender()
@ -46,6 +48,7 @@ void TileRender::onRender(const RenderState& rs) const
const RenderObjectLayer& rl = core->renderObjectLayers[this->layer];
const Vector M = rl.followCameraMult; // affected by parallaxLock
const float F = rl.followCamera;
const bool parallax = rl.followCamera > 0;
// Formula from RenderObject::getFollowCameraPosition() and optimized for speed
const Vector C = core->screenCenter;
@ -61,15 +64,18 @@ void TileRender::onRender(const RenderState& rs) const
for(size_t i = 0; i < storage.tiles.size(); ++i)
{
const TileData& tile = storage.tiles[i];
if(tile.flags & TILEFLAG_HIDDEN)
if(tile.flags & (TILEFLAG_HIDDEN | TILEFLAG_EDITOR_HIDDEN))
continue;
const Vector tilepos(tile.x, tile.y);
const Vector tmp = T + (F * tilepos);
const Vector pos = tilepos * M1 + (tmp * M); // lerp, used to select whether to use original v or parallax-corrected v
Vector pos(tile.x, tile.y);
if(parallax)
{
const Vector tmp = T + (F * pos);
pos = pos * M1 + (tmp * M); // lerp, used to select whether to use original v or parallax-corrected v
}
ElementTemplate * const et = tile.et;
if(Texture * const tex = et->tex.content())
const ElementTemplate * const et = tile.et;
if(const Texture * const tex = et->tex.content())
{
unsigned texid = tex->gltexid;
unsigned rep = tile.flags & TILEFLAG_REPEAT;
@ -96,7 +102,7 @@ void TileRender::onRender(const RenderState& rs) const
// this is only relevant in editor mode and is always 0 otherwise
glTranslatef(tile.beforeScaleOffsetX, tile.beforeScaleOffsetY, 0);
glScalef(tile.scalex * et->w, tile.scaley * et->h, 1);
glScalef(tile.scalex, tile.scaley, 1);
//glScalef(tile.scalex * et->w, tile.scaley * et->h, 1); // TODO use this + fixed verts
BlendType blend = BLEND_DEFAULT;
@ -141,6 +147,9 @@ void TileRender::onRender(const RenderState& rs) const
}
else
{
glPushMatrix();
glScalef(et->w, et->h, 1);
RenderState rx(rs);
rx.alpha = alpha;
grid->render(rx, upperLeftTextureCoordinates, lowerRightTextureCoordinates);
@ -150,6 +159,8 @@ void TileRender::onRender(const RenderState& rs) const
grid->renderDebugPoints(rs);
lastTexId = 0;
}
glPopMatrix();
}
if(renderBorders)
@ -183,7 +194,11 @@ void TileRender::onRender(const RenderState& rs) const
glPopMatrix();
}
RenderObject::lastTextureApplied = lastTexId;
RenderObject::lastTextureRepeat = !!lastTexRepeat;
}
void TileRender::onUpdate(float dt)
{
//this->position = core->screenCenter;
}

View file

@ -14,7 +14,8 @@ public:
TileRender(const TileStorage& tiles);
virtual ~TileRender();
virtual void onRender(const RenderState& rs) const;
virtual void onRender(const RenderState& rs) const OVERRIDE;
virtual void onUpdate(float dt) OVERRIDE;
bool renderBorders;

View file

@ -5,6 +5,15 @@
#include "TextureMgr.h"
#include "Core.h"
Tileset::Tileset()
{
}
Tileset::~Tileset()
{
clear();
}
bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t usedIdxLen)
{
elementTemplates.clear();
@ -13,24 +22,34 @@ bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t used
if(!in)
return false;
bool warn = false;
std::string line, gfx;
while (std::getline(in, line))
{
gfx.clear();
int idx=-1, w=0, h=0;
SimpleIStringStream is(line.c_str(), SimpleIStringStream::REUSE);
is >> idx >> gfx >> w >> h;
if(idx >= 0)
if(idx >= 0 && !gfx.empty())
{
ElementTemplate t;
t.idx = idx;
t.gfx = gfx;
t.w = w;
t.h = h;
elementTemplates.push_back(t);
if(idx < 1024)
{
ElementTemplate t;
t.idx = idx;
t.gfx = gfx;
t.w = w;
t.h = h;
elementTemplates.push_back(t);
}
else
warn = true;
}
}
in.close();
if(warn)
errorLog("Tileset indices of 1024 and above are reserved; ignored during load");
std::sort(elementTemplates.begin(), elementTemplates.end());
// begin preloading textures
@ -48,47 +67,174 @@ bool Tileset::loadFile(const char *fn, const unsigned char *usedIdx, size_t used
// drop duplicates
usedTex.resize(std::distance(usedTex.begin(), std::unique(usedTex.begin(), usedTex.end())));
std::ostringstream os;
os << "Loading " << usedTex.size()
<< " used textures out of the " << elementTemplates.size() << " tileset entries";
debugLog(os.str());
{
std::ostringstream os;
os << "Loading " << usedTex.size()
<< " used textures out of the " << elementTemplates.size() << " tileset entries";
debugLog(os.str());
}
// preload all used textures
size_t loaded = 0;
if(usedTex.size())
core->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
loaded = core->texmgr.loadBatch(NULL, &usedTex[0], usedTex.size());
{
std::ostringstream os;
os << "Loaded " << loaded << " textures successfully";
debugLog(os.str());
}
// finalize
size_t nfailed = 0;
std::ostringstream failed;
for (size_t i = 0; i < elementTemplates.size(); i++)
{
ElementTemplate& et = elementTemplates[i];
// only check those that are actualy loaded; otherwise this would load in textures
// that we didn't bother to batch-load above
if(!usedIdx || (et.idx < usedIdxLen && usedIdx[et.idx]))
{
if(!et.getTexture()) // assigns width/height and caches texture pointer
{
++nfailed;
failed << et.gfx << " ";
}
}
}
if(nfailed)
{
std::ostringstream os;
os << "The following " << nfailed << " textures failed to load and would be used by tiles:";
debugLog(os.str());
debugLog(failed.str());
}
return true;
}
void Tileset::clear()
{
for(size_t i = 0; i < dummies.size(); ++i)
delete dummies[i];
dummies.clear();
elementTemplates.clear();
}
ElementTemplate *Tileset::getByIdx(size_t idx)
const ElementTemplate *Tileset::getByIdx(size_t idx)
{
for (size_t i = 0; i < elementTemplates.size(); i++)
{
if (elementTemplates[i].idx == idx)
ElementTemplate& et = elementTemplates[i];
if (et.idx == idx)
{
return &elementTemplates[i];
et.getTexture(); // HACK: make sure the texture is loaded before this gets used
return &et;
}
}
return 0;
// a tile that gets an ET attached must remember its tileset id even if the entry is not present
// in the tileset. since the tile does not store the idx as an integer, we need to return a dummy element.
for (size_t i = 0; i < dummies.size(); i++)
{
ElementTemplate *et = dummies[i];
if (et->idx == idx)
return et;
}
{
std::ostringstream os;
os << "Tileset idx " << idx << " not found, creating dummy";
debugLog(os.str());
}
ElementTemplate *dummy = new ElementTemplate;
dummy->idx = idx;
dummies.push_back(dummy);
return dummy;
}
const ElementTemplate* Tileset::getAdjacent(size_t idx, int direction, bool wraparound)
{
ElementTemplate *et = _getAdjacent(idx, direction, wraparound);
if(et)
et->getTexture(); // load just in case
return et;
}
Texture* ElementTemplate::getTexture()
{
if(tex)
if(loaded)
return tex.content();
tex = core->getTexture(gfx);
if(!w)
w = tex->width;
if(!h)
h = tex->height;
loaded = true;
tex = core->getTexture(gfx); // may end up NULL
if(tex)
{
if(!w)
w = tex->width;
if(!h)
h = tex->height;
}
else
{
if(!w)
w = 64;
if(!h)
h = 64;
}
return tex.content();
}
ElementTemplate * Tileset::_getAdjacent(size_t idx, int direction, bool wraparound)
{
assert(direction == 1 || direction == -1);
const size_t maxn = elementTemplates.size();
size_t closest = 0;
int mindiff = 0;
for (size_t i = 0; i < maxn; i++)
{
if (elementTemplates[i].idx == idx)
{
if(wraparound)
{
if(!i && direction < 0)
return &elementTemplates.back();
if(i + direction >= maxn)
return &elementTemplates[0];
}
else
i += direction; // may underflow
return i < maxn ? &elementTemplates[i] : NULL;
}
int diff = labs((int)elementTemplates[i].idx - (int)idx);
if(diff < mindiff || !mindiff)
{
mindiff = diff;
closest = i;
}
}
// not found? pick whatever was closest to the non-existing idx, and go back/forward from there
// avoid going "twice" in the given direction
if(closest < idx && direction < 0)
direction = 0; // this is already a step back, don't step again
else if(closest > idx && direction > 0)
direction = 0; // this is already a step forward, don't step again
else if(wraparound)
{
if(!closest && direction < 0)
return &elementTemplates.back();
if(closest + direction >= maxn)
return &elementTemplates[0];
}
size_t i = closest + direction;
return i < maxn ? &elementTemplates[i] : NULL;
}

View file

@ -8,32 +8,47 @@
class ElementTemplate
{
public:
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; }
ElementTemplate() { w=0; h=0; idx=-1; tu1=tv1=0; tu2=tv2=1; loaded=false; }
inline bool operator<(const ElementTemplate& o) const { return idx < o.idx; }
Texture *getTexture(); // loads if not already loaded
// lazily assigned when tex is loaded
CountedPtr<Texture> tex;
CountedPtr<Texture> tex; // NULL if failed to load or not yet loaded
float w,h; // custom size if used, otherwise texture size
// fixed
float tu1, tu2, tv1, tv2; // texcoords
size_t idx;
std::string gfx;
bool loaded;
};
class Tileset
{
public:
Tileset();
~Tileset();
// pass usedIdx == NULL to preload all textures from tileset
// pass usedIdx != NULL to preload only textures where usedIdx[i] != 0
bool loadFile(const char *fn, const unsigned char *usedIdx, size_t usedIdxLen);
void clear();
ElementTemplate *getByIdx(size_t idx);
// return valid ET if found, or creates a dummy if not. never returns NULL.
const ElementTemplate *getByIdx(size_t idx);
// search for non-dummy ET in a given direction. used to cycle through ETs.
// never returns dummy ET. May return NULL.
const ElementTemplate *getAdjacent(size_t idx, int direction, bool wraparound);
std::vector<ElementTemplate> elementTemplates;
private:
ElementTemplate *_getAdjacent(size_t idx, int direction, bool wraparound);
std::vector<ElementTemplate*> dummies;
};