1
0
Fork 0
mirror of https://github.com/AquariaOSE/Aquaria.git synced 2025-02-27 08:34:02 +00:00

Implement element tags

A map element ("tile") can now be tagged with an int.
In the editor:
Ctrl+Numpad to quickly set tag 0-9
Ctrl+T to input tag number for selection

This tag can be used by scripts for more advanced tile
manipulation compared to the old node_setElementsInLayerActive().
This function now takes a 4th parameter, the tag.
If 0 or omitted, the behavior is unchanged.
Otherwise, only affect tiles with that tag.

Add Lua func refreshElementsWithTagCallback(tag, callback):
Call callback(templateIdx:int, texture:str, active:bool, layer:int, tag:int),
and the tile is set to active when the return value is true.

refreshElementsOnLayerCallback(tag, callback) is also changed to use
the same callback.

Also fix UB (dangling pointer)in Element::render()
that never even caused a single problem.
This commit is contained in:
fgenesis 2023-05-05 04:15:41 +02:00
parent c73aa23906
commit 33e8c0643d
5 changed files with 156 additions and 31 deletions

View file

@ -42,6 +42,7 @@ Element::Element() : Quad()
elementFlag = EF_NONE;
elementActive = true;
bgLayer = 0;
tag = 0;
templateIdx = -1;
eff = NULL;
}
@ -280,37 +281,66 @@ void Element::setElementEffectByIndex(int eidx)
}
}
// shamelessly ripped from paint.net default palette
static const Vector s_tagColors[] =
{
/* 0 */ Vector(0.5f, 0.5f, 0.5f),
/* 1 */ Vector(1,0,0),
/* 2 */ Vector(1, 0.415686f, 0),
/* 3 */ Vector(1,0.847059f, 0),
/* 4 */ Vector(0.298039f,1,0),
/* 5 */ Vector(0,1,1),
/* 6 */ Vector(0,0.580392,1),
/* 7 */ Vector(0,0.149020f,1),
/* 8 */ Vector(0.282353f,0,1),
/* 9 */ Vector(0.698039f,0,1),
/* 10 */ Vector(1,0,1), // anything outside of the pretty range
};
static inline const Vector& getTagColor(int tag)
{
const unsigned idx = std::min<unsigned>(unsigned(tag), Countof(s_tagColors)-1);
return s_tagColors[idx];
}
void Element::render(const RenderState& rs) const
{
if (!elementActive) return;
const RenderState *rsp = &rs;
if (dsq->game->isSceneEditorActive() && this->bgLayer == dsq->game->sceneEditor.bgLayer
&& dsq->game->sceneEditor.editType == ET_ELEMENTS)
{
RenderState rs2(rs);
rsp = &rs2;
rs2.forceRenderBorder = true;
rs2.forceRenderCenter = true;
rs2.renderBorderColor = Vector(0.5f, 0.5f, 0.5f);
Vector tagColor = getTagColor(tag);
bool hl = false;
if (!dsq->game->sceneEditor.selectedElements.empty())
{
for (size_t i = 0; i < dsq->game->sceneEditor.selectedElements.size(); i++)
{
if (this == dsq->game->sceneEditor.selectedElements[i])
rs2.renderBorderColor = Vector(1,1,1);
{
hl = true;
break;
}
}
}
else
{
if (dsq->game->sceneEditor.editingElement == this)
rs2.renderBorderColor = Vector(1,1,1);
hl = dsq->game->sceneEditor.editingElement == this;
}
}
if(hl)
tagColor += Vector(0.5f, 0.5f, 0.5f);
Quad::render(*rsp);
RenderState rs2(rs);
rs2.forceRenderBorder = true;
rs2.forceRenderCenter = true;
rs2.renderBorderColor = tagColor;
Quad::render(rs2);
}
else // render normally
Quad::render(rs);
}
void Element::fillGrid()
@ -336,3 +366,7 @@ void Element::fillGrid()
}
}
void Element::setTag(int tag)
{
this->tag = tag;
}

View file

@ -65,15 +65,17 @@ public:
void update(float dt);
size_t templateIdx;
int bgLayer;
int tag;
Element *bgLayerNext;
void render(const RenderState& rs) const OVERRIDE;
ElementFlag elementFlag;
void fillGrid();
bool isElementActive() { return elementActive; }
bool isElementActive() const { return elementActive; }
int getElementEffectIndex();
void setElementEffectByIndex(int e);
void setElementActive(bool v) { elementActive = v; }
void doInteraction(Entity *ent, float mult, float touchWidth);
void setTag(int tag);
protected:
void ensureEffectData();
void freeEffectData();

