summaryrefslogtreecommitdiff
path: root/src/editor/editor.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/editor/editor.cpp')
-rw-r--r--src/editor/editor.cpp399
1 files changed, 399 insertions, 0 deletions
diff --git a/src/editor/editor.cpp b/src/editor/editor.cpp
index ad23d1e..3873913 100644
--- a/src/editor/editor.cpp
+++ b/src/editor/editor.cpp
@@ -4,6 +4,402 @@
GAME_EDITOR* editor = 0;
+void editor_clear_gui_state_refs( GAME_EDITOR* e ) {
+ if( !e )
+ return;
+
+ e->gui.new_map_popup = 0;
+
+ e->gui.v2d = 0;
+ e->gui.v3d = 0;
+ e->gui.gridlabel = 0;
+ e->gui.props = 0;
+ e->gui.tool = 0;
+
+ e->gui.assets = 0;
+ e->gui.status = 0;
+ e->gui.assets_scroll = 0;
+
+ e->gui.header_toolbar = 0;
+ e->gui.header_viewtype_label = 0;
+ e->gui.header_back = 0;
+ e->gui.header_mode_2d = 0;
+ e->gui.header_mode_3d = 0;
+ e->gui.header_mode_sim = 0;
+ e->gui.header_viewtype = 0;
+}
+
+static void editor_push_undo_action( GAME_EDITOR* e, const GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
+ if( !e )
+ return;
+
+ e->redo_actions.clear();
+ e->undo_actions.push( action );
+}
+
+static void editor_refresh_after_map_change( GAME_EDITOR* e ) {
+ if( !e || !e->map )
+ return;
+
+ map_check_bounds( e->map );
+ if( e->gui.props && e->propgrid )
+ editor_update_properties_column( e );
+ if( e->gui.tool )
+ gui_editor_toolview_update( e->gui.tool );
+}
+
+static U8 editor_clr_eq( const CLR& a, const CLR& b ) {
+ return a.r == b.r && a.g == b.g && a.b == b.b && a.a == b.a;
+}
+
+static U8 editor_vertex_eq( const MAP_VERTEX& a, const MAP_VERTEX& b ) {
+ return a.pos == b.pos
+ && a.normal == b.normal
+ && a.uv == b.uv
+ && editor_clr_eq( a.clr, b.clr );
+}
+
+static U8 editor_wall_eq( const MAP_WALL& a, const MAP_WALL& b ) {
+ return a.start == b.start
+ && a.end == b.end
+ && a.uvstart == b.uvstart
+ && a.uvend == b.uvend
+ && a.propid == b.propid;
+}
+
+static U8 editor_poly_eq( const MAP_POLYGON& a, const MAP_POLYGON& b ) {
+ if( a.type != b.type || a.propid != b.propid )
+ return 0;
+ if( !( a.mins == b.mins ) || !( a.maxs == b.maxs ) )
+ return 0;
+ if( a.vertices.size != b.vertices.size )
+ return 0;
+
+ for( U32 i = 0; i < a.vertices.size; ++i ) {
+ if( !editor_vertex_eq( a.vertices.data[i], b.vertices.data[i] ) )
+ return 0;
+ }
+ return 1;
+}
+
+static I32 editor_clamp_valid_idx( I32 idx, I32 size ) {
+ if( size <= 0 )
+ return -1;
+ if( idx < 0 )
+ return 0;
+ if( idx >= size )
+ return size - 1;
+ return idx;
+}
+
+static I32 editor_idx_distance( I32 a, I32 b ) {
+ return a > b ? a - b : b - a;
+}
+
+template <typename MATCH_FN>
+static I32 editor_find_nearest_idx( I32 size, I32 expected_idx, MATCH_FN match ) {
+ if( size <= 0 )
+ return -1;
+
+ expected_idx = editor_clamp_valid_idx( expected_idx, size );
+ if( match( expected_idx ) )
+ return expected_idx;
+
+ I32 best = -1;
+ I32 best_dist = 0x7fffffff;
+ for( I32 i = 0; i < size; ++i ) {
+ if( !match( i ) )
+ continue;
+
+ I32 dist = editor_idx_distance( i, expected_idx );
+ if( best == -1 || dist < best_dist ) {
+ best = i;
+ best_dist = dist;
+ }
+ }
+
+ return best;
+}
+
+static I32 editor_find_wall_idx( WORLD_MAP* map, const MAP_WALL& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->walls.size, expected_idx, fn( I32 i ) {
+ return editor_wall_eq( map->walls[i], want );
+ } );
+}
+
+static I32 editor_find_poly_idx( WORLD_MAP* map, const MAP_POLYGON& want, I32 expected_idx ) {
+ if( !map )
+ return -1;
+
+ return editor_find_nearest_idx( (I32)map->polygons.size, expected_idx, fn( I32 i ) {
+ return editor_poly_eq( map->polygons[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 )
+ *clear_props_select = 1;
+ if( e->gui.props->seltype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.props->curselect == &w->start || e->gui.props->curselect == &w->end ) )
+ *clear_props_select = 1;
+ }
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_WALL && e->gui.v2d->curselect == w )
+ *clear_view_select = 1;
+ if( e->gui.v2d->seltype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.v2d->curselect == &w->start || e->gui.v2d->curselect == &w->end ) )
+ *clear_view_select = 1;
+
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_WALL && e->gui.v2d->curdrag == w )
+ *clear_view_drag = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_WVERTEX
+ && ( e->gui.v2d->curdrag == &w->start || e->gui.v2d->curdrag == &w->end ) )
+ *clear_view_drag = 1;
+ }
+}
+
+static void editor_mark_clear_poly_refs( GAME_EDITOR* e, MAP_POLYGON* p, U8* clear_props_select, U8* clear_view_select, U8* clear_view_drag ) {
+ if( e->gui.props ) {
+ if( e->gui.props->seltype == EDITOR_SELECT_POLY && e->gui.props->curselect == p )
+ *clear_props_select = 1;
+ if( e->gui.props->seltype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.props->curselect == v )
+ *clear_props_select = 1;
+ } );
+ }
+ }
+
+ if( e->gui.v2d ) {
+ if( e->gui.v2d->seltype == EDITOR_SELECT_POLY && e->gui.v2d->curselect == p )
+ *clear_view_select = 1;
+ if( e->gui.v2d->seltype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.v2d->curselect == v )
+ *clear_view_select = 1;
+ } );
+ }
+
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_POLY && e->gui.v2d->curdrag == p )
+ *clear_view_drag = 1;
+ if( e->gui.v2d->dragtype == EDITOR_SELECT_PVERTEX ) {
+ p->vertices.each( fn( MAP_VERTEX* v ) {
+ if( e->gui.v2d->curdrag == v )
+ *clear_view_drag = 1;
+ } );
+ }
+ }
+}
+
+void editor_undo_clear( GAME_EDITOR* e ) {
+ if( !e )
+ return;
+
+ e->undo_actions.clear();
+ e->redo_actions.clear();
+}
+
+void editor_undo_record_create_walls( GAME_EDITOR* e, I32 start_idx, I32 count ) {
+ if( !e || !e->map || count <= 0 )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->walls.size )
+ return;
+
+ I32 available = (I32)map->walls.size - start_idx;
+ if( count > available )
+ count = available;
+ if( count <= 0 )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_WALLS;
+ action.start_idx = start_idx;
+ for( I32 i = 0; i < count; ++i )
+ action.walls.push( map->walls[start_idx + i] );
+
+ editor_push_undo_action( e, action );
+}
+
+void editor_undo_record_create_poly( GAME_EDITOR* e, I32 start_idx ) {
+ if( !e || !e->map )
+ return;
+
+ WORLD_MAP* map = e->map;
+ if( start_idx < 0 || start_idx >= (I32)map->polygons.size )
+ return;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action{};
+ action.type = EDITOR_UNDO_CREATE_POLY;
+ action.start_idx = start_idx;
+ action.polys.push( map->polygons[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;
+
+ WORLD_MAP* map = e->map;
+ U8 clear_props_select = 0;
+ U8 clear_view_select = 0;
+ U8 clear_view_drag = 0;
+
+ switch( action.type ) {
+ case EDITOR_UNDO_CREATE_WALLS: {
+ I32 count = (I32)action.walls.size;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->walls.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = editor_find_wall_idx( map, action.walls[i], expected );
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->walls.size );
+
+ MAP_WALL* w = &map->walls[idx];
+ editor_mark_clear_wall_refs( e, w, &clear_props_select, &clear_view_select, &clear_view_drag );
+
+ map->walls.erase( idx );
+ }
+ } break;
+ case EDITOR_UNDO_CREATE_POLY: {
+ I32 count = (I32)action.polys.size;
+ if( count <= 0 )
+ return 0;
+ for( I32 i = count - 1; i >= 0; --i ) {
+ if( !map->polygons.size )
+ break;
+
+ I32 expected = action.start_idx + i;
+ I32 idx = i < (I32)action.polys.size ? editor_find_poly_idx( map, action.polys[i], expected ) : -1;
+ if( idx < 0 )
+ idx = editor_clamp_valid_idx( expected, (I32)map->polygons.size );
+
+ MAP_POLYGON* p = &map->polygons[idx];
+ editor_mark_clear_poly_refs( e, p, &clear_props_select, &clear_view_select, &clear_view_drag );
+
+ map->polygons.erase( idx );
+ }
+ } break;
+ default:
+ return 0;
+ }
+
+ if( e->gui.props && clear_props_select )
+ gui_editor_propview_select( e->gui.props, 0, EDITOR_SELECT_NONE );
+ if( e->gui.v2d ) {
+ if( clear_view_select ) {
+ e->gui.v2d->curselect = 0;
+ e->gui.v2d->seltype = EDITOR_SELECT_NONE;
+ }
+ if( clear_view_drag ) {
+ e->gui.v2d->curdrag = 0;
+ e->gui.v2d->dragtype = EDITOR_SELECT_NONE;
+ }
+ }
+
+ editor_refresh_after_map_change( e );
+
+ return 1;
+}
+
+static U8 editor_apply_undo_action_forward( GAME_EDITOR* e, GAME_EDITOR::EDITOR_UNDO_ACTION& action ) {
+ if( !e || !e->map )
+ return 0;
+
+ WORLD_MAP* map = e->map;
+ switch( action.type ) {
+ case EDITOR_UNDO_CREATE_WALLS: {
+ if( !action.walls.size )
+ return 0;
+
+ action.walls.each( fn( MAP_WALL* w ) {
+ map->walls.push( *w );
+ } );
+ } break;
+ case EDITOR_UNDO_CREATE_POLY: {
+ if( !action.polys.size )
+ return 0;
+
+ action.polys.each( fn( MAP_POLYGON* p ) {
+ map->polygons.push( *p );
+ } );
+ } break;
+ default:
+ return 0;
+ }
+
+ editor_refresh_after_map_change( e );
+
+ return 1;
+}
+
+U8 editor_undo( GAME_EDITOR* e ) {
+ if( !e || !e->map || !e->undo_actions.size )
+ return 0;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action = e->undo_actions.pop();
+ if( !editor_apply_undo_action_reverse( e, action ) ) {
+ e->undo_actions.push( action );
+ return 0;
+ }
+
+ e->redo_actions.push( action );
+ return 1;
+}
+
+U8 editor_redo( GAME_EDITOR* e ) {
+ if( !e || !e->map || !e->redo_actions.size )
+ return 0;
+
+ GAME_EDITOR::EDITOR_UNDO_ACTION action = e->redo_actions.pop();
+ if( !editor_apply_undo_action_forward( e, action ) ) {
+ e->redo_actions.push( action );
+ return 0;
+ }
+
+ e->undo_actions.push( action );
+ return 1;
+}
+
+void editor_register_grid_dependency( GAME_EDITOR* e, void* tag, EDITOR_GRID_DEP_CALLBACK cb ) {
+ if( !e || !tag || !cb )
+ return;
+
+ I32 idx = e->grid_dependencies.idx_where( fn( GAME_EDITOR::GRID_DEPENDENCY* dep ) {
+ return dep->tag == tag;
+ } );
+
+ if( idx != -1 ) {
+ e->grid_dependencies[idx].cb = cb;
+ return;
+ }
+
+ GAME_EDITOR::GRID_DEPENDENCY dep;
+ dep.tag = tag;
+ dep.cb = cb;
+ e->grid_dependencies.push( dep );
+}
+
+void editor_notify_grid_change( GAME_EDITOR* e ) {
+ if( !e )
+ return;
+
+ e->grid_dependencies.each( fn( GAME_EDITOR::GRID_DEPENDENCY* dep ) {
+ if( dep->cb ) {
+ dep->cb( e );
+ }
+ } );
+}
+
GAME_EDITOR* editor_create( GAME_DATA* game ) {
if( editor ) {
dlog( "editor_create() : attempted to create editor when one already exists\n" );
@@ -30,6 +426,7 @@ GAME_EDITOR* editor_create( GAME_DATA* game ) {
}
STAT editor_close( GAME_EDITOR* e ) {
+ editor_undo_clear( e );
game_unload_map( e->game );
e->map = 0;
@@ -38,6 +435,7 @@ STAT editor_close( GAME_EDITOR* e ) {
I32 w = e->wnd->w, h = e->wnd->h;
gui_free( e->wnd );
+ editor_clear_gui_state_refs( e );
e->wnd = gui_editorwindow( w, h );
} );
@@ -57,6 +455,7 @@ STAT editor_load_map( GAME_EDITOR* e, const char* mapname ) {
return STAT_ERR;
e->game->state.map = e->map = m;
+ editor_undo_clear( e );
gui_push_callback( e, pfn( void* d ) {
editor_create_map_view( (GAME_EDITOR*)d );
} );