generated from sigonasr2/CPlusPlusProjectTemplate
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.
357 lines
12 KiB
357 lines
12 KiB
#include "olcUTIL_Geometry2D.h"
|
|
#include "TSXParser.h"
|
|
#include "olcPGEX_TransformedView.h"
|
|
#include "olcUTIL_Camera2D.h"
|
|
#include "olcPGEX_QuickGUI.h"
|
|
|
|
using namespace olc;
|
|
using namespace olc::utils;
|
|
using namespace QuickGUI;
|
|
|
|
const std::string TILESET_DIR="./Tiles/";
|
|
|
|
class TiledCollisionEditor : public olc::PixelGameEngine
|
|
{
|
|
std::unordered_map<std::string,Tileset>tilesets;
|
|
std::unordered_map<std::string,Renderable>images;
|
|
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{};
|
|
|
|
bool dragNewObj=false;
|
|
vi2d upperLeftObjTile{};
|
|
|
|
Renderable circle;
|
|
Renderable createNewButtonImg;
|
|
Renderable editButtonImg;
|
|
|
|
TransformedView view;
|
|
|
|
public:
|
|
TiledCollisionEditor()
|
|
{
|
|
sAppName = "TiledCollisionEditor";
|
|
}
|
|
|
|
Manager gui;
|
|
|
|
ImageCheckBox*createNewButton;
|
|
ImageCheckBox*editButton;
|
|
|
|
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");
|
|
|
|
std::string tilesetFilename=TILESET_DIR+"Basic Tileset.tsx";
|
|
Tileset&tileset=tilesets[tilesetFilename]=TSXParser{tilesetFilename}.GetData();
|
|
|
|
Renderable&tilesetImg=images[tilesetFilename];
|
|
tilesetImg.Load(TILESET_DIR+tileset.filename);
|
|
|
|
if(tilesets.size()==1)activeTileset=tilesetFilename;
|
|
|
|
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;
|
|
return true;
|
|
}
|
|
|
|
float hundredsDigit=0.f;
|
|
float tensDigit=0.f;
|
|
float onesDigit=0.f;
|
|
|
|
float currentHealth=0;
|
|
float targetHealth=60;
|
|
|
|
void Update(){
|
|
const Tileset&tileset=tilesets[activeTileset];
|
|
|
|
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});
|
|
}
|
|
}
|
|
}
|
|
|
|
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){
|
|
(*editingQuad)[editingPoint]=GetSnapPoint();
|
|
}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){
|
|
#pragma region Select a point on a collision quad.
|
|
for(const Quadrilateral&quad:obj.collisionTiles){
|
|
for(size_t pointInd=0;const vf2d&point:quad){
|
|
if(geom2d::line<float>(point,view.ScreenToWorld(GetMousePos())).length()<4){
|
|
if(GetMouse(Mouse::LEFT).bPressed){
|
|
editingPoint=pointInd;
|
|
editingQuad=const_cast<Quadrilateral*>(&quad);
|
|
originalQuad=quad;
|
|
}
|
|
goto exitCollisionCheck;
|
|
}
|
|
pointInd++;
|
|
}
|
|
}
|
|
#pragma endregion
|
|
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::contains(tri,view.ScreenToWorld(GetMousePos()))){
|
|
dragTranslate=true;
|
|
editingQuad=const_cast<Quadrilateral*>(&quad);
|
|
upperLeftDragOffset=GetSnapPoint()-quad[0];
|
|
originalQuad=*editingQuad;
|
|
goto exitCollisionCheck;
|
|
}
|
|
}
|
|
}
|
|
exitCollisionCheck:
|
|
if(EditingQuad&&!dragging){
|
|
(*editingQuad)[editingPoint]=GetSnapPoint();
|
|
editingPoint++;
|
|
}
|
|
}
|
|
|
|
if(GetMouse(Mouse::RIGHT).bPressed||GetKey(ESCAPE).bPressed){
|
|
if(EditingQuad||dragTranslate){
|
|
editingPoint=4;
|
|
dragging=false;
|
|
*editingQuad=originalQuad;
|
|
editingQuad=nullptr;
|
|
dragTranslate=false;
|
|
}
|
|
}
|
|
}else{
|
|
if(GetMouse(Mouse::LEFT).bPressed){
|
|
Quadrilateral newQuad{GetSnapPoint()};
|
|
tilesets[activeTileset].objects[selectedObj].collisionTiles.push_back(newQuad);
|
|
dragging=true;
|
|
editingQuad=const_cast<Quadrilateral*>(&obj.collisionTiles.back());
|
|
originalQuad=*editingQuad;
|
|
}
|
|
}
|
|
|
|
if(GetMouse(Mouse::RIGHT).bPressed&&!EditingQuad&&!dragTranslate){
|
|
std::vector<Quadrilateral*>quadsToBeRemoved;
|
|
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::contains(tri,view.ScreenToWorld(GetMousePos()))){
|
|
//Delete this quad, the mouse is overlapping it!
|
|
quadsToBeRemoved.push_back(const_cast<Quadrilateral*>(&quad));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for(Quadrilateral*quad:quadsToBeRemoved){
|
|
std::erase_if(tilesets[activeTileset].objects[selectedObj].collisionTiles,[&](Quadrilateral&q){return &q==quad;});
|
|
}
|
|
}
|
|
|
|
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};
|
|
dragging=false;
|
|
}else
|
|
if(EditingQuad&&!dragging){
|
|
(*editingQuad)[editingPoint]=GetSnapPoint();
|
|
editingPoint=4;
|
|
}
|
|
dragTranslate=false;
|
|
}
|
|
}
|
|
}
|
|
|
|
void NewObjectUpdate(){
|
|
if(GetMouse(Mouse::LEFT).bPressed){
|
|
const Tileset&tileset=tilesets[activeTileset];
|
|
|
|
vf2d worldCoords=view.ScreenToWorld(GetMousePos());
|
|
upperLeftObjTile=vf2d{round(worldCoords.x/tileset.tilewidth)*tileset.tilewidth,round(worldCoords.y/tileset.tileheight)*tileset.tileheight};
|
|
dragNewObj=true;
|
|
}
|
|
if(GetMouse(Mouse::LEFT).bReleased){
|
|
dragNewObj=false;
|
|
|
|
const Tileset&tileset=tilesets[activeTileset];
|
|
//Check for intersection with other objects, if found then we deny creating this object this way.
|
|
for(auto&[name,obj]:tileset.objects){
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
bool OnUserUpdate(float fElapsedTime) override
|
|
{
|
|
Clear(VERY_DARK_BLUE);
|
|
|
|
view.HandlePanAndZoom();
|
|
|
|
const float CAMERA_MOVESPD = 150.f;
|
|
if(GetKey(W).bHeld)view.MoveWorldOffset(vf2d{0.f,-CAMERA_MOVESPD}*fElapsedTime/view.GetWorldScale());
|
|
if(GetKey(S).bHeld)view.MoveWorldOffset(vf2d{0.f,CAMERA_MOVESPD}*fElapsedTime/view.GetWorldScale());
|
|
if(GetKey(A).bHeld)view.MoveWorldOffset(vf2d{-CAMERA_MOVESPD,0.f}*fElapsedTime/view.GetWorldScale());
|
|
if(GetKey(D).bHeld)view.MoveWorldOffset(vf2d{CAMERA_MOVESPD,0.f}*fElapsedTime/view.GetWorldScale());
|
|
|
|
const Tileset&tileset=tilesets[activeTileset];
|
|
|
|
view.DrawDecal({0,0},images[activeTileset].Decal());
|
|
|
|
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(editingQuad==nullptr){
|
|
std::string selectedObj="";
|
|
}
|
|
|
|
if(createNewButton->bPressed)editButton->bChecked=false;
|
|
if(editButton->bPressed)createNewButton->bChecked=false;
|
|
|
|
if(GetMouseY()<ScreenHeight()-36||GetMouseX()>72){
|
|
if(!dragNewObj){
|
|
Update();
|
|
}else{
|
|
NewObjectUpdate();
|
|
}
|
|
}
|
|
|
|
//Font test.
|
|
/*DrawStringDecal({0,0},"the quick brown fox jumps over the lazy dog 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f});
|
|
DrawStringDecal({0,18},"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f});
|
|
DrawStringPropDecal({0,36},"the quick brown fox jumps over the lazy dog 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f});
|
|
DrawStringPropDecal({0,54},"THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG 1234567890 !@#$%^&*()-=_+[]{}\\;':\",./<>?~`",WHITE,{1.5f,1.5f});
|
|
*/
|
|
if(selectedObj.length()>0&&!dragNewObj){
|
|
const TilesetObject&obj=tileset.objects.at(selectedObj);
|
|
|
|
Quadrilateral*highlightedQuad=nullptr;
|
|
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::contains(tri,view.ScreenToWorld(GetMousePos()))){
|
|
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(geom2d::line<float>(point,view.ScreenToWorld(GetMousePos())).length()<4&&!highlighted){
|
|
view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,{1.f,1.f},YELLOW);
|
|
highlighted=true;
|
|
}else{
|
|
view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,{1.f,1.f},RED);
|
|
}
|
|
}
|
|
}
|
|
}else
|
|
if(dragNewObj){
|
|
|
|
}
|
|
|
|
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(objName)*0.25f;
|
|
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},objName,BLACK,vf2d{0.25f,0.25f});
|
|
view.DrawStringPropDecal(obj.bounds.pos+vf2d{1,1},objName,WHITE,vf2d{0.25f,0.25f});
|
|
|
|
if(geom2d::contains(obj.bounds,view.ScreenToWorld(GetMousePos()))){
|
|
selectedObj=objName;
|
|
}
|
|
}
|
|
|
|
gui.Update(this);
|
|
gui.DrawDecal(this);
|
|
|
|
DrawStringDecal(createNewButton->vPos+vf2d{3,0},"Q");
|
|
DrawStringDecal(editButton->vPos+vf2d{3,0},"E");
|
|
|
|
return true;
|
|
}
|
|
};
|
|
|
|
int main()
|
|
{
|
|
TiledCollisionEditor demo;
|
|
if (demo.Construct(640, 180, 4, 4))
|
|
demo.Start();
|
|
|
|
return 0;
|
|
}
|
|
|