diff --git a/Aquaria/Avatar.cpp b/Aquaria/Avatar.cpp index 90780d9..fc803ed 100644 --- a/Aquaria/Avatar.cpp +++ b/Aquaria/Avatar.cpp @@ -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); } } diff --git a/Aquaria/CMakeLists.txt b/Aquaria/CMakeLists.txt index 35591e5..b764d5d 100644 --- a/Aquaria/CMakeLists.txt +++ b/Aquaria/CMakeLists.txt @@ -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 diff --git a/Aquaria/Continuity.cpp b/Aquaria/Continuity.cpp index 8cdf1c6..fa54051 100644 --- a/Aquaria/Continuity.cpp +++ b/Aquaria/Continuity.cpp @@ -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"); diff --git a/Aquaria/DSQ.cpp b/Aquaria/DSQ.cpp index 78c4cfe..444db02 100644 --- a/Aquaria/DSQ.cpp +++ b/Aquaria/DSQ.cpp @@ -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 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; diff --git a/Aquaria/DSQ.h b/Aquaria/DSQ.h index b37ace0..f160db0 100644 --- a/Aquaria/DSQ.h +++ b/Aquaria/DSQ.h @@ -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 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); diff --git a/Aquaria/Element.h b/Aquaria/Element.h index 7e6211a..f2a9ce7 100644 --- a/Aquaria/Element.h +++ b/Aquaria/Element.h @@ -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(); diff --git a/Aquaria/Elements.h b/Aquaria/Elements.h deleted file mode 100644 index 4fd07c9..0000000 --- a/Aquaria/Elements.h +++ /dev/null @@ -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 diff --git a/Aquaria/Entity.cpp b/Aquaria/Entity.cpp index 46e44d1..9077160 100644 --- a/Aquaria/Entity.cpp +++ b/Aquaria/Entity.cpp @@ -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; diff --git a/Aquaria/Entity.h b/Aquaria/Entity.h index 5a3e0b8..bfcfccd 100644 --- a/Aquaria/Entity.h +++ b/Aquaria/Entity.h @@ -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); diff --git a/Aquaria/Game.cpp b/Aquaria/Game.cpp index 5200789..caf94db 100644 --- a/Aquaria/Game.cpp +++ b/Aquaria/Game.cpp @@ -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(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 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 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 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 elemsDefs; - elemsDefs.reserve(256); + std::vector 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 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(); diff --git a/Aquaria/Game.h b/Aquaria/Game.h index 729ba1b..3ca9e7c 100644 --- a/Aquaria/Game.h +++ b/Aquaria/Game.h @@ -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 using namespace tinyxml2; @@ -112,8 +112,6 @@ public: int idx; }; -typedef std::vector 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; diff --git a/Aquaria/GameStructs.h b/Aquaria/GameStructs.h index 6bde52b..12ce6a3 100644 --- a/Aquaria/GameStructs.h +++ b/Aquaria/GameStructs.h @@ -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() diff --git a/Aquaria/SceneEditor.cpp b/Aquaria/SceneEditor.cpp index 8141d7a..6d5726c 100644 --- a/Aquaria/SceneEditor.cpp +++ b/Aquaria/SceneEditor.cpp @@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include "Avatar.h" #include "GridRender.h" #include "Shot.h" +#include "Tile.h" #ifdef BBGE_BUILD_WINDOWS @@ -38,10 +39,15 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. #include #endif -const int minSelectionSize = 64; -PathRender *pathRender; +static const int minSelectionSize = 64; +static TileStorage& tilesForLayer(unsigned layer) +{ + assert(layer < MAX_TILE_LAYERS); + return dsq->tilemgr.tilestore[layer]; +} + SelectedEntity::SelectedEntity() { clear(); @@ -97,6 +103,7 @@ SceneEditor::SceneEditor() : ActionMapper(), on(false) { autoSaveFile = 0; selectedIdx = -1; + multi = NULL; } void SceneEditor::setBackgroundGradient() @@ -116,26 +123,16 @@ void SceneEditor::setBackgroundGradient() } } -void SceneEditor::changeDepth() -{ - if (editingElement) - { - editingElement->followCamera = 0.9f; - editingElement->cull = false; - } -} - -void SceneEditor::updateSelectedElementPosition(Vector dist) +void SceneEditor::updateSelectedElementPosition(Vector rel) { if (state == ES_MOVING) { - if (!selectedElements.empty()) + TileStorage& ts = getCurrentLayerTiles(); + for(size_t i = 0; i < selectedTiles.size(); ++i) { - dummy.position = oldPosition + dist; - } - else if (editingElement) - { - editingElement->position = oldPosition + dist; + TileData& t = ts.tiles[selectedTiles[i]]; + t.x += rel.x; + t.y += rel.y; } } } @@ -241,6 +238,90 @@ public: } }; +class MultiTileHelper : public Quad +{ + TileStorage& _ts; + std::vector _indices; + std::vector _quads; + + MultiTileHelper(TileStorage& ts, const size_t *indices, size_t n) + : Quad() + , _ts(ts), _indices(indices, indices + n) + { + _quads.reserve(n); + this->cull = false; + } + +public: + static MultiTileHelper *New(unsigned bgLayer, const size_t *indices, size_t n) + { + assert(n); + TileStorage& ts = dsq->tilemgr.tilestore[bgLayer]; + MultiTileHelper *th = new MultiTileHelper(ts, indices, n); + + Vector center; + + for(size_t i = 0; i < n; ++i) + { + TileData& t = ts.tiles[indices[i]]; + Vector pos(t.x, t.y); + center += pos; + t.flags |= TILEFLAG_EDITOR_HIDDEN; + + // clone tile to quad + Quad *q = new Quad; + q->position = pos; + q->renderBorder = true; + q->renderBorderColor = Vector(0.75f, 0.75f, 0.75f); + q->scale = Vector(t.scalex, t.scaley); + q->setTexturePointer(t.et->tex); + q->fhTo(!!(t.flags & TILEFLAG_FH)); + q->rotation.z = t.rotation; + q->repeatToFillScale = Vector(t.texscaleX, t.texscaleY); + q->repeatTextureToFill(!!(t.flags & TILEFLAG_REPEAT)); + th->addChild(q, PM_POINTER, RBP_ON); + th->_quads.push_back(q); + } + + th->position = center / float(n); + core->addRenderObject(th, LR_ELEMENTS1+bgLayer); + return th; + } + + virtual ~MultiTileHelper() + { + } + + void onUpdate(float dt) + { + for(size_t i = 0; i < _quads.size(); ++i) + _quads[i]->refreshRepeatTextureToFill(); + } + + void finish() + { + if(size_t n = _indices.size()) + { + for(size_t i = 0; i < n; ++i) + { + TileData& t = _ts.tiles[_indices[i]]; + Quad *q = _quads[i]; + t.flags &= ~TILEFLAG_EDITOR_HIDDEN; + q->alphaMod = 0; + + Vector posAndRot = q->getWorldPositionAndRotation(); + t.x = posAndRot.x; + t.y = posAndRot.y; + t.rotation = posAndRot.z; + Vector scale = q->getRealScale(); + t.scalex = scale.x; + t.scaley = scale.y; + } + } + this->safeKill(); // also deletes all children + } +}; + std::vector mainMenu; int execID = 0; @@ -482,10 +563,9 @@ void SceneEditor::init() { entityPageNum = 0; multiSelecting = false; - selectedElements.clear(); + selectedTiles.clear(); autoSaveTimer = 0; skinMinX = skinMinY = skinMaxX = skinMaxY = -1; - editingElement = 0; editingEntity = 0; pathRender = new PathRender(); core->getTopStateData()->addRenderObject(pathRender, LR_DEBUG_TEXT); @@ -536,9 +616,6 @@ void SceneEditor::init() addAction(MakeFunctionEvent(SceneEditor, changeShape), KEY_Y, 0); addAction(MakeFunctionEvent(SceneEditor, reversePath), KEY_T, 0); - - addAction(MakeFunctionEvent(SceneEditor, moveLayer), KEY_F10, 0); - addAction(MakeFunctionEvent(SceneEditor, nextElement), KEY_R, 1); addAction(MakeFunctionEvent(SceneEditor, prevElement), KEY_E, 1); addAction(MakeFunctionEvent(SceneEditor, selectZero), KEY_HOME, 1); @@ -624,9 +701,9 @@ void SceneEditor::init() nextElement(); - if (curElement < game->tileset.elementTemplates.size()) + if (curElement < dsq->tilemgr.tileset.elementTemplates.size()) { - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); + placer->setTexture(dsq->tilemgr.tileset.elementTemplates[curElement].gfx); placer->scale = Vector(1,1); } else @@ -635,8 +712,6 @@ void SceneEditor::init() } updateText(); - - doPrevElement(); } void SceneEditor::createAquarian() @@ -647,21 +722,32 @@ void SceneEditor::createAquarian() inCreateAqurian = true; std::string t = dsq->getUserInputString("Enter Aquarian:", ""); - stringToUpper(t); - Vector startPos = dsq->getGameCursorPosition(); - for (size_t i = 0; i < t.size(); i++) + if(!t.empty()) { - int v = 0; - if (t[i] >= 'A' && t[i] <= 'Z') + stringToUpper(t); + Vector startPos = dsq->getGameCursorPosition(); + std::vector defs; + defs.reserve(t.size()); + TileDef def(this->bgLayer); + def.y = startPos.y; + for (size_t i = 0; i < t.size(); i++) { - v = 1024+int(t[i] - 'A'); - } - if (t[i] == '.' || t[i] == ' ') - { - v = 1024+26; - } + if (t[i] >= 'A' && t[i] <= 'Z') + { + def.idx = 1024+int(t[i] - 'A'); + } + else if (t[i] == '.' || t[i] == ' ') + { + def.idx = 1024+26; + } + else + continue; - game->createElement(v, startPos + Vector(64*i,0), this->bgLayer); + def.x = startPos.x + 64*i; + defs.push_back(def); + } + if(size_t n = defs.size()) + dsq->tilemgr.createTiles(&defs[0], n); } inCreateAqurian = false; } @@ -731,19 +817,13 @@ void SceneEditor::setGridPattern(int gi) if(core->getCtrlState()) { const int tag = gi + 1; // key 0 is tag 0... otherwise it's all off by one - if (selectedElements.size()) - for (size_t i = 0; i < selectedElements.size(); ++i) - selectedElements[i]->setTag(tag); - else if (editingElement) - editingElement->setTag(tag); + if(size_t n = selectedTiles.size()) + getCurrentLayerTiles().setTag(tag, &selectedTiles[0], n); } else { - if (selectedElements.size()) - for (size_t i = 0; i < selectedElements.size(); ++i) - selectedElements[i]->setElementEffectByIndex(gi); - else if (editingElement) - editingElement->setElementEffectByIndex(gi); + if(size_t n = selectedTiles.size()) + getCurrentLayerTiles().setEffect(dsq->tilemgr.tileEffects, gi, &selectedTiles[0], n); } } @@ -779,49 +859,36 @@ void SceneEditor::setGridPattern9() void SceneEditor::moveToFront() { - if (editingElement && !core->getShiftState()) + if (selectedTiles.size() && !core->getShiftState()) { - std::vector copy = dsq->getElementsCopy(); - dsq->clearElements(); - - // move to the foreground ... this means that the editing element should be last in the list (Added last) - for (size_t i = 0; i < copy.size(); i++) - { - if (copy[i] != editingElement) - dsq->addElement(copy[i]); - } - dsq->addElement(editingElement); - - editingElement->moveToFront(); + getCurrentLayerTiles().moveToFront(&selectedTiles[0], selectedTiles.size()); } } void SceneEditor::moveToBack() { - if (editingElement && !core->getShiftState()) + if(selectedTiles.size() && !core->getShiftState()) { - std::vector copy = dsq->getElementsCopy(); - dsq->clearElements(); - - // move to the background ... this means that the editing element should be first in the list (Added first) - dsq->addElement(editingElement); - for (size_t i = 0; i < copy.size(); i++) - { - if (copy[i] != editingElement) - dsq->addElement(copy[i]); - } - - editingElement->moveToBack(); + getCurrentLayerTiles().moveToBack(&selectedTiles[0], selectedTiles.size()); } } +void SceneEditor::clearSelection() +{ + getCurrentLayerTiles().clearSelection(); + selectedTiles.clear(); + editingEntity = NULL; + editingPath = NULL; + selectedIdx = -1; +} + void SceneEditor::editModeElements() { - selectedIdx = -1; + clearSelection(); editType = ET_ELEMENTS; - if (curElement < game->tileset.elementTemplates.size()) + if (curElement < dsq->tilemgr.tileset.elementTemplates.size()) { - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); + placer->setTexture(dsq->tilemgr.tileset.elementTemplates[curElement].gfx); placer->scale = Vector(1,1); } placer->alpha = 0.5; @@ -832,47 +899,41 @@ void SceneEditor::editModeElements() void SceneEditor::editModeEntities() { - selectedIdx = -1; - //HACK: methinks target is useless now - //target->alpha.interpolateTo(0, 0.5); + clearSelection(); editType = ET_ENTITIES; - - placer->setTexture(selectedEntity.prevGfx); placer->alpha = 0.5; pathRender->alpha = 0; - selectedElements.clear(); - editingElement = NULL; - editingPath = NULL; } void SceneEditor::editModePaths() { - selectedIdx = -1; + clearSelection(); editType = ET_PATHS; placer->alpha = 0; pathRender->alpha = 0.5; - selectedElements.clear(); - editingElement = NULL; - editingEntity = NULL; } -Element *SceneEditor::getElementAtCursor() +int SceneEditor::getTileAtCursor() { - int minDist = -1; - Element *selected = 0; - for (Element *e = dsq->getFirstElementOnLayer(this->bgLayer); e; e = e->bgLayerNext) + const TileStorage& ts = getCurrentLayerTiles(); + float minDist = HUGE_VALF; + int selected = -1; + const size_t n = ts.tiles.size(); + Vector cursor = dsq->getGameCursorPosition(); + for(size_t i = 0; i < n; ++i) { - if (e->life == 1) + const TileData& t = ts.tiles[i]; + if (t.isVisible()) { - if (e->isCoordinateInside(dsq->getGameCursorPosition())) + if (t.isCoordinateInside(cursor.x, cursor.y)) { - Vector v = dsq->getGameCursorPosition() - e->position; - int dist = v.getSquaredLength2D(); - if (dist < minDist || minDist == -1) + Vector v = cursor - Vector(t.x, t.y); + float dist = v.getSquaredLength2D(); + if (dist < minDist || selected == -1) { minDist = dist; - selected = e; + selected = int(i); } } } @@ -909,21 +970,11 @@ void SceneEditor::deleteSelected() if (state != ES_SELECTING) return; if (editType == ET_ELEMENTS) { - if (selectedElements.size()>0) + if (size_t n = selectedTiles.size()) { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->safeKill(); - dsq->removeElement(selectedElements[i]); - } - selectedElements.clear(); - game->reconstructGrid(); - } - else if (editingElement) - { - editingElement->safeKill(); - dsq->removeElement(editingElement); - editingElement = 0; + TileStorage& ts = getCurrentLayerTiles(); + ts.deleteSome(&selectedTiles[0], n); + selectedTiles.clear(); game->reconstructGrid(); } } @@ -965,159 +1016,88 @@ void SceneEditor::deleteSelected() void SceneEditor::checkForRebuild() { - if (editType == ET_ELEMENTS && state != ES_SELECTING && !selectedElements.empty()) + if (editType == ET_ELEMENTS && state != ES_SELECTING && !selectedTiles.empty()) { - bool rebuild = false; - for (size_t i = 0; i < selectedElements.size(); i++) + const TileStorage& ts = getCurrentLayerTiles(); + const size_t n = ts.tiles.size(); + for (size_t i = 0; i < n; i++) { - if (selectedElements[i]->elementFlag == EF_SOLID || selectedElements[i]->elementFlag == EF_HURT) + if(ts.tiles[i].flags & TILEFLAG_SOLID) { - rebuild = true; + game->reconstructGrid(); break; } } - if (rebuild) - { - game->reconstructGrid(); - } - } - else if (editType == ET_ELEMENTS && state != ES_SELECTING && editingElement != 0 && (editingElement->elementFlag == EF_SOLID || editingElement->elementFlag == EF_HURT)) - { - game->reconstructGrid(); } } void SceneEditor::exitMoveState() { - if (!selectedElements.empty()) - { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position = selectedElements[i]->getWorldPosition(); - dummy.removeChild(selectedElements[i]); - } - core->removeRenderObject(&dummy, Core::DO_NOT_DESTROY_RENDER_OBJECT); - } + destroyMultiTileHelper(); checkForRebuild(); state = ES_SELECTING; } -void SceneEditor::enterMoveState() +void SceneEditor::enterAnyStateHelper(EditorStates newstate) { - if (state != ES_SELECTING) return; - state = ES_MOVING; + state = newstate; if (editType == ET_ELEMENTS) { - if (!selectedElements.empty()) + if(selectedTiles.size() == 1) { - dummy.rotation = Vector(0,0,0); - cursorOffset = dsq->getGameCursorPosition(); - groupCenter = getSelectedElementsCenter(); - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position -= groupCenter; - dummy.addChild(selectedElements[i], PM_NONE); - } - core->addRenderObject(&dummy, selectedElements[0]->layer); - dummy.cull = false; - dummy.position = groupCenter; - oldPosition = dummy.position; - dummy.scale = Vector(1,1,1); + const TileData& t = getCurrentLayerTiles().tiles[selectedTiles[0]]; + oldRepeatScale = Vector(t.texscaleX, t.texscaleY); + oldScale = Vector(t.scalex, t.scaley); + oldRotation = t.rotation; } - else if (editingElement) + else { - oldPosition = editingElement->position; - cursorOffset = dsq->getGameCursorPosition(); + oldRepeatScale = Vector(1, 1); // not handled for multi-selection + oldScale = Vector(1, 1); + oldRotation = 0; } + + MultiTileHelper *m = createMultiTileHelperFromSelection(); + oldPosition = m->position; + cursorOffset = dsq->getGameCursorPosition(); } else if (editType == ET_ENTITIES) { oldPosition = editingEntity->position; + oldRotation = editingEntity->rotation.z; + oldScale = editingEntity->scale; cursorOffset = editingEntity->position - dsq->getGameCursorPosition(); } else if (editType == ET_PATHS) { oldPosition = game->getPath(selectedIdx)->nodes[selectedNode].position; cursorOffset = oldPosition - dsq->getGameCursorPosition(); + oldScale = Vector(editingPath->rect.x2-editingPath->rect.x1, + editingPath->rect.y2-editingPath->rect.y1); } } +void SceneEditor::enterMoveState() +{ + if (state != ES_SELECTING) return; + enterAnyStateHelper(ES_MOVING); +} + void SceneEditor::enterRotateState() { if (state != ES_SELECTING) return; - if (editType == ET_ENTITIES) + if (editType == ET_ENTITIES || editType == ET_ELEMENTS) // can't rotate nodes { - state = ES_ROTATING; - oldRotation = editingEntity->rotation; - oldPosition = editingEntity->position; - cursorOffset = dsq->getGameCursorPosition(); - } - if (editType == ET_ELEMENTS) - { - if (!selectedElements.empty()) - { - state = ES_ROTATING; - dummy.rotation = Vector(0,0,0); - oldRotation = dummy.rotation; - cursorOffset = dsq->getGameCursorPosition(); - groupCenter = getSelectedElementsCenter(); - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position -= groupCenter; - dummy.addChild(selectedElements[i], PM_NONE); - } - core->addRenderObject(&dummy, selectedElements[0]->layer); - dummy.cull = false; - dummy.position = groupCenter; - dummy.scale = Vector(1,1,1); - } - else if (editingElement) - { - state = ES_ROTATING; - oldRotation = editingElement->rotation; - cursorOffset = dsq->getGameCursorPosition(); - } + enterAnyStateHelper(ES_ROTATING); } } void SceneEditor::enterScaleState() { if (state != ES_SELECTING) return; - if (editType == ET_ELEMENTS) + if (editType == ET_ELEMENTS || editType == ET_PATHS) // can't scale entities { - if (!selectedElements.empty()) - { - state = ES_SCALING; - dummy.rotation = Vector(0,0,0); - dummy.scale = Vector(1,1,1); - oldScale = dummy.scale; - oldRepeatScale = Vector(1, 1); // not handled for multi-selection - cursorOffset = dsq->getGameCursorPosition(); - groupCenter = getSelectedElementsCenter(); - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position -= groupCenter; - dummy.addChild(selectedElements[i], PM_NONE); - } - core->addRenderObject(&dummy, selectedElements[0]->layer); - dummy.cull = false; - dummy.position = groupCenter; - } - else if (editingElement) - { - oldPosition = editingElement->position; - state = ES_SCALING; - oldScale = editingElement->scale; - oldRepeatScale = editingElement->repeatToFillScale; - cursorOffset = dsq->getGameCursorPosition(); - } - } - else if (editType == ET_PATHS) - { - state = ES_SCALING; - oldScale = Vector(editingPath->rect.x2-editingPath->rect.x1, - editingPath->rect.y2-editingPath->rect.y1); - cursorOffset = dsq->getGameCursorPosition(); + enterAnyStateHelper(ES_SCALING); } } @@ -1171,79 +1151,34 @@ void SceneEditor::mouseButtonRightUp() updateEntitySaveData(editingEntity); if (editType == ET_ELEMENTS) { - - if (state == ES_ROTATING) - { - if (!selectedElements.empty()) - { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position = selectedElements[i]->getWorldPosition(); - selectedElements[i]->rotation = selectedElements[i]->getAbsoluteRotation(); - dummy.removeChild(selectedElements[i]); - } - core->removeRenderObject(&dummy, Core::DO_NOT_DESTROY_RENDER_OBJECT); - } - } - else if (state == ES_SCALING) - { - - if (!selectedElements.empty()) - { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->position = selectedElements[i]->getWorldPosition(); - selectedElements[i]->scale = selectedElements[i]->scale * dummy.scale; - selectedElements[i]->rotation = selectedElements[i]->getAbsoluteRotation(); - dummy.removeChild(selectedElements[i]); - } - core->removeRenderObject(&dummy, Core::DO_NOT_DESTROY_RENDER_OBJECT); - } - else if (editingElement) - { - Vector add = editingElement->beforeScaleOffset; - Vector newScale = editingElement->scale; - editingElement->beforeScaleOffset = Vector(0,0,0); - editingElement->scale = Vector(1,1,1); - editingElement->position = editingElement->getWorldCollidePosition(add); - editingElement->scale = newScale; - } - } + destroyMultiTileHelper(); checkForRebuild(); } state = ES_SELECTING; } +static ElementFlag nextSolidEF(ElementFlag ef) +{ + switch(ef) + { + case EF_NONE: return EF_SOLID; + case EF_SOLID: return EF_SOLID2; + case EF_SOLID2: return EF_SOLID3; + case EF_SOLID3: return EF_NONE; + } + return EF_NONE; +} void SceneEditor::toggleElementSolid() { - if (editingElement) + TileStorage& ts = getCurrentLayerTiles(); + if (size_t n = selectedTiles.size()) { - switch(editingElement->elementFlag) + for(size_t i = 0; i < n; ++i) { - default: - case EF_NONE: - { - std::ostringstream os; - os << "elementFlag: " << editingElement->elementFlag; - debugLog(os.str()); - debugLog("Solid"); - editingElement->elementFlag = EF_SOLID; - } - break; - case EF_SOLID: - debugLog("Solid2"); - editingElement->elementFlag = EF_SOLID2; - break; - case EF_SOLID2: - debugLog("Solid3"); - editingElement->elementFlag = EF_SOLID3; - break; - case EF_SOLID3: - debugLog("None"); - editingElement->elementFlag = EF_NONE; - break; + TileData& t = ts.tiles[selectedTiles[i]]; + t.flags = TileMgr::GetTileFlags(nextSolidEF(TileMgr::GetElementFlag((TileFlags)t.flags))); } game->reconstructGrid(true); } @@ -1251,30 +1186,41 @@ void SceneEditor::toggleElementSolid() void SceneEditor::toggleElementHurt() { - if (editingElement) + TileStorage& ts = getCurrentLayerTiles(); + if (size_t n = selectedTiles.size()) { - if (editingElement->elementFlag == EF_HURT) - editingElement->elementFlag = EF_NONE; + TileData& t0 = ts.tiles[selectedTiles[0]]; + const bool set = !(t0.flags & TILEFLAG_HURT); + + if(set) + ts.changeFlags(TILEFLAG_SOLID | TILEFLAG_HURT, 0, &selectedTiles[0], n); else - editingElement->elementFlag = EF_HURT; + ts.changeFlags(0, TILEFLAG_SOLID | TILEFLAG_HURT | TILEFLAG_SOLID_IN | TILEFLAG_SOLID_THICK, &selectedTiles[0], n); + game->reconstructGrid(true); } } void SceneEditor::toggleElementRepeat() { - if (editingElement) + TileStorage& ts = getCurrentLayerTiles(); + if (size_t n = selectedTiles.size()) { - editingElement->repeatTextureToFill(!editingElement->isRepeatingTextureToFill()); + TileData& t0 = ts.tiles[selectedTiles[0]]; + const bool set = !(t0.flags & TILEFLAG_REPEAT); + if(set) + ts.changeFlags(TILEFLAG_REPEAT, 0, &selectedTiles[0], n); + else + ts.changeFlags(0, TILEFLAG_REPEAT, &selectedTiles[0], n); } } void SceneEditor::mouseButtonLeft() { - if (multiSelecting || state != ES_SELECTING || !dummy.children.empty() || core->mouse.buttons.right) return; + if (multiSelecting || state != ES_SELECTING || core->mouse.buttons.right) return; if (editType == ET_ELEMENTS) { - if (selectedElements.empty() || editingElement) + if (!selectedTiles.empty()) { if (core->getShiftState()) { @@ -1323,7 +1269,7 @@ void SceneEditor::mouseButtonLeft() void SceneEditor::mouseButtonRight() { - if (multiSelecting || state != ES_SELECTING || !dummy.children.empty() || core->mouse.buttons.left) return; + if (multiSelecting || state != ES_SELECTING || core->mouse.buttons.left) return; if (editType == ET_ENTITIES) { if (editingEntity) @@ -1349,7 +1295,7 @@ void SceneEditor::mouseButtonRight() } if (editType == ET_ELEMENTS) { - if (selectedElements.empty() || editingElement) + if (!selectedTiles.empty()) { if (core->getShiftState()) enterScaleState(); @@ -1366,7 +1312,7 @@ void SceneEditor::up() { if (editType == ET_ELEMENTS && state == ES_SELECTING) { - if (editingElement || !selectedElements.empty()) + if (!selectedTiles.empty()) { enterMoveState(); updateSelectedElementPosition(Vector(0,-1)); @@ -1379,7 +1325,7 @@ void SceneEditor::down() { if (editType == ET_ELEMENTS && state == ES_SELECTING) { - if (editingElement || !selectedElements.empty()) + if (!selectedTiles.empty()) { enterMoveState(); updateSelectedElementPosition(Vector(0,1)); @@ -1407,22 +1353,22 @@ void SceneEditor::skinLevel() void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY) { - std::vector deleteElements; - size_t i = 0; - for (i = 0; i < dsq->getNumElements(); i++) + const int LAYER = 4; + TileStorage& ts = tilesForLayer(LAYER); + std::vector toAdd; + toAdd.reserve(ts.tiles.size()); // assume the number of tiles isn't going to change much { - Element *e = dsq->getElement(i); - if (e->bgLayer==4 && e->templateIdx >= 1 && e->templateIdx <= 4) + std::vector deleteTiles; + deleteTiles.reserve(ts.tiles.size()); // pessimistically assume we have to delete everything + for (size_t i = 0; i < ts.tiles.size(); i++) { - e->safeKill(); - deleteElements.push_back(e); + const TileData& t = ts.tiles[i]; + if (t.et->idx >= 1 && t.et->idx <= 4) // delete all tiles designated as wall rocks + deleteTiles.push_back(i); } + + ts.deleteSome(&deleteTiles[0], deleteTiles.size()); } - for (i = 0; i < deleteElements.size(); i++) - { - dsq->removeElement(deleteElements[i]); - } - deleteElements.clear(); int idx=1; int idxCount = 0; @@ -1470,58 +1416,54 @@ void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY) { TileVector t(x,y); Vector p = t.worldVector(); - Quad q; - q.rotation.z = rot; p += Vector(TILE_SIZE/2, TILE_SIZE/2,0); offset.z = 0; bool skip = false; - for (size_t i = 0; i < dsq->getNumElements(); i++) + for (size_t i = 0; i < toAdd.size(); i++) { - Element *e = dsq->getElement(i); - if (e->templateIdx <= 4 && e->templateIdx >= 1) + const TileDef& d = toAdd[i]; + if ((p - Vector(d.x, d.y)).getSquaredLength2D() < (50*50)) { - if ((p - e->position).getSquaredLength2D() < sqr(50)) - { - skip = true; - break; - } + skip = true; + break; } } if (!skip) { - std::vector cantUse; - cantUse.resize(4); - size_t i = 0; - for (i = 0; i < dsq->getNumElements(); i++) + unsigned cantUse[4] = {0}; + for (size_t i = 0; i < toAdd.size(); i++) { - Element *e = dsq->getElement(i); - if (e->templateIdx <= 4 && e->templateIdx >= 1) + const TileDef& d = toAdd[i]; + if ((p - Vector(d.x, d.y)).getSquaredLength2D() < sqr(120)) { - if ((p - e->position).getSquaredLength2D() < sqr(120)) - { - cantUse[e->templateIdx-1]++; - } + cantUse[d.idx]++; } } - size_t useIdx = rand()%cantUse.size()+1; - for (i = 0; i < cantUse.size(); i++) + size_t useIdx = rand()%4+1; + for (size_t i = 0; i < 4; i++) { size_t check = i + idxCount; - if (check >= cantUse.size()) - check -= cantUse.size(); - if (cantUse[check]<=0) + if (check >= 4) + check -= 4; + if (!cantUse[check]) { useIdx = check+1; } } - idxCount = rand()%cantUse.size(); + idxCount = rand()%4; - Element *e = game->createElement(useIdx, p, 4, &q); - e->offset = offset; + TileDef d(LAYER); + d.x = p.x + offset.x; + d.y = p.y + offset.y; + d.idx = useIdx; + d.rot = rot; + toAdd.push_back(d); + //Element *e = game->createElement(useIdx, p, 4, &q); + //e->offset = offset; idx++; if(idx > 4) @@ -1529,7 +1471,7 @@ void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY) } } } - for (size_t i = 0; i < dsq->getNumElements(); i++) + /*for (size_t i = 0; i < dsq->getNumElements(); i++) { Element *e = dsq->getElement(i); if (e->bgLayer == 4 && e->templateIdx >= 1 && e->templateIdx <= 4) @@ -1537,7 +1479,7 @@ void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY) e->position += e->offset; e->offset = Vector(0,0,0); } - } + }*/ } } @@ -1640,45 +1582,32 @@ void SceneEditor::placeAvatar() game->action(ACTION_PLACE_AVATAR, 0, -1, INPUT_NODEVICE); } -void SceneEditor::scaleElementUp() -{ - placer->scale += Vector(0.25, 0.25,0); -} - -void SceneEditor::scaleElementDown() -{ - placer->scale -= Vector(0.25, 0.25,0); -} - -void SceneEditor::scaleElement1() -{ - placer->scale = Vector(1,1,1); -} - void SceneEditor::flipElementHorz() { if (editType != ET_ELEMENTS) return; + const size_t n = selectedTiles.size(); + if(core->getCtrlState()) { - if(!selectedElements.empty() || editingElement) + if (n) { std::string inp = dsq->getUserInputString("Enter tag (int):"); if(inp.empty()) return; int tag = atoi(inp.c_str()); - if(!selectedElements.empty()) - for(size_t i = 0; i < selectedElements.size(); ++i) - selectedElements[i]->setTag(tag); - else - editingElement->setTag(tag); + getCurrentLayerTiles().setTag(tag, &selectedTiles[0], n); } return; } - if (editingElement) - editingElement->flipHorizontal(); + if (n) + { + TileStorage& ts = getCurrentLayerTiles(); + for(size_t i = 0; i < n; ++i) + ts.tiles[selectedTiles[i]].flags ^= TILEFLAG_FH; // toggle bit + } else this->placer->flipHorizontal(); } @@ -1688,36 +1617,15 @@ void SceneEditor::flipElementVert() if (editType != ET_ELEMENTS) return; - if (editingElement) - editingElement->flipVertical(); + if (size_t n = selectedTiles.size()) + { + // FIXME: vert + assert(0); + } else this->placer->flipVertical(); } -void SceneEditor::moveElementToLayer(Element *e, int bgLayer) -{ - if (!selectedElements.empty()) - { - for (size_t i = 0; i < selectedElements.size(); i++) - { - Element *e = selectedElements[i]; - core->removeRenderObject(e, Core::DO_NOT_DESTROY_RENDER_OBJECT); - dsq->removeElement(e); - e->bgLayer = bgLayer; - dsq->addElement(e); - core->addRenderObject(e, LR_ELEMENTS1+bgLayer); - } - } - else if (e) - { - core->removeRenderObject(e, Core::DO_NOT_DESTROY_RENDER_OBJECT); - dsq->removeElement(e); - e->bgLayer = bgLayer; - dsq->addElement(e); - core->addRenderObject(e, LR_ELEMENTS1+bgLayer); - } -} - void SceneEditor::updateMultiSelect() { if (!multiSelecting) return; @@ -1748,54 +1656,81 @@ void SceneEditor::updateMultiSelect() p2.y = secondPoint.y; } - selectedElements.clear(); + clearSelection(); - for (size_t i = 0; i < dsq->getNumElements(); i++) + const TileStorage& ts = getCurrentLayerTiles(); + for(size_t i = 0; i < ts.tiles.size(); ++i) { - Element *e = dsq->getElement(i); - if (e->bgLayer == bgLayer && e->position.x >= p1.x && e->position.y >= p1.y && e->position.x <= p2.x && e->position.y <= p2.y) - { - selectedElements.push_back(e); - } + const TileData& t = ts.tiles[i]; + if(t.isVisible() && t.x >= p1.x && t.y >= p1.y && t.x <= p2.x && t.y <= p2.y) + selectedTiles.push_back(i); } } } +void SceneEditor::setActiveLayer(unsigned bglayer) +{ + if(this->bgLayer == bglayer) + return; + + destroyMultiTileHelper(); + clearSelection(); + this->bgLayer = bglayer; +} + void SceneEditor::action(int id, int state, int source, InputDevice device) { - if (core->getCtrlState() && editingElement) + if(id >= ACTION_BGLAYER1 && id < ACTION_BGLAYEREND && state) { - if (id == ACTION_BGLAYEREND) - { - editingElement->setElementEffectByIndex(-1); - } - else if (id >= ACTION_BGLAYER1 && id < ACTION_BGLAYER10) - { - editingElement->setElementEffectByIndex(id - ACTION_BGLAYER1); - } - } - else if (editType == ET_ELEMENTS && state && id >= ACTION_BGLAYER1 && id < ACTION_BGLAYEREND) - { - int newLayer = id - ACTION_BGLAYER1; + const int newLayer = id - ACTION_BGLAYER1; - updateText(); - - if (core->getAltState()) + if(core->getAltState()) { game->setElementLayerVisible(newLayer, !game->isElementLayerVisible(newLayer)); return; // do not switch to the layer that was just hidden } - else if (core->getShiftState() && (editingElement || !selectedElements.empty())) - { - moveElementToLayer(editingElement, newLayer); - } - else - { - selectedElements.clear(); - } - this->bgLayer = newLayer; + if(editType == ET_ELEMENTS) + { + if(size_t N = selectedTiles.size()) + { + TileStorage& ts = getCurrentLayerTiles(); + if (core->getCtrlState()) + { + if (id == ACTION_BGLAYEREND) + { + ts.setEffect(dsq->tilemgr.tileEffects, -1, &selectedTiles[0], N); + } + else if (id >= ACTION_BGLAYER1 && id < ACTION_BGLAYER10) + { + ts.setEffect(dsq->tilemgr.tileEffects, id - ACTION_BGLAYER1, &selectedTiles[0], N); + } + } + else + { + updateText(); + + if (core->getShiftState()) + { + if(newLayer < MAX_TILE_LAYERS) + { + TileStorage& dst = dsq->tilemgr.tilestore[newLayer]; + const size_t idx = ts.moveToOther(dst, &selectedTiles[0], N); + setActiveLayer(newLayer); // this clears selected tiles + // update selected tiles so that when we switch to the layer they are still selected + assert(selectedTiles.empty()); + for(size_t i = 0; i < N; ++i) + selectedTiles.push_back(idx + i); + //ts.changeFlags(TILEFLAG_SELECTED, 0, &selectedTiles[0], N); // they still have that flag + } + } + + setActiveLayer(newLayer); + + } + } + } } if (id == ACTION_MULTISELECT && this->state == ES_SELECTING) @@ -1803,7 +1738,7 @@ void SceneEditor::action(int id, int state, int source, InputDevice device) if (state) { if (!multiSelecting) - selectedElements.clear(); + clearSelection(); multiSelecting = true; multiSelectPoint = dsq->getGameCursorPosition(); } @@ -1864,7 +1799,7 @@ void SceneEditor::loadScene() particleManager->loadParticleBank(dsq->particleBank1, dsq->particleBank2); Shot::loadShotBank(dsq->shotBank1, dsq->shotBank2); game->loadEntityTypeList(); - dsq->loadElementEffects(); + dsq->loadTileEffects(); dsq->continuity.loadSongBank(); dsq->loadStringBank(); } @@ -1877,22 +1812,6 @@ void SceneEditor::saveScene() dsq->screenMessage(game->sceneName + " FAILED to save!"); } -void SceneEditor::deleteSelectedElement() -{ - deleteElement(selectedIdx); - selectedIdx = -1; -} - -void SceneEditor::deleteElement(int selectedIdx) -{ - if (selectedIdx == -1) return; - Element *e = dsq->getElement(selectedIdx); - e->setLife(0.5); - e->setDecayRate(1); - e->fadeAlphaWithLife = true; - dsq->removeElement(selectedIdx); - game->reconstructGrid(); -} Quad *se_grad = 0; @@ -2074,55 +1993,26 @@ void SceneEditor::updateEntityPlacer() placer->scale = Vector(selectedEntity.prevScale,selectedEntity.prevScale); } -Element* SceneEditor::cycleElementNext(Element *e1) +void SceneEditor::cycleSelectedTiles(int direction) { - size_t ce = e1->templateIdx; - int idx=0; - for (size_t i = 0; i < game->tileset.elementTemplates.size(); i++) + size_t n = selectedTiles.size(); + if(!n) + return; + TileStorage& ts = getCurrentLayerTiles(); + const int maxn = (int)dsq->tilemgr.tileset.elementTemplates.size(); + if(!maxn) + return; + const ElementTemplate * const base = &dsq->tilemgr.tileset.elementTemplates[0]; + for(size_t i = 0; i < n; ++i) { - if (game->tileset.elementTemplates[i].idx == ce) - idx = i; + TileData& t = ts.tiles[selectedTiles[i]]; + const ElementTemplate *adj = dsq->tilemgr.tileset.getAdjacent(t.et->idx, direction, true); + if(adj) + t.et = adj; } - ce = idx; - ce++; - if (ce >= game->tileset.elementTemplates.size()) - ce = 0; - idx = game->tileset.elementTemplates[ce].idx; - if (idx < 1024) - { - Element *e = game->createElement(idx, e1->position, e1->bgLayer, e1); - e1->safeKill(); - dsq->removeElement(e1); - return e; - } - return e1; -} -Element* SceneEditor::cycleElementPrev(Element *e1) -{ - size_t ce = e1->templateIdx; - size_t idx=0; - for (size_t i = 0; i < game->tileset.elementTemplates.size(); i++) - { - if (game->tileset.elementTemplates[i].idx == ce) - { - idx = i; - break; - } - } - ce = idx; - ce--; - if (ce == -1) - ce = game->tileset.elementTemplates.size()-1; - idx = game->tileset.elementTemplates[ce].idx; - if (idx < 1024) - { - Element *e = game->createElement(idx, e1->position, e1->bgLayer, e1); - e1->safeKill(); - dsq->removeElement(e1); - return e; - } - return e1; + ts.refreshAll(); + checkForRebuild(); } void SceneEditor::nextElement() @@ -2145,48 +2035,24 @@ void SceneEditor::nextElement() if (editType == ET_ELEMENTS) { - if (game->tileset.elementTemplates.empty()) return; if (core->getCtrlState()) { - if (!selectedElements.empty()) + size_t n = selectedTiles.size(); + TileStorage& ts = getCurrentLayerTiles(); + for(size_t i = 0; i < n; ++i) { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i]->rotation.z = 0; - } - } - else if (editingElement) - { - editingElement->rotation.z = 0; + TileData& t = ts.tiles[selectedTiles[i]]; + t.rotation = 0; } + checkForRebuild(); } - else if (!selectedElements.empty()) + else if (!selectedTiles.empty()) { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i] = cycleElementNext(selectedElements[i]); - } - } - else if (editingElement) - { - cycleElementNext(editingElement); - editingElement = 0; + cycleSelectedTiles(1); } else { - int oldCur = curElement; - curElement++; - if (curElement >= game->tileset.elementTemplates.size()) - curElement = 0; - - if (game->tileset.elementTemplates[curElement].idx < 1024) - { - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); - } - else - { - curElement = oldCur; - } + cyclePlacer(1); } } } @@ -2207,70 +2073,30 @@ void SceneEditor::prevElement() if (editType == ET_ELEMENTS) { - if (game->tileset.elementTemplates.empty()) return; - if (!selectedElements.empty()) + if (!selectedTiles.empty()) { - for (size_t i = 0; i < selectedElements.size(); i++) - { - selectedElements[i] = cycleElementPrev(selectedElements[i]); - } - } - else if (editingElement) - { - cycleElementPrev(editingElement); - editingElement = 0; + cycleSelectedTiles(-1); } else { - doPrevElement(); + cyclePlacer(-1); } } } -void SceneEditor::doPrevElement() +void SceneEditor::cyclePlacer(int direction) { - size_t oldCur = curElement; - size_t maxn = game->tileset.elementTemplates.size(); + const int maxn = (int)dsq->tilemgr.tileset.elementTemplates.size(); + int nextidx = (int)curElement + direction; + if(nextidx < 0) + nextidx += maxn; + if(nextidx >= maxn) + nextidx -= maxn; - if(curElement) - curElement--; - else if(maxn) - curElement = maxn-1; - - if (maxn && curElement >= maxn) - curElement = maxn-1; - - if (maxn && game->tileset.elementTemplates[curElement].idx < 1024) + if (maxn && dsq->tilemgr.tileset.elementTemplates[curElement].idx < 1024) { - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); - } - else - { - curElement = oldCur; - } -} - -void SceneEditor::moveLayer() -{ - std::string s = dsq->getUserInputString("Enter 'fromLayer toLayer' (space inbetween, ESC/m-ty to cancel)"); - if (!s.empty()) - { - std::istringstream is(s); - int fromLayer, toLayer; - is >> fromLayer >> toLayer; - toLayer--; - fromLayer--; - for (size_t i = 0; i < dsq->getNumElements(); i++) - { - Element *e = dsq->getElement(i); - if (e) - { - if (e->bgLayer == fromLayer) - { - moveElementToLayer(e, toLayer); - } - } - } + placer->setTexture(dsq->tilemgr.tileset.elementTemplates[curElement].gfx); + curElement = nextidx; } } @@ -2280,15 +2106,9 @@ void SceneEditor::selectZero() if (editType == ET_ELEMENTS) { - if (game->tileset.elementTemplates.empty()) return; - if (editingElement) - { - } - else - { - curElement = 0; - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); - } + if (dsq->tilemgr.tileset.elementTemplates.empty()) return; + curElement = 0; + placer->setTexture(dsq->tilemgr.tileset.elementTemplates[curElement].gfx); } } @@ -2297,21 +2117,19 @@ void SceneEditor::selectEnd() if (state != ES_SELECTING) return; if (editType == ET_ELEMENTS) { - if (game->tileset.elementTemplates.empty()) return; - if (!editingElement) + if (dsq->tilemgr.tileset.elementTemplates.empty()) return; + + size_t largest = 0; + for (size_t i = 0; i < dsq->tilemgr.tileset.elementTemplates.size(); i++) { - size_t largest = 0; - for (size_t i = 0; i < game->tileset.elementTemplates.size(); i++) + ElementTemplate et = dsq->tilemgr.tileset.elementTemplates[i]; + if (et.idx < 1024 && i > largest) { - ElementTemplate et = game->tileset.elementTemplates[i]; - if (et.idx < 1024 && i > largest) - { - largest = i; - } + largest = i; } - curElement = largest; - placer->setTexture(game->tileset.elementTemplates[curElement].gfx); } + curElement = largest; + placer->setTexture(dsq->tilemgr.tileset.elementTemplates[curElement].gfx); } } @@ -2321,9 +2139,10 @@ void SceneEditor::placeElement() { if (!core->getShiftState() && !core->getKeyState(KEY_LALT)) { - game->createElement(game->tileset.elementTemplates[curElement].idx, placer->position, bgLayer, placer); + unsigned tilesetID = dsq->tilemgr.tileset.elementTemplates[curElement].idx; + dsq->tilemgr.createOneTile(tilesetID, bgLayer, placer->position.x, placer->position.y); + // FIXME: need to update grid or no? updateText(); - game->reconstructGrid(); } } else if (editType == ET_ENTITIES) @@ -2360,42 +2179,33 @@ void SceneEditor::cloneSelectedElement() { if (editType == ET_ELEMENTS) { - if (!selectedElements.empty()) + if (size_t n = selectedTiles.size()) { - std::vectorcopy; - Vector groupCenter = this->getSelectedElementsCenter(); - for (size_t i = 0; i < selectedElements.size(); i++) - { - Element *e1 = selectedElements[i]; - Vector dist = e1->position - groupCenter; - Element *e = game->createElement(e1->templateIdx, placer->position + Vector(40,40) + dist, e1->bgLayer, e1); - e->elementFlag = e1->elementFlag; - e->setElementEffectByIndex(e1->getElementEffectIndex()); - e->texOff = e1->texOff; - e->repeatToFillScale = e1->repeatToFillScale; - e->refreshRepeatTextureToFill(); - copy.push_back(e); - } - selectedElements.clear(); - selectedElements = copy; - copy.clear(); - } - else if (editingElement) - { - Element *e1 = editingElement; - Element *e = game->createElement(e1->templateIdx, placer->position + Vector(40,40), e1->bgLayer, e1); - e->elementFlag = e1->elementFlag; - e->setElementEffectByIndex(e1->getElementEffectIndex()); - e->texOff = e1->texOff; - e->repeatToFillScale = e1->repeatToFillScale; - e->refreshRepeatTextureToFill(); + TileStorage& ts = getCurrentLayerTiles(); + size_t newidx = ts.cloneSome(dsq->tilemgr.tileEffects, &selectedTiles[0], n); + assert(!multi); + unsigned allflags = 0; + + // select the clones + clearSelection(); + for(size_t i = 0; i < n; ++i) + { + selectedTiles.push_back(newidx + i); + TileData& t = ts.tiles[newidx + i]; + allflags |= t.flags; + t.flags |= TILEFLAG_SELECTED; + t.x += 40; + t.y += 40; + } + + if(allflags & TILEFLAG_SOLID) + game->reconstructGrid(true); } - game->reconstructGrid(); } else if (editType == ET_ENTITIES) { - + // TODO: make this work } else if (editType == ET_PATHS) { @@ -2456,8 +2266,6 @@ void SceneEditor::toggle(bool on) dsq->darkLayer.toggle(false); - game->rebuildElementUpdateList(); - for (int i = LR_ELEMENTS1; i <= LR_ELEMENTS8; i++) { dsq->getRenderObjectLayer(i)->update = true; @@ -2474,7 +2282,8 @@ void SceneEditor::toggle(bool on) { btnMenu->alpha = 0; - selectedElements.clear(); + destroyMultiTileHelper(); + clearSelection(); for (int i = 0; i < 9; i++) dsq->getRenderObjectLayer(LR_ELEMENTS1+i)->visible = true; @@ -2490,8 +2299,6 @@ void SceneEditor::toggle(bool on) dsq->darkLayer.toggle(true); - game->rebuildElementUpdateList(); - const float cameraOffset = 1/oldGlobalScale.x - 1/zoom.x; core->cameraPos.x -= cameraOffset * core->getVirtualWidth()/2; core->cameraPos.y -= cameraOffset * core->getVirtualHeight()/2; @@ -2506,6 +2313,8 @@ bool SceneEditor::isOn() void SceneEditor::updateText() { + // FIXME: make sure this isn't called while the editor isn't active + const Vector cursor = dsq->getGameCursorPosition(); TileVector tv(cursor); @@ -2517,27 +2326,18 @@ void SceneEditor::updateText() switch(editType) { case ET_ELEMENTS: - os << "elements (" << dsq->getNumElements() << ")"; - if (selectedElements.size() > 1) + os << "elements (" << dsq->tilemgr.getNumTiles() << ")"; + if (selectedTiles.size() > 1) { - os << " - " << selectedElements.size() << " selected"; + os << " - " << selectedTiles.size() << " selected"; } - else + else if(selectedTiles.size() == 1) { - Element *e; - if (!selectedElements.empty()) - e = selectedElements[0]; - else - e = editingElement; - if (e) - { - os << " id: " << e->templateIdx; - os << " efx: " << (e->getElementEffectIndex() + 1); // +1 so that it resembles the layout on numpad - os << " tag: " << e->tag; - ElementTemplate *et = game->getElementTemplateByIdx(e->templateIdx); - if (et) - os << " gfx: " << et->gfx; - } + const TileData& t = getCurrentLayerTiles().tiles[selectedTiles[0]]; + os << " id: " << t.et->idx; + os << " efx: " << (t.eff ? (t.eff->efxidx + 1) : 0); // +1 so that it resembles the layout on numpad + os << " tag: " << t.tag; + os << " gfx: " << t.et->gfx; } break; case ET_ENTITIES: @@ -2570,17 +2370,6 @@ void SceneEditor::updateText() text->setText(os.str()); } -Vector SceneEditor::getSelectedElementsCenter() -{ - Vector c; - for (size_t i = 0; i < selectedElements.size(); i++) - { - c += selectedElements[i]->position; - } - c /= float(selectedElements.size()); - return c; -} - void SceneEditor::update(float dt) { if (on) @@ -2609,21 +2398,24 @@ void SceneEditor::update(float dt) switch (editType) { case ET_ELEMENTS: - editingEntity = 0; - if (isActing(ACTION_MULTISELECT, -1) || !selectedElements.empty()) + { + bool ismulti = isActing(ACTION_MULTISELECT, -1); + int sel = -1; + if (state == ES_SELECTING && !ismulti) { - editingElement = 0; + selectedTiles.clear(); + sel = this->getTileAtCursor(); + if(sel >= 0) + selectedTiles.push_back(sel); } - if (state == ES_SELECTING && !isActing(ACTION_MULTISELECT, -1)) - editingElement = this->getElementAtCursor(); - if (editingElement) + if (sel >= 0 || ismulti) placer->alpha = 0; else placer->alpha = 0.5; + } break; case ET_ENTITIES: - editingElement = 0; if (state == ES_SELECTING) editingEntity = this->getEntityAtCursor(); if (editingEntity) @@ -2764,13 +2556,13 @@ void SceneEditor::update(float dt) float add = (dsq->getGameCursorPosition().x - cursorOffset.x)/2.4f; if (core->getCtrlState()) { - int a = (oldRotation.z + add)/45; + int a = (oldRotation + add)/45; add = a * 45; editingEntity->rotation.z = add; } else { - editingEntity->rotation.z = oldRotation.z + add; + editingEntity->rotation.z = oldRotation + add; } } } @@ -2787,51 +2579,27 @@ void SceneEditor::update(float dt) { case ES_SELECTING: { - float closest = sqr(800); - size_t i = 0; - for (i = 0; i < dsq->getNumElements(); i++) - { - Vector dist = dsq->getElement(i)->getFollowCameraPosition() - dsq->getGameCursorPosition(); - float len = dist.getSquaredLength2D(); - if (len < closest) - { - closest = len; - } - } } break; case ES_MOVING: + // FIXME: this should be relative? check this updateSelectedElementPosition(dsq->getGameCursorPosition() - cursorOffset); break; case ES_ROTATING: { - if (!selectedElements.empty()) + if (!selectedTiles.empty()) { - + assert(multi); float add = (dsq->getGameCursorPosition().x - cursorOffset.x)/2.4f; if (core->getCtrlState()) { - int a = (oldRotation.z + add)/45; + int a = (oldRotation + add)/45; add = a * 45; - dummy.rotation.z = add; + multi->rotation.z = add; } else { - dummy.rotation.z = oldRotation.z + add; - } - } - else if (editingElement) - { - float add = (dsq->getGameCursorPosition().x - cursorOffset.x)/2.4f; - if (core->getCtrlState()) - { - int a = (oldRotation.z + add)/45; - add = a * 45; - editingElement->rotation.z = add; - } - else - { - editingElement->rotation.z = oldRotation.z + add; + multi->rotation.z = oldRotation + add; } } } @@ -2856,52 +2624,41 @@ void SceneEditor::update(float dt) Vector add = Vector((dsq->getGameCursorPosition().x - cursorOffset.x)/100.0f, (dsq->getGameCursorPosition().y - cursorOffset.y)/100.0f); { - if (!selectedElements.empty()) + if (core->getKeyState(KEY_C)) + { + add.y = 0; + if (!right && !middle) + add.x *= -1; + } + else if (core->getKeyState(KEY_V)) + { + add.x = 0; + if (!down && !middle) + add.y *= -1; + } + else + { add.y = add.x; - else - { - if (core->getKeyState(KEY_C)) - { - add.y = 0; - if (!right && !middle) - add.x *= -1; - } - else if (core->getKeyState(KEY_V)) - { - add.x = 0; - if (!down && !middle) - add.y *= -1; - } - else - { - add.y = add.x; - uni = true; - } - - repeatScale = core->getKeyState(KEY_X); - if(repeatScale) - add *= 0.1f; + uni = true; } + + repeatScale = core->getKeyState(KEY_X); + if(repeatScale) + add *= 0.1f; } - if (!selectedElements.empty()) + if (!selectedTiles.empty()) { - if (core->getCtrlState()) + if (!core->getCtrlState()) { - dummy.scale = Vector(1,1); + multi->scale=oldScale + add; + if (multi->scale.x < MIN_SIZE) + multi->scale.x = MIN_SIZE; + if (multi->scale.y < MIN_SIZE) + multi->scale.y = MIN_SIZE; } - else - { - dummy.scale=oldScale + add; - if (dummy.scale.x < MIN_SIZE) - dummy.scale.x = MIN_SIZE; - if (dummy.scale.y < MIN_SIZE) - dummy.scale.y = MIN_SIZE; - } - - for (size_t i = 0; i < selectedElements.size(); i++) - selectedElements[i]->refreshRepeatTextureToFill(); } - else if (editingElement) + // FIXME: scaling + /*else if (editingElement) { Vector& editVec = repeatScale ? editingElement->repeatToFillScale : editingElement->scale; if (core->getCtrlState()) @@ -2940,7 +2697,7 @@ void SceneEditor::update(float dt) } editingElement->refreshRepeatTextureToFill(); - } + }*/ } break; case ES_MAX: @@ -2954,7 +2711,7 @@ void SceneEditor::nextEntityType() { if (editType == ET_ELEMENTS && state == ES_SELECTING) { - if (editingElement || !selectedElements.empty()) + if (!selectedTiles.empty()) { enterMoveState(); updateSelectedElementPosition(Vector(1,0)); @@ -2971,7 +2728,7 @@ void SceneEditor::prevEntityType() { if (editType == ET_ELEMENTS && state == ES_SELECTING) { - if (editingElement || !selectedElements.empty()) + if (!selectedTiles.empty()) { enterMoveState(); updateSelectedElementPosition(Vector(-1,0)); @@ -3020,11 +2777,16 @@ void SceneEditor::moveEverythingBy(int dx, int dy) p->nodes[ii].position += mv; } - const size_t ne = dsq->getNumElements(); - for(size_t i = 0; i < ne; ++i) + for(unsigned lr = 0; lr < MAX_TILE_LAYERS; ++lr) { - Element *elem = dsq->getElement(i); - elem->position += mv; + TileStorage& ts = tilesForLayer(lr); + const size_t ne = ts.tiles.size(); + for(size_t i = 0; i < ne; ++i) + { + TileData& t = ts.tiles[i]; + t.x += mv.x; + t.y += mv.y; + } } tinyxml2::XMLElement *sf = game->saveFile->FirstChildElement("SchoolFish"); @@ -3036,3 +2798,25 @@ void SceneEditor::moveEverythingBy(int dx, int dy) sf->SetAttribute("y", sy + dy); } } + +TileStorage& SceneEditor::getCurrentLayerTiles() +{ + return tilesForLayer(this->bgLayer); +} + +MultiTileHelper * SceneEditor::createMultiTileHelperFromSelection() +{ + assert(!multi); + if(selectedTiles.empty()) + return NULL; + return (multi = MultiTileHelper::New(bgLayer, &selectedTiles[0], selectedTiles.size())); +} + +void SceneEditor::destroyMultiTileHelper() +{ + if(multi) + { + multi->finish(); + multi = NULL; + } +} \ No newline at end of file diff --git a/Aquaria/SceneEditor.h b/Aquaria/SceneEditor.h index abb9eea..833facb 100644 --- a/Aquaria/SceneEditor.h +++ b/Aquaria/SceneEditor.h @@ -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 selectedElements; + std::vector 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 diff --git a/Aquaria/ScriptInterface.cpp b/Aquaria/ScriptInterface.cpp index 2604339..159cef8 100644 --- a/Aquaria/ScriptInterface.cpp +++ b/Aquaria/ScriptInterface.cpp @@ -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); } diff --git a/Aquaria/TileMgr.cpp b/Aquaria/TileMgr.cpp new file mode 100644 index 0000000..c37afa1 --- /dev/null +++ b/Aquaria/TileMgr.cpp @@ -0,0 +1,278 @@ +#include "TileMgr.h" +#include +#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& 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) +{ +} diff --git a/Aquaria/TileMgr.h b/Aquaria/TileMgr.h new file mode 100644 index 0000000..58bfaa6 --- /dev/null +++ b/Aquaria/TileMgr.h @@ -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& 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 diff --git a/BBGE/RenderObject.cpp b/BBGE/RenderObject.cpp index aebd0d6..672f428 100644 --- a/BBGE/RenderObject.cpp +++ b/BBGE/RenderObject.cpp @@ -97,6 +97,7 @@ RenderObject::RenderObject() shareAlphaWithChildren = false; shareColorWithChildren = false; + neverFollowCamera = false; } RenderObject::~RenderObject() diff --git a/BBGE/RenderObject.h b/BBGE/RenderObject.h index 4460596..83a18b0 100644 --- a/BBGE/RenderObject.h +++ b/BBGE/RenderObject.h @@ -242,6 +242,7 @@ public: bool _hidden; bool _fv, _fh; bool _markedForDelete; + bool neverFollowCamera; unsigned char pm; // unsigned char to save space diff --git a/BBGE/RenderObjectLayer.cpp b/BBGE/RenderObjectLayer.cpp index 48c5813..906f962 100644 --- a/BBGE/RenderObjectLayer.cpp +++ b/BBGE/RenderObjectLayer.cpp @@ -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: diff --git a/BBGE/RenderObject_inline.h b/BBGE/RenderObject_inline.h index dcd42e2..4bcdad2 100644 --- a/BBGE/RenderObject_inline.h +++ b/BBGE/RenderObject_inline.h @@ -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; diff --git a/BBGE/Texture.cpp b/BBGE/Texture.cpp index df4ffaa..cadc9d6 100644 --- a/BBGE/Texture.cpp +++ b/BBGE/Texture.cpp @@ -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); diff --git a/BBGE/Texture.h b/BBGE/Texture.h index 25e8fe1..bfd6012 100644 --- a/BBGE/Texture.h +++ b/BBGE/Texture.h @@ -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); diff --git a/BBGE/TextureMgr.cpp b/BBGE/TextureMgr.cpp index 3bc9f26..06fca12 100644 --- a/BBGE/TextureMgr.cpp +++ b/BBGE/TextureMgr.cpp @@ -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 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) diff --git a/BBGE/TextureMgr.h b/BBGE/TextureMgr.h index 53e05f3..e0bf3a7 100644 --- a/BBGE/TextureMgr.h +++ b/BBGE/TextureMgr.h @@ -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); diff --git a/BBGE/Tile.cpp b/BBGE/Tile.cpp index 3ee54fb..7b0db47 100644 --- a/BBGE/Tile.cpp +++ b/BBGE/Tile.cpp @@ -2,9 +2,9 @@ #include "RenderGrid.h" #include "Tileset.h" #include "Base.h" +#include -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 tmp(indices, indices + n); + std::sort(tmp.begin(), tmp.end()); + + std::vector 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; +} diff --git a/BBGE/Tile.h b/BBGE/Tile.h index 35c00a7..1dbe01d 100644 --- a/BBGE/Tile.h +++ b/BBGE/Tile.h @@ -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 prepared; std::vector configs; + +private: + void clearPrepared(); + std::vector 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 tiles; // must call refreshAll() after changing this + private: - std::vector tiles; std::vector indicesToUpdate; std::vector 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 }; diff --git a/BBGE/TileRender.cpp b/BBGE/TileRender.cpp index b83effc..ffa44cb 100644 --- a/BBGE/TileRender.cpp +++ b/BBGE/TileRender.cpp @@ -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; +} diff --git a/BBGE/TileRender.h b/BBGE/TileRender.h index f5e28db..10981eb 100644 --- a/BBGE/TileRender.h +++ b/BBGE/TileRender.h @@ -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; diff --git a/BBGE/Tileset.cpp b/BBGE/Tileset.cpp index edd1837..26e4a53 100644 --- a/BBGE/Tileset.cpp +++ b/BBGE/Tileset.cpp @@ -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; +} diff --git a/BBGE/Tileset.h b/BBGE/Tileset.h index 111eaff..857a85c 100644 --- a/BBGE/Tileset.h +++ b/BBGE/Tileset.h @@ -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 tex; + CountedPtr 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 elementTemplates; + +private: + ElementTemplate *_getAdjacent(size_t idx, int direction, bool wraparound); + + std::vector dummies; };