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