View file

@ -1889,7 +1889,7 @@ bool Game::loadSceneXML(std::string scene)
}
if (simpleElements->Attribute("repeatScale"))
{
SimpleIStringStream is2(simpleElements->Attribute("repeatScale"));
SimpleIStringStream is2(simpleElements->Attribute("repeatScale"), SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i)
{
Element *e = loadedElements[i];
@ -1904,6 +1904,18 @@ bool Game::loadSceneXML(std::string scene)
}
}
}
if (simpleElements->Attribute("tag"))
{
SimpleIStringStream is2(simpleElements->Attribute("tag"), SimpleIStringStream::REUSE);
for(size_t i = 0; i < loadedElements.size(); ++i)
{
Element *e = loadedElements[i];
int tag = 0;
if(!(is2 >> tag))
break;
e->setTag(tag);
}
}
simpleElements = simpleElements->NextSiblingElement("SE");
}
@ -2130,6 +2142,8 @@ bool Game::saveScene(std::string scene)
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++)
{
@ -2153,6 +2167,9 @@ bool Game::saveScene(std::string scene)
SE_rs << e->repeatToFillScale.x << " "
<< e->repeatToFillScale.y << " ";
}
simpleElements_tag[e->bgLayer] << e->tag << " ";
tagBitsUsed[e->bgLayer] |= e->tag;
}
if (dsq->game->entitySaveData.size() > 0)
@ -2187,9 +2204,16 @@ bool Game::saveScene(std::string scene)
XMLElement *simpleElementsXML = saveFile.NewElement("SE");
simpleElementsXML->SetAttribute("k", s.c_str());
simpleElementsXML->SetAttribute("l", i);
std::string repeatScaleStr = simpleElements_repeatScale[i].str();
if(!repeatScaleStr.empty())
simpleElementsXML->SetAttribute("repeatScale", repeatScaleStr.c_str());
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);
}
}

View file

@ -787,11 +787,23 @@ void SceneEditor::reversePath()
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);
}
else
{
if (selectedElements.size())
for (size_t i = 0; i < selectedElements.size(); ++i)
selectedElements[i]->setElementEffectByIndex(gi);
else if (editingElement)
editingElement->setElementEffectByIndex(gi);
}
}
void SceneEditor::setGridPattern0()
@ -1755,6 +1767,23 @@ void SceneEditor::flipElementHorz()
if (editType != ET_ELEMENTS)
return;
if(core->getCtrlState())
{
if(!selectedElements.empty() || editingElement)
{
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);
}
return;
}
if (editingElement)
editingElement->flipHorizontal();
else
@ -2607,10 +2636,11 @@ void SceneEditor::updateText()
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;
os << " efx: " << (e->getElementEffectIndex() + 1); // +1 so that it resembles the layout on numpad
}
}
break;

View file

@ -7672,9 +7672,10 @@ luaFunc(node_setElementsInLayerActive)
{
int 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 (e && p->isCoordinateInside(e->position))
if (e && (!tag || e->tag == tag) && p->isCoordinateInside(e->position))
{
e->setElementActive(v);
}
@ -7683,22 +7684,55 @@ luaFunc(node_setElementsInLayerActive)
luaReturnNil();
}
luaFunc(refreshElementsOnLayerCallback)
static int pushElementData(lua_State *L, const Element *e)
{
int layer = lua_tointeger(L, 1);
for (Element *e = dsq->getFirstElementOnLayer(layer); e; e = e->bgLayerNext)
{
bool on = e->isElementActive();
lua_pushvalue(L, 2); // the callback
lua_pushinteger(L, e->templateIdx);
lua_pushstring(L, e->texture->name.c_str());
lua_pushboolean(L, e->isElementActive());
lua_call(L, 3, 1);
lua_pushinteger(L, e->bgLayer);
lua_pushinteger(L, e->tag);
return 5;
}
// (layer, func)
luaFunc(refreshElementsOnLayerCallback)
{
const int layer = lua_tointeger(L, 1);
size_t done = 0;
for (Element *e = dsq->getFirstElementOnLayer(layer); e; e = e->bgLayerNext)
{
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;
}
luaReturnNil();
luaReturnInt(done);
}
// (tag, func)
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)
{
Element *e = dsq->getElement(i);
if(e->tag == tag)
{
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;
}
}
luaReturnInt(done);
}
@ -10747,6 +10781,7 @@ static const struct {
luaRegister(node_setElementsInLayerActive),
luaRegister(refreshElementsOnLayerCallback),
luaRegister(refreshElementsWithTagCallback),
luaRegister(entity_setHealth),