2023-07-10 00:29:21 +00:00
|
|
|
#include "Tile.h"
|
2023-07-11 20:30:28 +00:00
|
|
|
#include "RenderGrid.h"
|
|
|
|
#include "Tileset.h"
|
|
|
|
#include "Base.h"
|
2023-07-13 22:19:33 +00:00
|
|
|
#include <algorithm>
|
2023-07-14 03:21:16 +00:00
|
|
|
#include "Texture.h"
|
2023-07-10 00:29:21 +00:00
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
TileStorage::TileStorage()
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TileStorage::~TileStorage()
|
|
|
|
{
|
|
|
|
destroyAll();
|
|
|
|
}
|
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
TileStorage::Sizes TileStorage::stats() const
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
Sizes sz;
|
|
|
|
sz.tiles = tiles.size();
|
|
|
|
sz.update = indicesToUpdate.size();
|
|
|
|
sz.collide = indicesToCollide.size();
|
|
|
|
return sz;
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
void TileStorage::moveToFront(size_t *indices, size_t n)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
if(n)
|
|
|
|
{
|
|
|
|
_moveToFront(indices, n);
|
|
|
|
refreshAll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
void TileStorage::moveToBack(size_t *indices, size_t n)
|
2023-07-13 22:19:33 +00:00
|
|
|
{
|
|
|
|
if(n)
|
|
|
|
{
|
|
|
|
_moveToBack(indices, n);
|
|
|
|
refreshAll();
|
|
|
|
}
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TileStorage::update(float dt)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < indicesToUpdate.size(); ++i)
|
|
|
|
{
|
|
|
|
TileData& t = tiles[indicesToUpdate[i]];
|
|
|
|
assert(t.flags & TILEFLAG_OWN_EFFDATA); // known to be set if this ends up on the list
|
|
|
|
t.eff->update(dt, &t);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileStorage::doInteraction(const Vector& pos, const Vector& vel, float mult, float touchWidth)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < indicesToCollide.size(); ++i)
|
|
|
|
{
|
|
|
|
TileData& t = tiles[indicesToCollide[i]];
|
|
|
|
t.eff->doInteraction(t, pos, vel, mult, touchWidth);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
void TileStorage::_moveToFront(size_t *indices, size_t n)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
// move tile to front -> move it to the back of the list, to be rendered last aka on top of everything else
|
2023-07-13 22:19:33 +00:00
|
|
|
|
|
|
|
if(n == 1)
|
|
|
|
{
|
|
|
|
TileData tile = tiles[*indices];
|
|
|
|
tiles.erase(tiles.begin() + *indices);
|
2024-05-12 15:45:01 +00:00
|
|
|
*indices = tiles.size();
|
2023-07-13 22:19:33 +00:00
|
|
|
tiles.push_back(tile);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
_moveToPos(MV_END, indices, n);
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
void TileStorage::_moveToBack(size_t *indices, size_t n)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
// move tile to back -> move it to the front of the list, to be rendered first aka underneath everything else
|
2023-07-13 22:19:33 +00:00
|
|
|
|
|
|
|
if(n == 1)
|
|
|
|
{
|
|
|
|
TileData tile = tiles[*indices];
|
|
|
|
tiles.erase(tiles.begin() + *indices);
|
|
|
|
tiles.insert(tiles.begin(), tile);
|
2024-05-12 15:45:01 +00:00
|
|
|
*indices = 0;
|
2023-07-13 22:19:33 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
_moveToPos(MV_BEGIN, indices, n);
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
2024-05-12 15:45:01 +00:00
|
|
|
void TileStorage::_moveToPos(MoveTarget where, size_t * indices, size_t n)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2024-07-13 04:47:07 +00:00
|
|
|
const size_t N = tiles.size();
|
|
|
|
std::vector<TileData> tt;
|
|
|
|
tt.reserve(N);
|
|
|
|
std::vector<unsigned char> used(N, 0);
|
2023-07-13 22:19:33 +00:00
|
|
|
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
2024-07-13 04:47:07 +00:00
|
|
|
used[indices[i]] = 1;
|
2023-07-13 22:19:33 +00:00
|
|
|
|
2024-07-13 04:47:07 +00:00
|
|
|
if(where == MV_BEGIN)
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
tt.push_back(tiles[indices[i]]);
|
2024-05-12 15:45:01 +00:00
|
|
|
|
2024-07-13 04:47:07 +00:00
|
|
|
for(size_t i = 0; i < N; ++i)
|
|
|
|
if(!used[i])
|
|
|
|
tt.push_back(tiles[i]);
|
2023-07-13 22:19:33 +00:00
|
|
|
|
2024-07-13 04:47:07 +00:00
|
|
|
if(where == MV_END)
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
tt.push_back(tiles[indices[i]]);
|
|
|
|
|
|
|
|
tiles.swap(tt);
|
|
|
|
|
|
|
|
size_t offs = where == MV_BEGIN ? 0 : tiles.size() - n;
|
2024-05-12 15:45:01 +00:00
|
|
|
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
indices[i] = i + offs;
|
2023-07-13 22:19:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t TileStorage::moveToOther(TileStorage& other, const size_t *indices, size_t n)
|
|
|
|
{
|
|
|
|
const size_t firstNewIdx = other.tiles.size();
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
other.tiles.push_back(tiles[indices[i]]);
|
|
|
|
|
|
|
|
std::vector<TileData> tmp;
|
|
|
|
tmp.swap(tiles);
|
|
|
|
tiles.reserve(tmp.size() - n);
|
|
|
|
for(size_t i = 0; i < tmp.size(); ++i)
|
|
|
|
{
|
2024-05-08 01:25:19 +00:00
|
|
|
for(size_t k = 0; k < n; ++k) // not particularly efficient, could be much better by sorting first but eh
|
2023-07-11 20:30:28 +00:00
|
|
|
if(indices[k] == i)
|
|
|
|
goto skip;
|
|
|
|
|
|
|
|
tiles.push_back(tmp[i]);
|
|
|
|
|
|
|
|
skip: ;
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshAll();
|
|
|
|
other.refreshAll();
|
2023-07-13 22:19:33 +00:00
|
|
|
return firstNewIdx;
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
2023-07-14 03:21:16 +00:00
|
|
|
static void dropEffect(TileData& t)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
if(t.flags & TILEFLAG_OWN_EFFDATA)
|
|
|
|
{
|
|
|
|
delete t.eff;
|
|
|
|
t.flags &= ~TILEFLAG_OWN_EFFDATA;
|
|
|
|
}
|
|
|
|
t.eff = NULL;
|
|
|
|
}
|
|
|
|
|
2023-07-14 03:21:16 +00:00
|
|
|
static void dropRepeat(TileData& t)
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
if(t.rep)
|
2023-07-14 03:21:16 +00:00
|
|
|
{
|
|
|
|
delete t.rep;
|
2023-08-09 00:41:04 +00:00
|
|
|
t.rep = NULL;
|
2023-07-14 03:21:16 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dropAll(TileData& t)
|
|
|
|
{
|
|
|
|
dropEffect(t);
|
|
|
|
dropRepeat(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
void TileStorage::deleteSome(const size_t* indices, size_t n)
|
|
|
|
{
|
2023-12-29 21:43:43 +00:00
|
|
|
if(!n)
|
|
|
|
return;
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
std::vector<TileData> tmp;
|
|
|
|
tmp.swap(tiles);
|
|
|
|
tiles.reserve(tmp.size() - n);
|
|
|
|
|
|
|
|
for(size_t i = 0; i < tmp.size(); ++i)
|
|
|
|
{
|
2023-10-19 22:37:00 +00:00
|
|
|
for(size_t k = 0; k < n; ++k) // not particularly efficient, could be much better by sorting first but eh
|
2023-07-11 20:30:28 +00:00
|
|
|
if(indices[k] == i)
|
|
|
|
{
|
2023-07-14 03:21:16 +00:00
|
|
|
dropAll(tmp[i]);
|
2023-07-11 20:30:28 +00:00
|
|
|
goto skip;
|
|
|
|
}
|
|
|
|
|
|
|
|
tiles.push_back(tmp[i]);
|
|
|
|
|
|
|
|
skip: ;
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileStorage::destroyAll()
|
|
|
|
{
|
|
|
|
const size_t n = tiles.size();
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
2023-07-14 03:21:16 +00:00
|
|
|
dropAll(tiles[i]);
|
2023-07-11 20:30:28 +00:00
|
|
|
tiles.clear();
|
|
|
|
indicesToCollide.clear();
|
|
|
|
indicesToUpdate.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileStorage::setTag(unsigned tag, const size_t* indices, size_t n)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
tiles[indices[i]].tag = tag;
|
|
|
|
// don't need to refresh here
|
|
|
|
}
|
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
void TileStorage::setEffect(const TileEffectStorage& effstore, int idx, const size_t* indices, size_t n)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
effstore.assignEffect(tiles[indices[i]], idx);
|
|
|
|
refreshAll();
|
|
|
|
}
|
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
void TileStorage::changeFlags(unsigned flagsToSet, unsigned flagsToUnset, const size_t* indices, size_t n)
|
|
|
|
{
|
2024-05-07 23:24:01 +00:00
|
|
|
assert(!(flagsToSet & flagsToUnset)); // don't set and unset flag at the same time
|
|
|
|
const unsigned unsetMask = ~flagsToUnset;
|
|
|
|
const unsigned setRep = flagsToSet & TILEFLAG_REPEAT;
|
2023-07-13 22:19:33 +00:00
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
{
|
2024-05-07 23:24:01 +00:00
|
|
|
TileData& t = tiles[indices[i]];
|
|
|
|
unsigned tmp = t.flags & unsetMask;
|
|
|
|
t.flags = tmp | flagsToSet;
|
|
|
|
if(setRep && !t.rep) // setting the flag does not create the attached data, do that if necessary
|
|
|
|
t.setRepeatOn();
|
2023-07-13 22:19:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-08 17:23:03 +00:00
|
|
|
void TileStorage::select(const size_t *indices, size_t n)
|
|
|
|
{
|
|
|
|
changeFlags(TILEFLAG_SELECTED, 0, indices, n);
|
|
|
|
}
|
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
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];
|
2023-08-09 00:41:04 +00:00
|
|
|
if(t.rep)
|
|
|
|
{
|
|
|
|
t.rep = new TileRepeatData(*t.rep); // must be done BEFORE assigning eff
|
|
|
|
}
|
2023-07-13 22:19:33 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
void TileStorage::refreshAll()
|
|
|
|
{
|
|
|
|
indicesToCollide.clear();
|
|
|
|
indicesToUpdate.clear();
|
|
|
|
|
|
|
|
const size_t n = tiles.size();
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
{
|
2023-07-14 03:21:16 +00:00
|
|
|
TileData& t = tiles[i];
|
|
|
|
t.refreshRepeat();
|
2023-07-13 22:19:33 +00:00
|
|
|
if(!(t.flags & TILEFLAG_HIDDEN))
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
if(const TileEffectData *e = t.eff)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
if(t.flags & TILEFLAG_OWN_EFFDATA)
|
|
|
|
{
|
|
|
|
indicesToUpdate.push_back(i);
|
|
|
|
if(e->efxtype == EFX_WAVY)
|
|
|
|
indicesToCollide.push_back(i);
|
|
|
|
}
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileStorage::clearSelection()
|
|
|
|
{
|
|
|
|
const size_t n = tiles.size();
|
|
|
|
for(size_t i = 0; i < n; ++i)
|
|
|
|
tiles[i].flags &= ~TILEFLAG_SELECTED;
|
|
|
|
}
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
TileEffectData::TileEffectData(const TileEffectConfig& cfg, const TileData *t)
|
2023-07-11 20:30:28 +00:00
|
|
|
: efxtype(cfg.type), efxidx(cfg.index)
|
2023-07-13 22:19:33 +00:00
|
|
|
, grid(NULL), alpha(1), blend(BLEND_DEFAULT)
|
2023-08-09 00:41:04 +00:00
|
|
|
, ownGrid(false), shared(false)
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
|
|
|
switch(cfg.type)
|
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
case EFX_NONE:
|
|
|
|
assert(false);
|
|
|
|
break;
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
case EFX_WAVY:
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
assert(t);
|
|
|
|
float bity = t->et->h/float(cfg.u.wavy.segsy);
|
2023-07-11 20:30:28 +00:00
|
|
|
wavy.wavy.resize(cfg.u.wavy.segsy, 0.0f);
|
|
|
|
wavy.flip = cfg.u.wavy.flip;
|
|
|
|
wavy.min = bity;
|
|
|
|
wavy.max = bity*1.2f;
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
DynamicRenderGrid *g = _ensureGrid(2, cfg.u.wavy.segsy, t);
|
2023-07-13 22:19:33 +00:00
|
|
|
g->gridType = GRID_UNDEFINED; // we do the grid update manually
|
2023-07-11 20:30:28 +00:00
|
|
|
|
|
|
|
wavy.angleOffset = 0;
|
|
|
|
wavy.magnitude = 0;
|
|
|
|
wavy.lerpIn = 0;
|
|
|
|
wavy.hitPerc = 0;
|
|
|
|
wavy.effectMult = 0;
|
|
|
|
wavy.waving = false;
|
|
|
|
wavy.flip = false;
|
|
|
|
wavy.touching = false;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EFX_SEGS:
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
DynamicRenderGrid *g = _ensureGrid(cfg.u.segs.x, cfg.u.segs.y, t);
|
2023-07-11 20:30:28 +00:00
|
|
|
g->setSegs(cfg.u.segs.dgox, cfg.u.segs.dgoy, cfg.u.segs.dgmx, cfg.u.segs.dgmy, cfg.u.segs.dgtm, cfg.u.segs.dgo);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EFX_ALPHA:
|
|
|
|
{
|
|
|
|
alpha.x = cfg.u.alpha.val0;
|
|
|
|
alpha.interpolateTo(cfg.u.alpha.val1, cfg.u.alpha.time, -1, cfg.u.alpha.pingpong, cfg.u.alpha.ease);
|
|
|
|
blend = cfg.u.alpha.blend;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-24 22:40:14 +00:00
|
|
|
TileEffectData::TileEffectData(const TileEffectData& o)
|
|
|
|
: efxtype(o.efxtype), efxidx(o.efxidx), grid(NULL)
|
|
|
|
, alpha(o.alpha), blend(o.blend)
|
|
|
|
, ownGrid(false), shared(false), wavy(o.wavy)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileEffectData::deleteGrid()
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
if(ownGrid)
|
2023-08-24 22:40:14 +00:00
|
|
|
{
|
|
|
|
ownGrid = false;
|
2023-08-09 00:41:04 +00:00
|
|
|
delete grid;
|
2023-08-24 22:40:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TileEffectData::~TileEffectData()
|
|
|
|
{
|
|
|
|
deleteGrid();
|
2023-08-09 00:41:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
DynamicRenderGrid *TileEffectData::_ensureGrid(size_t w, size_t h, const TileData *t)
|
|
|
|
{
|
|
|
|
DynamicRenderGrid *g = grid;
|
|
|
|
if(ownGrid)
|
|
|
|
{
|
|
|
|
assert(g);
|
|
|
|
return g;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(t && t->rep)
|
|
|
|
{
|
|
|
|
assert(!shared); // a shared instance MUST have its own grid and MUST NOT refer to the grid of any tile
|
2023-08-24 22:40:14 +00:00
|
|
|
deleteGrid();
|
2023-08-09 00:41:04 +00:00
|
|
|
g = &t->rep->grid;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!g)
|
|
|
|
{
|
|
|
|
g = new DynamicRenderGrid;
|
|
|
|
ownGrid = true;
|
|
|
|
}
|
|
|
|
grid = g;
|
|
|
|
TexCoordBox tc;
|
|
|
|
if(t)
|
|
|
|
tc = t->getTexcoords();
|
|
|
|
else
|
|
|
|
tc.setStandard();
|
|
|
|
g->init(w, h, tc);
|
|
|
|
if(t && t->rep)
|
|
|
|
t->rep->refresh(*t);
|
|
|
|
return g;
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TileEffectData::Wavy::update(float dt)
|
|
|
|
{
|
|
|
|
if (touching)
|
|
|
|
{
|
|
|
|
touching = false;
|
|
|
|
float ramp = touchVel.getLength2D()/800.0f;
|
|
|
|
if (ramp < 0) ramp = 0;
|
|
|
|
if (ramp > 1) ramp = 1;
|
|
|
|
|
|
|
|
magnitude = 100 * ramp + 16;
|
|
|
|
|
|
|
|
if (touchVel.x < 0)
|
|
|
|
magnitude = -magnitude;
|
|
|
|
|
|
|
|
angleOffset = (hitPerc-0.5f)*PI;
|
|
|
|
|
|
|
|
wavySave = wavy;
|
|
|
|
lerpIn = 0;
|
2024-02-06 03:01:13 +00:00
|
|
|
waving = true;
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (waving)
|
|
|
|
{
|
2024-02-06 03:01:13 +00:00
|
|
|
const float spd = PI*1.1f;
|
|
|
|
const float magRedSpd = 48;
|
|
|
|
const float lerpSpd = 5.0;
|
|
|
|
const float wavySzInv = 1.0f / float(wavy.size());
|
2023-07-11 20:30:28 +00:00
|
|
|
for (size_t i = 0; i < wavy.size(); i++)
|
|
|
|
{
|
2024-02-06 03:01:13 +00:00
|
|
|
const float m = float(i)*wavySzInv;
|
|
|
|
float weight = m;
|
2023-07-11 20:30:28 +00:00
|
|
|
if (flip)
|
|
|
|
weight = 1.0f-weight;
|
|
|
|
if (weight < 0.125f)
|
|
|
|
weight *= 0.5f;
|
2024-02-06 03:01:13 +00:00
|
|
|
float val = sinf(angleOffset + m*PI)*(magnitude*effectMult)*weight;
|
2023-07-11 20:30:28 +00:00
|
|
|
if (!wavySave.empty())
|
2024-02-06 03:01:13 +00:00
|
|
|
val = val * lerpIn + (wavySave[i] * (1.0f-lerpIn));
|
|
|
|
wavy[i] = val;
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (lerpIn < 1)
|
|
|
|
{
|
|
|
|
lerpIn += dt*lerpSpd;
|
|
|
|
if (lerpIn > 1)
|
|
|
|
lerpIn = 1;
|
|
|
|
}
|
|
|
|
angleOffset += dt*spd;
|
|
|
|
if (magnitude > 0)
|
|
|
|
{
|
|
|
|
magnitude -= magRedSpd*dt;
|
|
|
|
if (magnitude < 0)
|
2024-02-06 03:01:13 +00:00
|
|
|
stop();
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
magnitude += magRedSpd*dt;
|
|
|
|
if (magnitude > 0)
|
2024-02-06 03:01:13 +00:00
|
|
|
stop();
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-06 03:01:13 +00:00
|
|
|
void TileEffectData::Wavy::stop()
|
|
|
|
{
|
|
|
|
magnitude = 0;
|
|
|
|
waving = false;
|
|
|
|
}
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
void TileEffectData::update(float dt, const TileData *t)
|
|
|
|
{
|
|
|
|
switch(efxtype)
|
|
|
|
{
|
|
|
|
case EFX_WAVY:
|
2024-02-06 03:01:13 +00:00
|
|
|
if(!(wavy.waving || wavy.touching))
|
|
|
|
break;
|
2023-07-11 20:30:28 +00:00
|
|
|
wavy.update(dt);
|
|
|
|
if(const size_t N = wavy.wavy.size())
|
|
|
|
grid->setFromWavy(&wavy.wavy[0], N, t->et->w);
|
2024-02-06 03:01:13 +00:00
|
|
|
// fall through
|
2023-07-11 20:30:28 +00:00
|
|
|
|
|
|
|
case EFX_SEGS:
|
|
|
|
grid->update(dt);
|
|
|
|
break;
|
|
|
|
|
|
|
|
case EFX_ALPHA:
|
|
|
|
alpha.update(dt);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileEffectData::doInteraction(const TileData& t, const Vector& pos, const Vector& vel, float mult, float touchWidth)
|
|
|
|
{
|
|
|
|
assert(efxtype == EFX_WAVY);
|
|
|
|
|
|
|
|
const Vector tp(t.x, t.y);
|
|
|
|
|
|
|
|
if (pos.x > tp.x-touchWidth && pos.x < tp.x+touchWidth)
|
|
|
|
{
|
|
|
|
float h = t.et->h*t.scaley;
|
|
|
|
float h2 = h * 0.5f;
|
|
|
|
if (pos.y < tp.y+h2 && pos.y > tp.y-h2)
|
|
|
|
{
|
|
|
|
wavy.touching = true;
|
|
|
|
wavy.waving = true;
|
|
|
|
float hitPerc = tp.y - h2 - pos.y;
|
|
|
|
hitPerc /= h;
|
|
|
|
hitPerc = (1.0f-hitPerc)-1.0f;
|
|
|
|
wavy.hitPerc = hitPerc;
|
|
|
|
wavy.touchVel = vel;
|
|
|
|
wavy.effectMult = mult;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-13 22:19:33 +00:00
|
|
|
TileEffectStorage::TileEffectStorage()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TileEffectStorage::~TileEffectStorage()
|
|
|
|
{
|
|
|
|
clear();
|
|
|
|
}
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
void TileEffectStorage::assignEffect(TileData& t, int index) const
|
|
|
|
{
|
2023-07-14 03:21:16 +00:00
|
|
|
dropEffect(t);
|
2023-07-11 20:30:28 +00:00
|
|
|
|
|
|
|
if(index < 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
size_t idx = size_t(index);
|
2023-08-09 00:41:04 +00:00
|
|
|
if(idx >= configs.size())
|
|
|
|
return;
|
|
|
|
|
2023-07-11 20:30:28 +00:00
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
bool needinstance = false;
|
|
|
|
if(idx < configs.size())
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
needinstance = configs[idx].needsOwnInstanceForTile(t);
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
2023-08-09 00:41:04 +00:00
|
|
|
|
|
|
|
if(needinstance)
|
2023-07-11 20:30:28 +00:00
|
|
|
{
|
2023-07-13 22:19:33 +00:00
|
|
|
if(configs[idx].type == EFX_NONE)
|
|
|
|
return;
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
t.eff = new TileEffectData(configs[idx], &t);
|
2023-07-11 20:30:28 +00:00
|
|
|
t.flags |= TILEFLAG_OWN_EFFDATA;
|
|
|
|
}
|
2023-08-09 00:41:04 +00:00
|
|
|
else if(idx < prepared.size() && prepared[idx])
|
|
|
|
{
|
|
|
|
t.eff = prepared[idx];
|
|
|
|
}
|
2023-07-11 20:30:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TileEffectStorage::update(float dt)
|
|
|
|
{
|
|
|
|
for(size_t i = 0; i < prepared.size(); ++i)
|
|
|
|
if(TileEffectData *eff = prepared[i])
|
|
|
|
eff->update(dt, NULL);
|
|
|
|
}
|
2023-07-13 22:19:33 +00:00
|
|
|
|
|
|
|
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)
|
2023-08-09 00:41:04 +00:00
|
|
|
{
|
|
|
|
prepared[i] = new TileEffectData(c, NULL);
|
|
|
|
prepared[i]->shared = true;
|
|
|
|
}
|
2023-07-13 22:19:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
2023-07-14 03:21:16 +00:00
|
|
|
|
2023-07-20 20:30:56 +00:00
|
|
|
TileRepeatData::TileRepeatData()
|
2023-08-09 00:41:04 +00:00
|
|
|
: texscaleX(1), texscaleY(1)
|
|
|
|
, texOffX(0), texOffY(0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
TileRepeatData::TileRepeatData(const TileRepeatData& o)
|
|
|
|
: texscaleX(o.texscaleX), texscaleY(o.texscaleY)
|
|
|
|
, texOffX(o.texOffX), texOffY(o.texOffY)
|
2023-07-20 20:30:56 +00:00
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
TexCoordBox TileRepeatData::calcTexCoords(const TileData& t) const
|
2023-07-14 03:21:16 +00:00
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
const ElementTemplate& et = *t.et;
|
|
|
|
|
2023-07-14 03:21:16 +00:00
|
|
|
float tw, th;
|
|
|
|
if(et.tex)
|
|
|
|
{
|
|
|
|
tw = et.tex->width;
|
|
|
|
th = et.tex->height;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
tw = et.w;
|
|
|
|
th = et.h;
|
|
|
|
}
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
TexCoordBox tc;
|
|
|
|
tc.u1 = texOffX;
|
|
|
|
tc.v1 = texOffY;
|
|
|
|
tc.u2 = (et.w*t.scalex*texscaleX)/tw + texOffX;
|
|
|
|
tc.v2 = (et.h*t.scaley*texscaleY)/th + texOffY;
|
2023-10-20 00:04:21 +00:00
|
|
|
|
|
|
|
// HACK: partially repeated textures have a weird Y axis. assuming a repeat factor of 0.4,
|
|
|
|
// instead of texcoords from 0 -> 0.4 everything is biased towards the opposite end, ie. 0.6 -> 1.
|
|
|
|
// This is especially true for partial repeats, we always need to bias towards the other end.
|
|
|
|
// And NOTE: without this, maps may look deceivingly correct, but they really are not.
|
|
|
|
tc.v2 = 1 - tc.v2;
|
|
|
|
tc.v1 = 1 - tc.v1;
|
|
|
|
std::swap(tc.v1, tc.v2);
|
2023-07-14 03:21:16 +00:00
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
return tc;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileRepeatData::refresh(const TileData& t)
|
|
|
|
{
|
|
|
|
TexCoordBox tc = calcTexCoords(t);
|
|
|
|
|
|
|
|
/*if(t.eff)
|
|
|
|
if(const DynamicRenderGrid *g = t.eff->grid)
|
|
|
|
{
|
|
|
|
grid.init(g->width(), g->height(), grid.getTexCoords());
|
|
|
|
grid.gridType = g->gridType;
|
|
|
|
}*/
|
|
|
|
|
|
|
|
if(grid.empty())
|
|
|
|
grid.init(2, 2, tc);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
grid.setTexCoords(tc);
|
|
|
|
grid.reset();
|
|
|
|
grid.updateVBO();
|
|
|
|
}
|
2023-07-14 03:21:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
TileRepeatData* TileData::setRepeatOn(float texscalex, float texscaley, float offx, float offy)
|
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
flags |= TILEFLAG_REPEAT;
|
2023-07-14 03:21:16 +00:00
|
|
|
if(!rep)
|
|
|
|
rep = new TileRepeatData;
|
|
|
|
rep->texscaleX = texscalex;
|
|
|
|
rep->texscaleY = texscaley;
|
|
|
|
rep->texOffX = offx;
|
|
|
|
rep->texOffY = offy;
|
2023-08-09 00:41:04 +00:00
|
|
|
rep->refresh(*this);
|
|
|
|
|
2023-08-24 22:40:14 +00:00
|
|
|
// link eff->grid to rep->grid. create own instance if necessary.
|
|
|
|
/*if(eff)
|
|
|
|
{
|
|
|
|
const unsigned char gridtype = eff->grid ? eff->grid->gridType : GRID_UNDEFINED;
|
|
|
|
if(flags & TILEFLAG_OWN_EFFDATA)
|
|
|
|
{
|
|
|
|
assert(!eff->shared);
|
|
|
|
eff->deleteGrid();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
eff = new TileEffectData(*eff);
|
|
|
|
flags |= TILEFLAG_OWN_EFFDATA;
|
|
|
|
}
|
|
|
|
assert(!eff->ownGrid);
|
|
|
|
eff->grid = &rep->grid;
|
|
|
|
eff->grid->gridType = gridtype;
|
|
|
|
}*/
|
2023-08-09 00:41:04 +00:00
|
|
|
|
2023-07-14 03:21:16 +00:00
|
|
|
return rep;
|
|
|
|
}
|
|
|
|
|
|
|
|
void TileData::setRepeatOff()
|
|
|
|
{
|
|
|
|
flags &= ~TILEFLAG_REPEAT;
|
2023-08-09 00:41:04 +00:00
|
|
|
// don't delete this->rep; if we're in editor mode we don't want to lose the repeat data just yet
|
|
|
|
// also, a TileEffectData may point to rep->grid
|
2023-07-14 03:21:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void TileData::refreshRepeat()
|
|
|
|
{
|
2024-05-07 23:24:01 +00:00
|
|
|
assert(!(flags & TILEFLAG_REPEAT) || rep);
|
2023-08-09 00:41:04 +00:00
|
|
|
if(rep)
|
|
|
|
{
|
|
|
|
rep->refresh(*this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TileData::hasStandardTexcoords() const
|
|
|
|
{
|
|
|
|
// repeat applies per-tile texcoords, so if that's set it's non-standard
|
|
|
|
return !rep && et->tc.isStandard();
|
|
|
|
}
|
|
|
|
|
|
|
|
const TexCoordBox& TileData::getTexcoords() const
|
|
|
|
{
|
2024-05-07 23:24:01 +00:00
|
|
|
assert(!(flags & TILEFLAG_REPEAT) || rep);
|
2023-08-09 00:41:04 +00:00
|
|
|
return !(flags & TILEFLAG_REPEAT)
|
|
|
|
? et->tc
|
|
|
|
: rep->grid.getTexCoords();
|
|
|
|
}
|
|
|
|
|
|
|
|
const RenderGrid *TileData::getGrid() const
|
|
|
|
{
|
2023-08-24 22:40:14 +00:00
|
|
|
if(eff && eff->grid)
|
|
|
|
return eff->grid; // this points to rep.grid if eff is present and repeat is on
|
|
|
|
|
2023-08-09 00:41:04 +00:00
|
|
|
if(flags & TILEFLAG_REPEAT)
|
|
|
|
return &rep->getGrid();
|
|
|
|
|
|
|
|
return et->grid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool TileEffectConfig::needsOwnInstanceForTile(const TileData& t) const
|
|
|
|
{
|
|
|
|
const bool rep = !!(t.flags & TILEFLAG_REPEAT);
|
|
|
|
|
|
|
|
switch(type)
|
2023-07-14 03:21:16 +00:00
|
|
|
{
|
2023-08-09 00:41:04 +00:00
|
|
|
case EFX_NONE:
|
|
|
|
case EFX_ALPHA:
|
|
|
|
return false;
|
|
|
|
|
|
|
|
case EFX_WAVY:
|
|
|
|
return true;
|
|
|
|
|
|
|
|
case EFX_SEGS:
|
|
|
|
return rep || !t.hasStandardTexcoords();
|
2023-07-14 03:21:16 +00:00
|
|
|
}
|
2023-08-09 00:41:04 +00:00
|
|
|
|
|
|
|
assert(false);
|
|
|
|
return true; // uhhhh
|
2023-07-14 03:21:16 +00:00
|
|
|
}
|