You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

682 lines
22 KiB

#include "olcUTIL_Geometry2D.h"
#include "TSXParser.h"
#include "olcPGEX_TransformedView.h"
#include "olcUTIL_Camera2D.h"
#include "olcPGEX_QuickGUI.h"
#include <variant>
#include <deque>
using namespace olc;
using namespace olc::utils;
using namespace QuickGUI;
const std::string TILESET_DIR="./Tiles/";
class TiledCollisionEditor : public olc::PixelGameEngine
{
Tileset currentTileset;
Renderable mapImage;
std::string activeTileset;
Quadrilateral*editingQuad=nullptr;
Quadrilateral originalQuad;
std::string selectedObj="";
int editingPoint=4; //0-3 for the index we are editing within editingQuad.
bool dragging=false;
bool dragTranslate=false;
vf2d upperLeftDragOffset{};
Quadrilateral*highlightedQuad=nullptr;
std::string nameEditObj="";
size_t lastSelectedItem=0;
bool dragNewObj=false;
vi2d upperLeftObjTile{};
vi2d lowerRightObjTile{};
bool selectingFile=false;
Renderable circle;
Renderable createNewButtonImg;
Renderable undoButtonImg;
Renderable redoButtonImg;
Renderable editButtonImg;
bool loadedFirstFile=false;
TransformedView view;
std::vector<std::string>tilesetList;
std::unordered_map<std::string,TilesetObject>previousObjState;
std::deque<std::unordered_map<std::string,TilesetObject>>redoList;
std::deque<std::unordered_map<std::string,TilesetObject>>undoList;
public:
TiledCollisionEditor()
{
sAppName = "TiledCollisionEditor";
}
Manager gui;
Manager selectionGui;
ImageCheckBox*createNewButton=nullptr;
ImageCheckBox*editButton=nullptr;
ImageButton*undoButton=nullptr;
ImageButton*redoButton=nullptr;
TextBox*nameBox=nullptr;
Button*openButton=nullptr;
ListBox*tilesetsList=nullptr;
TSXParser parsedMap{""};
Button*closeButton=nullptr;
public:
bool OnUserCreate() override
{
SetFontSprite("font3.png");
circle.Create(5,5);
SetDrawTarget(circle.Sprite());
Clear(BLANK);
FillCircle({2,2},2);
SetDrawTarget(nullptr);
circle.Decal()->Update();
createNewButtonImg.Load("newCollisionButton.png");
editButtonImg.Load("EditButton.png");
undoButtonImg.Load("undoButton.png");
redoButtonImg.Load("redoButton.png");
createNewButton=new ImageCheckBox{gui,createNewButtonImg,false,vf2d{4.f,ScreenHeight()-36.f},{32.f,32.f},{4,1},{32,32}};
createNewButton->hotkey=Q;
editButton=new ImageCheckBox{gui,editButtonImg,true,vf2d{40.f,ScreenHeight()-36.f},{32.f,32.f},{4,4},{32,32}};
editButton->hotkey=E;
undoButton=new ImageButton{gui,undoButtonImg,vf2d{ScreenWidth()-72.f,ScreenHeight()-36.f},{32.f,32.f},{4,4},{32,32}};
redoButton=new ImageButton{gui,redoButtonImg,vf2d{ScreenWidth()-36.f,ScreenHeight()-36.f},{32.f,32.f},{4,4},{32,32}};
nameBox=new TextBox{gui,"",vf2d{76.f,ScreenHeight()-36.f+6.f},{128,20.f},{1,1}};
nameBox->bHasBackground=true;
openButton=new Button{gui,"Open",{ScreenWidth()-32.f,0.f},{32,12.f},{0.5f,0.5f}};
closeButton=new Button{selectionGui,"Close",{ScreenWidth()-32.f,0.f},{32,12.f},{0.5f,0.5f}};
std::filesystem::path dir{TILESET_DIR};
for(auto const&dir:std::filesystem::directory_iterator(dir)){
if(dir.path().string().ends_with(".tsx")){
tilesetList.push_back(dir.path().string());
}
}
tilesetsList=new ListBox{selectionGui,tilesetList,{ScreenWidth()/2-240.f,ScreenHeight()/2-60.f},{480.f,120.f},16.f};
previousObjState=currentTileset.objects;
return true;
}
void SaveFile(bool ignoreUndoEntry=false){
if(!ignoreUndoEntry){
undoList.push_back(previousObjState);
previousObjState=currentTileset.objects;
redoList.clear();
if(undoList.size()>20)undoList.pop_front();
}
std::stringstream file;
if(file.good()){
const std::vector<XMLTag>originalData=parsedMap.originalData;
const std::vector<NonObject>nonObjects=parsedMap.nonObjects;
auto xmlTag=std::find_if(originalData.begin(),originalData.end(),[](const XMLTag tag){return tag.tag=="?xml";});
if(xmlTag!=originalData.end())file<<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"<<std::endl;
const Tileset&activeSet=currentTileset;
file<<std::format("<tileset version=\"1.10\" tiledversion=\"1.10.2\" name=\"{}\" tilewidth=\"{}\" tileheight=\"{}\" tilecount=\"{}\" columns=\"{}\">",
activeSet.name,activeSet.tilewidth,activeSet.tileheight,activeSet.tilecount,activeSet.columns)<<std::endl;
auto transformationsTag=std::find_if(originalData.begin(),originalData.end(),[](const XMLTag tag){return tag.tag=="transformations";});
if(transformationsTag!=originalData.end())file<<(*transformationsTag).OutputTag("/>")<<std::endl;
file<<std::format("<image source=\"{}\" width=\"{}\" height=\"{}\"/>",
activeSet.filename,activeSet.imagewidth,activeSet.imageheight)<<std::endl;
std::vector<std::pair<int,std::string>>tiles;
for(auto&[name,obj]:activeSet.objects){
const std::vector<std::pair<int,std::string>>objTiles{obj.OutputTag(activeSet)};
tiles.insert(tiles.end(),objTiles.begin(),objTiles.end());
}
for(const NonObject&obj:nonObjects){
tiles.push_back(obj.OutputTag());
}
std::sort(tiles.begin(),tiles.end(),[](const std::pair<int,std::string>tileData1,const std::pair<int,std::string>tileData2){
return tileData1.first<tileData2.first;
});
for(auto&[tileID,str]:tiles){
file<<str<<std::endl;
}
file<<"</tileset>"<<std::endl;
}else{
std::cout<<"WARNING! File not available for saving! Failed to save!"<<std::endl;
}
std::ofstream saveFile{activeTileset};
saveFile<<file.str()<<std::endl;
saveFile.close();
std::cout<<"Save Successful!"<<std::endl;
}
void ResetState(){
editingPoint=4;
dragging=false;
editingQuad=nullptr;
dragTranslate=false;
highlightedQuad=nullptr;
selectedObj="";
dragNewObj=false;
}
void Update(){
const Tileset&tileset=currentTileset;
if(selectedObj.length()>0){
if(GetKey(DEL).bReleased){
currentTileset.objects.erase(selectedObj);
selectedObj="";
ResetState();
SaveFile();
return;
}
const TilesetObject&obj=tileset.objects.at(selectedObj);
const bool EditingQuad=(editingPoint<4||dragging)&&editingQuad!=nullptr;
auto GetSnapPoint=[&](){
vf2d worldCoords=view.ScreenToWorld(GetMousePos());
if(GetKey(CTRL).bHeld){
return vf2d{round(worldCoords.x),round(worldCoords.y)};
}
return vf2d{round(worldCoords.x/(tileset.tilewidth/4))*tileset.tilewidth/4,round(worldCoords.y/(tileset.tileheight/4))*tileset.tileheight/4};
};
if(EditingQuad&&!dragging){
vf2d newEditPoint=GetSnapPoint();
newEditPoint.x=std::clamp(newEditPoint.x,float(obj.bounds.left().start.x),float(obj.bounds.right().start.x));
newEditPoint.y=std::clamp(newEditPoint.y,float(obj.bounds.top().start.y),float(obj.bounds.bottom().start.y));
(*editingQuad)[editingPoint]=newEditPoint;
}else
if(EditingQuad&&dragging){
vf2d cursorPos=GetSnapPoint();
vf2d initialPoint=(*editingQuad)[0];
(*editingQuad)[1]=vf2d{cursorPos.x,initialPoint.y};
(*editingQuad)[2]=GetSnapPoint();
(*editingQuad)[3]=vf2d{initialPoint.x,cursorPos.y};
}
if(dragTranslate){
vf2d translateAmt=GetSnapPoint()-originalQuad[0]-upperLeftDragOffset;
for(size_t pointInd=0;vf2d&point:*editingQuad){
(*editingQuad)[pointInd]=originalQuad[pointInd]+translateAmt;
pointInd++;
}
}
if(editButton->bChecked){
if(GetMouse(Mouse::LEFT).bPressed){
if(highlightedQuad!=nullptr){
#pragma region Select a point on a collision quad.
for(size_t pointInd=0;const vf2d&point:*highlightedQuad){
if(geom2d::line<float>(point,view.ScreenToWorld(GetMousePos())).length()<4/view.GetWorldScale().x){
editingPoint=pointInd;
editingQuad=highlightedQuad;
originalQuad=*highlightedQuad;
goto exitCollisionCheck;
}
pointInd++;
}
#pragma endregion
dragTranslate=true;
editingQuad=highlightedQuad;
upperLeftDragOffset=GetSnapPoint()-(*highlightedQuad)[0];
originalQuad=*editingQuad;
goto exitCollisionCheck;
}
exitCollisionCheck:
if(EditingQuad&&!dragging){
vf2d newEditPoint=GetSnapPoint();
newEditPoint.x=std::clamp(newEditPoint.x,float(obj.bounds.left().start.x),float(obj.bounds.right().start.x));
newEditPoint.y=std::clamp(newEditPoint.y,float(obj.bounds.top().start.y),float(obj.bounds.bottom().start.y));
(*editingQuad)[editingPoint]=newEditPoint;
editingPoint++;
}
}
if(GetMouse(Mouse::RIGHT).bPressed||GetKey(ESCAPE).bReleased){
if(EditingQuad||dragTranslate){
ResetState();
}
}
}else{
if(GetMouse(Mouse::LEFT).bPressed){
Quadrilateral newQuad{GetSnapPoint()};
currentTileset.objects[selectedObj].collisionTiles.push_back(newQuad);
dragging=true;
editingQuad=const_cast<Quadrilateral*>(&obj.collisionTiles.back());
originalQuad=*editingQuad;
}
}
if(GetMouse(Mouse::RIGHT).bPressed&&!EditingQuad&&!dragTranslate){
if(highlightedQuad!=nullptr){
std::erase_if(currentTileset.objects[selectedObj].collisionTiles,[&](Quadrilateral&q){return &q==highlightedQuad;});
ResetState();
SaveFile();
}
}
if(GetMouse(Mouse::LEFT).bReleased){
if(EditingQuad&&dragging){
vf2d cursorPos=GetSnapPoint();
vf2d initialPoint=(*editingQuad)[0];
(*editingQuad)[1]=vf2d{cursorPos.x,initialPoint.y};
(*editingQuad)[2]=GetSnapPoint();
(*editingQuad)[3]=vf2d{initialPoint.x,cursorPos.y};
ResetState();
SaveFile();
}else
if(EditingQuad&&!dragging){
vf2d newEditPoint=GetSnapPoint();
newEditPoint.x=std::clamp(newEditPoint.x,float(obj.bounds.left().start.x),float(obj.bounds.right().start.x));
newEditPoint.y=std::clamp(newEditPoint.y,float(obj.bounds.top().start.y),float(obj.bounds.bottom().start.y));
(*editingQuad)[editingPoint]=newEditPoint;
editingPoint=4;
dragging=false;
editingQuad=nullptr;
dragTranslate=false;
highlightedQuad=nullptr;
SaveFile();
}else
if(dragTranslate){
SaveFile();
}
dragTranslate=false;
editingQuad=nullptr;
}
}
}
void NewObjectUpdate(){
const Tileset&tileset=currentTileset;
if(GetMouse(Mouse::LEFT).bReleased){
dragNewObj=false;
vf2d worldCoords=view.ScreenToWorld(GetMousePos());
vi2d newUpperLeftTile=upperLeftObjTile;
vi2d newLowerRightTile=lowerRightObjTile;
if(worldCoords.x<upperLeftObjTile.x){
newLowerRightTile.x=int(floor(worldCoords.x/tileset.tilewidth)*tileset.tilewidth);
std::swap(newUpperLeftTile.x,newLowerRightTile.x);
}
if(worldCoords.y<upperLeftObjTile.y){
newLowerRightTile.y=int(floor(worldCoords.y/tileset.tileheight)*tileset.tileheight);
std::swap(newUpperLeftTile.y,newLowerRightTile.y);
}
if(newLowerRightTile.x==newUpperLeftTile.x)newLowerRightTile.x+=tileset.tilewidth;
if(newLowerRightTile.y==newUpperLeftTile.y)newLowerRightTile.y+=tileset.tileheight;
geom2d::rect<int>newObjRect{newUpperLeftTile,newLowerRightTile-newUpperLeftTile};
const Tileset&tileset=currentTileset;
//Check for intersection with other objects, if found then we deny creating this object this way.
bool intersectionFound=false;
for(auto&[name,obj]:tileset.objects){
geom2d::rect<float>offsetBounds{obj.bounds.pos+vf2d{0.5f,0.5f},obj.bounds.size-vf2d{1.f,1.f}};
if(geom2d::overlaps(offsetBounds,newObjRect)){
intersectionFound=true;
break;
}
}
if(!intersectionFound){
std::string objName=std::format("Object{}",currentTileset.objects.size());
TilesetObject&newObj=currentTileset.objects[objName];
newObj.name=objName;
for(int y=0;y<newObjRect.size.y/tileset.tileheight;y++){
for(int x=0;x<newObjRect.size.x/tileset.tilewidth;x++){
int tileX=newUpperLeftTile.x/tileset.tilewidth+x;
int tileY=newUpperLeftTile.y/tileset.tileheight+y;
int tileID=tileY*tileset.columns+tileX;
newObj.AddTile(tileset,tileID);
}
}
SaveFile();
}
}
}
void OnTextEntryComplete(const std::string&sText)override{
currentTileset.objects[nameEditObj].name=sText;
SaveFile();
}
void EditorUpdate(){
view.HandlePanAndZoom();
const Tileset&tileset=currentTileset;
const float CAMERA_MOVESPD = 150.f;
if(!nameBox->m_bTextEdit){
if(selectedObj.length()>0&&GetKey(R).bReleased){
TextEntryEnable(true, nameBox->sText);
nameBox->m_bTextEdit=true;
nameEditObj=currentTileset.objects[selectedObj].name;
}
if(GetKey(W).bHeld)view.MoveWorldOffset(vf2d{0.f,-CAMERA_MOVESPD}*GetElapsedTime()/view.GetWorldScale());
if(GetKey(S).bHeld)view.MoveWorldOffset(vf2d{0.f,CAMERA_MOVESPD}*GetElapsedTime()/view.GetWorldScale());
if(GetKey(A).bHeld)view.MoveWorldOffset(vf2d{-CAMERA_MOVESPD,0.f}*GetElapsedTime()/view.GetWorldScale());
if(GetKey(D).bHeld)view.MoveWorldOffset(vf2d{CAMERA_MOVESPD,0.f}*GetElapsedTime()/view.GetWorldScale());
if(((undoButton->bPressed||GetKey(CTRL).bHeld&&GetKey(Z).bReleased))&&
undoList.size()>0){
redoList.push_back(currentTileset.objects);
currentTileset.objects.clear();
currentTileset.objects=undoList.back();
undoList.pop_back();
previousObjState=currentTileset.objects;
std::cout<<"Undo List Size:"<<undoList.size()<<std::endl;
SaveFile(true);
ResetState();
gui.Update(this);
return; //Don't process the rest of this function because we clicked and so we skip input.
}
if(((redoButton->bPressed||GetKey(CTRL).bHeld&&GetKey(Y).bReleased||GetKey(CTRL).bHeld&&GetKey(SHIFT).bHeld&&GetKey(Z).bReleased))&&
redoList.size()>0){
undoList.push_back(currentTileset.objects);
currentTileset.objects.clear();
currentTileset.objects=redoList.back();
previousObjState=currentTileset.objects;
redoList.pop_back();
std::cout<<"Redo List Size:"<<redoList.size()<<std::endl;
SaveFile(true);
ResetState();
gui.Update(this);
return;
}
if(openButton->bReleased){
selectingFile=true;
openButton->Reset();
gui.Update(this);
return;
}
}
const bool EditingQuad=(editingPoint<4||dragging)&&editingQuad!=nullptr;
if(editingQuad==nullptr&&!EditingQuad){
selectedObj="";
for(auto&[objName,obj]:tileset.objects){
if(geom2d::contains(obj.bounds,view.ScreenToWorld(GetMousePos()))){
selectedObj=objName;
nameBox->sText=obj.name;
break;
}
}
}
if(createNewButton->bPressed)editButton->bChecked=false;
if(editButton->bPressed)createNewButton->bChecked=false;
undoButton->Enable(undoList.size()>0);
redoButton->Enable(redoList.size()>0);
gui.Update(this);
if((GetMouseY()<ScreenHeight()-36||GetMouseX()>204)&&
(GetMouseX()<ScreenWidth()-72||GetMouseY()<ScreenHeight()-36)&&
(GetMouseX()<ScreenWidth()-32||GetMouseY()>ScreenHeight()+12)&&
!nameBox->m_bTextEdit&&
tileset.columns>0&&
view.ScreenToWorld(GetMousePos()).x<tileset.columns*tileset.tilewidth&&
view.ScreenToWorld(GetMousePos()).y<tileset.tilecount/tileset.columns*tileset.tileheight&&
view.ScreenToWorld(GetMousePos()).x>=0&&view.ScreenToWorld(GetMousePos()).y>=0){
if(selectedObj.length()==0){
if(GetMouse(Mouse::LEFT).bPressed){
vf2d worldCoords=view.ScreenToWorld(GetMousePos());
upperLeftObjTile=vi2d{int(floor(worldCoords.x/tileset.tilewidth)*tileset.tilewidth),int(floor(worldCoords.y/tileset.tileheight)*tileset.tileheight)};
dragNewObj=true;
}
}
if(!dragNewObj){
Update();
}else{
NewObjectUpdate();
}
}
}
void SelectingFileUpdate(){
selectionGui.Update(this);
if(tilesetList.size()>0&&!loadedFirstFile){
loadedFirstFile=true;
tilesetsList->nSelectedItem=0;
goto loadFile;
}
if(tilesetsList->bSelectionChanged){
loadFile:
const std::string tilesetFilename{tilesetList[tilesetsList->nSelectedItem]};
parsedMap={tilesetFilename};
mapImage.Load(TILESET_DIR+parsedMap.GetData().filename);
currentTileset=parsedMap.GetData();
activeTileset=tilesetFilename;
undoList.clear();
redoList.clear();
previousObjState=currentTileset.objects;
ResetState();
}
if(GetKey(ESCAPE).bReleased){
selectingFile=false;
openButton->Reset();
gui.Update(this);
}
if(closeButton->bReleased){
closeButton->Reset();
selectingFile=false;
openButton->Reset();
gui.Update(this);
}
GradientFillRectDecal({0.f,0.f},GetScreenSize()/2.f,BLACK,BLACK,{0,0,0,0},BLACK);
GradientFillRectDecal({0.f,ScreenHeight()/2.f},GetScreenSize()/2.f,BLACK,BLACK,BLACK,{0,0,0,0});
GradientFillRectDecal(GetScreenSize()/2.f,GetScreenSize()/2.f,{0,0,0,0},BLACK,BLACK,BLACK);
GradientFillRectDecal({ScreenWidth()/2.f,0.f},GetScreenSize()/2.f,BLACK,{0,0,0,0},BLACK,BLACK);
selectionGui.DrawDecal(this);
}
void RenderTileset(){
const Tileset&tileset=currentTileset;
if(mapImage.Decal()!=nullptr){
view.DrawDecal({0,0},mapImage.Decal());
}
if(tileset.columns>0){
if(selectedObj.length()>0){
for(int y=0;y<tileset.tilecount/tileset.columns;y++){
for(int x=0;x<tileset.columns;x++){
if(!geom2d::contains(tileset.objects.at(selectedObj).bounds,vf2d{float(x*tileset.tilewidth)+tileset.tilewidth/2,float(y*tileset.tileheight)+tileset.tileheight/2})){
view.FillRectDecal(vf2d{float(x),float(y)}*tileset.tilewidth,vf2d{float(tileset.tilewidth),float(tileset.tileheight)},{0,0,0,128});
}
}
}
}
for(int y=0;y<tileset.tilecount/tileset.columns;y++){
for(int x=0;x<tileset.columns;x++){
view.DrawRectDecal(vf2d{float(x),float(y)}*tileset.tilewidth,vf2d{float(tileset.tilewidth),float(tileset.tileheight)},GREY);
}
}
}
if(selectedObj.length()>0&&!dragNewObj){
const TilesetObject&obj=tileset.objects.at(selectedObj);
if(highlightedQuad!=nullptr){
std::array<geom2d::triangle<float>,2>collisionTris{
geom2d::triangle<float>{(*highlightedQuad)[0],(*highlightedQuad)[1],(*highlightedQuad)[2]},
geom2d::triangle<float>{(*highlightedQuad)[0],(*highlightedQuad)[2],(*highlightedQuad)[3]},
};
for(geom2d::triangle<float>&tri:collisionTris){
if(geom2d::overlaps(tri,geom2d::circle<float>(view.ScreenToWorld(GetMousePos()),3.f))){
goto renderQuads;
}
}
highlightedQuad=nullptr;
}else{
for(const Quadrilateral&quad:obj.collisionTiles){
std::array<geom2d::triangle<float>,2>collisionTris{
geom2d::triangle<float>{quad[0],quad[1],quad[2]},
geom2d::triangle<float>{quad[0],quad[2],quad[3]},
};
for(geom2d::triangle<float>&tri:collisionTris){
if(geom2d::overlaps(tri,geom2d::circle<float>(view.ScreenToWorld(GetMousePos()),3.f))){
highlightedQuad=const_cast<Quadrilateral*>(&quad);
goto renderQuads;
}
}
}
}
renderQuads:
for(const Quadrilateral&quad:obj.collisionTiles){
std::vector<vf2d>points;
std::vector<vf2d>uvs;
std::vector<Pixel>cols;
points.assign(quad.begin(),quad.end());
uvs.assign(4,{0.f,0.f});
cols.assign(4,(!GetMouse(Mouse::LEFT).bHeld&&highlightedQuad==&quad)?Pixel{255,20,20,150}:Pixel{255,40,40,128});
view.DrawPolygonDecal(nullptr,points,uvs,cols);
for(bool highlighted=false;const vf2d&point:quad){
if(highlightedQuad==&quad){
if(geom2d::line<float>(point,view.ScreenToWorld(GetMousePos())).length()<4/view.GetWorldScale().x&&!highlighted){
view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,vf2d{1.f,1.f}/view.GetWorldScale(),YELLOW);
highlighted=true;
}else{
view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,vf2d{1.f,1.f}/view.GetWorldScale(),RED);
}
}else{
view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,vf2d{0.25f,0.25f}/view.GetWorldScale(),DARK_GREY);
}
}
}
}else
if(dragNewObj){
vf2d worldCoords=view.ScreenToWorld(GetMousePos());
lowerRightObjTile=vi2d{int(ceil(worldCoords.x/tileset.tilewidth)*tileset.tilewidth),int(ceil(worldCoords.y/tileset.tileheight)*tileset.tileheight)};
vi2d newUpperLeftTile=upperLeftObjTile;
vi2d newLowerRightTile=lowerRightObjTile;
if(worldCoords.x<upperLeftObjTile.x){
newLowerRightTile.x=int(floor(worldCoords.x/tileset.tilewidth)*tileset.tilewidth);
std::swap(newUpperLeftTile.x,newLowerRightTile.x);
}
if(worldCoords.y<upperLeftObjTile.y){
newLowerRightTile.y=int(floor(worldCoords.y/tileset.tileheight)*tileset.tileheight);
std::swap(newUpperLeftTile.y,newLowerRightTile.y);
}
if(newLowerRightTile.x==newUpperLeftTile.x)newLowerRightTile.x+=tileset.tilewidth;
if(newLowerRightTile.y==newUpperLeftTile.y)newLowerRightTile.y+=tileset.tileheight;
Pixel overlayCol={0,0,255,160};
geom2d::rect<int>newObjRect{newUpperLeftTile,newLowerRightTile-newUpperLeftTile};
for(auto&[name,obj]:tileset.objects){
geom2d::rect<float>offsetBounds{obj.bounds.pos+vf2d{0.5f,0.5f},obj.bounds.size-vf2d{1.f,1.f}};
if(geom2d::overlaps(offsetBounds,newObjRect)){
overlayCol={255,0,0,160};
break;
}
}
view.FillRectDecal(newObjRect.pos,newObjRect.size,overlayCol);
}
for(auto&[objName,obj]:tileset.objects){
view.DrawLineDecal(obj.bounds.pos,obj.bounds.pos+vf2d{0.f,float(obj.bounds.size.y)},YELLOW);
view.DrawLineDecal(obj.bounds.pos,obj.bounds.pos+vf2d{float(obj.bounds.size.x),0.f},YELLOW);
view.DrawLineDecal(obj.bounds.pos+obj.bounds.size,obj.bounds.pos+obj.bounds.size+vf2d{0.f,-float(obj.bounds.size.y)},YELLOW);
view.DrawLineDecal(obj.bounds.pos+obj.bounds.size,obj.bounds.pos+obj.bounds.size+vf2d{-float(obj.bounds.size.x),0.f},YELLOW);
vi2d nameTextSize=GetTextSizeProp(obj.name)*0.25f;
if(!geom2d::overlaps(geom2d::rect<float>{obj.bounds.pos,nameTextSize+vf2d{2,2}},view.ScreenToWorld(GetMousePos()))){
view.GradientFillRectDecal(obj.bounds.pos,nameTextSize+vf2d{2,2},RED,{255,0,0,64},{255,0,0,64},RED);
view.DrawStringPropDecal(obj.bounds.pos+vf2d{1.25f,1.25f},obj.name,BLACK,vf2d{0.25f,0.25f});
view.DrawStringPropDecal(obj.bounds.pos+vf2d{1,1},obj.name,WHITE,vf2d{0.25f,0.25f});
}
}
gui.DrawDecal(this);
if(!nameBox->m_bTextEdit){
createNewButton->Enable(true);
editButton->Enable(true);
FillRectDecal(nameBox->vPos,nameBox->vSize,{0,0,0,150});
if(selectedObj.length()>0){
DrawStringPropDecal(nameBox->vPos+vf2d{2.f,0.f},"R to Edit",WHITE,{0.6f,0.6f});
}
}else{
createNewButton->Enable(false);
editButton->Enable(false);
}
DrawStringDecal(createNewButton->vPos+vf2d{3,0},"Q");
DrawStringDecal(editButton->vPos+vf2d{3,0},"E");
}
bool OnUserUpdate(float fElapsedTime) override
{
Clear(VERY_DARK_BLUE);
const bool editingFile=!selectingFile;
if(IsFocused()&&editingFile){
EditorUpdate();
}
RenderTileset();
if(IsFocused()&&selectingFile){
SelectingFileUpdate();
}
return true;
}
};
int main()
{
TiledCollisionEditor demo;
if (demo.Construct(640, 180, 4, 4, false, true))
demo.Start();
return 0;
}