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

#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;
}