summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkasull <qsullian@gmail.com>2026-03-11 00:24:47 -0400
committerkasull <qsullian@gmail.com>2026-03-11 00:24:47 -0400
commitae6718ec0fb21077b767e189aca26b0fed488775 (patch)
treea4216103d8a9a77edbc470dc4ab094e77ac30261
parentbc1ea16c5be92e3bc810b0a30e01fbc9a7f191a9 (diff)
editor object placement and context menus
replace the editor menubar dropdown flow with a reusable context menu component merge sprite/entity placement into a single object tool persist entities, and add undo support for created sprites and entities
-rw-r--r--src/editor/editor.cpp138
-rw-r--r--src/editor/editor.h55
-rw-r--r--src/editor/editor_contextmenu.cpp235
-rw-r--r--src/editor/editor_layout.cpp2
-rw-r--r--src/editor/editor_menubar.cpp337
-rw-r--r--src/editor/editor_window.cpp28
-rw-r--r--src/editor/properties.cpp16
-rw-r--r--src/editor/toolview.cpp169
-rw-r--r--src/editor/view2d.cpp149
-rw-r--r--src/game/world/map.cpp56
-rw-r--r--src/game/world/map.h2
-rw-r--r--src/gui/list.cpp3
12 files changed, 903 insertions, 287 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp
index 3873913..dee5080 100644
--- a/src/editor/editor.cpp
+++ b/src/editor/editor.cpp
@@ -1,6 +1,7 @@
#include "editor.h"
#include "../game.h"
+#include "../game/object.h"
GAME_EDITOR* editor = 0;
@@ -20,6 +21,7 @@ void editor_clear_gui_state_refs( GAME_EDITOR* e ) {
e->gui.status = 0;
e->gui.assets_scroll = 0;
+ e->gui.contextmenu = 0;
e->gui.header_toolbar = 0;
e->gui.header_viewtype_label = 0;
e->gui.header_back = 0;
@@ -82,6 +84,18 @@ static U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) {
return 1;
}
+static U8 editor_sprite_eq( const MAP_SPRITE& a, const MAP_SPRITE& b ) {
+ return a.pos == b.pos
+ && a.size == b.size
+ && editor_clr_eq( a.clr, b.clr )
+ && a.tex == b.tex;
+}
+
+static U8 editor_entity_eq( const MAP_ENTITY& a, const MAP_ENTITY& b ) {
+ return a.pos == b.pos
+ && a.classid == b.classid;
+}
+
static I32 editor_clamp_valid_idx( I32 idx, I32 size ) {
if( size <= 0 )
return -1;
@@ -139,6 +153,24 @@ static I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 ex
} );
}
+static I32 editor_find_sprite_idx( WORLD_MAP* map, const MAP_SPRITE& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->sprites.size, expected_idx, fn( I32 i ) {
+ return editor_sprite_eq( map->sprites[i], want );
+ } );
+}
+
+static I32 editor_find_entity_idx( WORLD_MAP* map, const MAP_ENTITY& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->entities.size, expected_idx, fn( I32 i ) {
+ return editor_entity_eq( map->entities[i], want );
+ } );
+}
+
static void editor_mark_clear_wall_refs( GAME_EDITOR* e, MAP_WALL* w, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
if( e->gui.props ) {
if( e->gui.props->seltype == EDITOR_SELECT_WALL && e->gui.props->curselect == w )
@@ -196,6 +228,30 @@ static void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* cle
}
}
+static void editor_mark_clear_sprite_refs( GAME_EDITOR* e, MAP_SPRITE* s, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
+ if( e->gui.props && e->gui.props->seltype == EDITOR_SELECT_SPRITE && e->gui.props->curselect == s )
+ *clear_props_select = 1;
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_SPRITE && e->gui.v2d->curselect == s )
+ *clear_view_select = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_SPRITE && e->gui.v2d->curdrag == s )
+ *clear_view_drag = 1;
+ }
+}
+
+static void editor_mark_clear_entity_refs( GAME_EDITOR* e, MAP_ENTITY* ent, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
+ if( e->gui.props && e->gui.props->seltype == EDITOR_SELECT_ENT && e->gui.props->curselect == ent )
+ *clear_props_select = 1;
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_ENT && e->gui.v2d->curselect == ent )
+ *clear_view_select = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_ENT && e->gui.v2d->curdrag == ent )
+ *clear_view_drag = 1;
+ }
+}
+
void editor_undo_clear( GAME_EDITOR* e ) {
if( !e )
return;
@@ -243,6 +299,38 @@ void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx ) {
editor_push_undo_action( e, action );
}
+void editor_undo_record_create_sprite( GAME_EDITOR* e, I32 start_idx ) {
+ if( !e || !e->map )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->sprites.size )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_SPRITES;
+ action.start_idx = start_idx;
+ action.sprites.push( map->sprites[start_idx] );
+
+ editor_push_undo_action( e, action );
+}
+
+void editor_undo_record_create_entity( GAME_EDITOR* e, I32 start_idx ) {
+ if( !e || !e->map )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->entities.size )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_ENTITIES;
+ action.start_idx = start_idx;
+ action.entities.push( map->entities[start_idx] );
+
+ editor_push_undo_action( e, action );
+}
+
static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
if( !e || !e->map )
return 0;
@@ -289,6 +377,38 @@ static U8 editor_apply_undo_action_reverse( GAME_EDITOR* e, GAME_EDITOR::EDITOR_
map->polygons.erase( idx );
}
} break;
+ case EDITOR_UNDO_CREATE_SPRITES: {
+ I32 count = (I32)action.sprites.size;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->sprites.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = editor_find_sprite_idx( map, action.sprites[i], expected );
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->sprites.size );
+
+ MAP_SPRITE* s = &map->sprites[idx];
+ editor_mark_clear_sprite_refs( e, s, &clear_props_select, &clear_view_select, &clear_view_drag );
+ map->sprites.erase( idx );
+ }
+ } break;
+ case EDITOR_UNDO_CREATE_ENTITIES: {
+ I32 count = (I32)action.entities.size;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->entities.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = editor_find_entity_idx( map, action.entities[i], expected );
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->entities.size );
+
+ MAP_ENTITY* ent = &map->entities[idx];
+ editor_mark_clear_entity_refs( e, ent, &clear_props_select, &clear_view_select, &clear_view_drag );
+ map->entities.erase( idx );
+ }
+ } break;
default:
return 0;
}
@@ -333,6 +453,22 @@ static U8 editor_apply_undo_action_forward( GAME_EDITOR* e, GAME_EDITOR::EDITOR_
map->polygons.push( *p );
} );
} break;
+ case EDITOR_UNDO_CREATE_SPRITES: {
+ if( !action.sprites.size )
+ return 0;
+
+ action.sprites.each( fn( MAP_SPRITE* s ) {
+ map->sprites.push( *s );
+ } );
+ } break;
+ case EDITOR_UNDO_CREATE_ENTITIES: {
+ if( !action.entities.size )
+ return 0;
+
+ action.entities.each( fn( MAP_ENTITY* ent ) {
+ map->entities.push( *ent );
+ } );
+ } break;
default:
return 0;
}
@@ -415,6 +551,8 @@ GAME_EDITOR* editor_create( GAME_DATA* game ) {
e->tool.polysides = EDITOR_DEFAULT_POLY_SIDES;
e->tool.wallheight = EDITOR_DEFAULT_WALL_HEIGHT;
e->tool.placementheight = EDITOR_DEFAULT_PLACEMENT_HEIGHT;
+ e->tool.entclass = OBJCLASS_TRIGGER;
+ e->tool.objecttype = EDITOR_OBJECT_SPRITE;
e->game = game;
gui_init( game );
diff --git a/src/editor/editor.h b/src/editor/editor.h
index 6735d47..828f2e4 100644
--- a/src/editor/editor.h
+++ b/src/editor/editor.h
@@ -14,8 +14,7 @@ enum EditorTools_t {
EDITOR_TOOL_SELECT,
EDITOR_TOOL_WALL,
EDITOR_TOOL_POLY,
- EDITOR_TOOL_SPRITE,
- EDITOR_TOOL_ENT
+ EDITOR_TOOL_OBJECT
};
enum EditorWallShape_t {
@@ -38,7 +37,9 @@ enum EditorSelectType_t {
enum EditorUndoType_t {
EDITOR_UNDO_NONE = 0,
EDITOR_UNDO_CREATE_WALLS = 1,
- EDITOR_UNDO_CREATE_POLY = 2
+ EDITOR_UNDO_CREATE_POLY = 2,
+ EDITOR_UNDO_CREATE_SPRITES = 3,
+ EDITOR_UNDO_CREATE_ENTITIES = 4
};
enum EditorInfoBoxType_t {
@@ -58,15 +59,22 @@ enum Editor2DViewType_t {
EDITOR_2DVIEW_FRONT_ELEVATION = 2
};
-enum EditorMenubarEntryType_t {
- EDITOR_MENUBAR_ENTRY_FUNCTION = 0,
- EDITOR_MENUBAR_ENTRY_SUBMENU = 1
+enum EditorObjectType_t {
+ EDITOR_OBJECT_SPRITE = 0,
+ EDITOR_OBJECT_ENTITY = 1
};
-struct EDITOR_MENUBAR_ENTRY {
+typedef void( *EDITOR_CONTEXTMENU_CALLBACK )( void* data );
+struct EDITOR_CONTEXTMENU_ITEM;
+typedef U8( *EDITOR_CONTEXTMENU_ENABLED_CALLBACK )( const EDITOR_CONTEXTMENU_ITEM* item );
+
+struct EDITOR_CONTEXTMENU_ITEM {
char text[64]{};
- U8 type{};
- LIST<EDITOR_MENUBAR_ENTRY> entries{};
+ void* data{};
+ EDITOR_CONTEXTMENU_CALLBACK cb{};
+ EDITOR_CONTEXTMENU_ENABLED_CALLBACK enabled_cb{};
+ U8 enabled{1};
+ LIST<EDITOR_CONTEXTMENU_ITEM> items{};
};
struct GAME_EDITOR;
@@ -85,6 +93,7 @@ struct GAME_EDITOR_TOOL {
/* entity */
U32 entclass;
void* entprops;
+ I32 objecttype;
};
struct GAME_EDITOR {
@@ -115,6 +124,7 @@ struct GAME_EDITOR {
I32 view_mode{};
I32 view2d_type{};
+ struct GUI_EDITOR_CONTEXTMENU* contextmenu{};
GUI_BASE* header_toolbar{};
GUI_LABEL* header_viewtype_label{};
GUI_BUTTON* header_back{};
@@ -145,6 +155,8 @@ struct GAME_EDITOR {
I32 start_idx{};
LIST<MAP_WALL> walls{};
LIST<MAP_POLYGON> polys{};
+ LIST<MAP_SPRITE> sprites{};
+ LIST<MAP_ENTITY> entities{};
};
LIST<EDITOR_UNDO_ACTION> undo_actions{};
LIST<EDITOR_UNDO_ACTION> redo_actions{};
@@ -197,11 +209,14 @@ extern void editor_create_map_view( GAME_EDITOR* e );
extern void editor_update_properties_column( GAME_EDITOR* e );
extern const char* editor_tool_name();
extern void editor_set_view_mode( I32 mode );
-extern void editor_toolbar_set_open( struct GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open );
-extern U8 editor_toolbar_menu_open( const struct GUI_EDITOR_TOOLBAR* bar );
+extern void editor_show_contextmenu( I32 x, I32 y, LIST<EDITOR_CONTEXTMENU_ITEM>* items, const char* title = 0 );
+extern void editor_hide_contextmenu();
+extern U8 editor_contextmenu_open();
extern void editor_undo_clear( GAME_EDITOR* e );
extern void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 count );
extern void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx );
+extern void editor_undo_record_create_sprite( GAME_EDITOR* e, I32 start_idx );
+extern void editor_undo_record_create_entity( GAME_EDITOR* e, I32 start_idx );
extern U8 editor_undo( GAME_EDITOR* e );
extern U8 editor_redo( GAME_EDITOR* e );
@@ -233,8 +248,11 @@ struct GUI_EDITOR_2DVIEW : GUI_VIEW {
U8 poly_drag;
VEC2 poly_start;
VEC2 poly_end;
+ VEC3 pending_object_pos;
I32 pending_wall_undo_idx;
+ I32 pending_object_undo_idx;
+ U8 pending_object_undo_type;
};
struct GUI_EDITOR_PROPVIEW : GUI_VIEW {
@@ -247,6 +265,8 @@ struct GUI_EDITOR_PROPVIEW : GUI_VIEW {
struct GUI_EDITOR_TOOLVIEW : GUI_VIEW {
GUI_VIEW* itemview;
U8 wallshape_dropdown_open;
+ U8 objecttype_dropdown_open;
+ U8 entclass_dropdown_open;
I32 scroll;
I32 content_h;
};
@@ -283,12 +303,19 @@ struct GUI_EDITOR_VIEWTYPE_SEGMENT : GUI_BASE {
I32 held_seg{-1};
};
+struct GUI_EDITOR_CONTEXTMENU : GUI_BASE {
+ LIST<EDITOR_CONTEXTMENU_ITEM>* items{};
+ U8 initheld{1};
+ U8 held{};
+ I32 held_idx{-1};
+ char title[64]{};
+};
+
struct GUI_EDITOR_TOOLBAR : GUI_BASE {
U8 held{};
I32 held_root{-1};
- I32 held_sub{-1};
- I32 open_root{-1};
- LIST<EDITOR_MENUBAR_ENTRY> entries{};
+ I32 active_root{-1};
+ LIST<EDITOR_CONTEXTMENU_ITEM> entries{};
};
extern GUI_EDITORWINDOW* gui_editorwindow( I32 w, I32 h );
diff --git a/src/editor/editor_contextmenu.cpp b/src/editor/editor_contextmenu.cpp
new file mode 100644
index 0000000..2b0bb1f
--- /dev/null
+++ b/src/editor/editor_contextmenu.cpp
@@ -0,0 +1,235 @@
+#include "editor_gui_internal.h"
+
+static U8 editor_contextmenu_item_enabled( const EDITOR_CONTEXTMENU_ITEM* item ) {
+ if( !item )
+ return 0;
+
+ if( item->enabled_cb )
+ return item->enabled_cb( item );
+
+ return item->enabled;
+}
+
+static I32 editor_contextmenu_title_h( const GUI_EDITOR_CONTEXTMENU* menu ) {
+ return ( menu && menu->title[0] ) ? 18 : 0;
+}
+
+static I32 editor_contextmenu_row_h() {
+ return 20;
+}
+
+static I32 editor_contextmenu_width( LIST<EDITOR_CONTEXTMENU_ITEM>* items, const char* title ) {
+ I32 width = EDITOR_TOOLBAR_DROPDOWN_W;
+ if( title && title[0] ) {
+ I32 text_w = 0;
+ gui_draw_get_str_bounds( &text_w, 0, FNT_JPN12, "%s", title );
+ width = max( width, text_w + 18 );
+ }
+
+ if( !items )
+ return width;
+
+ for( I32 i = 0; i < (I32)items->size; ++i ) {
+ I32 text_w = 0;
+ gui_draw_get_str_bounds( &text_w, 0, FNT_JPN12, "%s", ( *items )[i].text );
+ width = max( width, text_w + 20 );
+ }
+
+ return width;
+}
+
+static I32 editor_contextmenu_height( LIST<EDITOR_CONTEXTMENU_ITEM>* items, const char* title ) {
+ I32 height = 4 + editor_contextmenu_row_h() * ( items ? (I32)items->size : 0 );
+ if( title && title[0] )
+ height += 18 + 1;
+ return max( height, editor_contextmenu_row_h() + 4 );
+}
+
+static I32 editor_contextmenu_items_y( GUI_EDITOR_CONTEXTMENU* menu ) {
+ I32 y = gui_rely( menu ) + 2;
+ if( menu->title[0] )
+ y += editor_contextmenu_title_h( menu ) + 1;
+ return y;
+}
+
+static I32 editor_contextmenu_hit_test( GUI_EDITOR_CONTEXTMENU* menu, I32 mx, I32 my ) {
+ if( !menu || !menu->items )
+ return -1;
+
+ I32 x = gui_relx( menu );
+ I32 y = gui_rely( menu );
+ if( mx < x || mx >= x + menu->w || my < y || my >= y + menu->h )
+ return -1;
+
+ I32 items_y = editor_contextmenu_items_y( menu );
+ if( my < items_y )
+ return -1;
+
+ I32 idx = ( my - items_y ) / editor_contextmenu_row_h();
+ if( idx < 0 || idx >= (I32)menu->items->size )
+ return -1;
+
+ return idx;
+}
+
+static void gui_editor_contextmenu_draw_fn( void* ptr ) {
+ GUI_EDITOR_CONTEXTMENU* menu = (GUI_EDITOR_CONTEXTMENU*)ptr;
+ if( !menu || !menu->items || !menu->items->size )
+ return;
+
+ I32 x = gui_relx( menu );
+ I32 y = gui_rely( menu );
+ I32 mx = 0, my = 0;
+ if( editor_menu_hover_mask_active ) {
+ mx = editor_menu_hover_real_x;
+ my = editor_menu_hover_real_y;
+ } else {
+ gui_cursor_pos( &mx, &my );
+ }
+ I32 hit = editor_contextmenu_hit_test( menu, mx, my );
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ CLR hover_fill = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
+ CLR active_fill = CLR::blend( ui_clr.bg_sec, ui_clr.border, 0.22f );
+
+ CLR border = gui_is_fg_window( menu ) ? ui_clr.border : ui_clr.border_inactive;
+ gui_draw_frect( x, y, menu->w, menu->h, border );
+ gui_draw_frect( x + 1, y + 1, max( 1, menu->w - 2 ), max( 1, menu->h - 2 ), ui_clr.bg_sec );
+
+ I32 cy = y + 2;
+ if( menu->title[0] ) {
+ gui_draw_str( x + 8, cy + 1, ALIGN_L, FNT_JPN12, ui_clr.txt, "%s", menu->title );
+ cy += editor_contextmenu_title_h( menu );
+ gui_draw_line( x + 1, cy, x + menu->w - 2, cy, border );
+ cy += 1;
+ }
+
+ for( I32 i = 0; i < (I32)menu->items->size; ++i ) {
+ EDITOR_CONTEXTMENU_ITEM* item = &( *menu->items )[i];
+ U8 enabled = editor_contextmenu_item_enabled( item );
+ U8 hovered = hit == i;
+ U8 active = menu->held && menu->held_idx == i && m1 && enabled;
+
+ CLR fill = ui_clr.bg_sec;
+ if( active )
+ fill = active_fill;
+ else if( hovered )
+ fill = hover_fill;
+
+ gui_draw_frect( x + 1, cy + i * editor_contextmenu_row_h(), max( 1, menu->w - 2 ), editor_contextmenu_row_h(), fill );
+ gui_draw_str(
+ x + 8,
+ cy + i * editor_contextmenu_row_h() + 2,
+ ALIGN_L,
+ FNT_JPN12,
+ enabled ? ui_clr.txt : ui_clr.txt_inactive,
+ "%s",
+ item->text
+ );
+ }
+}
+
+static void gui_editor_contextmenu_input_fn( void* ptr ) {
+ GUI_EDITOR_CONTEXTMENU* menu = (GUI_EDITOR_CONTEXTMENU*)ptr;
+ if( !menu || !menu->items || !menu->items->size )
+ return;
+
+ U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
+ U8 m2 = gui_mbutton_down( GUI_MBTNRIGHT );
+ if( menu->initheld ) {
+ if( m1 || m2 )
+ return;
+ menu->initheld = 0;
+ return;
+ }
+
+ I32 mx = 0, my = 0;
+ gui_cursor_pos( &mx, &my );
+ I32 hit = editor_contextmenu_hit_test( menu, mx, my );
+
+ if( m1 || m2 ) {
+ if( !menu->held ) {
+ menu->held = 1;
+ menu->held_idx = hit;
+ }
+ return;
+ }
+
+ if( !menu->held )
+ return;
+
+ if( hit == -1 ) {
+ editor_hide_contextmenu();
+ return;
+ }
+
+ if( hit == menu->held_idx ) {
+ EDITOR_CONTEXTMENU_ITEM* item = &( *menu->items )[hit];
+ if( editor_contextmenu_item_enabled( item ) && item->cb ) {
+ item->cb( item->data );
+ editor_hide_contextmenu();
+ return;
+ }
+ }
+
+ menu->held = 0;
+ menu->held_idx = -1;
+}
+
+void editor_hide_contextmenu() {
+ if( !editor || !editor->gui.contextmenu )
+ return;
+
+ GUI_EDITOR_CONTEXTMENU* menu = editor->gui.contextmenu;
+ editor->gui.contextmenu = 0;
+
+ if( menu->parent ) {
+ I32 idx = menu->parent->children.idx_of( (GUI_BASE*)menu );
+ if( idx != -1 )
+ menu->parent->children.erase( idx );
+ }
+
+ GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)editor->gui.header_toolbar;
+ if( bar )
+ bar->active_root = -1;
+
+ gui_free( menu );
+}
+
+U8 editor_contextmenu_open() {
+ return editor && editor->gui.contextmenu && editor->gui.contextmenu->enabled;
+}
+
+void editor_show_contextmenu( I32 x, I32 y, LIST<EDITOR_CONTEXTMENU_ITEM>* items, const char* title ) {
+ editor_hide_contextmenu();
+ if( !editor || !editor->wnd || !items || !items->size )
+ return;
+
+ GUI_EDITOR_CONTEXTMENU* menu = new GUI_EDITOR_CONTEXTMENU;
+ menu->items = items;
+ menu->draw_fn = gui_editor_contextmenu_draw_fn;
+ menu->input_fn = gui_editor_contextmenu_input_fn;
+ menu->enabled = 1;
+ menu->initheld = 1;
+ menu->held = 0;
+ menu->held_idx = -1;
+ menu->parent = editor->wnd;
+ snprintf( menu->name, sizeof( menu->name ), "EDITOR_CONTEXTMENU" );
+ snprintf( menu->title, sizeof( menu->title ), "%s", title ? title : "" );
+
+ menu->w = editor_contextmenu_width( items, title );
+ menu->h = editor_contextmenu_height( items, title );
+ menu->xbound = menu->w;
+ menu->ybound = menu->h;
+
+ if( x + menu->w > editor->wnd->w - 2 )
+ x = max( 1, editor->wnd->w - menu->w - 2 );
+ if( y + menu->h > editor->wnd->h - 2 )
+ y = max( 1, editor->wnd->h - menu->h - 2 );
+ if( x < 1 ) x = 1;
+ if( y < 1 ) y = 1;
+
+ menu->x = x;
+ menu->y = y;
+ editor->wnd->children.push( menu );
+ editor->gui.contextmenu = menu;
+}
diff --git a/src/editor/editor_layout.cpp b/src/editor/editor_layout.cpp
index f98ef00..de3ffb3 100644
--- a/src/editor/editor_layout.cpp
+++ b/src/editor/editor_layout.cpp
@@ -151,7 +151,7 @@ void editor_layout_start_menu( GAME_EDITOR* e ) {
}
static void editor_layout_tool_buttons( GUI_EDITORWINDOW* wnd, const EDITOR_LAYOUT& l ) {
- const char* names[] = { "none", "select", "wall", "poly", "sprite", "ent" };
+ const char* names[] = { "none", "select", "wall", "poly", "object" };
I32 y = l.tool_btn_y;
for( U32 i = 0; i < sizeof( names ) / sizeof( names[0] ); ++i ) {
GUI_BUTTON* btn = (GUI_BUTTON*)gui_find_node( wnd, names[i] );
diff --git a/src/editor/editor_menubar.cpp b/src/editor/editor_menubar.cpp
index c15053f..b89d1c6 100644
--- a/src/editor/editor_menubar.cpp
+++ b/src/editor/editor_menubar.cpp
@@ -35,39 +35,8 @@ void editor_apply_view_mode( GAME_EDITOR* e ) {
e->gui.v3d->enabled = 1;
}
-U8 editor_toolbar_menu_open( const GUI_EDITOR_TOOLBAR* bar ) {
- return bar && bar->open_root >= 0;
-}
-
-static I32 editor_toolbar_find_root( GUI_EDITOR_TOOLBAR* bar, const char* text ) {
- if( !bar || !text )
- return -1;
-
- for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
- if( !strcmp( bar->entries[i].text, text ) )
- return i;
- }
-
- return -1;
-}
-
-void editor_toolbar_set_open( GUI_EDITOR_TOOLBAR* bar, U8 file_open, U8 edit_open ) {
- if( !bar )
- return;
-
- bar->open_root = -1;
- if( file_open )
- bar->open_root = editor_toolbar_find_root( bar, "file" );
- else if( edit_open )
- bar->open_root = editor_toolbar_find_root( bar, "edit" );
-}
-
void editor_toolbar_close_menu() {
- if( !editor )
- return;
-
- GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)editor->gui.header_toolbar;
- editor_toolbar_set_open( bar, 0, 0 );
+ editor_hide_contextmenu();
}
void editor_set_view_mode( I32 mode ) {
@@ -97,13 +66,53 @@ void editor_raise_header_toolbar( GAME_EDITOR* e ) {
parent->children.push( bar );
}
-static void editor_menubar_set_entry( EDITOR_MENUBAR_ENTRY* entry, const char* text, U8 type ) {
- if( !entry )
+static void editor_contextmenu_set_item(
+ EDITOR_CONTEXTMENU_ITEM* item,
+ const char* text,
+ EDITOR_CONTEXTMENU_CALLBACK cb = 0,
+ void* data = 0,
+ EDITOR_CONTEXTMENU_ENABLED_CALLBACK enabled_cb = 0
+) {
+ if( !item )
return;
- entry->entries.clear();
- snprintf( entry->text, sizeof( entry->text ), "%s", text );
- entry->type = type;
+ item->items.clear();
+ snprintf( item->text, sizeof( item->text ), "%s", text ? text : "" );
+ item->cb = cb;
+ item->data = data;
+ item->enabled = 1;
+ item->enabled_cb = enabled_cb;
+}
+
+static void editor_contextmenu_save_cb( void* ) {
+ if( editor )
+ editor_save_map( editor );
+}
+
+static void editor_contextmenu_undo_cb( void* ) {
+ if( editor )
+ editor_undo( editor );
+}
+
+static void editor_contextmenu_redo_cb( void* ) {
+ if( editor )
+ editor_redo( editor );
+}
+
+static void editor_contextmenu_set_view_mode_cb( void* data ) {
+ editor_set_view_mode( (I32)(I64)data );
+}
+
+static void editor_contextmenu_set_tool_cb( void* data ) {
+ editor_settool( (U8)(I64)data );
+}
+
+static U8 editor_contextmenu_undo_enabled( const EDITOR_CONTEXTMENU_ITEM* ) {
+ return editor && editor->undo_actions.size > 0;
+}
+
+static U8 editor_contextmenu_redo_enabled( const EDITOR_CONTEXTMENU_ITEM* ) {
+ return editor && editor->redo_actions.size > 0;
}
static void editor_toolbar_init_entries( GUI_EDITOR_TOOLBAR* bar ) {
@@ -113,32 +122,31 @@ static void editor_toolbar_init_entries( GUI_EDITOR_TOOLBAR* bar ) {
bar->entries.clear();
bar->entries.resize( 4 );
- editor_menubar_set_entry( &bar->entries[0], "file", EDITOR_MENUBAR_ENTRY_SUBMENU );
- bar->entries[0].entries.resize( 1 );
- editor_menubar_set_entry( &bar->entries[0].entries[0], "save", EDITOR_MENUBAR_ENTRY_FUNCTION );
-
- editor_menubar_set_entry( &bar->entries[1], "edit", EDITOR_MENUBAR_ENTRY_SUBMENU );
- bar->entries[1].entries.resize( 2 );
- editor_menubar_set_entry( &bar->entries[1].entries[0], "undo", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[1].entries[1], "redo", EDITOR_MENUBAR_ENTRY_FUNCTION );
-
- editor_menubar_set_entry( &bar->entries[2], "view", EDITOR_MENUBAR_ENTRY_SUBMENU );
- bar->entries[2].entries.resize( 3 );
- editor_menubar_set_entry( &bar->entries[2].entries[0], "2d view", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[2].entries[1], "3d view", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[2].entries[2], "simulation", EDITOR_MENUBAR_ENTRY_FUNCTION );
-
- editor_menubar_set_entry( &bar->entries[3], "tools", EDITOR_MENUBAR_ENTRY_SUBMENU );
- bar->entries[3].entries.resize( 6 );
- editor_menubar_set_entry( &bar->entries[3].entries[0], "none", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[3].entries[1], "select", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[3].entries[2], "wall", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[3].entries[3], "poly", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[3].entries[4], "sprite", EDITOR_MENUBAR_ENTRY_FUNCTION );
- editor_menubar_set_entry( &bar->entries[3].entries[5], "ent", EDITOR_MENUBAR_ENTRY_FUNCTION );
+ editor_contextmenu_set_item( &bar->entries[0], "file" );
+ bar->entries[0].items.resize( 1 );
+ editor_contextmenu_set_item( &bar->entries[0].items[0], "save", editor_contextmenu_save_cb );
+
+ editor_contextmenu_set_item( &bar->entries[1], "edit" );
+ bar->entries[1].items.resize( 2 );
+ editor_contextmenu_set_item( &bar->entries[1].items[0], "undo", editor_contextmenu_undo_cb, 0, editor_contextmenu_undo_enabled );
+ editor_contextmenu_set_item( &bar->entries[1].items[1], "redo", editor_contextmenu_redo_cb, 0, editor_contextmenu_redo_enabled );
+
+ editor_contextmenu_set_item( &bar->entries[2], "view" );
+ bar->entries[2].items.resize( 3 );
+ editor_contextmenu_set_item( &bar->entries[2].items[0], "2d view", editor_contextmenu_set_view_mode_cb, (void*)(I64)EDITOR_VIEWMODE_2D );
+ editor_contextmenu_set_item( &bar->entries[2].items[1], "3d view", editor_contextmenu_set_view_mode_cb, (void*)(I64)EDITOR_VIEWMODE_3D );
+ editor_contextmenu_set_item( &bar->entries[2].items[2], "simulation", editor_contextmenu_set_view_mode_cb, (void*)(I64)EDITOR_VIEWMODE_SIM );
+
+ editor_contextmenu_set_item( &bar->entries[3], "tools" );
+ bar->entries[3].items.resize( 5 );
+ editor_contextmenu_set_item( &bar->entries[3].items[0], "none", editor_contextmenu_set_tool_cb, (void*)(I64)EDITOR_TOOL_NONE );
+ editor_contextmenu_set_item( &bar->entries[3].items[1], "select", editor_contextmenu_set_tool_cb, (void*)(I64)EDITOR_TOOL_SELECT );
+ editor_contextmenu_set_item( &bar->entries[3].items[2], "wall", editor_contextmenu_set_tool_cb, (void*)(I64)EDITOR_TOOL_WALL );
+ editor_contextmenu_set_item( &bar->entries[3].items[3], "poly", editor_contextmenu_set_tool_cb, (void*)(I64)EDITOR_TOOL_POLY );
+ editor_contextmenu_set_item( &bar->entries[3].items[4], "object", editor_contextmenu_set_tool_cb, (void*)(I64)EDITOR_TOOL_OBJECT );
}
-static I32 editor_toolbar_root_width( EDITOR_MENUBAR_ENTRY* entry ) {
+static I32 editor_toolbar_root_width( const EDITOR_CONTEXTMENU_ITEM* entry ) {
if( !entry )
return 44;
@@ -147,20 +155,6 @@ static I32 editor_toolbar_root_width( EDITOR_MENUBAR_ENTRY* entry ) {
return max( 44, text_w + 16 );
}
-static I32 editor_toolbar_dropdown_width( EDITOR_MENUBAR_ENTRY* entry ) {
- if( !entry || entry->type != EDITOR_MENUBAR_ENTRY_SUBMENU )
- return EDITOR_TOOLBAR_DROPDOWN_W;
-
- I32 width = EDITOR_TOOLBAR_DROPDOWN_W;
- for( I32 i = 0; i < (I32)entry->entries.size; ++i ) {
- I32 text_w = 0;
- gui_draw_get_str_bounds( &text_w, 0, FNT_JPN12, "%s", entry->entries[i].text );
- width = max( width, text_w + 20 );
- }
-
- return width;
-}
-
static void editor_toolbar_root_rect( GUI_EDITOR_TOOLBAR* bar, I32 idx, I32* rx, I32* ry, I32* rw, I32* rh ) {
I32 x = gui_relx( bar );
I32 y = gui_rely( bar );
@@ -168,35 +162,22 @@ static void editor_toolbar_root_rect( GUI_EDITOR_TOOLBAR* bar, I32 idx, I32* rx,
for( I32 i = 0; i < idx; ++i )
cx += editor_toolbar_root_width( &bar->entries[i] ) + 4;
- I32 w = editor_toolbar_root_width( &bar->entries[idx] );
- *rx = cx;
- *ry = y + 2;
- *rw = w;
- *rh = max( 1, bar->h - 4 );
+ if( rx ) *rx = cx;
+ if( ry ) *ry = y + 2;
+ if( rw ) *rw = editor_toolbar_root_width( &bar->entries[idx] );
+ if( rh ) *rh = max( 1, bar->h - 4 );
}
-static void editor_toolbar_sub_rect( GUI_EDITOR_TOOLBAR* bar, I32 root_idx, I32 row, I32* rx, I32* ry, I32* rw, I32* rh ) {
- I32 root_x = 0, root_y = 0, root_w = 0, root_h = 0;
- editor_toolbar_root_rect( bar, root_idx, &root_x, &root_y, &root_w, &root_h );
- I32 row_h = max( 18, root_h );
-
- *rx = root_x;
- *ry = gui_rely( bar ) + bar->h + row * row_h;
- *rw = editor_toolbar_dropdown_width( &bar->entries[root_idx] );
- *rh = row_h;
-}
+struct EDITOR_TOOLBAR_HIT {
+ I32 root{-1};
+};
static U8 editor_toolbar_pt_in_rect( I32 mx, I32 my, I32 x, I32 y, I32 w, I32 h ) {
return mx >= x && mx < x + w && my >= y && my < y + h;
}
-struct EDITOR_MENUBAR_HIT {
- I32 root{-1};
- I32 sub{-1};
-};
-
-static EDITOR_MENUBAR_HIT editor_toolbar_hit_test( GUI_EDITOR_TOOLBAR* bar, I32 mx, I32 my ) {
- EDITOR_MENUBAR_HIT hit{};
+static EDITOR_TOOLBAR_HIT editor_toolbar_hit_test( GUI_EDITOR_TOOLBAR* bar, I32 mx, I32 my ) {
+ EDITOR_TOOLBAR_HIT hit{};
for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
I32 x = 0, y = 0, w = 0, h = 0;
editor_toolbar_root_rect( bar, i, &x, &y, &w, &h );
@@ -206,76 +187,9 @@ static EDITOR_MENUBAR_HIT editor_toolbar_hit_test( GUI_EDITOR_TOOLBAR* bar, I32
}
}
- if( bar->open_root >= 0 && bar->open_root < (I32)bar->entries.size ) {
- EDITOR_MENUBAR_ENTRY* root = &bar->entries[bar->open_root];
- if( root->type == EDITOR_MENUBAR_ENTRY_SUBMENU ) {
- for( I32 i = 0; i < (I32)root->entries.size; ++i ) {
- I32 x = 0, y = 0, w = 0, h = 0;
- editor_toolbar_sub_rect( bar, bar->open_root, i, &x, &y, &w, &h );
- if( editor_toolbar_pt_in_rect( mx, my, x, y, w, h ) ) {
- hit.root = bar->open_root;
- hit.sub = i;
- return hit;
- }
- }
- }
- }
-
return hit;
}
-static U8 editor_toolbar_entry_enabled( const EDITOR_MENUBAR_ENTRY* root, const EDITOR_MENUBAR_ENTRY* entry ) {
- if( !root || !entry )
- return 0;
-
- if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "undo" ) )
- return editor && editor->undo_actions.size > 0;
- if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "redo" ) )
- return editor && editor->redo_actions.size > 0;
-
- return 1;
-}
-
-static void editor_toolbar_invoke( const EDITOR_MENUBAR_ENTRY* root, const EDITOR_MENUBAR_ENTRY* entry ) {
- if( !root || !entry || !editor )
- return;
- if( !editor_toolbar_entry_enabled( root, entry ) )
- return;
-
- if( !strcmp( root->text, "file" ) && !strcmp( entry->text, "save" ) ) {
- editor_save_map( editor );
- return;
- }
- if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "undo" ) ) {
- editor_undo( editor );
- return;
- }
- if( !strcmp( root->text, "edit" ) && !strcmp( entry->text, "redo" ) ) {
- editor_redo( editor );
- return;
- }
- if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "2d view" ) ) {
- editor_set_view_mode( EDITOR_VIEWMODE_2D );
- return;
- }
- if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "3d view" ) ) {
- editor_set_view_mode( EDITOR_VIEWMODE_3D );
- return;
- }
- if( !strcmp( root->text, "view" ) && !strcmp( entry->text, "simulation" ) ) {
- editor_set_view_mode( EDITOR_VIEWMODE_SIM );
- return;
- }
- if( !strcmp( root->text, "tools" ) ) {
- if( !strcmp( entry->text, "none" ) ) editor_settool( EDITOR_TOOL_NONE );
- else if( !strcmp( entry->text, "select" ) ) editor_settool( EDITOR_TOOL_SELECT );
- else if( !strcmp( entry->text, "wall" ) ) editor_settool( EDITOR_TOOL_WALL );
- else if( !strcmp( entry->text, "poly" ) ) editor_settool( EDITOR_TOOL_POLY );
- else if( !strcmp( entry->text, "sprite" ) ) editor_settool( EDITOR_TOOL_SPRITE );
- else if( !strcmp( entry->text, "ent" ) ) editor_settool( EDITOR_TOOL_ENT );
- }
-}
-
static void gui_editor_toolbar_draw_fn( void* ptr ) {
GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
I32 x = gui_relx( bar );
@@ -289,107 +203,61 @@ static void gui_editor_toolbar_draw_fn( void* ptr ) {
}
U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- EDITOR_MENUBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
+ EDITOR_TOOLBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
CLR hover_fill = CLR::blend( ui_clr.border, ui_clr.bg, 0.70f );
CLR active_fill = CLR::blend( ui_clr.bg_sec, ui_clr.border, 0.22f );
- CLR border = gui_is_fg_window( bar ) ? ui_clr.border : ui_clr.border_inactive;
gui_draw_frect( x, y, bar->w, bar->h, ui_clr.bg_sec );
-
for( I32 i = 0; i < (I32)bar->entries.size; ++i ) {
I32 rx = 0, ry = 0, rw = 0, rh = 0;
editor_toolbar_root_rect( bar, i, &rx, &ry, &rw, &rh );
- U8 is_hover = hit.root == i && hit.sub == -1;
- U8 is_active = bar->held && bar->held_root == i && bar->held_sub == -1 && m1;
- U8 is_open = bar->open_root == i;
-
- CLR fill = ui_clr.bg_sec;
- if( is_open || is_hover )
- fill = hover_fill;
- if( is_active )
- fill = active_fill;
-
- if( is_open || is_active || is_hover )
- gui_draw_frect( rx, ry, rw, rh, fill );
- gui_draw_str( rx + 6, y + bar->h / 2 - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, "%s", bar->entries[i].text );
- }
-
- if( bar->open_root < 0 || bar->open_root >= (I32)bar->entries.size )
- return;
- EDITOR_MENUBAR_ENTRY* root = &bar->entries[bar->open_root];
- if( root->type != EDITOR_MENUBAR_ENTRY_SUBMENU )
- return;
-
- if( !root->entries.size )
- return;
-
- I32 panel_x = 0, panel_y = 0, panel_w = 0, panel_h = 0;
- editor_toolbar_sub_rect( bar, bar->open_root, 0, &panel_x, &panel_y, &panel_w, &panel_h );
- I32 row_h = panel_h;
- gui_draw_frect( panel_x, panel_y, panel_w, row_h * root->entries.size, border );
- gui_draw_frect( panel_x + 1, panel_y + 1, max( 1, panel_w - 2 ), max( 1, row_h * (I32)root->entries.size - 2 ), ui_clr.bg_sec );
-
- for( I32 i = 0; i < (I32)root->entries.size; ++i ) {
- EDITOR_MENUBAR_ENTRY* entry = &root->entries[i];
- I32 rx = 0, ry = 0, rw = 0, rh = 0;
- editor_toolbar_sub_rect( bar, bar->open_root, i, &rx, &ry, &rw, &rh );
-
- U8 enabled = editor_toolbar_entry_enabled( root, entry );
- U8 is_hover = hit.root == bar->open_root && hit.sub == i;
- U8 is_active = bar->held && bar->held_root == bar->open_root && bar->held_sub == i && m1;
+ U8 hovered = hit.root == i;
+ U8 active = bar->held && bar->held_root == i && m1;
+ U8 open = editor_contextmenu_open() && bar->active_root == i;
CLR fill = ui_clr.bg_sec;
- if( enabled && is_active )
+ if( active )
fill = active_fill;
- else if( is_hover )
+ else if( open || hovered )
fill = hover_fill;
- gui_draw_frect( rx + 1, ry + 1, max( 1, rw - 2 ), max( 1, rh - 2 ), fill );
- CLR txt_clr = enabled ? ui_clr.txt : ui_clr.txt_inactive;
- gui_draw_str( rx + 8, ry + rh / 2 - 8, ALIGN_L, FNT_JPN12, txt_clr, "%s", entry->text );
+ if( open || hovered || active )
+ gui_draw_frect( rx, ry, rw, rh, fill );
+
+ gui_draw_str( rx + 6, y + bar->h / 2 - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, "%s", bar->entries[i].text );
}
}
static void gui_editor_toolbar_input_fn( void* ptr ) {
GUI_EDITOR_TOOLBAR* bar = (GUI_EDITOR_TOOLBAR*)ptr;
- I32 mx, my;
+ I32 mx = 0, my = 0;
gui_cursor_pos( &mx, &my );
+
U8 m1 = gui_mbutton_down( GUI_MBTNLEFT );
- EDITOR_MENUBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
+ EDITOR_TOOLBAR_HIT hit = editor_toolbar_hit_test( bar, mx, my );
if( m1 ) {
if( !bar->held ) {
bar->held = 1;
bar->held_root = hit.root;
- bar->held_sub = hit.sub;
- if( hit.root == -1 && editor_toolbar_menu_open( bar ) )
- bar->open_root = -1;
}
return;
}
- if( bar->held && bar->held_root == hit.root && bar->held_sub == hit.sub ) {
- if( hit.sub >= 0 ) {
- EDITOR_MENUBAR_ENTRY* root = &bar->entries[hit.root];
- EDITOR_MENUBAR_ENTRY* sub = &root->entries[hit.sub];
- editor_toolbar_invoke( root, sub );
- bar->open_root = -1;
- } else if( hit.root >= 0 ) {
- EDITOR_MENUBAR_ENTRY* root = &bar->entries[hit.root];
- if( root->type == EDITOR_MENUBAR_ENTRY_SUBMENU ) {
- if( bar->open_root == hit.root ) bar->open_root = -1;
- else bar->open_root = hit.root;
- } else {
- editor_toolbar_invoke( root, root );
- bar->open_root = -1;
- }
+ if( bar->held && bar->held_root == hit.root && hit.root >= 0 ) {
+ if( editor_contextmenu_open() && bar->active_root == hit.root ) {
+ editor_hide_contextmenu();
+ } else {
+ I32 rx = 0;
+ editor_toolbar_root_rect( bar, hit.root, &rx, 0, 0, 0 );
+ editor_show_contextmenu( rx, gui_rely( bar ) + bar->h, &bar->entries[hit.root].items );
+ bar->active_root = hit.root;
}
}
bar->held = 0;
bar->held_root = -1;
- bar->held_sub = -1;
}
GUI_EDITOR_TOOLBAR* gui_editor_toolbar( I32 x, I32 y, I32 w, I32 h, const char* name ) {
@@ -402,14 +270,13 @@ GUI_EDITOR_TOOLBAR* gui_editor_toolbar( I32 x, I32 y, I32 w, I32 h, const char*
bar->w = w;
bar->h = h;
bar->xbound = w;
- bar->ybound = h + 120;
+ bar->ybound = h;
bar->draw_fn = gui_editor_toolbar_draw_fn;
bar->input_fn = gui_editor_toolbar_input_fn;
bar->held = 0;
bar->held_root = -1;
- bar->held_sub = -1;
- bar->open_root = -1;
- strcpy( bar->name, name );
+ bar->active_root = -1;
+ snprintf( bar->name, sizeof( bar->name ), "%s", name );
editor_toolbar_init_entries( bar );
GUI_VIEW* parent = gui_get_view();
diff --git a/src/editor/editor_window.cpp b/src/editor/editor_window.cpp
index bd70418..f81b1a8 100644
--- a/src/editor/editor_window.cpp
+++ b/src/editor/editor_window.cpp
@@ -6,9 +6,8 @@ void gui_editorwindow_draw_fn( void* ptr ) {
GUI_EDITORWINDOW* wnd = (GUI_EDITORWINDOW*)ptr;
editor_raise_header_toolbar( editor );
- GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
- GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
- U8 menu_open = editor_toolbar_menu_open( tbar );
+ GUI_BASE* contextmenu = ( editor && editor->map ) ? (GUI_BASE*)editor->gui.contextmenu : 0;
+ U8 menu_open = editor_contextmenu_open();
F32 saved_mx = input.mouse.pos.x;
F32 saved_my = input.mouse.pos.y;
@@ -28,11 +27,16 @@ void gui_editorwindow_draw_fn( void* ptr ) {
wnd->children.each( fn( GUI_BASE** ptr ) {
GUI_BASE* it = *ptr;
+ if( it == contextmenu )
+ return;
if( !it->enabled ) return;
if( it->draw_fn ) it->draw_fn( it );
else dlog( "gui_editorwindow_draw_fn(): child %p has no draw_fn", it );
} );
+ if( contextmenu && contextmenu->enabled && contextmenu->draw_fn )
+ contextmenu->draw_fn( contextmenu );
+
input.mouse.pos.x = saved_mx;
input.mouse.pos.y = saved_my;
editor_menu_hover_mask_active = 0;
@@ -43,16 +47,20 @@ static void gui_editorwindow_input_fn( void* ptr ) {
editor_raise_header_toolbar( editor );
GUI_BASE* toolbar = ( editor && editor->map ) ? editor->gui.header_toolbar : 0;
+ GUI_BASE* contextmenu = ( editor && editor->map ) ? (GUI_BASE*)editor->gui.contextmenu : 0;
+ U8 menu_was_open = editor_contextmenu_open();
if( toolbar && toolbar->enabled && toolbar->input_fn )
toolbar->input_fn( toolbar );
- GUI_EDITOR_TOOLBAR* tbar = (GUI_EDITOR_TOOLBAR*)toolbar;
- if( editor_toolbar_menu_open( tbar ) )
+ if( menu_was_open && editor && editor->gui.contextmenu && editor->gui.contextmenu->input_fn )
+ editor->gui.contextmenu->input_fn( editor->gui.contextmenu );
+
+ if( menu_was_open || editor_contextmenu_open() )
return;
wnd->children.each( fn( GUI_BASE** childptr ) {
GUI_BASE* child = *childptr;
- if( !child || child == toolbar || !child->enabled || !child->input_fn )
+ if( !child || child == toolbar || child == contextmenu || !child->enabled || !child->input_fn )
return;
child->input_fn( child );
} );
@@ -125,8 +133,7 @@ const char* editor_tool_name() {
case EDITOR_TOOL_SELECT: return "select";
case EDITOR_TOOL_WALL: return "wall";
case EDITOR_TOOL_POLY: return "poly";
- case EDITOR_TOOL_SPRITE: return "sprite";
- case EDITOR_TOOL_ENT: return "ent";
+ case EDITOR_TOOL_OBJECT: return "object";
default: return "none";
}
}
@@ -136,8 +143,7 @@ static U8 EDITOR_TOOL_BUTTON_TYPES[] = {
EDITOR_TOOL_SELECT,
EDITOR_TOOL_WALL,
EDITOR_TOOL_POLY,
- EDITOR_TOOL_SPRITE,
- EDITOR_TOOL_ENT
+ EDITOR_TOOL_OBJECT
};
static void settool_btn_cb( void* ptr ) {
@@ -154,7 +160,7 @@ void editor_create_toolview_column( GAME_EDITOR* e ) {
I32 x = 320;
I32 y = EDITOR_LAYOUT_CONTENT_Y + EDITOR_LAYOUT_VIEW_DEFAULT_H + EDITOR_LAYOUT_VIEW_TOOL_GAP + EDITOR_LAYOUT_TOOL_BTN_TOP_GAP;
I32 off = 23;
- const char* labels[] = { "none", "select", "wall", "poly", "sprite", "ent" };
+ const char* labels[] = { "none", "select", "wall", "poly", "object" };
for( U32 i = 0; i < sizeof( labels ) / sizeof( labels[0] ); ++i ) {
GUI_BUTTON* btn = gui_button( x, y, 45, 20, labels[i], settool_btn_cb );
diff --git a/src/editor/properties.cpp b/src/editor/properties.cpp
index fa3a117..ac21bb0 100644
--- a/src/editor/properties.cpp
+++ b/src/editor/properties.cpp
@@ -1,6 +1,7 @@
#include "editor.h"
#include "../render/gl.h"
#include "../game/assets.h"
+#include "../game/object.h"
const I32 PROPVIEW_TITLE_OFFSET = 15;
const I32 PROPVIEW_PAD = 10;
@@ -211,6 +212,7 @@ void gui_editor_propview_create_mapprops( GUI_EDITOR_PROPVIEW* view ) {
y += space;
} );
gui_label( x, y, "sprites: %d", m->sprites.size ); y += space;
+ gui_label( x, y, "entities: %d", m->entities.size ); y += space;
gui_label( x, y, "loaded textures: %d", m->textures.size ); y += space;
gui_vectorinput( x, y, propview_input_width( view ), "spawn position", (F32*)&m->startpos, 3, -INFINITY, INFINITY, step ); y += (space+18);
GUI_FLOATINPUT* ang = gui_floatinput( x, y, propview_input_width( view ), "spawn angle", &m->startang, -180.f, 180.f, 1.f ); y += (space+18);
@@ -318,7 +320,21 @@ void gui_editor_propview_create_surfprops( GUI_EDITOR_PROPVIEW* view ) {
}
void gui_editor_propview_create_entprops( GUI_EDITOR_PROPVIEW* view ) {
+ MAP_ENTITY* e = (MAP_ENTITY*)view->curselect;
+ WORLD_MAP* m = editor->map;
+
+ I32 x = 10, y = 10;
+ I32 space = 20;
+ F32 step = editor->propgrid ? editor->grid : 0.25f;
+
+ I32 ent_idx = m->entities.idx_where( fn( MAP_ENTITY* me ) {
+ return me == e;
+ } );
+ gui_label( x, y, "idx: %d", ent_idx ); y += space;
+ gui_label( x, y, "class: %s", obj_classid_to_name( e->classid ) ); y += space;
+ gui_label( x, y, "class id: %u", e->classid ); y += space;
+ gui_vectorinput( x, y, propview_input_width( view ), "position", (F32*)&e->pos, 3, -INFINITY, INFINITY, step ); y += (space+18);
}
void gui_editor_propview_update( GUI_EDITOR_PROPVIEW* view ) {
diff --git a/src/editor/toolview.cpp b/src/editor/toolview.cpp
index 56e55f2..25c308d 100644
--- a/src/editor/toolview.cpp
+++ b/src/editor/toolview.cpp
@@ -1,5 +1,6 @@
#include "editor.h"
#include "../util/string.h"
+#include "../game/object.h"
const U32 TOOLVIEW_TITLE_OFFSET = 15;
const I32 TOOLVIEW_INNER_X = 10;
@@ -7,6 +8,8 @@ const I32 TOOLVIEW_INNER_PAD = 20;
const I32 TOOLVIEW_ROW_HEIGHT = 20;
const I32 TOOLVIEW_ROW_GAP = 4;
const I32 TOOLVIEW_WALL_SHAPE_LIST_HEIGHT = 54;
+const I32 TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT = 36;
+const I32 TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT = 54;
const I32 TOOLVIEW_SCROLL_STEP = 20;
const I32 TOOLVIEW_SCROLLBAR_W = 8;
const I32 TOOLVIEW_SCROLLBAR_MIN_H = 18;
@@ -116,6 +119,48 @@ void gui_editor_toolview_wallshape_select_cb( void* ptr ) {
gui_editor_toolview_queue_refresh( view );
}
+void gui_editor_toolview_objecttype_dropdown_toggle_cb( void* ptr ) {
+ GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
+ GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn );
+ if( !view )
+ return;
+
+ view->objecttype_dropdown_open = !view->objecttype_dropdown_open;
+ view->entclass_dropdown_open = 0;
+ gui_editor_toolview_queue_refresh( view );
+}
+
+void gui_editor_toolview_objecttype_select_cb( void* ptr ) {
+ GUI_LIST* list = (GUI_LIST*)ptr;
+ GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list );
+ if( !view )
+ return;
+
+ view->objecttype_dropdown_open = 0;
+ view->entclass_dropdown_open = 0;
+ gui_editor_toolview_queue_refresh( view );
+}
+
+void gui_editor_toolview_entclass_dropdown_toggle_cb( void* ptr ) {
+ GUI_BUTTON* btn = (GUI_BUTTON*)ptr;
+ GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( btn );
+ if( !view )
+ return;
+
+ view->entclass_dropdown_open = !view->entclass_dropdown_open;
+ gui_editor_toolview_queue_refresh( view );
+}
+
+void gui_editor_toolview_entclass_select_cb( void* ptr ) {
+ GUI_LIST* list = (GUI_LIST*)ptr;
+ GUI_EDITOR_TOOLVIEW* view = gui_editor_toolview_from_item_child( list );
+ if( !view )
+ return;
+
+ view->entclass_dropdown_open = 0;
+ gui_editor_toolview_queue_refresh( view );
+}
+
I32 gui_editor_toolview_create_wallshape_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) {
static LIST<GUI_LIST_ENTRY> shape_entries{};
if( !shape_entries.size ) {
@@ -162,6 +207,102 @@ I32 gui_editor_toolview_create_wallshape_dropdown( GUI_EDITOR_TOOLVIEW* view, I3
return y;
}
+I32 gui_editor_toolview_create_objecttype_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) {
+ static LIST<GUI_LIST_ENTRY> object_entries{};
+ if( !object_entries.size ) {
+ GUI_LIST_ENTRY sprite{};
+ sprite.val = EDITOR_OBJECT_SPRITE;
+ strcpy( sprite.title, "sprite" );
+ object_entries.push( sprite );
+
+ GUI_LIST_ENTRY entity{};
+ entity.val = EDITOR_OBJECT_ENTITY;
+ strcpy( entity.title, "entity" );
+ object_entries.push( entity );
+ }
+
+ const char* object_name = editor->tool.objecttype == EDITOR_OBJECT_ENTITY ? "entity" : "sprite";
+ char dropdown_title[64];
+ sprintf( dropdown_title, "kind: %s v", object_name );
+
+ gui_button(
+ TOOLVIEW_INNER_X,
+ y,
+ view->w - TOOLVIEW_INNER_PAD,
+ TOOLVIEW_ROW_HEIGHT,
+ dropdown_title,
+ gui_editor_toolview_objecttype_dropdown_toggle_cb
+ );
+
+ y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP;
+ if( !view->objecttype_dropdown_open )
+ return y;
+
+ GUI_LIST* object_type = gui_list(
+ TOOLVIEW_INNER_X,
+ y,
+ view->w - TOOLVIEW_INNER_PAD,
+ TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT,
+ "object type",
+ &object_entries,
+ &editor->tool.objecttype
+ );
+
+ object_type->cb = gui_editor_toolview_objecttype_select_cb;
+ y += TOOLVIEW_OBJECT_TYPE_LIST_HEIGHT + TOOLVIEW_ROW_GAP;
+ return y;
+}
+
+I32 gui_editor_toolview_create_entclass_dropdown( GUI_EDITOR_TOOLVIEW* view, I32 y ) {
+ static LIST<GUI_LIST_ENTRY> entclass_entries{};
+ if( !entclass_entries.size ) {
+ GUI_LIST_ENTRY trigger{};
+ trigger.val = OBJCLASS_TRIGGER;
+ strcpy( trigger.title, "trigger" );
+ entclass_entries.push( trigger );
+
+ GUI_LIST_ENTRY player{};
+ player.val = OBJCLASS_PLAYER;
+ strcpy( player.title, "player" );
+ entclass_entries.push( player );
+
+ GUI_LIST_ENTRY npc{};
+ npc.val = OBJCLASS_BASENPC;
+ strcpy( npc.title, "base_npc" );
+ entclass_entries.push( npc );
+ }
+
+ char dropdown_title[64];
+ sprintf( dropdown_title, "class: %s v", obj_classid_to_name( editor->tool.entclass ) );
+
+ gui_button(
+ TOOLVIEW_INNER_X,
+ y,
+ view->w - TOOLVIEW_INNER_PAD,
+ TOOLVIEW_ROW_HEIGHT,
+ dropdown_title,
+ gui_editor_toolview_entclass_dropdown_toggle_cb
+ );
+
+ y += TOOLVIEW_ROW_HEIGHT + TOOLVIEW_ROW_GAP;
+ if( !view->entclass_dropdown_open )
+ return y;
+
+ GUI_LIST* entclass = gui_list(
+ TOOLVIEW_INNER_X,
+ y,
+ view->w - TOOLVIEW_INNER_PAD,
+ TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT,
+ "entity class",
+ &entclass_entries,
+ (I32*)&editor->tool.entclass
+ );
+
+ entclass->cb = gui_editor_toolview_entclass_select_cb;
+ y += TOOLVIEW_ENTITY_CLASS_LIST_HEIGHT + TOOLVIEW_ROW_GAP;
+ return y;
+}
+
I32 gui_editor_toolview_create_poly_sides_input( GUI_EDITOR_TOOLVIEW* view, I32 y ) {
return gui_editor_toolview_create_float_input(
view,
@@ -204,11 +345,10 @@ I32 gui_editor_toolview_create_placement_height_input( GUI_EDITOR_TOOLVIEW* view
void gui_editor_toolview_get_title( GUI_EDITOR_TOOLVIEW* view, char* out ) {
switch( editor->tool.type ) {
- case EDITOR_TOOL_ENT: memcpy( out, "tool: entity", 12 ); break;
+ case EDITOR_TOOL_OBJECT: memcpy( out, "tool: object", 12 ); break;
case EDITOR_TOOL_WALL: memcpy( out, "tool: wall", 10 ); break;
case EDITOR_TOOL_POLY: memcpy( out, "tool: polygon", 14 ); break;
case EDITOR_TOOL_SELECT: memcpy( out, "tool: select", 12 ); break;
- case EDITOR_TOOL_SPRITE: memcpy( out, "tool: sprite", 12 ); break;
default: memcpy( out, "tool: none", 10 ); break;
}
}
@@ -243,6 +383,27 @@ void gui_editor_toolview_update( GUI_EDITOR_TOOLVIEW* view ) {
y = gui_editor_toolview_create_poly_sides_input( view, y );
y = gui_editor_toolview_create_placement_height_input( view, y );
}
+ else if( editor->tool.type == EDITOR_TOOL_OBJECT ) {
+ y = gui_editor_toolview_create_objecttype_dropdown( view, y );
+ if( view->objecttype_dropdown_open ) {
+ view->content_h = y + 6;
+ gui_editor_toolview_clamp_scroll( view );
+ gui_set_view( oldview );
+ return;
+ }
+
+ if( editor->tool.objecttype == EDITOR_OBJECT_ENTITY ) {
+ y = gui_editor_toolview_create_entclass_dropdown( view, y );
+ if( view->entclass_dropdown_open ) {
+ view->content_h = y + 6;
+ gui_editor_toolview_clamp_scroll( view );
+ gui_set_view( oldview );
+ return;
+ }
+ }
+
+ y = gui_editor_toolview_create_placement_height_input( view, y );
+ }
view->content_h = y + 6;
gui_editor_toolview_clamp_scroll( view );
@@ -279,7 +440,7 @@ void gui_editor_toolview_draw_fn( void* ptr ) {
I32 w = view->w;
I32 h = view->h;
- gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "contextual tool options" );
+ gui_draw_str( x, y, ALIGN_L, FNT_JPN12, ui_clr.txt, "tool options" );
y += TOOLVIEW_TITLE_OFFSET;
CLR col = gui_is_fg_window( view )? ui_clr.border : ui_clr.border_inactive;
@@ -341,6 +502,8 @@ GUI_EDITOR_TOOLVIEW* gui_editor_toolview( I32 x, I32 y, I32 w, I32 h ) {
view->draw_fn = gui_editor_toolview_draw_fn;
view->input_fn = gui_editor_toolview_input_fn;
view->wallshape_dropdown_open = 0;
+ view->objecttype_dropdown_open = 0;
+ view->entclass_dropdown_open = 0;
view->scroll = 0;
view->content_h = 0;
diff --git a/src/editor/view2d.cpp b/src/editor/view2d.cpp
index 8dbe42c..f1b5b17 100644
--- a/src/editor/view2d.cpp
+++ b/src/editor/view2d.cpp
@@ -1,6 +1,7 @@
#include <math.h>
#include "editor.h"
#include "../render/gl_2d.h"
+#include "../game/object.h"
#include "../game/objlist.h"
#include "../game/world/bsp.h"
@@ -13,6 +14,7 @@ const I32 EDITOR_POLY_SIDES_RUNTIME_MAX = 256;
VEC2 gui_editor_2dview_screen_to_world( GUI_EDITOR_2DVIEW* view, I32 x, I32 y );
VEC2 gui_editor_2dview_world_to_screen( GUI_EDITOR_2DVIEW* view, VEC2 world );
F32 gui_editor_2dview_placement_z();
+void gui_editor_2dview_draw_object_preview( GUI_EDITOR_2DVIEW* view );
VEC3 gui_editor_2dview_plane_to_world3( VEC2 plane, F32 depth ) {
return { plane.x, plane.y, depth };
@@ -650,6 +652,25 @@ void gui_editor_2dview_draw_sprites( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
} );
}
+void gui_editor_2dview_draw_entities( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
+ WORLD_MAP* m = editor->map;
+
+ m->entities.each( fn( MAP_ENTITY* e ) {
+ VEC2 pos = gui_editor_2dview_world3_to_screen( view, e->pos );
+ I32 px = (I32)pos.x;
+ I32 py = (I32)pos.y;
+ U8 sel = gui_editor_2dview_is_gizmo_active( view, e, EDITOR_SELECT_ENT );
+
+ if( sel ) {
+ gui_draw_line( px - 8, py, px + 8, py, CLR::WHITE( 0.7f ) );
+ gui_draw_line( px, py - 8, px, py + 8, CLR::WHITE( 0.7f ) );
+ }
+ gui_draw_line( px - 6, py, px + 6, py, CLR::YELLOW() );
+ gui_draw_line( px, py - 6, px, py + 6, CLR::YELLOW() );
+ gui_draw_str( px + 8, py - 8, ALIGN_L, FNT_JPN12, ui_clr.txt, "%s", obj_classid_to_name( e->classid ) );
+ } );
+}
+
void gui_editor_2dview_draw_player( GUI_EDITOR_2DVIEW* view, I32 x, I32 y ) {
if( !objl->pl )
@@ -712,10 +733,12 @@ void gui_editor_2dview_draw_fn( void* ptr ) {
gui_draw_push_clip( x + offx, y + offy, w - offx, h - offy - EDITORVIEW_TOOLBAR_OFFSET );
gui_editor_2dview_draw_polygons( view, x + offx, y + offy );
gui_editor_2dview_draw_walls( view, x + offx, y + offy );
+ gui_editor_2dview_draw_entities( view, x + offx, y + offy );
gui_editor_2dview_draw_sprites( view, x + offx, y + offy );
gui_editor_2dview_draw_player( view, x + offx, y + offy );
gui_editor_2dview_draw_origin( view, x + offx, y + offy );
gui_editor_2dview_draw_poly_preview( view );
+ gui_editor_2dview_draw_object_preview( view );
gui_draw_pop_clip();
gui_draw_push_clip( x, y + 16, view->w, view->h );
@@ -882,6 +905,42 @@ void gui_editor_2dview_input_select_drag_sprite( GUI_EDITOR_2DVIEW* view ) {
}
}
+void gui_editor_2dview_draw_object_preview( GUI_EDITOR_2DVIEW* view ) {
+ if( editor->tool.type != EDITOR_TOOL_OBJECT || !view->held )
+ return;
+
+ VEC2 pos = gui_editor_2dview_world3_to_screen( view, view->pending_object_pos );
+ I32 px = (I32)pos.x;
+ I32 py = (I32)pos.y;
+
+ if( view->pending_object_undo_type == EDITOR_SELECT_ENT ) {
+ gui_draw_line( px - 6, py, px + 6, py, CLR::WHITE( 0.8f ) );
+ gui_draw_line( px, py - 6, px, py + 6, CLR::WHITE( 0.8f ) );
+ return;
+ }
+
+ F32 wantedsize = editor->spritesize;
+ gui_draw_rect(
+ (I32)( pos.x - wantedsize * 0.5f ),
+ (I32)( pos.y - wantedsize * 0.5f ),
+ (I32)wantedsize,
+ (I32)wantedsize,
+ CLR::WHITE( 0.8f )
+ );
+}
+
+void gui_editor_2dview_input_select_drag_entity( GUI_EDITOR_2DVIEW* view ) {
+ MAP_ENTITY* e = (MAP_ENTITY*)view->curdrag;
+
+ gui_editor_2dview_snap_plane( &e->pos );
+
+ VEC2 mv = gui_editor_2dview_input_get_drag_vec( view );
+ if( !is_zero( mv ) ) {
+ gui_editor_2dview_add_plane_delta( &e->pos, mv );
+ gui_editor_2dview_input_select_onmove( view );
+ }
+}
+
void gui_editor_2dview_input_select_drag_origin( GUI_EDITOR_2DVIEW* view ) {
WORLD_MAP* m = editor->map;
@@ -928,6 +987,7 @@ void gui_editor_2dview_input_select_drag( GUI_EDITOR_2DVIEW* view ) {
switch( view->dragtype ) {
case EDITOR_SELECT_WALL: gui_editor_2dview_input_select_drag_wall( view ); break;
case EDITOR_SELECT_POLY: gui_editor_2dview_input_select_drag_polygon( view ); break;
+ case EDITOR_SELECT_ENT: gui_editor_2dview_input_select_drag_entity( view ); break;
case EDITOR_SELECT_SPRITE: gui_editor_2dview_input_select_drag_sprite( view ); break;
case EDITOR_SELECT_ORIGIN: gui_editor_2dview_input_select_drag_origin( view ); break;
case EDITOR_SELECT_WVERTEX:
@@ -1055,6 +1115,30 @@ void gui_editor_2dview_input_select_sprites( GUI_EDITOR_2DVIEW* view ) {
gui_editor_2dview_input_select_drag( view );
}
+void gui_editor_2dview_input_select_entities( GUI_EDITOR_2DVIEW* view ) {
+ WORLD_MAP* m = editor->map;
+
+ I32 mx, my;
+ gui_cursor_pos( &mx, &my );
+
+ VEC2 mpos = { (F32)mx, (F32)my };
+ F32 mindist = editor->spritesize;
+ for( U32 i = 0; i < m->entities.size; ++i ) {
+ MAP_ENTITY* e = &m->entities[i];
+ VEC2 screen = gui_editor_2dview_world3_to_screen( view, e->pos );
+ F32 dist = vec_dist( mpos, screen );
+ if( dist < mindist ) {
+ mindist = dist;
+ gui_editor_2dview_select( view, e, EDITOR_SELECT_ENT );
+ }
+ }
+
+ if( !view->curselect && !view->held )
+ return;
+
+ gui_editor_2dview_input_select_drag( view );
+}
+
void gui_editor_2dview_input_select_origin( GUI_EDITOR_2DVIEW* view ) {
WORLD_MAP* m = editor->map;
@@ -1090,6 +1174,8 @@ void gui_editor_2dview_input_tool_select( GUI_EDITOR_2DVIEW* view ) {
view->seltype = EDITOR_SELECT_NONE;
gui_editor_2dview_input_select_origin( view );
if( view->curselect ) return;
+ gui_editor_2dview_input_select_entities( view );
+ if( view->curselect ) return;
gui_editor_2dview_input_select_sprites( view );
if( view->curselect ) return;
gui_editor_2dview_input_select_walls( view );
@@ -1230,13 +1316,35 @@ void gui_editor_2dview_input_tool_poly( GUI_EDITOR_2DVIEW* view ) {
view->poly_end = world;
}
-void gui_editor_2dview_input_tool_ent( GUI_EDITOR_2DVIEW* view ) {
-
-}
-
-void gui_editor_2dview_input_tool_sprite( GUI_EDITOR_2DVIEW* view ) {
+void gui_editor_2dview_input_tool_object( GUI_EDITOR_2DVIEW* view ) {
U8 m1 = gui_mbutton_down( 0 );
if( !m1 ) {
+ if( view->held ) {
+ if( view->pending_object_undo_type == EDITOR_SELECT_ENT ) {
+ MAP_ENTITY ent{};
+ ent.pos = view->pending_object_pos;
+ ent.classid = editor->tool.entclass;
+
+ I32 idx = editor->map->entities.size;
+ editor->map->entities.push( ent );
+ editor_undo_record_create_entity( editor, idx );
+ }
+ else if( view->pending_object_undo_type == EDITOR_SELECT_SPRITE ) {
+ MAP_SPRITE news{};
+ news.pos = view->pending_object_pos;
+ news.size = { 20.f, 20.f };
+ news.clr = { 1.f, 1.f, 1.f, 1.f };
+ news.tex = 0;
+
+ I32 idx = editor->map->sprites.size;
+ editor->map->sprites.push( news );
+ editor_undo_record_create_sprite( editor, idx );
+ }
+ }
+ view->pending_object_undo_idx = -1;
+ view->pending_object_undo_type = EDITOR_SELECT_NONE;
+ view->curdrag = 0;
+ view->dragtype = EDITOR_SELECT_NONE;
view->held = 0;
return;
}
@@ -1246,17 +1354,10 @@ void gui_editor_2dview_input_tool_sprite( GUI_EDITOR_2DVIEW* view ) {
VEC2 world = gui_editor_2dview_screen_to_world( view, mx, my );
if( !view->held ) {
- VEC3 wpos = gui_editor_2dview_plane_to_world3( world, gui_editor_2dview_placement_z() );
- MAP_SPRITE news;
- news.pos = wpos;
- gui_editor_2dview_snap_plane( &news.pos );
- news.size = { 20.f, 20.f };
- news.clr = { 1.f, 1.f, 1.f, 1.f };
- news.tex = 0;
-
- I32 idx = editor->map->sprites.size;
- editor->map->sprites.push( news );
- view->curdrag = &editor->map->sprites[idx];
+ view->pending_object_pos = gui_editor_2dview_plane_to_world3( world, gui_editor_2dview_placement_z() );
+ gui_editor_2dview_snap_plane( &view->pending_object_pos );
+ view->pending_object_undo_idx = -1;
+ view->pending_object_undo_type = editor->tool.objecttype == EDITOR_OBJECT_ENTITY ? EDITOR_SELECT_ENT : EDITOR_SELECT_SPRITE;
view->held = 1;
view->oldmx = mx;
@@ -1266,7 +1367,8 @@ void gui_editor_2dview_input_tool_sprite( GUI_EDITOR_2DVIEW* view ) {
return;
}
- gui_editor_2dview_input_select_drag_sprite( view );
+ view->pending_object_pos = gui_editor_2dview_plane_to_world3( world, gui_editor_2dview_placement_z() );
+ gui_editor_2dview_snap_plane( &view->pending_object_pos );
}
@@ -1281,8 +1383,7 @@ void gui_editor_2dview_input_tool_draw( GUI_EDITOR_2DVIEW* view ) {
switch( editor->tool.type ) {
case EDITOR_TOOL_WALL: return gui_editor_2dview_input_tool_wall( view );
case EDITOR_TOOL_POLY: return gui_editor_2dview_input_tool_poly( view );
- case EDITOR_TOOL_ENT: return gui_editor_2dview_input_tool_ent( view );
- case EDITOR_TOOL_SPRITE: return gui_editor_2dview_input_tool_sprite( view );
+ case EDITOR_TOOL_OBJECT: return gui_editor_2dview_input_tool_object( view );
default: return;
}
}
@@ -1321,6 +1422,11 @@ void gui_editor_view2d_delete_obj( GUI_EDITOR_2DVIEW* view ) {
if( idx != -1 )
editor->map->sprites.erase( idx );
}; break;
+ case EDITOR_SELECT_ENT: {
+ I32 idx = editor->map->entities.idx_of( (MAP_ENTITY*)it );
+ if( idx != -1 )
+ editor->map->entities.erase( idx );
+ }; break;
case EDITOR_SELECT_PVERTEX: {
I32 vidx = -1, idx = editor->map->polygons.idx_where( fn( MAP_POLYGON* p ) {
vidx = p->vertices.idx_where( fn( MAP_VERTEX* v ) {
@@ -1424,8 +1530,7 @@ void gui_editor_2dview_input_fn( void* ptr ) {
case EDITOR_TOOL_SELECT: return gui_editor_2dview_input_tool_select( view );
case EDITOR_TOOL_WALL:
case EDITOR_TOOL_POLY:
- case EDITOR_TOOL_SPRITE:
- case EDITOR_TOOL_ENT:
+ case EDITOR_TOOL_OBJECT:
return gui_editor_2dview_input_tool_draw( view );
default: return gui_editor_2dview_input_tool_none( view );
}
@@ -1508,6 +1613,8 @@ GUI_EDITOR_2DVIEW* gui_editor_2dview( I32 x, I32 y, I32 w, I32 h ) {
view->poly_start = { 0.f, 0.f };
view->poly_end = { 0.f, 0.f };
view->pending_wall_undo_idx = -1;
+ view->pending_object_undo_idx = -1;
+ view->pending_object_undo_type = EDITOR_SELECT_NONE;
GUI_BASE* parent = gui_get_view();
if( !parent )
diff --git a/src/game/world/map.cpp b/src/game/world/map.cpp
index ba6bb00..a44f191 100644
--- a/src/game/world/map.cpp
+++ b/src/game/world/map.cpp
@@ -303,6 +303,40 @@ STAT map_sprites_from_section( WORLD_MAP* m, GAME_DATA* g, CFG_SECTION* sprites,
return parsec > 0 ? STAT_OK : STAT_ERR;
}
+STAT map_entities_from_section( WORLD_MAP* m, GAME_DATA*, CFG_SECTION* entities, U32 entityc ) {
+ if( !entityc ) {
+ dlog( "map_entities_from_section() : no entities in %s\n", m->name );
+ return STAT_OK;
+ }
+
+ U32 parsec = 0;
+ char entitysec[256] = { 0 };
+ for( U32 i = 0; i < entityc; ++i ) {
+ MAP_ENTITY entity{};
+
+ sprintf( entitysec, "%d", i );
+ CFG_SECTION* s = cfg_section( entities, entitysec );
+ if( !s ) {
+ dlog( "map_entities_from_section() : missing entity %d in %s\n", i, m->name );
+ continue;
+ }
+
+ CFG_INT* classid = cfg_int( s, "classid" );
+ CFG_VEC3* pos = cfg_vec3( s, "pos" );
+ if( !classid || !pos ) {
+ dlog( "map_entities_from_section() : invalid entity %d in %s\n", i, m->name );
+ continue;
+ }
+
+ entity.classid = (U32)classid->value;
+ entity.pos = pos->value;
+ m->entities.push( entity );
+ ++parsec;
+ }
+
+ return parsec > 0 ? STAT_OK : STAT_ERR;
+}
+
STAT map_skybox_from_section( CFG_SECTION* s, GAME_DATA* g, WORLD_MAP* m ) {
CFG_SECTION* props = cfg_section( s, "props" );
if( !props )
@@ -496,6 +530,13 @@ WORLD_MAP* map_from_file( GAME_DATA* game, const char* path ) {
if( !OK( map_sprites_from_section( m, game, sprites, spritec->value ) ) ) { delete m; return 0; }
}
+ CFG_INT* entityc = cfg_int( s, "entitycount" );
+ if( entityc ) {
+ CFG_SECTION* entities = cfg_section( s, "entities" );
+ if( !entities ) { dlog( errstr, path, "entities" ); delete m; return 0; }
+ if( !OK( map_entities_from_section( m, game, entities, entityc->value ) ) ) { delete m; return 0; }
+ }
+
CFG_VEC3* startpos = cfg_vec3( s, "startpos" );
if( !startpos ) {
dlog( errstr, path, "startpos" );
@@ -537,6 +578,7 @@ void map_serialize_info( CFG_SECTION* s, WORLD_MAP* map ) {
cfg_int( "polycount", s, map->polygons.size );
cfg_int( "propcount", s, map->props.size );
cfg_int( "spritecount", s, map->sprites.size );
+ cfg_int( "entitycount", s, map->entities.size );
cfg_vec3( "startpos", s, map->startpos );
cfg_float( "startang", s, map->startang );
}
@@ -597,6 +639,19 @@ void map_serialize_sprites( CFG_SECTION* s, WORLD_MAP* map ) {
} );
}
+void map_serialize_entities( CFG_SECTION* s, WORLD_MAP* map ) {
+ CFG_SECTION* entities = cfg_section_new( "entities", s );
+ char name[64];
+
+ U32 i = 0;
+ map->entities.each( fn( MAP_ENTITY* e ) {
+ sprintf( name, "%d", i++ );
+ CFG_SECTION* entitysec = cfg_section_new( name, entities );
+ cfg_int( "classid", entitysec, e->classid );
+ cfg_vec3( "pos", entitysec, e->pos );
+ } );
+}
+
void map_serialize_props( CFG_SECTION* s, WORLD_MAP* map ) {
CFG_SECTION* props = cfg_section_new( "props", s );
char name[64];
@@ -623,6 +678,7 @@ CFG_SECTION* map_serialize( WORLD_MAP* map ) {
map_serialize_walls( s, map );
map_serialize_polygons( s, map );
map_serialize_sprites( s, map );
+ map_serialize_entities( s, map );
return s;
}
diff --git a/src/game/world/map.h b/src/game/world/map.h
index 29022db..f3f56f8 100644
--- a/src/game/world/map.h
+++ b/src/game/world/map.h
@@ -68,6 +68,7 @@ struct MAP_SPRITE {
};
struct MAP_ENTITY {
+ VEC3 pos;
U32 classid;
LIST<struct OBJECT_PROP*> props;
};
@@ -82,6 +83,7 @@ struct WORLD_MAP {
LIST<MAP_WALL> walls;
LIST<MAP_POLYGON> polygons;
LIST<MAP_SPRITE> sprites;
+ LIST<MAP_ENTITY> entities;
LIST<SURF_PROPS> props;
MAP_SKYBOX skybox;
F32 w;
diff --git a/src/gui/list.cpp b/src/gui/list.cpp
index 2feff47..0a2a3d3 100644
--- a/src/gui/list.cpp
+++ b/src/gui/list.cpp
@@ -66,10 +66,9 @@ void gui_list_input_fn( void* ptr ) {
if( m1 ) {
if( !list->held ) {
GUI_LIST_ENTRY* e = &list->plist->data[idx];
- I32 prevval = *list->pval;
*list->pval = e->val;
- if( list->cb && prevval != *list->pval )
+ if( list->cb )
list->cb( list );
}
list->held = 1;