1 /*
2  * Portions of this file are copyright Rebirth contributors and licensed as
3  * described in COPYING.txt.
4  * Portions of this file are copyright Parallax Software and licensed
5  * according to the Parallax license below.
6  * See COPYING.txt for license details.
7 
8 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
9 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
10 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
11 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
12 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
13 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
14 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
15 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
16 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
17 COPYRIGHT 1993-1998 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18 */
19 
20 /*
21  *
22  * Editor loop for Inferno
23  *
24  */
25 
26 //#define DEMO 1
27 
28 #define	DIAGNOSTIC_MESSAGE_MAX				90
29 #define	EDITOR_STATUS_MESSAGE_DURATION	4		//	Shows for 3+..4 seconds
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <stdarg.h>
34 #include <string.h>
35 #include <time.h>
36 
37 #include "inferno.h"
38 #include "args.h"
39 #include "segment.h"
40 #include "gr.h"
41 #include "palette.h"
42 #include "physfsx.h"
43 #include "event.h"
44 #include "window.h"
45 #include "game.h"
46 #include "messagebox.h"
47 #include "ui.h"
48 #include "editor.h"
49 #include "editor/esegment.h"
50 #include "state.h"
51 #include "gamesave.h"
52 #include "gameseg.h"
53 #include "key.h"
54 #include "kconfig.h"
55 #include "mouse.h"
56 #include "dxxerror.h"
57 #include "kfuncs.h"
58 #ifdef INCLUDE_XLISP
59 #include "medlisp.h"
60 #endif
61 #include "u_mem.h"
62 #include "render.h"
63 #include "game.h"
64 #include "gamefont.h"
65 #include "menu.h"
66 #include "slew.h"
67 #include "player.h"
68 #include "kdefs.h"
69 #include "func.h"
70 #include "textures.h"
71 #include "text.h"
72 #include "screens.h"
73 #include "texmap.h"
74 #include "object.h"
75 #include "effects.h"
76 #include "info.h"
77 #include "console.h"
78 #include "texpage.h"		// Textue selection paging stuff
79 #include "objpage.h"		// Object selection paging stuff
80 #include "d_enumerate.h"
81 
82 #include "medmisc.h"
83 #include "meddraw.h"
84 #include "medsel.h"
85 #include "medwall.h"
86 
87 #include "fuelcen.h"
88 #include "gameseq.h"
89 #include "mission.h"
90 #include "newmenu.h"
91 
92 #if defined(DXX_BUILD_DESCENT_II)
93 #include "gamepal.h"
94 #endif
95 
96 #include "compiler-range_for.h"
97 #include "d_levelstate.h"
98 
99 //#define _MARK_ON 1
100 //#include <wsample.h>		//should come after inferno.h to get mark setting //Not included here.
101 
102 #define COMPRESS_INTERVAL	5			// seconds
103 
104 static void med_show_warning(const char *s);
105 
106 //char *undo_status[128];
107 
108 int initializing;
109 
110 //these are instances of canvases, pointed to by variables below
111 grs_subcanvas _canv_editor_game; // the game on the editor screen
112 static grs_subcanvas _canv_editor; // the canvas that the editor writes to
113 
114 //these are pointers to our canvases
115 grs_canvas *Canv_editor;			//the editor screen
116 grs_subcanvas *const Canv_editor_game = &_canv_editor_game; //the game on the editor screen
117 
118 window *Pad_info;		// Keypad text
119 
120 grs_font_ptr editor_font;
121 
122 //where the editor is looking
123 vms_vector Ed_view_target;
124 
125 editor_gamestate gamestate = editor_gamestate::none;
126 
127 editor_dialog *EditorWindow;
128 
129 int	Large_view_index = -1;
130 
131 std::unique_ptr<UI_GADGET_USERBOX> GameViewBox, LargeViewBox, GroupViewBox;
132 
133 static std::unique_ptr<UI_GADGET_ICON>
134 	ViewIcon,
135 	AllIcon,
136 	AxesIcon,
137 	ChaseIcon,
138 	OutlineIcon,
139 	LockIcon;
140 
141 //grs_canvas * BigCanvas[2];
142 //int CurrentBigCanvas = 0;
143 //int BigCanvasFirstTime = 1;
144 
145 int	Found_seg_index=0;				// Index in Found_segs corresponding to Cursegp
146 
print_status_bar(const std::array<char,DIAGNOSTIC_MESSAGE_MAX> & message)147 static void print_status_bar(const std::array<char, DIAGNOSTIC_MESSAGE_MAX> &message)
148 {
149 	gr_set_default_canvas();
150 	auto &canvas = *grd_curcanv;
151 	const auto &editor_font = *::editor_font;
152 	gr_set_fontcolor(canvas, CBLACK, CGREY);
153 	const auto &&[w, h] = gr_get_string_size(editor_font, message.data());
154 	gr_string(canvas, editor_font, 4, 583, message.data(), w, h);
155 	gr_set_fontcolor(canvas, CBLACK, CWHITE);
156 	gr_rect(canvas, 4+w, 583, 799, 599, CGREY);
157 }
158 
159 static std::array<char, DIAGNOSTIC_MESSAGE_MAX> status_line;
160 
161 struct tm	Editor_status_last_time;
162 
163 void (editor_status_fmt)( const char *format, ... )
164 {
165 	va_list ap;
166 
167 	va_start(ap, format);
168 	vsnprintf(status_line.data(), status_line.size(), format, ap);
169 	va_end(ap);
170 
171 	Editor_status_last_time = Editor_time_of_day;
172 }
173 
editor_status(const char * text)174 void editor_status( const char *text)
175 {
176 	Editor_status_last_time = Editor_time_of_day;
177 	strcpy(status_line.data(), text);
178 }
179 
180 // 	int  tm_sec;	/* seconds after the minute -- [0,61] */
181 // 	int  tm_min;	/* minutes after the hour	-- [0,59] */
182 // 	int  tm_hour;	/* hours after midnight	-- [0,23] */
183 // 	int  tm_mday;	/* day of the month		-- [1,31] */
184 // 	int  tm_mon;	/* months since January	-- [0,11] */
185 // 	int  tm_year;	/* years since 1900				*/
186 // 	int  tm_wday;	/* days since Sunday		-- [0,6]  */
187 // 	int  tm_yday;	/* days since January 1	-- [0,365]*/
188 // 	int  tm_isdst;	/* Daylight Savings Time flag */
189 
clear_editor_status(void)190 static void clear_editor_status(void)
191 {
192 	int cur_time = Editor_time_of_day.tm_hour * 3600 + Editor_time_of_day.tm_min*60 + Editor_time_of_day.tm_sec;
193 	int erase_time = Editor_status_last_time.tm_hour * 3600 + Editor_status_last_time.tm_min*60 + Editor_status_last_time.tm_sec + EDITOR_STATUS_MESSAGE_DURATION;
194 
195 	if (cur_time > erase_time) {
196 		std::fill(status_line.begin(), std::prev(status_line.end()), ' ');
197 		status_line.back() = 0;
198 		Editor_status_last_time.tm_hour = 99;
199 	}
200 }
201 
editor_slew_init()202 static inline void editor_slew_init()
203 {
204 	auto &Objects = LevelUniqueObjectState.Objects;
205 	auto &vmobjptr = Objects.vmptr;
206 	Viewer = ConsoleObject;
207 	slew_init(vmobjptr(ConsoleObject));
208 	init_player_object();
209 }
210 
DropIntoDebugger()211 int DropIntoDebugger()
212 {
213 	Int3();
214 	return 1;
215 }
216 
217 
218 #ifdef INCLUDE_XLISP
CallLisp()219 int CallLisp()
220 {
221 	medlisp_go();
222 	return 1;
223 }
224 #endif
225 
226 
ExitEditor()227 int ExitEditor()
228 {
229 	if (SafetyCheck())  {
230 		ModeFlag = 1;
231 	}
232 	return 1;
233 }
234 
GotoGameScreen()235 int	GotoGameScreen()
236 {
237 	auto &Objects = LevelUniqueObjectState.Objects;
238 	auto &vmobjptr = Objects.vmptr;
239 	pause_game_world_time p;
240 
241 //@@	init_player_stats();
242 //@@
243 //@@	Player_init.pos = Player->pos;
244 //@@	Player_init.orient = Player->orient;
245 //@@	Player_init.segnum = Player->segnum;
246 
247 
248 // -- must always save gamesave.sge/lvl because the restore-objects code relies on it
249 // -- that code could be made smarter and use the original file, if appropriate.
250 //	if (mine_changed)
251 	switch (gamestate)
252 	{
253 		case editor_gamestate::none:
254 			// Always use the simple mission when playing level (for now at least)
255 			create_new_mission();
256 			Current_level_num = 1;
257 			if (save_level(
258 #if defined(DXX_BUILD_DESCENT_II)
259 					LevelSharedSegmentState.DestructibleLights,
260 #endif
261 					"GAMESAVE.LVL"))
262 				return 0;
263 			editor_status("Level saved.\n");
264 			break;
265 
266 		case editor_gamestate::unsaved:
267 		case editor_gamestate::saved:
268 			if (!SafetyCheck())		// if mine edited but not saved, warn the user!
269 				return 0;
270 
271 			const auto &&console = vmobjptr(get_local_player().objnum);
272 			Viewer = ConsoleObject = console;
273 			set_player_id(console, Player_num);
274 			fly_init(*ConsoleObject);
275 
276 			if (!state_save_all_sub(PLAYER_DIRECTORY_STRING("gamesave.sge"), "Editor generated"))
277 			{
278 				editor_slew_init();
279 
280 				return 0;
281 			}
282 			gamestate = editor_gamestate::saved;
283 			editor_status("Game saved.\n");
284 			break;
285 	}
286 
287 	ModeFlag = 3;
288 	return 1;
289 }
290 
GotoMainMenu()291 int GotoMainMenu()
292 {
293 	ModeFlag = 2;
294 	return 1;
295 }
296 
297 static std::array<int (*)(), 2048> KeyFunction;
298 
medkey_init()299 static void medkey_init()
300 {
301 	char keypress[100];
302 	int key;
303 	int np;
304 	char LispCommand[DIAGNOSTIC_MESSAGE_MAX];
305 
306 	KeyFunction = {};
307 
308 	if (auto keyfile = PHYSFSX_openReadBuffered("GLOBAL.KEY").first)
309 	{
310 		PHYSFSX_gets_line_t<200> line_buffer;
311 		while (PHYSFSX_fgets(line_buffer, keyfile))
312 		{
313 			sscanf(line_buffer, " %s %s ", keypress, LispCommand);
314 			//ReadLispMacro( keyfile, LispCommand );
315 
316 			if ( (key=DecodeKeyText( keypress ))!= -1 )
317 			{
318 				Assert( key < 2048);
319 				KeyFunction[key] = func_get( LispCommand, &np );
320 			} else {
321 				UserError( "Bad key %s in GLOBAL.KEY!", keypress );
322 			}
323 		}
324 	}
325 }
326 
327 static int padnum=0;
328 //@@short camera_objnum;			//a camera for viewing. Who knows, might become handy
329 
330 static void init_editor_screen(grs_canvas &canvas);
331 static void gamestate_restore_check();
332 static void close_editor();
333 
334 namespace dsx {
init_editor()335 void init_editor()
336 {
337 	static const char pads[][13] = {
338 		"segmove.pad",
339 		"segsize.pad",
340 		"curve.pad",
341 		"texture.pad",
342 		"object.pad",
343 		"objmov.pad",
344 		"group.pad",
345 		"lighting.pad",
346 		"test.pad"
347 					};
348 	ModeFlag = Game_wind ? 3 : 2;	// go back to where we were unless we loaded everything properly
349 
350 	// first, make sure we can find the files we need
351 	std::array<char, PATH_MAX> pathname;
352 	PHYSFSX_addRelToSearchPath("editor/data", pathname, physfs_search_path::append);	// look in source directory first (for work in progress)
353 	PHYSFSX_addRelToSearchPath("editor", pathname, physfs_search_path::append);		// then in editor directory
354 	PHYSFSX_addRelToSearchPath("editor.zip", pathname, physfs_search_path::append);	// then in a zip file
355 	PHYSFSX_addRelToSearchPath("editor.dxa", pathname, physfs_search_path::append);	// or addon pack
356 
357 	if (!ui_init())
358 	{
359 		close_editor();
360 		return;
361 	}
362 
363 	init_med_functions();	// Must be called before medlisp_init
364 
365 	for (auto &&[idx, value] : enumerate(pads))
366 		if (!ui_pad_read(idx, value))
367 		{
368 			close_editor();
369 			return;
370 		}
371 
372 	medkey_init();
373 
374 	game_flush_inputs(Controls);
375 
376 	editor_font = gr_init_font(*grd_curcanv, "pc8x16.fnt");
377 	if (!editor_font)
378 	{
379 		Warning("Could not find pc8x16.fnt");
380 		close_editor();
381 		return;
382 	}
383 
384 	if (!menubar_init(*grd_curcanv, "MED.MNU"))
385 	{
386 		close_editor();
387 		return;
388 	}
389 
390 	Draw_all_segments = 1;						// Say draw all segments, not just connected ones
391 
392 	if (!Cursegp)
393 		Cursegp = imsegptridx(segment_first);
394 
395 	init_autosave();
396 
397 	Clear_window = 1;	//	do full window clear.
398 
399 	InitCurve();
400 
401 	restore_effect_bitmap_icons();
402 
403 	if (!set_screen_mode(SCREEN_EDITOR))
404 	{
405 		close_editor();
406 		return;
407 	}
408 #if defined(DXX_BUILD_DESCENT_I)
409 	gr_use_palette_table( "palette.256" );
410 	gr_palette_load( gr_palette );
411 #elif defined(DXX_BUILD_DESCENT_II)
412 	load_palette(Current_level_palette,1,0);
413 #endif
414 
415 	//Editor renders into full (320x200) game screen
416 
417 	game_init_render_sub_buffers(*grd_curcanv, 0, 0, 320, 200);
418 	gr_init_sub_canvas(_canv_editor, grd_curscreen->sc_canvas, 0, 0, SWIDTH, SHEIGHT);
419 	Canv_editor = &_canv_editor;
420 	gr_set_current_canvas(*Canv_editor);
421 	init_editor_screen(*grd_curcanv); // load the main editor dialog
422 	gr_set_default_canvas();
423 	set_warn_func(med_show_warning);
424 
425 	//	_MARK_("start of editor");//Nuked to compile -KRB
426 
427 	//@@	//create a camera for viewing in the editor. copy position from ConsoleObject
428 	//@@	camera_objnum = obj_create(OBJ_CAMERA,0,ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0);
429 	//@@	Viewer = &Objects[camera_objnum];
430 	//@@	slew_init(Viewer);		//camera is slewing
431 
432 	editor_slew_init();
433 
434 	Update_flags = UF_ALL;
435 
436 	//set the wire-frame window to be the current view
437 	current_view = &LargeView;
438 
439 	gr_set_current_canvas( GameViewBox->canvas );
440 	gr_set_curfont(*grd_curcanv, *editor_font);
441 	// No font scaling!
442 	FNTScaleX.reset(1);
443 	FNTScaleY.reset(1);
444 	ui_pad_goto(padnum);
445 
446 	ModeFlag = 0;	// success!
447 
448 	gamestate_restore_check();
449 }
450 }
451 
ShowAbout()452 int ShowAbout()
453 {
454 	ui_messagebox( -2, -2, 1, 	"INFERNO Mine Editor\n\n"		\
455 									"Copyright (c) 1993  Parallax Software Corp.",
456 									"OK");
457 	return 0;
458 }
459 
SetPlayerFromCurseg()460 int SetPlayerFromCurseg()
461 {
462 	move_player_2_segment(Cursegp,Curside);
463 	Update_flags |= UF_ED_STATE_CHANGED | UF_GAME_VIEW_CHANGED;
464 	return 1;
465 }
466 
fuelcen_create_from_curseg()467 int fuelcen_create_from_curseg()
468 {
469 	Cursegp->special = segment_special::fuelcen;
470 	fuelcen_activate(Cursegp);
471 	return 1;
472 }
473 
repaircen_create_from_curseg()474 int repaircen_create_from_curseg()
475 {
476 	Int3();	//	-- no longer supported!
477 //	Cursegp->special = segment_special::repaircen;
478 //	fuelcen_activate(Cursegp, Cursegp->special);
479 	return 1;
480 }
481 
controlcen_create_from_curseg()482 int controlcen_create_from_curseg()
483 {
484 	Cursegp->special = segment_special::controlcen;
485 	fuelcen_activate(Cursegp);
486 	return 1;
487 }
488 
robotmaker_create_from_curseg()489 int robotmaker_create_from_curseg()
490 {
491 	Cursegp->special = segment_special::robotmaker;
492 	fuelcen_activate(Cursegp);
493 	return 1;
494 }
495 
fuelcen_reset_all()496 int fuelcen_reset_all()	{
497 	fuelcen_reset();
498 	return 1;
499 }
500 
fuelcen_delete_from_curseg()501 int fuelcen_delete_from_curseg() {
502 	fuelcen_delete( Cursegp );
503 	return 1;
504 }
505 
506 
507 //@@//this routine places the viewer in the center of the side opposite to curside,
508 //@@//with the view toward the center of curside
509 //@@int SetPlayerFromCursegMinusOne()
510 //@@{
511 //@@	vms_vector vp;
512 //@@
513 //@@//	int newseg,newside;
514 //@@//	get_previous_segment(SEG_PTR_2_NUM(Cursegp),Curside,&newseg,&newside);
515 //@@//	move_player_2_segment(&Segments[newseg],newside);
516 //@@
517 //@@	med_compute_center_point_on_side(&Player->obj_position,Cursegp,Side_opposite[Curside]);
518 //@@	med_compute_center_point_on_side(&vp,Cursegp,Curside);
519 //@@	vm_vec_sub2(&vp,&Player->position);
520 //@@	vm_vector_2_matrix(&Player->orient,&vp,NULL,NULL);
521 //@@
522 //@@	Player->seg = SEG_PTR_2_NUM(Cursegp);
523 //@@
524 //@@	Update_flags |= UF_GAME_VIEW_CHANGED;
525 //@@	return 1;
526 //@@}
527 
528 //this constant determines how much of the window will be occupied by the
529 //viewed side when SetPlayerFromCursegMinusOne() is called.  It actually
530 //determine how from from the center of the window the farthest point will be
531 #define SIDE_VIEW_FRAC (f1_0*8/10)	//80%
532 
533 
move_player_2_segment_and_rotate(const vmsegptridx_t seg,const unsigned side)534 static void move_player_2_segment_and_rotate(const vmsegptridx_t seg, const unsigned side)
535 {
536 	auto &Objects = LevelUniqueObjectState.Objects;
537 	auto &vmobjptr = Objects.vmptr;
538 	auto &vmobjptridx = Objects.vmptridx;
539         static int edgenum=0;
540 
541 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
542 	auto &Vertices = LevelSharedVertexState.get_vertices();
543 	auto &vcvertptr = Vertices.vcptr;
544 	compute_segment_center(vcvertptr, ConsoleObject->pos,seg);
545 	auto vp = compute_center_point_on_side(vcvertptr, seg, side);
546 	vm_vec_sub2(vp,ConsoleObject->pos);
547 
548 	auto &sv = Side_to_verts[Curside];
549 	auto &verts = Cursegp->verts;
550 	const auto upvec = vm_vec_sub(vcvertptr(verts[sv[edgenum % 4]]), vcvertptr(verts[sv[(edgenum + 3) % 4]]));
551 	edgenum++;
552 
553 	vm_vector_2_matrix(ConsoleObject->orient,vp,&upvec,nullptr);
554 //	vm_vector_2_matrix(&ConsoleObject->orient,&vp,NULL,NULL);
555 
556 	obj_relink(vmobjptr, vmsegptr, vmobjptridx(ConsoleObject), seg);
557 }
558 
SetPlayerFromCursegAndRotate()559 int SetPlayerFromCursegAndRotate()
560 {
561 	move_player_2_segment_and_rotate(Cursegp,Curside);
562 	Update_flags |= UF_ED_STATE_CHANGED | UF_GAME_VIEW_CHANGED;
563 	return 1;
564 }
565 
566 
567 //sets the player facing curseg/curside, normal to face0 of curside, and
568 //far enough away to see all of curside
SetPlayerFromCursegMinusOne()569 int SetPlayerFromCursegMinusOne()
570 {
571 	auto &Objects = LevelUniqueObjectState.Objects;
572 	auto &vmobjptr = Objects.vmptr;
573 	auto &vmobjptridx = Objects.vmptridx;
574 	std::array<g3s_point, 4> corner_p;
575 	fix max,view_dist=f1_0*10;
576         static int edgenum=0;
577 	const auto view_vec = vm_vec_negated(Cursegp->shared_segment::sides[Curside].normals[0]);
578 
579 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
580 	auto &Vertices = LevelSharedVertexState.get_vertices();
581 	auto &vcvertptr = Vertices.vcptr;
582 	const auto &&side_center = compute_center_point_on_side(vcvertptr, Cursegp, Curside);
583 	const auto view_vec2 = vm_vec_copy_scale(view_vec,view_dist);
584 	vm_vec_sub(ConsoleObject->pos,side_center,view_vec2);
585 
586 	auto &sv = Side_to_verts[Curside];
587 	auto &verts = Cursegp->verts;
588 	const auto upvec = vm_vec_sub(vcvertptr(verts[sv[edgenum % 4]]), vcvertptr(verts[sv[(edgenum + 3) % 4]]));
589 	edgenum++;
590 
591 	vm_vector_2_matrix(ConsoleObject->orient,view_vec,&upvec,nullptr);
592 
593 	gr_set_current_canvas(*Canv_editor_game);
594 	g3_start_frame(*grd_curcanv);
595 	g3_set_view_matrix(ConsoleObject->pos,ConsoleObject->orient,Render_zoom);
596 
597 	for (unsigned i = max = 0; i < 4; ++i)
598 	{
599 		g3_rotate_point(corner_p[i], vcvertptr(verts[sv[i]]));
600 		if (labs(corner_p[i].p3_x) > max) max = labs(corner_p[i].p3_x);
601 		if (labs(corner_p[i].p3_y) > max) max = labs(corner_p[i].p3_y);
602 	}
603 
604 	view_dist = fixmul(view_dist,fixdiv(fixdiv(max,SIDE_VIEW_FRAC),corner_p[0].p3_z));
605 	const auto view_vec3 = vm_vec_copy_scale(view_vec,view_dist);
606 	vm_vec_sub(ConsoleObject->pos,side_center,view_vec3);
607 
608 	//obj_relink(ConsoleObject-Objects, SEG_PTR_2_NUM(Cursegp) );
609 	//update_object_seg(ConsoleObject);		//might have backed right out of curseg
610 
611 	const auto &&newseg = find_point_seg(LevelSharedSegmentState, LevelUniqueSegmentState, ConsoleObject->pos, Cursegp);
612 	if (newseg != segment_none)
613 		obj_relink(vmobjptr, vmsegptr, vmobjptridx(ConsoleObject), newseg);
614 
615 	Update_flags |= UF_ED_STATE_CHANGED | UF_GAME_VIEW_CHANGED;
616 	return 1;
617 }
618 
619 #if !DXX_USE_OGL
ToggleLighting(void)620 static int ToggleLighting(void)
621 {
622 	Lighting_on++;
623 	if (Lighting_on >= 2)
624 		Lighting_on = 0;
625 
626 	Update_flags |= UF_GAME_VIEW_CHANGED;
627 
628 	switch (Lighting_on) {
629 		case 0:
630 			diagnostic_message("Lighting off.");
631 			break;
632 		case 1:
633 			diagnostic_message("Static lighting.");
634 			break;
635 		case 2:
636 			diagnostic_message("Ship lighting.");
637 			break;
638 		case 3:
639 			diagnostic_message("Ship and static lighting.");
640 			break;
641 	}
642 
643 	return Lighting_on;
644 }
645 #endif
646 
FindConcaveSegs()647 int FindConcaveSegs()
648 {
649 	find_concave_segs();
650 
651 	Update_flags |= UF_ED_STATE_CHANGED;		//list may have changed
652 
653 	return 1;
654 }
655 
ToggleOutlineMode()656 static int ToggleOutlineMode()
657 {
658 #ifndef NDEBUG
659 	int mode;
660 
661 	mode=toggle_outline_mode();
662 
663         if (mode)
664          {
665 		//if (keypress != KEY_O)
666 			diagnostic_message("[Ctrl-O] Outline Mode ON.");
667 		//else
668 		//	diagnostic_message("Outline Mode ON.");
669          }
670 	else
671          {
672 		//if (keypress != KEY_O)
673 			diagnostic_message("[Ctrl-O] Outline Mode OFF.");
674 		//else
675 		//	diagnostic_message("Outline Mode OFF.");
676          }
677 
678 	Update_flags |= UF_GAME_VIEW_CHANGED;
679 	return mode;
680 #else
681 	return 1;
682 #endif
683 }
684 
685 //@@int do_reset_orient()
686 //@@{
687 //@@	slew_reset_orient(SlewObj);
688 //@@
689 //@@	Update_flags |= UF_GAME_VIEW_CHANGED;
690 //@@
691 //@@	* reinterpret_cast<uint8_t *>(0x417) &= ~0x20;
692 //@@
693 //@@	return 1;
694 //@@}
695 
GameZoomOut()696 int GameZoomOut()
697 {
698 	Render_zoom = fixmul(Render_zoom,68985);
699 	Update_flags |= UF_GAME_VIEW_CHANGED;
700 	return 1;
701 }
702 
GameZoomIn()703 int GameZoomIn()
704 {
705 	Render_zoom = fixmul(Render_zoom,62259);
706 	Update_flags |= UF_GAME_VIEW_CHANGED;
707 	return 1;
708 }
709 
710 
med_keypad_goto_0()711 static int med_keypad_goto_0()	{	ui_pad_goto(0);	return 0;	}
med_keypad_goto_1()712 static int med_keypad_goto_1()	{	ui_pad_goto(1);	return 0;	}
med_keypad_goto_2()713 static int med_keypad_goto_2()	{	ui_pad_goto(2);	return 0;	}
med_keypad_goto_3()714 static int med_keypad_goto_3()	{	ui_pad_goto(3);	return 0;	}
med_keypad_goto_4()715 static int med_keypad_goto_4()	{	ui_pad_goto(4);	return 0;	}
med_keypad_goto_5()716 static int med_keypad_goto_5()	{	ui_pad_goto(5);	return 0;	}
med_keypad_goto_6()717 static int med_keypad_goto_6()	{	ui_pad_goto(6);	return 0;	}
med_keypad_goto_7()718 static int med_keypad_goto_7()	{	ui_pad_goto(7);	return 0;	}
med_keypad_goto_8()719 static int med_keypad_goto_8()	{	ui_pad_goto(8);	return 0;	}
720 
721 #define	PAD_WIDTH	30
722 #define	PAD_WIDTH1	(PAD_WIDTH + 7)
723 
724 int editor_screen_open = 0;
725 
726 //setup the editors windows, canvases, gadgets, etc.
init_editor_screen(grs_canvas & canvas)727 static void init_editor_screen(grs_canvas &canvas)
728 {
729 //	grs_bitmap * bmp;
730 
731 	if (editor_screen_open) return;
732 
733 	grd_curscreen->sc_canvas.cv_font = editor_font.get();
734 
735 	//create canvas for game on the editor screen
736 	initializing = 1;
737 	gr_set_current_canvas(*Canv_editor);
738 	Canv_editor->cv_font = editor_font.get();
739 	gr_init_sub_canvas(*Canv_editor_game, *Canv_editor, GAMEVIEW_X, GAMEVIEW_Y, GAMEVIEW_W, GAMEVIEW_H);
740 
741 	//Editor renders into full (320x200) game screen
742 
743 	init_info = 1;
744 
745 	//do other editor screen setup
746 
747 	// Since the palette might have changed, find some good colors...
748 	CBLACK = gr_find_closest_color( 1, 1, 1 );
749 	CGREY = gr_find_closest_color( 28, 28, 28 );
750 	CWHITE = gr_find_closest_color( 38, 38, 38 );
751 	CBRIGHT = gr_find_closest_color( 60, 60, 60 );
752 	CRED = gr_find_closest_color( 63, 0, 0 );
753 
754 	gr_set_curfont(canvas, *editor_font);
755 	gr_set_fontcolor(canvas, CBLACK, CWHITE);
756 
757 	EditorWindow = window_create<editor_dialog>(0, 0, ED_SCREEN_W, ED_SCREEN_H, DF_FILLED);
758 	auto e = EditorWindow;
759 
760 	LargeViewBox = ui_add_gadget_userbox(*e, LVIEW_X, LVIEW_Y, LVIEW_W, LVIEW_H);
761 	ui_gadget_calc_keys(*e);	//make tab work for all windows
762 
763 	GameViewBox	= ui_add_gadget_userbox(*e, GAMEVIEW_X, GAMEVIEW_Y, GAMEVIEW_W, GAMEVIEW_H);
764 
765 //	GameViewBox->when_tab = GameViewBox->when_btab =  LargeViewBox;
766 //	LargeViewBox->when_tab = LargeViewBox->when_btab =  GameViewBox;
767 
768 	ViewIcon	= ui_add_gadget_icon(*e, "Lock\nview", 455, 25 + 530, 40, 22,	KEY_V+KEY_CTRLED, ToggleLockViewToCursegp);
769 	AllIcon	= ui_add_gadget_icon(*e, "Draw\nall", 500, 25 + 530, 40, 22,	-1, ToggleDrawAllSegments);
770 	AxesIcon	= ui_add_gadget_icon(*e, "Coord\naxes", 545, 25 + 530, 40, 22,	KEY_D+KEY_CTRLED, ToggleCoordAxes);
771 	ChaseIcon	= ui_add_gadget_icon(*e, "Chase\nmode", 635, 25 + 530, 40, 22,	-1,				ToggleChaseMode);
772 	OutlineIcon = ui_add_gadget_icon(*e, "Out\nline", 680, 25 + 530, 40, 22,	KEY_O+KEY_CTRLED,			ToggleOutlineMode);
773 	LockIcon	= ui_add_gadget_icon(*e, "Lock\nstep", 725, 25 + 530, 40, 22,	KEY_L+KEY_CTRLED,			ToggleLockstep);
774 
775 	meddraw_init_views(LargeViewBox->canvas.get());
776 
777 	ui_pad_activate(*e, PAD_X, PAD_Y);
778 	Pad_info = info_window_create();
779 	e->pad_prev = ui_add_gadget_button(*e, PAD_X + 6, PAD_Y + (30 * 5) + 22, PAD_WIDTH, 20, "<<", med_keypad_goto_prev);
780 	e->pad_next = ui_add_gadget_button(*e, PAD_X + PAD_WIDTH1 + 6, PAD_Y + (30 * 5) + 22, PAD_WIDTH, 20, ">>", med_keypad_goto_next);
781 
782 	{	int	i;
783 		i = 0;	e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "SR", med_keypad_goto_0);
784 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "SS", med_keypad_goto_1);
785 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "CF", med_keypad_goto_2);
786 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "TM", med_keypad_goto_3);
787 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "OP", med_keypad_goto_4);
788 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "OR", med_keypad_goto_5);
789 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "GE", med_keypad_goto_6);
790 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "LI", med_keypad_goto_7);
791 		i++;		e->pad_goto[i] = ui_add_gadget_button(*e, PAD_X+16+(i+2)*PAD_WIDTH1, PAD_Y+(30*5)+22, PAD_WIDTH, 20, "TT", med_keypad_goto_8);
792 	}
793 
794 	menubar_show();
795 
796 	// INIT TEXTURE STUFF
797 	texpage_init(e);
798 	objpage_init(e);
799 
800 	e->keyboard_focus_gadget = LargeViewBox.get();
801 
802 	Update_flags = UF_ALL;
803 	initializing = 0;
804 	editor_screen_open = 1;
805 }
806 
807 //shutdown ui on the editor screen
close_editor_screen()808 void close_editor_screen()
809 {
810 	if (!editor_screen_open) return;
811 
812 	editor_screen_open = 0;
813 	ui_pad_deactivate();
814 	if (Pad_info)
815 		window_close(Pad_info);
816 
817 	close_all_windows();
818 
819 	// CLOSE TEXTURE STUFF
820 	texpage_close();
821 	objpage_close();
822 
823 	menubar_hide();
824 
825 }
826 
med_show_warning(const char * s)827 static void med_show_warning(const char *s)
828 {
829 	grs_canvas &save_canv = *grd_curcanv;
830 
831 	//gr_pal_fade_in(grd_curscreen->pal);	//in case palette is blacked
832 
833 	ui_messagebox(-2,-2,1,s,"OK");
834 
835 	gr_set_current_canvas(save_canv);
836 }
837 
838 // Returns 1 if OK to trash current mine.
SafetyCheck()839 int SafetyCheck()
840 {
841 	if (mine_changed) {
842 		const auto x = nm_messagebox_str(menu_title{"Warning!"}, nm_messagebox_tie("Cancel", TXT_OK), menu_subtitle{"You are about to lose work."});
843 		if (x<1) {
844 			return 0;
845 		}
846 	}
847 	return 1;
848 }
849 
850 //called at the end of the program
851 
close_editor()852 static void close_editor()
853 {
854 	//	_MARK_("end of editor");//Nuked to compile -KRB
855 
856 #if defined(WIN32) || defined(__APPLE__) || defined(__MACH__)
857 	set_warn_func(msgbox_warning);
858 #else
859 	clear_warn_func();
860 #endif
861 
862 	close_editor_screen();
863 
864 	//kill our camera object
865 
866 	Viewer = ConsoleObject;					//reset viewer
867 	//@@obj_delete(camera_objnum);
868 
869 	padnum = ui_pad_get_current();
870 
871 	close_autosave();
872 
873 	ui_close();
874 
875 	editor_font.reset();
876 
877 	PHYSFSX_removeRelFromSearchPath("editor/data");
878 	PHYSFSX_removeRelFromSearchPath("editor");
879 	PHYSFSX_removeRelFromSearchPath("editor.zip");
880 	PHYSFSX_removeRelFromSearchPath("editor.dxa");
881 
882 	switch (ModeFlag)
883 	{
884 		case 1:
885 			break;
886 
887 		case 2:
888 			set_screen_mode(SCREEN_MENU);		//put up menu screen
889 			show_menus();
890 			break;
891 
892 		case 3:
893 			if (Game_wind)
894 				return;	// if we're already playing a game, don't touch!
895 
896 			switch (gamestate)
897 			{
898 				case editor_gamestate::none:
899 					StartNewGame(Current_level_num);
900 					break;
901 
902 				case editor_gamestate::saved:
903 					state_restore_all_sub(
904 #if defined(DXX_BUILD_DESCENT_II)
905 						LevelSharedSegmentState.DestructibleLights, secret_restore::none,
906 #endif
907 						PLAYER_DIRECTORY_STRING("gamesave.sge")
908 					);
909 					break;
910 
911 				default:
912 					Int3();	// shouldn't happen
913 					break;
914 			}
915 			break;
916 	}
917 
918 	return;
919 }
920 
921 //variables for find segments process
922 
923 // ---------------------------------------------------------------------------------------------------
924 //	Subtract all elements in Found_segs from selected list.
subtract_found_segments_from_selected_list(void)925 static void subtract_found_segments_from_selected_list(void)
926 {
927 	range_for (const auto &foundnum, Found_segs)
928 	{
929 		selected_segment_array_t::iterator i = Selected_segs.find(foundnum), e = Selected_segs.end();
930 		if (i != e)
931 		{
932 			*i = *-- e;
933 			Selected_segs.erase(e);
934 		}
935 	}
936 }
937 
938 // ---------------------------------------------------------------------------------------------------
939 //	Add all elements in Found_segs to selected list.
add_found_segments_to_selected_list(void)940 static void add_found_segments_to_selected_list(void) {
941 	range_for (const auto &foundnum, Found_segs)
942 	{
943 		selected_segment_array_t::iterator i = Selected_segs.find(foundnum), e = Selected_segs.end();
944 		if (i == e)
945 			Selected_segs.emplace_back(foundnum);
946 	}
947 }
948 
gamestate_restore_check()949 void gamestate_restore_check()
950 {
951 	auto &Objects = LevelUniqueObjectState.Objects;
952 	auto &vmobjptr = Objects.vmptr;
953 	auto &vmobjptridx = Objects.vmptridx;
954 	obj_position Save_position;
955 
956 	if (gamestate == editor_gamestate::saved)
957 	{
958 		if (ui_messagebox(-2, -2, 2, "Do you wish to restore game state?\n", "Yes", "No") == 1)
959 		{
960 
961 			// Save current position
962 			Save_position.pos = ConsoleObject->pos;
963 			Save_position.orient = ConsoleObject->orient;
964 			Save_position.segnum = ConsoleObject->segnum;
965 
966 			if (!state_restore_all_sub(
967 #if defined(DXX_BUILD_DESCENT_II)
968 					LevelSharedSegmentState.DestructibleLights, secret_restore::none,
969 #endif
970 					PLAYER_DIRECTORY_STRING("gamesave.sge")
971 			))
972 				return;
973 
974 			// Switch back to slew mode - loading saved game made ConsoleObject flying
975 			editor_slew_init();
976 
977 			// Restore current position
978 			if (Save_position.segnum <= Highest_segment_index) {
979 				ConsoleObject->pos = Save_position.pos;
980 				ConsoleObject->orient = Save_position.orient;
981 				obj_relink(vmobjptr, vmsegptr, vmobjptridx(ConsoleObject), vmsegptridx(Save_position.segnum));
982 			}
983 
984 			Update_flags |= UF_WORLD_CHANGED;
985 		}
986 		else
987 			gamestate = editor_gamestate::none;
988 	}
989 }
990 
RestoreGameState()991 int RestoreGameState()
992 {
993 	if (!SafetyCheck())
994 		return 0;
995 
996 	if (!state_restore_all_sub(
997 #if defined(DXX_BUILD_DESCENT_II)
998 			LevelSharedSegmentState.DestructibleLights, secret_restore::none,
999 #endif
1000 			PLAYER_DIRECTORY_STRING("gamesave.sge")
1001 	))
1002 		return 0;
1003 
1004 	// Switch back to slew mode - loading saved game made ConsoleObject flying
1005 	editor_slew_init();
1006 
1007 	gamestate = editor_gamestate::saved;
1008 
1009 	editor_status("Gamestate restored.\n");
1010 
1011 	Update_flags |= UF_WORLD_CHANGED;
1012 	return 1;
1013 }
1014 
1015 // Handler for the main editor dialog
callback_handler(const d_event & event)1016 window_event_result editor_dialog::callback_handler(const d_event &event)
1017 {
1018 	auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
1019 	auto &Vertices = LevelSharedVertexState.get_vertices();
1020 	editor_view *new_cv;
1021 	int keypress = 0;
1022 	window_event_result rval = window_event_result::ignored;
1023 
1024 	if (event.type == EVENT_WINDOW_CREATED)
1025 		return window_event_result::ignored;
1026 
1027 	if (event.type == EVENT_KEY_COMMAND)
1028 		keypress = event_key_get(event);
1029 	else if (event.type == EVENT_WINDOW_CLOSE)
1030 	{
1031 		EditorWindow = NULL;
1032 		close_editor();
1033 		return window_event_result::ignored;
1034 	}
1035 
1036 	// Update the windows
1037 
1038 	if (event.type == EVENT_UI_DIALOG_DRAW)
1039 	{
1040 		// Draw status box
1041 		gr_set_default_canvas();
1042 		gr_rect(*grd_curcanv, STATUS_X,STATUS_Y,STATUS_X+STATUS_W-1,STATUS_Y+STATUS_H-1, CGREY);
1043 
1044 		medlisp_update_screen();
1045 		calc_frame_time();
1046 		texpage_do(event);
1047 		objpage_do(event);
1048 		ui_pad_draw(EditorWindow, PAD_X, PAD_Y);
1049 
1050 		print_status_bar(status_line);
1051 		TimedAutosave(mine_filename);	// shows the time, hence here
1052 		set_editor_time_of_day();
1053 		return window_event_result::handled;
1054 	}
1055 
1056 	if ((selected_gadget == GameViewBox.get() && !render_3d_in_big_window) ||
1057 		(selected_gadget == LargeViewBox.get() && render_3d_in_big_window))
1058 		switch (event.type)
1059 		{
1060 			case EVENT_MOUSE_BUTTON_UP:
1061 			case EVENT_MOUSE_BUTTON_DOWN:
1062 				break;
1063 			case EVENT_MOUSE_MOVED:
1064 				if (!keyd_pressed[ KEY_LCTRL ] && !keyd_pressed[ KEY_RCTRL ])
1065 					break;
1066 				DXX_BOOST_FALLTHROUGH;
1067 #if DXX_MAX_BUTTONS_PER_JOYSTICK
1068 			case EVENT_JOYSTICK_BUTTON_UP:
1069 			case EVENT_JOYSTICK_BUTTON_DOWN:
1070 #endif
1071 #if DXX_MAX_AXES_PER_JOYSTICK
1072 			case EVENT_JOYSTICK_MOVED:
1073 #endif
1074 			case EVENT_KEY_COMMAND:
1075 			case EVENT_KEY_RELEASE:
1076 			case EVENT_IDLE:
1077 				kconfig_read_controls(Controls, event, 1);
1078 
1079 				if (slew_frame(0))
1080 				{		//do movement and check keys
1081 					Update_flags |= UF_GAME_VIEW_CHANGED;
1082 					if (Gameview_lockstep)
1083 					{
1084 						Cursegp = imsegptridx(ConsoleObject->segnum);
1085 						med_create_new_segment_from_cursegp();
1086 						Update_flags |= UF_ED_STATE_CHANGED;
1087 					}
1088 
1089 					rval = window_event_result::handled;
1090 				}
1091 				break;
1092 			case EVENT_LOOP_BEGIN_LOOP:
1093 				kconfig_begin_loop(Controls);
1094 				break;
1095 
1096 			default:
1097 				break;
1098 		}
1099 
1100 	//do non-essential stuff in idle event
1101 	if (event.type == EVENT_IDLE)
1102 	{
1103 		check_wall_validity();
1104 
1105 		if (Gameview_lockstep) {
1106 			static segment *old_cursegp=NULL;
1107 			static int old_curside=-1;
1108 
1109 			if (old_cursegp!=Cursegp || old_curside!=Curside) {
1110 				SetPlayerFromCursegMinusOne();
1111 				old_cursegp = Cursegp;
1112 				old_curside = Curside;
1113 			}
1114 		}
1115 
1116 		if ( event_get_idle_seconds() > COMPRESS_INTERVAL )
1117 		{
1118 			med_compress_mine();
1119 			event_reset_idle_seconds();
1120 		}
1121 
1122 	//	Commented out because it occupies about 25% of time in twirling the mine.
1123 	// Removes some Asserts....
1124 	//		med_check_all_vertices();
1125 		clear_editor_status();		// if enough time elapsed, clear editor status message
1126 	}
1127 
1128 	gr_set_current_canvas( GameViewBox->canvas );
1129 
1130 	// Remove keys used for slew
1131 	switch(keypress)
1132 	{
1133 		case KEY_PAD9:
1134 		case KEY_PAD7:
1135 		case KEY_PADPLUS:
1136 		case KEY_PADMINUS:
1137 		case KEY_PAD8:
1138 		case KEY_PAD2:
1139 		case KEY_LBRACKET:
1140 		case KEY_RBRACKET:
1141 		case KEY_PAD1:
1142 		case KEY_PAD3:
1143 		case KEY_PAD6:
1144 		case KEY_PAD4:
1145 			keypress = 0;
1146 	}
1147 	if ((keypress&0xff)==KEY_LSHIFT) keypress=0;
1148 	if ((keypress&0xff)==KEY_RSHIFT) keypress=0;
1149 	if ((keypress&0xff)==KEY_LCTRL) keypress=0;
1150 	if ((keypress&0xff)==KEY_RCTRL) keypress=0;
1151 	if ((keypress&0xff)==KEY_LMETA) keypress=0;
1152 	if ((keypress&0xff)==KEY_RMETA) keypress=0;
1153 //		if ((keypress&0xff)==KEY_LALT) keypress=0;
1154 //		if ((keypress&0xff)==KEY_RALT) keypress=0;
1155 
1156 	//=================== DO FUNCTIONS ====================
1157 
1158 	if ( KeyFunction[ keypress ] != NULL )
1159 	{
1160 		KeyFunction[keypress]();
1161 		keypress = 0;
1162 		rval = window_event_result::handled;
1163 	}
1164 
1165 	switch (keypress)
1166 	{
1167 		case 0:
1168 		case KEY_Z:
1169 		case KEY_G:
1170 		case KEY_LALT:
1171 		case KEY_RALT:
1172 		case KEY_LCTRL:
1173 		case KEY_RCTRL:
1174 		case KEY_LSHIFT:
1175 		case KEY_RSHIFT:
1176 		case KEY_LAPOSTRO:
1177 			break;
1178 		case KEY_SHIFTED + KEY_L:
1179 #if !DXX_USE_OGL
1180 			ToggleLighting();
1181 #endif
1182 			rval = window_event_result::handled;
1183 			break;
1184 		case KEY_F1:
1185 			render_3d_in_big_window = !render_3d_in_big_window;
1186 			Update_flags |= UF_ALL;
1187 			rval = window_event_result::handled;
1188 			break;
1189 		default:
1190 			if (rval == window_event_result::ignored)
1191 			{
1192 				char kdesc[100];
1193 				GetKeyDescription( kdesc, keypress );
1194 				editor_status_fmt("Error: %s isn't bound to anything.", kdesc  );
1195 			}
1196 	}
1197 
1198 	//================================================================
1199 
1200 	if (ModeFlag)
1201 	{
1202 		return window_event_result::close;
1203 	}
1204 
1205 	new_cv = current_view;
1206 
1207 	if (new_cv != current_view ) {
1208 		current_view->ev_changed = 1;
1209 		new_cv->ev_changed = 1;
1210 		current_view = new_cv;
1211 	}
1212 
1213 	// DO TEXTURE STUFF
1214 	if (texpage_do(event))
1215 		rval = window_event_result::handled;
1216 
1217 	if (objpage_do(event))
1218 		rval = window_event_result::handled;
1219 
1220 
1221 	// Process selection of Cursegp using mouse.
1222 	if (GADGET_PRESSED(LargeViewBox.get()) && !render_3d_in_big_window)
1223 	{
1224 		int	xcrd,ycrd;
1225 		xcrd = LargeViewBox->b1_drag_x1;
1226 		ycrd = LargeViewBox->b1_drag_y1;
1227 
1228 		find_segments(xcrd,ycrd,LargeViewBox->canvas.get(),&LargeView,Cursegp,Big_depth);	// Sets globals N_found_segs, Found_segs
1229 
1230 		// If shift is down, then add segment to found list
1231 		if (keyd_pressed[ KEY_LSHIFT ] || keyd_pressed[ KEY_RSHIFT ])
1232 			subtract_found_segments_from_selected_list();
1233 		else
1234 			add_found_segments_to_selected_list();
1235 
1236 		Found_seg_index = 0;
1237 
1238 		if (!Found_segs.empty())
1239 		{
1240 			sort_seg_list(Found_segs,ConsoleObject->pos);
1241 			Cursegp = imsegptridx(Found_segs[0]);
1242 			med_create_new_segment_from_cursegp();
1243 			if (Lock_view_to_cursegp)
1244 			{
1245 				auto &vcvertptr = Vertices.vcptr;
1246 				set_view_target_from_segment(vcvertptr, Cursegp);
1247 			}
1248 		}
1249 
1250 		Update_flags |= UF_ED_STATE_CHANGED | UF_VIEWPOINT_MOVED;
1251 	}
1252 
1253 	if (event.type == EVENT_UI_USERBOX_DRAGGED && &ui_event_get_gadget(event) == GameViewBox.get())
1254 	{
1255 		int	x, y;
1256 		x = GameViewBox->b1_drag_x2;
1257 		y = GameViewBox->b1_drag_y2;
1258 
1259 		gr_set_current_canvas( GameViewBox->canvas );
1260 		gr_rect(*grd_curcanv, x-1, y-1, x+1, y+1, 15);
1261 	}
1262 
1263 	// Set current segment and side by clicking on a polygon in game window.
1264 	//	If ctrl pressed, also assign current texture map to that side.
1265 	//if (GameViewBox->mouse_onme && (GameViewBox->b1_done_dragging || GameViewBox->b1_clicked)) {
1266 	if ((GADGET_PRESSED(GameViewBox.get()) && !render_3d_in_big_window) ||
1267 		(GADGET_PRESSED(LargeViewBox.get()) && render_3d_in_big_window))
1268 	{
1269 		int	xcrd,ycrd;
1270 		int side,face;
1271 
1272 		if (render_3d_in_big_window) {
1273 			xcrd = LargeViewBox->b1_drag_x1;
1274 			ycrd = LargeViewBox->b1_drag_y1;
1275 		}
1276 		else {
1277 			xcrd = GameViewBox->b1_drag_x1;
1278 			ycrd = GameViewBox->b1_drag_y1;
1279 		}
1280 
1281 		//Int3();
1282 
1283 		segnum_t seg;
1284 		objnum_t obj;
1285 		if (find_seg_side_face(xcrd,ycrd,seg,obj,side,face))
1286 		{
1287 
1288 			if (obj != object_none) {							//found an object
1289 
1290 				Cur_object_index = obj;
1291 				editor_status_fmt("Object %d selected.",Cur_object_index);
1292 
1293 				Update_flags |= UF_ED_STATE_CHANGED;
1294 			}
1295 			else {
1296 
1297 				//	See if either shift key is down and, if so, assign texture map
1298 				if (keyd_pressed[KEY_LSHIFT] || keyd_pressed[KEY_RSHIFT]) {
1299 					Cursegp = imsegptridx(seg);
1300 					Curside = side;
1301 					AssignTexture();
1302 					med_create_new_segment_from_cursegp();
1303 					editor_status("Texture assigned");
1304 				} else if (keyd_pressed[KEY_G])	{
1305 					const unique_segment &us = vcsegptr(seg);
1306 					const auto tmap = us.sides[side].tmap_num;
1307 					texpage_grab_current(tmap);
1308 					editor_status( "Texture grabbed." );
1309 				} else if (keyd_pressed[ KEY_LAPOSTRO] ) {
1310 					move_object_to_mouse_click();
1311 				} else {
1312 					Cursegp = imsegptridx(seg);
1313 					Curside = side;
1314 					med_create_new_segment_from_cursegp();
1315 					editor_status("Curseg and curside selected");
1316 				}
1317 			}
1318 
1319 			Update_flags |= UF_ED_STATE_CHANGED;
1320 		}
1321 		else
1322 			editor_status("Click on non-texture ingored");
1323 
1324 	}
1325 
1326 	// Allow specification of LargeView using mouse
1327 	if (event.type == EVENT_MOUSE_MOVED && (keyd_pressed[ KEY_LCTRL ] || keyd_pressed[ KEY_RCTRL ]))
1328 	{
1329 		int dx, dy, dz;
1330 
1331 		event_mouse_get_delta(event, &dx, &dy, &dz);
1332 		if ((dx != 0) && (dy != 0))
1333 		{
1334 			vms_matrix	MouseRotMat;
1335 
1336 			GetMouseRotation( dx, dy, &MouseRotMat );
1337 			LargeView.ev_matrix = vm_matrix_x_matrix(LargeView.ev_matrix,MouseRotMat);
1338 			LargeView.ev_changed = 1;
1339 			Large_view_index = -1;			// say not one of the orthogonal views
1340 			rval = window_event_result::handled;
1341 		}
1342 	}
1343 
1344 	if (event.type == EVENT_MOUSE_MOVED)
1345 	{
1346 		int dx, dy, dz;
1347 
1348 		event_mouse_get_delta(event, &dx, &dy, &dz);
1349 		if (dz != 0)
1350 		{
1351 			current_view->ev_dist += dz*10000;
1352 			current_view->ev_changed = 1;
1353 		}
1354 	}
1355 
1356 	return rval;
1357 }
1358