mirror of
https://github.com/AquariaOSE/Aquaria.git
synced 2025-07-03 06:24:32 +00:00
Rewrite SceneEditor level generation and skinning, use light pink to designate areas that should not be skinned
This also fixes a long-standing off-by-one with the generated ObsRows: Before this commit, the game would (upon F11) eat every black pixel from the map template that was directly left of a white pixel, eg. #### ## # ## ... would become ### # # ... GENERATED MAPS ARE NOW DIFFERENT! With this bug gone, dumping obs (F8) and loading that back in as a map template (F11) should now be fully round-trip compatible and not lose pixels anymore. Extra feature: (R>=200, G in [128..199], B>=200) in the map template now designate zones that should not be obstructed but also not generate border rocks when skinned (F12). Makes editing energy temple styled maps much easier since we don't have to manually erase tiles on layer "5" anymore, all the time.
This commit is contained in:
parent
5c3c0037c9
commit
d7ff053efd
7 changed files with 154 additions and 154 deletions
|
@ -120,10 +120,6 @@ Entity *Game::getNearestEntity(const Vector &pos, float radius, Entity *ignore,
|
||||||
return closest;
|
return closest;
|
||||||
}
|
}
|
||||||
|
|
||||||
ObsRow::ObsRow(int tx, int ty, int len) : tx(tx), ty(ty), len(len)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
int Game::getNumberOfEntitiesNamed(const std::string &name)
|
int Game::getNumberOfEntitiesNamed(const std::string &name)
|
||||||
{
|
{
|
||||||
int c = 0;
|
int c = 0;
|
||||||
|
@ -388,7 +384,7 @@ Element* Game::createElement(size_t idx, Vector position, size_t bgLayer, Render
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::addObsRow(int tx, int ty, int len)
|
void Game::addObsRow(unsigned tx, unsigned ty, unsigned len)
|
||||||
{
|
{
|
||||||
ObsRow obsRow(tx, ty, len);
|
ObsRow obsRow(tx, ty, len);
|
||||||
obsRows.push_back(obsRow);
|
obsRows.push_back(obsRow);
|
||||||
|
@ -610,7 +606,7 @@ void Game::reconstructGrid(bool force)
|
||||||
for (size_t i = 0; i < obsRows.size(); i++)
|
for (size_t i = 0; i < obsRows.size(); i++)
|
||||||
{
|
{
|
||||||
o = &obsRows[i];
|
o = &obsRows[i];
|
||||||
for (int tx = 0; tx < o->len; tx++)
|
for (unsigned tx = 0; tx < o->len; tx++)
|
||||||
{
|
{
|
||||||
setGrid(TileVector(o->tx + tx, o->ty), OT_BLACK);
|
setGrid(TileVector(o->tx + tx, o->ty), OT_BLACK);
|
||||||
}
|
}
|
||||||
|
|
|
@ -101,8 +101,9 @@ public:
|
||||||
class ObsRow
|
class ObsRow
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ObsRow(int tx, int ty, int len);
|
inline ObsRow(unsigned tx, unsigned ty, unsigned len)
|
||||||
int tx, ty, len;
|
: tx(tx), ty(ty), len(len) {}
|
||||||
|
const unsigned tx, ty, len;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum FlagCheckType
|
enum FlagCheckType
|
||||||
|
@ -254,7 +255,7 @@ public:
|
||||||
int saveWaterLevel;
|
int saveWaterLevel;
|
||||||
void warpCameraTo(RenderObject *r);
|
void warpCameraTo(RenderObject *r);
|
||||||
|
|
||||||
void addObsRow(int tx, int ty, int len);
|
void addObsRow(unsigned tx, unsigned ty, unsigned len);
|
||||||
void clearObsRows();
|
void clearObsRows();
|
||||||
Entity *getEntityAtCursor();
|
Entity *getEntityAtCursor();
|
||||||
Vector cameraMin, cameraMax;
|
Vector cameraMin, cameraMax;
|
||||||
|
|
|
@ -1483,18 +1483,6 @@ void SceneEditor::down()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Row
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
Row()
|
|
||||||
{
|
|
||||||
x1=x2=y=0;
|
|
||||||
rows=1;
|
|
||||||
}
|
|
||||||
int x1, x2, y;
|
|
||||||
int rows;
|
|
||||||
};
|
|
||||||
|
|
||||||
void SceneEditor::regenLevel()
|
void SceneEditor::regenLevel()
|
||||||
{
|
{
|
||||||
generateLevel();
|
generateLevel();
|
||||||
|
@ -1551,15 +1539,27 @@ void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
float dist=0;
|
float dist=0;
|
||||||
wallNormal = dsq->game->getWallNormal(t.worldVector(), 5, &dist, OT_MASK_BLACK);
|
wallNormal = dsq->game->getWallNormal(t.worldVector(), 5, &dist, OT_MASK_BLACK);
|
||||||
offset = wallNormal*(-TILE_SIZE*0.6f);
|
offset = wallNormal*(-TILE_SIZE*0.6f);
|
||||||
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), wallNormal, rot);
|
MathFunctions::calculateAngleBetweenVectorsInDegrees(Vector(0,0,0), wallNormal, rot);
|
||||||
rot = 180-(360-rot);
|
rot = 180-(360-rot);
|
||||||
addTile = true;
|
addTile = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(addTile)
|
||||||
|
{
|
||||||
|
const unsigned char u = tileProps.at(x, y-1, TPR_DEFAULT);
|
||||||
|
const unsigned char d = tileProps.at(x, y+1, TPR_DEFAULT);
|
||||||
|
const unsigned char l = tileProps.at(x-1, y, TPR_DEFAULT);
|
||||||
|
const unsigned char r = tileProps.at(x+1, y, TPR_DEFAULT);
|
||||||
|
if(u == TPR_DONT_SKIN
|
||||||
|
|| d == TPR_DONT_SKIN
|
||||||
|
|| l == TPR_DONT_SKIN
|
||||||
|
|| r == TPR_DONT_SKIN)
|
||||||
|
{
|
||||||
|
addTile = false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (addTile)
|
if (addTile)
|
||||||
|
@ -1637,160 +1637,73 @@ void SceneEditor::skinLevel(int minX, int minY, int maxX, int maxY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SceneEditor::TileProperty SceneEditor::GetColorProperty(unsigned char r, unsigned char g, unsigned char b)
|
||||||
|
{
|
||||||
|
if(r >= 200 && g > 127 && g < 200 && b >= 200)
|
||||||
|
return TPR_DONT_SKIN;
|
||||||
|
|
||||||
|
return TPR_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
void SceneEditor::generateLevel()
|
void SceneEditor::generateLevel()
|
||||||
{
|
{
|
||||||
|
tileProps.clear();
|
||||||
std::string file=getMapTemplateFilename();
|
std::string file=getMapTemplateFilename();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
size_t maxX=0, maxY=0;
|
size_t maxX=0, maxY=0;
|
||||||
const int YELLOW=0, RED=1, GREEN=2, BLUE=3, PURPLE=4, ORANGE=5, BROWN=6, MAX=7;
|
|
||||||
int firstColorX[MAX], firstColorY[MAX];
|
|
||||||
int lastColorX[MAX], lastColorY[MAX];
|
|
||||||
Vector colorVects[MAX];
|
|
||||||
colorVects[YELLOW] = Vector(1,1,0);
|
|
||||||
colorVects[RED] = Vector(1,0,0);
|
|
||||||
colorVects[GREEN] = Vector(0,1,0);
|
|
||||||
colorVects[BLUE] = Vector(0,0,1);
|
|
||||||
colorVects[PURPLE] = Vector(1,0,1);
|
|
||||||
colorVects[ORANGE] = Vector(1,0.5,0);
|
|
||||||
colorVects[BROWN] = Vector(0.5,0.25,0);
|
|
||||||
for (int i = 0; i < MAX; i++)
|
|
||||||
{
|
|
||||||
firstColorX[i] = firstColorY[i] = -1;
|
|
||||||
lastColorX[i] = lastColorY[i] = -1;
|
|
||||||
}
|
|
||||||
const ImageData img = imageLoadGeneric(file.c_str(), true);
|
const ImageData img = imageLoadGeneric(file.c_str(), true);
|
||||||
if (img.pixels)
|
if (img.pixels)
|
||||||
{
|
{
|
||||||
|
dsq->game->clearObsRows();
|
||||||
|
|
||||||
assert(img.channels == 4);
|
assert(img.channels == 4);
|
||||||
std::vector<Row> rows;
|
tileProps.init(img.w, img.h);
|
||||||
std::vector<Vector> positions;
|
|
||||||
const int maxRowCount = 9999;
|
|
||||||
int rowCount = 0;
|
|
||||||
int scale = TILE_SIZE;
|
|
||||||
int c = 0;
|
int c = 0;
|
||||||
|
|
||||||
for (size_t y = 0; y < img.h; y++)
|
for (size_t y = 0; y < img.h; y++)
|
||||||
{
|
{
|
||||||
Vector lastElement;
|
bool isobs = false; // start assuming that there is no obstruction
|
||||||
lastElement = Vector(0,0,0);
|
size_t xobs = 0;
|
||||||
bool hasLastElement = false;
|
for(size_t x = 0; x < img.w; ++x)
|
||||||
Vector *firstRowElement = 0;
|
|
||||||
Row row;
|
|
||||||
rowCount = 0;
|
|
||||||
positions.clear();
|
|
||||||
for (size_t x = 0; x < img.w; x++)
|
|
||||||
{
|
{
|
||||||
Vector *e = 0;
|
tileProps(x, y) = GetColorProperty(img.pixels[c], img.pixels[c+1], img.pixels[c+2]);
|
||||||
if
|
|
||||||
(
|
|
||||||
(
|
|
||||||
img.pixels[c] < 48 &&
|
|
||||||
img.pixels[c+1] < 48 &&
|
|
||||||
img.pixels[c+2] < 48
|
|
||||||
)
|
|
||||||
||
|
|
||||||
(
|
|
||||||
img.pixels[c] == 128
|
|
||||||
&&
|
|
||||||
img.pixels[c+1] == 255
|
|
||||||
&&
|
|
||||||
img.pixels[c+2] == 128
|
|
||||||
)
|
|
||||||
)
|
|
||||||
{
|
|
||||||
if (x > maxX)
|
|
||||||
maxX = x;
|
|
||||||
if (y > maxY)
|
|
||||||
maxY = y;
|
|
||||||
positions.push_back(Vector(x*scale+(scale/2.0f),y*scale+(scale/2.0f)));
|
|
||||||
e = &positions[positions.size()-1];
|
|
||||||
}
|
|
||||||
if (img.pixels[c] < 32 &&
|
|
||||||
img.pixels[c+1] > 200 &&
|
|
||||||
img.pixels[c+2] > 200)
|
|
||||||
{
|
|
||||||
dsq->game->saveWaterLevel = dsq->game->waterLevel.x = y*TILE_SIZE;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < MAX; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
bool p1, p2, p3;
|
// anything that is close to black is obstruction
|
||||||
p1=p2=p3=false;
|
bool obs = img.pixels[c] < 48 &&
|
||||||
int diff;
|
img.pixels[c+1] < 48 &&
|
||||||
diff = fabsf((colorVects[i].x*255) - img.pixels[c]);
|
img.pixels[c+2] < 48;
|
||||||
p1 = (diff < 5);
|
|
||||||
diff = fabsf((colorVects[i].y*255) - img.pixels[c+1]);
|
|
||||||
p2 = (diff < 5);
|
|
||||||
diff = fabsf((colorVects[i].z*255) - img.pixels[c+2]);
|
|
||||||
p3 = (diff < 5);
|
|
||||||
|
|
||||||
if (p1 && p2 && p3)
|
if(obs != isobs)
|
||||||
|
{
|
||||||
|
isobs = obs;
|
||||||
|
if(obs)
|
||||||
{
|
{
|
||||||
lastColorX[i] = x;
|
xobs = x; // just changed from not-obs to obs, record start
|
||||||
lastColorY[i] = y;
|
if (x > maxX)
|
||||||
if (firstColorX[i] == -1)
|
maxX = x;
|
||||||
{
|
if (y > maxY)
|
||||||
firstColorX[i] = x;
|
maxY = y;
|
||||||
firstColorY[i] = y;
|
}
|
||||||
}
|
else if(x != xobs) // safeguard against left side starting with obs
|
||||||
|
{
|
||||||
|
assert(x > xobs);
|
||||||
|
dsq->game->addObsRow(xobs, y, x - xobs); // just changed from obs to not-obs, emit row
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
c += img.channels;
|
c += img.channels;
|
||||||
if ((e==0 && firstRowElement) || (firstRowElement && rowCount >= maxRowCount && hasLastElement)
|
|
||||||
|| (firstRowElement && x == img.w-1))
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (hasLastElement)
|
|
||||||
row.x2 = lastElement.x;
|
|
||||||
|
|
||||||
|
|
||||||
hasLastElement = false;
|
|
||||||
firstRowElement = 0;
|
|
||||||
|
|
||||||
bool add = true;
|
|
||||||
|
|
||||||
if (add)
|
|
||||||
rows.push_back(row);
|
|
||||||
}
|
|
||||||
if (!firstRowElement && e)
|
|
||||||
{
|
|
||||||
row.x1 = e->x;
|
|
||||||
row.y = e->y;
|
|
||||||
firstRowElement = e;
|
|
||||||
rowCount = 1;
|
|
||||||
}
|
|
||||||
if (e)
|
|
||||||
{
|
|
||||||
lastElement = *e;
|
|
||||||
hasLastElement = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
hasLastElement = false;
|
|
||||||
rowCount ++ ;
|
|
||||||
}
|
}
|
||||||
}
|
if(isobs) // right side ends with obs, add final row
|
||||||
|
|
||||||
dsq->game->clearObsRows();
|
|
||||||
size_t i = 0;
|
|
||||||
for (i = 0; i < rows.size(); i++)
|
|
||||||
{
|
|
||||||
int w = rows[i].x2 - rows[i].x1;
|
|
||||||
|
|
||||||
int useY = rows[i].y;
|
|
||||||
if (rows[i].rows > 1)
|
|
||||||
{
|
{
|
||||||
useY += (rows[i].rows-1)*TILE_SIZE/2;
|
dsq->game->addObsRow(xobs, y, img.w - xobs);
|
||||||
|
if (img.w > maxX)
|
||||||
|
maxX = img.w;
|
||||||
|
if (y > maxY)
|
||||||
|
maxY = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
dsq->game->addObsRow(rows[i].x1/TILE_SIZE, rows[i].y/TILE_SIZE, w/TILE_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
dsq->game->reconstructGrid(true);
|
dsq->game->reconstructGrid(true);
|
||||||
|
@ -2663,12 +2576,14 @@ bool SceneEditor::isOn()
|
||||||
|
|
||||||
void SceneEditor::updateText()
|
void SceneEditor::updateText()
|
||||||
{
|
{
|
||||||
|
const Vector cursor = dsq->getGameCursorPosition();
|
||||||
|
TileVector tv(cursor);
|
||||||
|
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << dsq->game->sceneName << " bgL[" << bgLayer << "] (" <<
|
os << dsq->game->sceneName << " bgL[" << bgLayer << "] (" <<
|
||||||
(int)dsq->cameraPos.x << "," << (int)dsq->cameraPos.y << ") ("
|
(int)dsq->cameraPos.x << "," << (int)dsq->cameraPos.y << ") ("
|
||||||
|
<< (int)cursor.x << "," << (int)cursor.y << ") T("
|
||||||
|
<< tv.x << "," << tv.y << ") ";
|
||||||
<< (int)dsq->getGameCursorPosition().x << "," << (int)dsq->getGameCursorPosition().y << ")" << " ";
|
|
||||||
switch(editType)
|
switch(editType)
|
||||||
{
|
{
|
||||||
case ET_ELEMENTS:
|
case ET_ELEMENTS:
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#include "DebugFont.h"
|
#include "DebugFont.h"
|
||||||
#include "ActionMapper.h"
|
#include "ActionMapper.h"
|
||||||
#include "Quad.h"
|
#include "Quad.h"
|
||||||
|
#include "DataStructures.h"
|
||||||
|
|
||||||
class Element;
|
class Element;
|
||||||
class Entity;
|
class Entity;
|
||||||
|
@ -178,6 +179,14 @@ protected:
|
||||||
void changeShape();
|
void changeShape();
|
||||||
int skinMinX, skinMinY, skinMaxX, skinMaxY;
|
int skinMinX, skinMinY, skinMaxX, skinMaxY;
|
||||||
|
|
||||||
|
enum TileProperty
|
||||||
|
{
|
||||||
|
TPR_DEFAULT,
|
||||||
|
TPR_DONT_SKIN
|
||||||
|
};
|
||||||
|
Array2d<unsigned char> tileProps; // only filled on generateLevel()
|
||||||
|
static TileProperty GetColorProperty(unsigned char r, unsigned char g, unsigned char b);
|
||||||
|
|
||||||
void setGridPattern(int gi);
|
void setGridPattern(int gi);
|
||||||
void setGridPattern0();
|
void setGridPattern0();
|
||||||
void setGridPattern1();
|
void setGridPattern1();
|
||||||
|
|
|
@ -21,6 +21,8 @@ set(BBGE_SRCS
|
||||||
Core.h
|
Core.h
|
||||||
DarkLayer.cpp
|
DarkLayer.cpp
|
||||||
DarkLayer.h
|
DarkLayer.h
|
||||||
|
DataStructures.cpp
|
||||||
|
DataStructures.h
|
||||||
DebugFont.cpp
|
DebugFont.cpp
|
||||||
DebugFont.h
|
DebugFont.h
|
||||||
Emitter.cpp
|
Emitter.cpp
|
||||||
|
|
0
BBGE/DataStructures.cpp
Normal file
0
BBGE/DataStructures.cpp
Normal file
77
BBGE/DataStructures.h
Normal file
77
BBGE/DataStructures.h
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef BBGE_DATASTRUCTURES_H
|
||||||
|
#define BBGE_DATASTRUCTURES_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
class Array2d
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
size_t _w, _h;
|
||||||
|
std::vector<T> _v;
|
||||||
|
|
||||||
|
public:
|
||||||
|
Array2d() : _w(0), _h(0) {}
|
||||||
|
Array2d(size_t w, size_t h) : _w(w), _h(h), _v(w*h) {}
|
||||||
|
|
||||||
|
size_t width() const {return _w;}
|
||||||
|
size_t height() const {return _h;}
|
||||||
|
void init(size_t w, size_t h)
|
||||||
|
{
|
||||||
|
_w = w;
|
||||||
|
_h = h;
|
||||||
|
_v.resize(w*h);
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear()
|
||||||
|
{
|
||||||
|
_w = _h = 0;
|
||||||
|
_v.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void fill(const T& v)
|
||||||
|
{
|
||||||
|
std::fill(_v.begin(), _v.end(), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
void copy2d(size_t dstx, size_t dsty, const Array2d<T>& src, size_t srcx, size_t srcy, size_t w, size_t h)
|
||||||
|
{
|
||||||
|
assert(dstx + w <= width());
|
||||||
|
assert(dsty + h <= height());
|
||||||
|
assert(srcx + w <= src.width());
|
||||||
|
assert(srcy + h <= src.height());
|
||||||
|
|
||||||
|
for(size_t y = 0; y < h; ++y)
|
||||||
|
{
|
||||||
|
T *dstrow = row(dsty + y);
|
||||||
|
const T *srcrow = src.row(srcy + y);
|
||||||
|
std::copy(srcrow + srcx, srcrow + srcx + w, dstrow + dstx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const T& at(size_t x, size_t y, const T& def) const
|
||||||
|
{
|
||||||
|
return x < _w && y < _h ? _v[y * _w + x] : def;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline T& operator()(size_t x, size_t y)
|
||||||
|
{
|
||||||
|
return _v[y * _w + x];
|
||||||
|
}
|
||||||
|
inline const T& operator()(size_t x, size_t y) const
|
||||||
|
{
|
||||||
|
return _v[y * _w + x];
|
||||||
|
}
|
||||||
|
|
||||||
|
const T *data() const { return _v.empty() ? NULL : &_v[0]; }
|
||||||
|
T *data() { return _v.empty() ? NULL : &_v[0]; }
|
||||||
|
|
||||||
|
const T *row(size_t y) const { return &_v[y * _w]; }
|
||||||
|
T *row(size_t y) { return &_v[y * _w]; }
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Add a link
Reference in a new issue