@ -4,6 +4,7 @@
# include "olcUTIL_Camera2D.h"
# include "olcUTIL_Camera2D.h"
# include "olcPGEX_QuickGUI.h"
# include "olcPGEX_QuickGUI.h"
# include <variant>
# include <variant>
# include <deque>
using namespace olc ;
using namespace olc ;
using namespace olc : : utils ;
using namespace olc : : utils ;
@ -13,8 +14,8 @@ const std::string TILESET_DIR="./Tiles/";
class TiledCollisionEditor : public olc : : PixelGameEngine
class TiledCollisionEditor : public olc : : PixelGameEngine
{
{
std : : unordered_map < std : : string , Tileset > tilesets ;
Tileset currentTileset ;
std : : unordered_map < std : : string , Renderable > images ;
Renderable mapImage ;
std : : string activeTileset ;
std : : string activeTileset ;
Quadrilateral * editingQuad = nullptr ;
Quadrilateral * editingQuad = nullptr ;
Quadrilateral originalQuad ;
Quadrilateral originalQuad ;
@ -24,17 +25,32 @@ class TiledCollisionEditor : public olc::PixelGameEngine
bool dragTranslate = false ;
bool dragTranslate = false ;
vf2d upperLeftDragOffset { } ;
vf2d upperLeftDragOffset { } ;
Quadrilateral * highlightedQuad = nullptr ;
Quadrilateral * highlightedQuad = nullptr ;
std : : string nameEditObj = " " ;
size_t lastSelectedItem = 0 ;
bool dragNewObj = false ;
bool dragNewObj = false ;
vi2d upperLeftObjTile { } ;
vi2d upperLeftObjTile { } ;
vi2d lowerRightObjTile { } ;
vi2d lowerRightObjTile { } ;
bool selectingFile = false ;
Renderable circle ;
Renderable circle ;
Renderable createNewButtonImg ;
Renderable createNewButtonImg ;
Renderable undoButtonImg ;
Renderable redoButtonImg ;
Renderable editButtonImg ;
Renderable editButtonImg ;
bool loadedFirstFile = false ;
TransformedView view ;
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 :
public :
TiledCollisionEditor ( )
TiledCollisionEditor ( )
{
{
@ -42,10 +58,17 @@ public:
}
}
Manager gui ;
Manager gui ;
Manager selectionGui ;
ImageCheckBox * createNewButton = nullptr ;
ImageCheckBox * createNewButton = nullptr ;
ImageCheckBox * editButton = nullptr ;
ImageCheckBox * editButton = nullptr ;
ImageButton * undoButton = nullptr ;
ImageButton * redoButton = nullptr ;
TextBox * nameBox = nullptr ;
Button * openButton = nullptr ;
ListBox * tilesetsList = nullptr ;
TSXParser parsedMap { " " } ;
TSXParser parsedMap { " " } ;
Button * closeButton = nullptr ;
public :
public :
bool OnUserCreate ( ) override
bool OnUserCreate ( ) override
{
{
@ -60,38 +83,57 @@ public:
createNewButtonImg . Load ( " newCollisionButton.png " ) ;
createNewButtonImg . Load ( " newCollisionButton.png " ) ;
editButtonImg . Load ( " EditButton.png " ) ;
editButtonImg . Load ( " EditButton.png " ) ;
undoButtonImg . Load ( " undoButton.png " ) ;
std : : string tilesetFilename = TILESET_DIR + " Basic Tileset.tsx " ;
redoButtonImg . Load ( " redoButton.png " ) ;
parsedMap = { tilesetFilename } ;
Tileset & tileset = tilesets [ tilesetFilename ] = parsedMap . 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 = new ImageCheckBox { gui , createNewButtonImg , false , vf2d { 4.f , ScreenHeight ( ) - 36.f } , { 32.f , 32.f } , { 4 , 1 } , { 32 , 32 } } ;
createNewButton - > hotkey = Q ;
createNewButton - > hotkey = Q ;
editButton = new ImageCheckBox { gui , editButtonImg , true , vf2d { 40.f , ScreenHeight ( ) - 36.f } , { 32.f , 32.f } , { 4 , 4 } , { 32 , 32 } } ;
editButton = new ImageCheckBox { gui , editButtonImg , true , vf2d { 40.f , ScreenHeight ( ) - 36.f } , { 32.f , 32.f } , { 4 , 4 } , { 32 , 32 } } ;
editButton - > hotkey = E ;
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 ;
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 ;
std : : stringstream file ;
if ( file . good ( ) ) {
if ( file . good ( ) ) {
const std : : vector < XMLTag > originalData = parsedMap . originalData ;
const std : : vector < XMLTag > originalData = parsedMap . originalData ;
const std : : vector < NonObject > nonObjects = parsedMap . nonObjects ;
const std : : vector < NonObject > nonObjects = parsedMap . nonObjects ;
auto xmlTag = std : : find_if ( originalData . begin ( ) , originalData . end ( ) , [ ] ( const XMLTag tag ) { return tag . tag = = " ?xml " ; } ) ;
auto xmlTag = std : : find_if ( originalData . begin ( ) , originalData . end ( ) , [ ] ( const XMLTag tag ) { return tag . tag = = " ?xml " ; } ) ;
file < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> " < < std : : endl ;
if ( xmlTag ! = originalData . end ( ) ) file < < " <?xml version= \" 1.0 \" encoding= \" UTF-8 \" ?> " < < std : : endl ;
const Tileset & activeSet = tilesets . at ( activeTileset ) ;
const Tileset & activeSet = currentTileset ;
file < < std : : format ( " <tileset name= \" {} \" tilewidth= \" {} \" tileheight= \" {} \" tilecount= \" {} \" columns= \" {} \" > " ,
file < < std : : format ( " <tileset version= \" 1.10 \" tiledversion= \" 1.10.2 \" name= \" {} \" tilewidth= \" {} \" tileheight= \" {} \" tilecount= \" {} \" columns= \" {} \" > " ,
activeSet . name , activeSet . tilewidth , activeSet . tileheight , ( activeSet . imagewidth / activeSet . tilewidth ) * ( activeSet . imageheight / activeSet . tileheight ) , activeSet . columns ) < < std : : endl ;
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 " ; } ) ;
auto transformationsTag = std : : find_if ( originalData . begin ( ) , originalData . end ( ) , [ ] ( const XMLTag tag ) { return tag . tag = = " transformations " ; } ) ;
file < < ( * transformationsTag ) . OutputTag ( " /> " ) < < std : : endl ;
if ( transformationsTag ! = originalData . end ( ) ) file < < ( * transformationsTag ) . OutputTag ( " /> " ) < < std : : endl ;
file < < std : : format ( " <image source= \" {} \" width= \" {} \" height= \" {} \" /> " ,
file < < std : : format ( " <image source= \" {} \" width= \" {} \" height= \" {} \" /> " ,
activeSet . filename , activeSet . imagewidth , activeSet . imageheight ) < < std : : endl ;
activeSet . filename , activeSet . imagewidth , activeSet . imageheight ) < < std : : endl ;
@ -123,28 +165,32 @@ public:
std : : ofstream saveFile { activeTileset } ;
std : : ofstream saveFile { activeTileset } ;
saveFile < < file . str ( ) < < std : : endl ;
saveFile < < file . str ( ) < < std : : endl ;
saveFile . close ( ) ;
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 ( ) {
void Update ( ) {
const Tileset & tileset = tilesets [ activeTileset ] ;
const Tileset & tileset = currentTileset ;
if ( selectedObj . length ( ) > 0 ) {
if ( selectedObj . length ( ) > 0 ) {
if ( GetKey ( DEL ) . bPressed ) {
if ( GetKey ( DEL ) . bRelea sed ) {
tilesets [ activeTileset ] . objects . erase ( selectedObj ) ;
currentTileset . objects . erase ( selectedObj ) ;
selectedObj = " " ;
selectedObj = " " ;
ResetState ( ) ;
SaveFile ( ) ;
SaveFile ( ) ;
return ;
return ;
}
}
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 TilesetObject & obj = tileset . objects . at ( selectedObj ) ;
const bool EditingQuad = ( editingPoint < 4 | | dragging ) & & editingQuad ! = nullptr ;
const bool EditingQuad = ( editingPoint < 4 | | dragging ) & & editingQuad ! = nullptr ;
@ -158,7 +204,12 @@ public:
} ;
} ;
if ( EditingQuad & & ! dragging ) {
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 ;
} else
} else
if ( EditingQuad & & dragging ) {
if ( EditingQuad & & dragging ) {
vf2d cursorPos = GetSnapPoint ( ) ;
vf2d cursorPos = GetSnapPoint ( ) ;
@ -180,7 +231,7 @@ public:
if ( highlightedQuad ! = nullptr ) {
if ( highlightedQuad ! = nullptr ) {
# pragma region Select a point on a collision quad.
# pragma region Select a point on a collision quad.
for ( size_t pointInd = 0 ; const vf2d & point : * highlightedQuad ) {
for ( size_t pointInd = 0 ; const vf2d & point : * highlightedQuad ) {
if ( geom2d : : line < float > ( point , view . ScreenToWorld ( GetMousePos ( ) ) ) . length ( ) < 4 ) {
if ( geom2d : : line < float > ( point , view . ScreenToWorld ( GetMousePos ( ) ) ) . length ( ) < 4 / view . GetWorldScale ( ) . x ) {
editingPoint = pointInd ;
editingPoint = pointInd ;
editingQuad = highlightedQuad ;
editingQuad = highlightedQuad ;
originalQuad = * highlightedQuad ;
originalQuad = * highlightedQuad ;
@ -198,24 +249,25 @@ public:
}
}
exitCollisionCheck :
exitCollisionCheck :
if ( EditingQuad & & ! dragging ) {
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 + + ;
editingPoint + + ;
}
}
}
}
if ( GetMouse ( Mouse : : RIGHT ) . bPressed | | GetKey ( ESCAPE ) . bPressed ) {
if ( GetMouse ( Mouse : : RIGHT ) . bPressed | | GetKey ( ESCAPE ) . bRelea sed ) {
if ( EditingQuad | | dragTranslate ) {
if ( EditingQuad | | dragTranslate ) {
editingPoint = 4 ;
ResetState ( ) ;
dragging = false ;
* editingQuad = originalQuad ;
editingQuad = nullptr ;
dragTranslate = false ;
}
}
}
}
} else {
} else {
if ( GetMouse ( Mouse : : LEFT ) . bPressed ) {
if ( GetMouse ( Mouse : : LEFT ) . bPressed ) {
Quadrilateral newQuad { GetSnapPoint ( ) } ;
Quadrilateral newQuad { GetSnapPoint ( ) } ;
tilesets [ activeTileset ] . objects [ selectedObj ] . collisionTiles . push_back ( newQuad ) ;
currentTileset . objects [ selectedObj ] . collisionTiles . push_back ( newQuad ) ;
dragging = true ;
dragging = true ;
editingQuad = const_cast < Quadrilateral * > ( & obj . collisionTiles . back ( ) ) ;
editingQuad = const_cast < Quadrilateral * > ( & obj . collisionTiles . back ( ) ) ;
originalQuad = * editingQuad ;
originalQuad = * editingQuad ;
@ -224,11 +276,8 @@ public:
if ( GetMouse ( Mouse : : RIGHT ) . bPressed & & ! EditingQuad & & ! dragTranslate ) {
if ( GetMouse ( Mouse : : RIGHT ) . bPressed & & ! EditingQuad & & ! dragTranslate ) {
if ( highlightedQuad ! = nullptr ) {
if ( highlightedQuad ! = nullptr ) {
std : : erase_if ( tilesets [ activeTileset ] . objects [ selectedObj ] . collisionTiles , [ & ] ( Quadrilateral & q ) { return & q = = highlightedQuad ; } ) ;
std : : erase_if ( currentTileset . objects [ selectedObj ] . collisionTiles , [ & ] ( Quadrilateral & q ) { return & q = = highlightedQuad ; } ) ;
editingPoint = 4 ;
ResetState ( ) ;
dragging = false ;
editingQuad = nullptr ;
dragTranslate = false ;
SaveFile ( ) ;
SaveFile ( ) ;
}
}
}
}
@ -240,12 +289,21 @@ public:
( * editingQuad ) [ 1 ] = vf2d { cursorPos . x , initialPoint . y } ;
( * editingQuad ) [ 1 ] = vf2d { cursorPos . x , initialPoint . y } ;
( * editingQuad ) [ 2 ] = GetSnapPoint ( ) ;
( * editingQuad ) [ 2 ] = GetSnapPoint ( ) ;
( * editingQuad ) [ 3 ] = vf2d { initialPoint . x , cursorPos . y } ;
( * editingQuad ) [ 3 ] = vf2d { initialPoint . x , cursorPos . y } ;
dragging = false ;
ResetState ( ) ;
SaveFile ( ) ;
SaveFile ( ) ;
} else
} else
if ( EditingQuad & & ! dragging ) {
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 ;
editingPoint = 4 ;
dragging = false ;
editingQuad = nullptr ;
dragTranslate = false ;
highlightedQuad = nullptr ;
SaveFile ( ) ;
SaveFile ( ) ;
} else
} else
if ( dragTranslate ) {
if ( dragTranslate ) {
@ -258,7 +316,7 @@ public:
}
}
void NewObjectUpdate ( ) {
void NewObjectUpdate ( ) {
const Tileset & tileset = tilesets [ activeTileset ] ;
const Tileset & tileset = currentTileset ;
if ( GetMouse ( Mouse : : LEFT ) . bReleased ) {
if ( GetMouse ( Mouse : : LEFT ) . bReleased ) {
dragNewObj = false ;
dragNewObj = false ;
@ -280,7 +338,7 @@ public:
geom2d : : rect < int > newObjRect { newUpperLeftTile , newLowerRightTile - newUpperLeftTile } ;
geom2d : : rect < int > newObjRect { 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.
//Check for intersection with other objects, if found then we deny creating this object this way.
bool intersectionFound = false ;
bool intersectionFound = false ;
for ( auto & [ name , obj ] : tileset . objects ) {
for ( auto & [ name , obj ] : tileset . objects ) {
@ -292,9 +350,9 @@ public:
}
}
if ( ! intersectionFound ) {
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 ;
newObj . name = objName ;
for ( int y = 0 ; y < newObjRect . size . y / tileset . tileheight ; y + + ) {
for ( int y = 0 ; y < newObjRect . size . y / tileset . tileheight ; y + + ) {
for ( int x = 0 ; x < newObjRect . size . x / tileset . tilewidth ; x + + ) {
for ( int x = 0 ; x < newObjRect . size . x / tileset . tilewidth ; x + + ) {
@ -309,33 +367,70 @@ public:
}
}
}
}
bool OnUserUpdate ( float fElapsedTime ) override
void OnTextEntryComplete ( const std : : string & sText ) override {
{
currentTileset . objects [ nameEditObj ] . name = sText ;
Clear ( VERY_DARK_BLUE ) ;
SaveFile ( ) ;
}
void EditorUpdate ( ) {
view . HandlePanAndZoom ( ) ;
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 ] ;
const Tileset & tileset = currentTileset ;
view . DrawDecal ( { 0 , 0 } , images [ activeTileset ] . Decal ( ) ) ;
const float CAMERA_MOVESPD = 150.f ;
for ( int y = 0 ; y < tileset . tilecount / tileset . columns ; y + + ) {
if ( ! nameBox - > m_bTextEdit ) {
for ( int x = 0 ; x < tileset . columns ; x + + ) {
if ( selectedObj . length ( ) > 0 & & GetKey ( R ) . bReleased ) {
view . DrawRectDecal ( vf2d { float ( x ) , float ( y ) } * tileset . tilewidth , vf2d { float ( tileset . tilewidth ) , float ( tileset . tileheight ) } , GREY ) ;
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 ) {
if ( editingQuad = = nullptr & & ! EditingQuad ) {
selectedObj = " " ;
selectedObj = " " ;
for ( auto & [ objName , obj ] : tileset . objects ) {
for ( auto & [ objName , obj ] : tileset . objects ) {
if ( geom2d : : contains ( obj . bounds , view . ScreenToWorld ( GetMousePos ( ) ) ) ) {
if ( geom2d : : contains ( obj . bounds , view . ScreenToWorld ( GetMousePos ( ) ) ) ) {
selectedObj = objName ;
selectedObj = objName ;
nameBox - > sText = obj . name ;
break ;
break ;
}
}
}
}
@ -343,8 +438,19 @@ public:
if ( createNewButton - > bPressed ) editButton - > bChecked = false ;
if ( createNewButton - > bPressed ) editButton - > bChecked = false ;
if ( editButton - > bPressed ) createNewButton - > 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 ( ) > 72 ) {
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 ( selectedObj . length ( ) = = 0 ) {
if ( GetMouse ( Mouse : : LEFT ) . bPressed ) {
if ( GetMouse ( Mouse : : LEFT ) . bPressed ) {
vf2d worldCoords = view . ScreenToWorld ( GetMousePos ( ) ) ;
vf2d worldCoords = view . ScreenToWorld ( GetMousePos ( ) ) ;
@ -358,13 +464,77 @@ public:
NewObjectUpdate ( ) ;
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 ) ;
}
}
}
//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 ) {
if ( selectedObj . length ( ) > 0 & & ! dragNewObj ) {
const TilesetObject & obj = tileset . objects . at ( selectedObj ) ;
const TilesetObject & obj = tileset . objects . at ( selectedObj ) ;
@ -405,14 +575,14 @@ public:
for ( bool highlighted = false ; const vf2d & point : quad ) {
for ( bool highlighted = false ; const vf2d & point : quad ) {
if ( highlightedQuad = = & quad ) {
if ( highlightedQuad = = & quad ) {
if ( geom2d : : line < float > ( point , view . ScreenToWorld ( GetMousePos ( ) ) ) . length ( ) < 4 & & ! highlighted ) {
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 , { 1.f , 1.f } , YELLOW ) ;
view . DrawRotatedDecal ( point , circle . Decal ( ) , 0.f , circle . Sprite ( ) - > Size ( ) / 2 , vf2d { 1.f , 1.f } / view . GetWorldScale ( ) , YELLOW ) ;
highlighted = true ;
highlighted = true ;
} else {
} 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 {
} 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 { 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 ) ;
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 ;
vi2d nameTextSize = GetTextSizeProp ( obj . name ) * 0.25f ;
view . GradientFillRectDecal ( obj . bounds . pos , nameTextSize + vf2d { 2 , 2 } , RED , { 255 , 0 , 0 , 64 } , { 255 , 0 , 0 , 64 } , RED ) ;
if ( ! geom2d : : overlaps ( geom2d : : rect < float > { obj . bounds . pos , nameTextSize + vf2d { 2 , 2 } } , view . ScreenToWorld ( GetMousePos ( ) ) ) ) {
view . DrawStringPropDecal ( obj . bounds . pos + vf2d { 1.25f , 1.25f } , objName , BLACK , vf2d { 0.25f , 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 , 1 } , objName , WHITE , vf2d { 0.25f , 0.25f } ) ;
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 } ) ;
if ( geom2d : : contains ( obj . bounds , view . ScreenToWorld ( GetMousePos ( ) ) ) ) {
selectedObj = objName ;
}
}
}
}
gui . Update ( this ) ;
gui . DrawDecal ( 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 ( createNewButton - > vPos + vf2d { 3 , 0 } , " Q " ) ;
DrawStringDecal ( editButton - > vPos + vf2d { 3 , 0 } , " E " ) ;
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 ;
return true ;
}
}
@ -480,7 +675,7 @@ public:
int main ( )
int main ( )
{
{
TiledCollisionEditor demo ;
TiledCollisionEditor demo ;
if ( demo . Construct ( 640 , 180 , 4 , 4 ) )
if ( demo . Construct ( 640 , 180 , 4 , 4 , false , true ) )
demo . Start ( ) ;
demo . Start ( ) ;
return 0 ;
return 0 ;