diff --git a/TiledCollisionEditor/Tiles/Basic Tileset - Copy - Copy - Copy - Copy - Copy.tsx b/TiledCollisionEditor/Tiles/Basic Tileset - Copy - Copy - Copy - Copy - Copy.tsx index 9fb636f..b17b5d1 100644 --- a/TiledCollisionEditor/Tiles/Basic Tileset - Copy - Copy - Copy - Copy - Copy.tsx +++ b/TiledCollisionEditor/Tiles/Basic Tileset - Copy - Copy - Copy - Copy - Copy.tsx @@ -1,824 +1,988 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TiledCollisionEditor/Tiles/Basic Tileset.tsx b/TiledCollisionEditor/Tiles/Basic Tileset.tsx index a3a34ea..e144d6e 100644 --- a/TiledCollisionEditor/Tiles/Basic Tileset.tsx +++ b/TiledCollisionEditor/Tiles/Basic Tileset.tsx @@ -19,116 +19,113 @@ - + - + - + - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -144,105 +141,105 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -258,105 +255,105 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + @@ -372,549 +369,622 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + + - - - - - - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + - + - + - + - + - + - + - + - + - + - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TiledCollisionEditor/Tiles/BasicGrass.tsx b/TiledCollisionEditor/Tiles/BasicGrass.tsx new file mode 100644 index 0000000..78443f6 --- /dev/null +++ b/TiledCollisionEditor/Tiles/BasicGrass.tsx @@ -0,0 +1,250 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/TiledCollisionEditor/Tiles/tileset.png b/TiledCollisionEditor/Tiles/tileset.png new file mode 100644 index 0000000..69b359f Binary files /dev/null and b/TiledCollisionEditor/Tiles/tileset.png differ diff --git a/TiledCollisionEditor/main.cpp b/TiledCollisionEditor/main.cpp index 9e21dcf..298b635 100644 --- a/TiledCollisionEditor/main.cpp +++ b/TiledCollisionEditor/main.cpp @@ -4,6 +4,7 @@ #include "olcUTIL_Camera2D.h" #include "olcPGEX_QuickGUI.h" #include +#include using namespace olc; using namespace olc::utils; @@ -13,8 +14,8 @@ const std::string TILESET_DIR="./Tiles/"; class TiledCollisionEditor : public olc::PixelGameEngine { - std::unordered_maptilesets; - std::unordered_mapimages; + Tileset currentTileset; + Renderable mapImage; std::string activeTileset; Quadrilateral*editingQuad=nullptr; Quadrilateral originalQuad; @@ -24,17 +25,32 @@ class TiledCollisionEditor : public olc::PixelGameEngine 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::vectortilesetList; + + std::unordered_mappreviousObjState; + + std::deque>redoList; + std::deque>undoList; + public: TiledCollisionEditor() { @@ -42,10 +58,17 @@ public: } 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 { @@ -60,38 +83,57 @@ public: createNewButtonImg.Load("newCollisionButton.png"); editButtonImg.Load("EditButton.png"); - - std::string tilesetFilename=TILESET_DIR+"Basic Tileset.tsx"; - parsedMap={tilesetFilename}; - Tileset&tileset=tilesets[tilesetFilename]=parsedMap.GetData(); - - Renderable&tilesetImg=images[tilesetFilename]; - tilesetImg.Load(TILESET_DIR+tileset.filename); - - if(tilesets.size()==1)activeTileset=tilesetFilename; + 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(){ + 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::vectororiginalData=parsedMap.originalData; const std::vectornonObjects=parsedMap.nonObjects; auto xmlTag=std::find_if(originalData.begin(),originalData.end(),[](const XMLTag tag){return tag.tag=="?xml";}); - file<<""<"<", - activeSet.name,activeSet.tilewidth,activeSet.tileheight,(activeSet.imagewidth/activeSet.tilewidth)*(activeSet.imageheight/activeSet.tileheight),activeSet.columns)<", + activeSet.name,activeSet.tilewidth,activeSet.tileheight,activeSet.tilecount,activeSet.columns)<")<")<", activeSet.filename,activeSet.imagewidth,activeSet.imageheight)<0){ - if(GetKey(DEL).bPressed){ - tilesets[activeTileset].objects.erase(selectedObj); + if(GetKey(DEL).bReleased){ + currentTileset.objects.erase(selectedObj); selectedObj=""; + ResetState(); SaveFile(); return; } - for(int y=0;y(point,view.ScreenToWorld(GetMousePos())).length()<4){ + if(geom2d::line(point,view.ScreenToWorld(GetMousePos())).length()<4/view.GetWorldScale().x){ editingPoint=pointInd; editingQuad=highlightedQuad; originalQuad=*highlightedQuad; @@ -198,24 +249,25 @@ public: } exitCollisionCheck: if(EditingQuad&&!dragging){ - (*editingQuad)[editingPoint]=GetSnapPoint(); + 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).bPressed){ + if(GetMouse(Mouse::RIGHT).bPressed||GetKey(ESCAPE).bReleased){ if(EditingQuad||dragTranslate){ - editingPoint=4; - dragging=false; - *editingQuad=originalQuad; - editingQuad=nullptr; - dragTranslate=false; + ResetState(); } } }else{ if(GetMouse(Mouse::LEFT).bPressed){ Quadrilateral newQuad{GetSnapPoint()}; - tilesets[activeTileset].objects[selectedObj].collisionTiles.push_back(newQuad); + currentTileset.objects[selectedObj].collisionTiles.push_back(newQuad); dragging=true; editingQuad=const_cast(&obj.collisionTiles.back()); originalQuad=*editingQuad; @@ -224,11 +276,8 @@ public: if(GetMouse(Mouse::RIGHT).bPressed&&!EditingQuad&&!dragTranslate){ if(highlightedQuad!=nullptr){ - std::erase_if(tilesets[activeTileset].objects[selectedObj].collisionTiles,[&](Quadrilateral&q){return &q==highlightedQuad;}); - editingPoint=4; - dragging=false; - editingQuad=nullptr; - dragTranslate=false; + std::erase_if(currentTileset.objects[selectedObj].collisionTiles,[&](Quadrilateral&q){return &q==highlightedQuad;}); + ResetState(); SaveFile(); } } @@ -240,12 +289,21 @@ public: (*editingQuad)[1]=vf2d{cursorPos.x,initialPoint.y}; (*editingQuad)[2]=GetSnapPoint(); (*editingQuad)[3]=vf2d{initialPoint.x,cursorPos.y}; - dragging=false; + ResetState(); SaveFile(); }else if(EditingQuad&&!dragging){ - (*editingQuad)[editingPoint]=GetSnapPoint(); + 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){ @@ -258,7 +316,7 @@ public: } void NewObjectUpdate(){ - const Tileset&tileset=tilesets[activeTileset]; + const Tileset&tileset=currentTileset; if(GetMouse(Mouse::LEFT).bReleased){ dragNewObj=false; @@ -280,7 +338,7 @@ public: geom2d::rectnewObjRect{newUpperLeftTile,newLowerRightTile-newUpperLeftTile}; - const Tileset&tileset=tilesets[activeTileset]; + 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){ @@ -292,9 +350,9 @@ public: } if(!intersectionFound){ - std::string objName=std::format("Object{}",tilesets[activeTileset].objects.size()); + std::string objName=std::format("Object{}",currentTileset.objects.size()); - TilesetObject&newObj=tilesets[activeTileset].objects[objName]; + TilesetObject&newObj=currentTileset.objects[objName]; newObj.name=objName; for(int y=0;ym_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:"<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:"<bReleased){ + selectingFile=true; + openButton->Reset(); + gui.Update(this); + return; } } + + const bool EditingQuad=(editingPoint<4||dragging)&&editingQuad!=nullptr; - if(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; } } @@ -343,8 +438,19 @@ public: 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()72){ + if((GetMouseY()204)&& + (GetMouseX()ScreenHeight()+12)&& + !nameBox->m_bTextEdit&& + tileset.columns>0&& + view.ScreenToWorld(GetMousePos()).x=0&&view.ScreenToWorld(GetMousePos()).y>=0){ if(selectedObj.length()==0){ if(GetMouse(Mouse::LEFT).bPressed){ vf2d worldCoords=view.ScreenToWorld(GetMousePos()); @@ -358,13 +464,77 @@ public: 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?~`",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); @@ -405,14 +575,14 @@ public: for(bool highlighted=false;const vf2d&point:quad){ if(highlightedQuad==&quad){ - if(geom2d::line(point,view.ScreenToWorld(GetMousePos())).length()<4&&!highlighted){ - view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,{1.f,1.f},YELLOW); + if(geom2d::line(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,{1.f,1.f},RED); + 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,{0.25f,0.25f},DARK_GREY); + view.DrawRotatedDecal(point,circle.Decal(),0.f,circle.Sprite()->Size()/2,vf2d{0.25f,0.25f}/view.GetWorldScale(),DARK_GREY); } } } @@ -457,21 +627,46 @@ public: 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; + vi2d nameTextSize=GetTextSizeProp(obj.name)*0.25f; + if(!geom2d::overlaps(geom2d::rect{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.Update(this); 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; } @@ -480,7 +675,7 @@ public: int main() { TiledCollisionEditor demo; - if (demo.Construct(640, 180, 4, 4)) + if (demo.Construct(640, 180, 4, 4, false, true)) demo.Start(); return 0; diff --git a/TiledCollisionEditor/olcPGEX_QuickGUI.h b/TiledCollisionEditor/olcPGEX_QuickGUI.h index 3513f9f..25a8d64 100644 --- a/TiledCollisionEditor/olcPGEX_QuickGUI.h +++ b/TiledCollisionEditor/olcPGEX_QuickGUI.h @@ -91,6 +91,8 @@ namespace olc::QuickGUI public: // Switches the control on/off void Enable(const bool bEnable); + //Resets the state of a control such as when transitioning from a screen to another screen, we want to reset the state to normal. + void Reset(); // Sets whether or not the control is interactive/displayed bool bVisible = true; @@ -447,6 +449,15 @@ namespace olc::QuickGUI { m_state = bEnable ? State::Normal : State::Disabled; } + + void BaseControl::Reset(){ + m_state = State::Normal; + bPressed = false; + bHeld = false; + bReleased = false; + bHovered = false; + } + #pragma endregion #pragma region Manager @@ -706,31 +717,31 @@ namespace olc::QuickGUI void Button::Update(olc::PixelGameEngine* pge) { + bPressed = false; + bReleased = false; if (m_state == State::Disabled || !bVisible) return; - bPressed = false; - bReleased = false; float fElapsedTime = pge->GetElapsedTime(); olc::vf2d vMouse = pge->GetMousePos(); if (m_state != State::Click) { - if (vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && + if ((vMouse.x >= vPos.x && vMouse.x < vPos.x + vSize.x && vMouse.y >= vPos.y && vMouse.y < vPos.y + vSize.y - ||pge->GetKey(hotkey).bPressed||pge->GetKey(hotkey).bHeld||pge->GetKey(hotkey).bReleased) + ||(hotkey!=olc::Key::NONE&&(pge->GetKey(hotkey).bPressed||pge->GetKey(hotkey).bHeld)))) { m_fTransition += fElapsedTime * m_manager.fHoverSpeedOn; m_state = State::Hover; bHovered = true; - bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed||pge->GetKey(hotkey).bPressed; + bPressed = pge->GetMouse(olc::Mouse::LEFT).bPressed||(hotkey!=olc::Key::NONE&&pge->GetKey(hotkey).bPressed); if (bPressed) { m_state = State::Click; } - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld||pge->GetKey(hotkey).bHeld; + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld||(hotkey!=olc::Key::NONE&&pge->GetKey(hotkey).bHeld); } else { @@ -741,8 +752,8 @@ namespace olc::QuickGUI } else { - bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld||pge->GetKey(hotkey).bHeld; - bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased||pge->GetKey(hotkey).bReleased; + bHeld = pge->GetMouse(olc::Mouse::LEFT).bHeld||(hotkey!=olc::Key::NONE&&pge->GetKey(hotkey).bHeld); + bReleased = pge->GetMouse(olc::Mouse::LEFT).bReleased||(hotkey!=olc::Key::NONE&&pge->GetKey(hotkey).bReleased); if (bReleased) m_state = State::Normal; } @@ -861,7 +872,7 @@ namespace olc::QuickGUI return; ImageButton::Update(pge); - if (bPressed) bChecked = !bChecked; + if (bPressed) bChecked = true; } void ImageCheckBox::Draw(olc::PixelGameEngine* pge) @@ -1031,7 +1042,7 @@ namespace olc::QuickGUI if(fMaxFillRect(vTextPos - olc::vi2d(1,-1), {int32_t(vSize.x - m_group.fGrabRad * 2), int(fontSize)}, m_group.colHover); - pge->DrawStringProp(vTextPos + olc::vi2d(0,2), m_vList[idx],olc::WHITE,fontSize/10); + pge->DrawStringProp(vTextPos, m_vList[idx],olc::WHITE,fontSize/10); vTextPos.y += fontSize; } @@ -1176,7 +1187,7 @@ namespace olc::QuickGUI return; if (bHasBackground) - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colNormal); + pge->FillRectDecal(vPos + olc::vf2d(1, -1), vSize - olc::vf2d(2, 2), m_manager.colNormal); size_t idx0 = size_t(m_pSlider->fValue); size_t idx1 = std::min(idx0 + size_t((vSize.y - 4) / fontSize), m_vList.size()); @@ -1185,13 +1196,13 @@ namespace olc::QuickGUI for (size_t idx = idx0; idx < idx1; idx++) { if (idx == nSelectedItem) - pge->FillRectDecal(vTextPos - olc::vi2d(1, -1), { vSize.x - m_group.fGrabRad * 2.0f, fontSize }, m_group.colHover); + pge->FillRectDecal(vTextPos - olc::vi2d(1, 1), { vSize.x - m_group.fGrabRad * 2.0f, fontSize }, m_group.colHover); float width = pge->GetTextSizeProp(m_vList[idx]).x*fontSize/10; if (width>vSize.x-m_manager.fGrabRad*2){ float scaleX = (vSize.x-m_manager.fGrabRad*2)/width; - pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,2), m_vList[idx], olc::WHITE, olc::vf2d{scaleX,1}*fontSize/10); + pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,-5), m_vList[idx], olc::WHITE, olc::vf2d{scaleX,1}*fontSize/10); } else { - pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,2), m_vList[idx], olc::WHITE, olc::vf2d{1,1}*fontSize/10); + pge->DrawStringPropDecal(vTextPos + olc::vi2d(0,-5), m_vList[idx], olc::WHITE, olc::vf2d{1,1}*fontSize/10); } vTextPos.y += fontSize; } @@ -1199,7 +1210,7 @@ namespace olc::QuickGUI if (bHasBorder) { pge->SetDecalMode(olc::DecalMode::WIREFRAME); - pge->FillRectDecal(vPos + olc::vf2d(1, 1), vSize - olc::vf2d(2, 2), m_manager.colBorder); + pge->FillRectDecal(vPos + olc::vf2d(1, -1), vSize - olc::vf2d(2, 2), m_manager.colBorder); pge->SetDecalMode(olc::DecalMode::NORMAL); } diff --git a/TiledCollisionEditor/pixelGameEngine.h b/TiledCollisionEditor/pixelGameEngine.h index d6d535a..c15237a 100644 --- a/TiledCollisionEditor/pixelGameEngine.h +++ b/TiledCollisionEditor/pixelGameEngine.h @@ -1025,6 +1025,10 @@ namespace olc uint32_t GetFPS() const; // Gets last update of elapsed time float GetElapsedTime() const; + // Returns whether the mouse cursor exists inside the window or outside of it. + const bool IsMouseInsideWindow() const; + // Gets Actual Window pos + const olc::vi2d& GetWindowPos() const; // Gets Actual Window size const olc::vi2d& GetWindowSize() const; // Gets pixel scale @@ -1227,6 +1231,7 @@ namespace olc olc::vi2d vMouseWindowPos = { 0, 0 }; int32_t nMouseWheelDeltaCache = 0; olc::vi2d vWindowSize = { 0, 0 }; + olc::vi2d vWindowPos = { 0, 0 }; olc::vi2d vViewPos = { 0, 0 }; olc::vi2d vViewSize = { 0,0 }; bool bFullScreen = false; @@ -1296,6 +1301,7 @@ namespace olc // "Break In" Functions void olc_UpdateMouse(int32_t x, int32_t y); void olc_UpdateMouseWheel(int32_t delta); + void olc_UpdateWindowPos(int32_t x, int32_t y); void olc_UpdateWindowSize(int32_t x, int32_t y); void olc_UpdateViewport(); void olc_ConstructFontSheet(); @@ -2131,6 +2137,12 @@ namespace olc float PixelGameEngine::GetElapsedTime() const { return fLastElapsed; } + const bool PixelGameEngine::IsMouseInsideWindow() const + { return GetMouseX()>=0&&GetMouseY()>=0&&GetMouseX()= (int32_t)vScreenSize.x) vMousePosCache.x = vScreenSize.x - 1; - if (vMousePosCache.y >= (int32_t)vScreenSize.y) vMousePosCache.y = vScreenSize.y - 1; - if (vMousePosCache.x < 0) vMousePosCache.x = 0; - if (vMousePosCache.y < 0) vMousePosCache.y = 0; } void PixelGameEngine::olc_UpdateMouseState(int32_t button, bool state) @@ -5513,6 +5525,8 @@ namespace olc olc_hWnd = CreateWindowEx(dwExStyle, olcT("OLC_PIXEL_GAME_ENGINE"), olcT(""), dwStyle, vTopLeft.x, vTopLeft.y, width, height, NULL, NULL, GetModuleHandle(nullptr), this); + MoveWindow(olc_hWnd,vTopLeft.x,vTopLeft.y,width,height,false); //A hack to get the window's position updated in the correct spot (WM_MOVE reports the correct upper-left corner of the client area) + DragAcceptFiles(olc_hWnd, true); // Create Keyboard Mapping @@ -5582,7 +5596,18 @@ namespace olc return olc::OK; } - virtual olc::rcode HandleSystemEvent() override { return olc::rcode::FAIL; } + virtual olc::rcode HandleSystemEvent() override { + struct tagPOINT p{0,0}; + //Update mouse positions and states outside the window. + GetCursorPos(&p); + ptrPGE->olc_UpdateMouse(p.x-ptrPGE->GetWindowPos().x,p.y-ptrPGE->GetWindowPos().y); + ptrPGE->olc_UpdateMouseState(0,GetAsyncKeyState(VK_LBUTTON)>>7); + ptrPGE->olc_UpdateMouseState(1,GetAsyncKeyState(VK_RBUTTON)>>7); + ptrPGE->olc_UpdateMouseState(2,GetAsyncKeyState(VK_MBUTTON)>>7); + ptrPGE->olc_UpdateMouseState(3,GetAsyncKeyState(VK_XBUTTON1)>>7); + ptrPGE->olc_UpdateMouseState(4,GetAsyncKeyState(VK_XBUTTON2)>>7); + return olc::rcode::OK; + } // Windows Event Handler - this is statically connected to the windows event system static LRESULT CALLBACK olc_WindowEvent(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) @@ -5597,6 +5622,13 @@ namespace olc ptrPGE->olc_UpdateMouse(ix, iy); return 0; } + case WM_MOVE: + { + uint16_t x = lParam & 0xFFFF; uint16_t y = (lParam >> 16) & 0xFFFF; + int16_t ix = *(int16_t*)&x; int16_t iy = *(int16_t*)&y; + ptrPGE->olc_UpdateWindowPos(ix, iy); + return 0; + } case WM_SIZE: ptrPGE->olc_UpdateWindowSize(lParam & 0xFFFF, (lParam >> 16) & 0xFFFF); return 0; case WM_MOUSEWHEEL: ptrPGE->olc_UpdateMouseWheel(GET_WHEEL_DELTA_WPARAM(wParam)); return 0; case WM_MOUSELEAVE: ptrPGE->olc_UpdateMouseFocus(false); return 0; @@ -5842,11 +5874,13 @@ namespace olc { XWindowAttributes gwa; XGetWindowAttributes(olc_Display, olc_Window, &gwa); + ptrPGE->olc_UpdateWindowPos(gwa.x, gwa.y); ptrPGE->olc_UpdateWindowSize(gwa.width, gwa.height); } else if (xev.type == ConfigureNotify) { XConfigureEvent xce = xev.xconfigure; + ptrPGE->olc_UpdateWindowPos(gwa.x, gwa.y); ptrPGE->olc_UpdateWindowSize(xce.width, xce.height); } else if (xev.type == KeyPress) @@ -6325,19 +6359,19 @@ namespace olc mapKeys[DOM_PK_QUOTE] = Key::OEM_7; mapKeys[DOM_PK_BACKSLASH] = Key::OEM_8; // Keyboard Callbacks - emscripten_set_keydown_callback("#canvas", 0, 1, keyboard_callback); - emscripten_set_keyup_callback("#canvas", 0, 1, keyboard_callback); + emscripten_set_keydown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, keyboard_callback); + emscripten_set_keyup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, keyboard_callback); // Mouse Callbacks emscripten_set_wheel_callback("#canvas", 0, 1, wheel_callback); - emscripten_set_mousedown_callback("#canvas", 0, 1, mouse_callback); - emscripten_set_mouseup_callback("#canvas", 0, 1, mouse_callback); - emscripten_set_mousemove_callback("#canvas", 0, 1, mouse_callback); + emscripten_set_mousedown_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_mouseup_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); + emscripten_set_mousemove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, mouse_callback); // Touch Callbacks - emscripten_set_touchstart_callback("#canvas", 0, 1, touch_callback); - emscripten_set_touchmove_callback("#canvas", 0, 1, touch_callback); - emscripten_set_touchend_callback("#canvas", 0, 1, touch_callback); + emscripten_set_touchstart_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, touch_callback); + emscripten_set_touchmove_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, touch_callback); + emscripten_set_touchend_callback(EMSCRIPTEN_EVENT_TARGET_WINDOW, 0, 1, touch_callback); // Canvas Focus Callbacks emscripten_set_blur_callback("#canvas", 0, 1, focus_callback); @@ -6373,6 +6407,8 @@ namespace olc // is using one of the default or minimal emscripten page layouts Module.olc_AssumeDefaultShells = (document.querySelectorAll('.emscripten').length >= 3) ? true : false; + oncontextmenu=function(e){return false}; //Because we can click outside the window, we want to disable normal right-click context menu for the application. + // olc_ResizeHandler // // Used by olc_Init, and is called when a resize observer and fullscreenchange event is triggered. @@ -6511,7 +6547,7 @@ namespace olc // Move if (eventType == EMSCRIPTEN_EVENT_TOUCHMOVE) { - ptrPGE->olc_UpdateMouse(e->touches->targetX, e->touches->targetY); + ptrPGE->olc_UpdateMouse(e->touches->targetX-ptrPGE->GetWindowPos().x-EM_ASM_INT({return window.scrollX}), e->touches->targetY-ptrPGE->GetWindowPos().y-EM_ASM_INT({return window.scrollY})); } // Start @@ -6535,7 +6571,7 @@ namespace olc { //Mouse Movement if (eventType == EMSCRIPTEN_EVENT_MOUSEMOVE) - ptrPGE->olc_UpdateMouse(e->targetX, e->targetY); + ptrPGE->olc_UpdateMouse(e->targetX-ptrPGE->GetWindowPos().x-EM_ASM_INT({return window.scrollX}), e->targetY-ptrPGE->GetWindowPos().y-EM_ASM_INT({return window.scrollY})); //Mouse button press @@ -6578,7 +6614,10 @@ namespace olc { return olc::OK; } virtual olc::rcode HandleSystemEvent() override - { return olc::OK; } + { + ptrPGE->olc_UpdateWindowPos(EM_ASM_INT({return Module.canvas.getBoundingClientRect().left}),EM_ASM_INT({return Module.canvas.getBoundingClientRect().top})); + return olc::OK; + } static void MainLoop() { diff --git a/TiledCollisionEditor/redoButton.png b/TiledCollisionEditor/redoButton.png new file mode 100644 index 0000000..de26dda Binary files /dev/null and b/TiledCollisionEditor/redoButton.png differ diff --git a/TiledCollisionEditor/undoButton.png b/TiledCollisionEditor/undoButton.png new file mode 100644 index 0000000..c776081 Binary files /dev/null and b/TiledCollisionEditor/undoButton.png differ