Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 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
 * Dialog box to edit robot properties.
23
 *
24
 */
25
 
26
#include <stdlib.h>
27
#include <stdio.h>
28
#include <math.h>
29
#include <string.h>
30
 
31
#include "screens.h"
32
#include "inferno.h"
33
#include "segment.h"
34
#include "event.h"
35
#include "editor.h"
36
#include "editor/esegment.h"
37
#include "editor/medmisc.h"
38
#include "timer.h"
39
#include "objpage.h"
40
#include "maths.h"
41
#include "dxxerror.h"
42
#include "kdefs.h"
43
#include        "object.h"
44
#include "robot.h"
45
#include "game.h"
46
#include "powerup.h"
47
#include "ai.h"
48
#include "hostage.h"
49
#include "eobject.h"
50
#include "medwall.h"
51
#include "medrobot.h"
52
#include "eswitch.h"
53
#include "ehostage.h"
54
#include "key.h"
55
#include "centers.h"
56
#include "bm.h"
57
#include "u_mem.h"
58
 
59
#include "compiler-range_for.h"
60
#include "d_enumerate.h"
61
#include <memory>
62
 
63
static int GoodyNextID();
64
static int GoodyPrevID();
65
 
66
//-------------------------------------------------------------------------
67
// Variables for this module...
68
//-------------------------------------------------------------------------
69
static UI_DIALOG                                *MainWindow = NULL;
70
 
71
namespace {
72
 
73
struct robot_dialog
74
{
75
        std::unique_ptr<UI_GADGET_USERBOX> robotViewBox, containsViewBox;
76
        std::unique_ptr<UI_GADGET_BUTTON> quitButton, prev_powerup_type, next_powerup_type, prev_powerup_id, next_powerup_id, prev_powerup_count, next_powerup_count, prev_robot_type, next_robot_type, next_segment, prev_object, next_object, delete_object, new_object, set_path;
77
        std::array<std::unique_ptr<UI_GADGET_RADIO>, 6> initialMode;                    //      Number of boxes, AI modes
78
        fix64 time;
79
        vms_angvec angles, goody_angles;
80
        int old_object;
81
};
82
 
83
}
84
 
85
namespace dsx {
86
static window_event_result robot_dialog_handler(UI_DIALOG *dlg,const d_event &event, robot_dialog *r);
87
 
88
}
89
static void call_init_ai_object(vmobjptridx_t objp, ai_behavior behavior)
90
{
91
        segnum_t        hide_segment;
92
 
93
        if (behavior == ai_behavior::AIB_STATION)
94
                hide_segment = Cursegp;
95
        else {
96
                if (Markedsegp != segment_none)
97
                        hide_segment = Markedsegp;
98
                else
99
                        hide_segment = Cursegp;
100
        }
101
 
102
        init_ai_object(objp, behavior, hide_segment);
103
}
104
 
105
//-------------------------------------------------------------------------
106
// Called when user presses "Next Type" button.  This only works for polygon
107
// objects and it just selects the next polygon model for the current object.
108
//-------------------------------------------------------------------------
109
static int RobotNextType()
110
{
111
        auto &Objects = LevelUniqueObjectState.Objects;
112
        auto &vmobjptridx = Objects.vmptridx;
113
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
114
        if (Cur_object_index != object_none )   {
115
                const auto &&obj = vmobjptridx(Cur_object_index);
116
                if (obj->type == OBJ_ROBOT)
117
                {
118
                        obj->id++;
119
                        if (obj->id >= LevelSharedRobotInfoState.N_robot_types)
120
                                obj->id = 0;
121
 
122
                        //Set polygon-object-specific data
123
                        obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
124
                        obj->rtype.pobj_info.subobj_flags = 0;
125
                        //set Physics info
126
                        obj->mtype.phys_info.flags |= (PF_LEVELLING);
127
                        obj->shields = Robot_info[get_robot_id(obj)].strength;
128
                        call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
129
 
130
                        Cur_object_id = get_robot_id(obj);
131
                }
132
        }
133
        Update_flags |= UF_WORLD_CHANGED;
134
        return 1;
135
}
136
 
137
//-------------------------------------------------------------------------
138
// Called when user presses "Prev Type" button.  This only works for polygon
139
// objects and it just selects the prev polygon model for the current object.
140
//-------------------------------------------------------------------------
141
static int RobotPrevType()
142
{
143
        auto &Objects = LevelUniqueObjectState.Objects;
144
        auto &vmobjptridx = Objects.vmptridx;
145
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
146
        if (Cur_object_index != object_none )   {
147
                const auto &&obj = vmobjptridx(Cur_object_index);
148
                if (obj->type == OBJ_ROBOT)
149
                {
150
                        if (obj->id == 0 )
151
                                obj->id = LevelSharedRobotInfoState.N_robot_types - 1;
152
                        else
153
                                obj->id--;
154
 
155
                        //Set polygon-object-specific data
156
                        obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
157
                        obj->rtype.pobj_info.subobj_flags = 0;
158
                        //set Physics info
159
                        obj->mtype.phys_info.flags |= (PF_LEVELLING);
160
                        obj->shields = Robot_info[get_robot_id(obj)].strength;
161
                        call_init_ai_object(obj, ai_behavior::AIB_NORMAL);
162
 
163
                        Cur_object_id = get_robot_id(obj);
164
                }
165
        }
166
        Update_flags |= UF_WORLD_CHANGED;
167
        return 1;
168
}
169
 
170
//-------------------------------------------------------------------------
171
// Dummy function for Mike to write.
172
//-------------------------------------------------------------------------
173
static int med_set_ai_path()
174
{
175
        return 1;
176
}
177
 
178
// #define OBJ_NONE             255     //unused object
179
// #define OBJ_WALL             0               //A wall... not really an object, but used for collisions
180
// #define OBJ_FIREBALL 1               //a fireball, part of an explosion
181
// #define OBJ_ROBOT            2               //an evil enemy
182
// #define OBJ_HOSTAGE  3               //a hostage you need to rescue
183
// #define OBJ_PLAYER   4               //the player on the console
184
// #define OBJ_WEAPON   5               //a laser, missile, etc
185
// #define OBJ_CAMERA   6               //a camera to slew around with
186
// #define OBJ_POWERUP  7               //a powerup you can pick up
187
// #define OBJ_DEBRIS   8               //a piece of robot
188
// #define OBJ_CNTRLCEN 9               //the control center
189
// #define OBJ_FLARE            10              //the control center
190
// #define MAX_OBJECT_TYPES     11
191
 
192
 
193
#define GOODY_TYPE_MAX  MAX_OBJECT_TYPES
194
#define GOODY_X 6
195
#define GOODY_Y 132
196
 
197
//#define       GOODY_ID_MAX_ROBOT      6
198
//#define       GOODY_ID_MAX_POWERUP    9
199
#define GOODY_COUNT_MAX 4
200
 
201
int             Cur_goody_type = OBJ_POWERUP;
202
int             Cur_goody_id = 0;
203
int             Cur_goody_count = 0;
204
 
205
static void update_goody_info(void)
206
{
207
        auto &Objects = LevelUniqueObjectState.Objects;
208
        auto &vmobjptr = Objects.vmptr;
209
        if (Cur_object_index != object_none )   {
210
                auto &obj = *vmobjptr(Cur_object_index);
211
                if (obj.type == OBJ_ROBOT)
212
                {
213
                        obj.contains_type = Cur_goody_type;
214
                        obj.contains_id = Cur_goody_id;
215
                        obj.contains_count = Cur_goody_count;
216
                }
217
        }
218
}
219
 
220
// #define OBJ_WALL             0               //A wall... not really an object, but used for collisions
221
// #define OBJ_FIREBALL 1               //a fireball, part of an explosion
222
// #define OBJ_ROBOT            2               //an evil enemy
223
// #define OBJ_HOSTAGE  3               //a hostage you need to rescue
224
// #define OBJ_PLAYER   4               //the player on the console
225
// #define OBJ_WEAPON   5               //a laser, missile, etc
226
// #define OBJ_CAMERA   6               //a camera to slew around with
227
// #define OBJ_POWERUP  7               //a powerup you can pick up
228
// #define OBJ_DEBRIS   8               //a piece of robot
229
// #define OBJ_CNTRLCEN 9               //the control center
230
// #define OBJ_FLARE            10              //the control center
231
// #define MAX_OBJECT_TYPES     11
232
 
233
 
234
static int GoodyNextType()
235
{
236
        Cur_goody_type++;
237
        while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
238
                if (Cur_goody_type > GOODY_TYPE_MAX)
239
                        Cur_goody_type=0;
240
                else
241
                        Cur_goody_type++;
242
        }
243
 
244
        GoodyNextID();
245
        GoodyPrevID();
246
 
247
        update_goody_info();
248
        return 1;
249
}
250
 
251
static int GoodyPrevType()
252
{
253
        Cur_goody_type--;
254
        while (!((Cur_goody_type == OBJ_ROBOT) || (Cur_goody_type == OBJ_POWERUP))) {
255
                if (Cur_goody_type < 0)
256
                        Cur_goody_type = GOODY_TYPE_MAX;
257
                else
258
                        Cur_goody_type--;
259
        }
260
 
261
        GoodyNextID();
262
        GoodyPrevID();
263
 
264
        update_goody_info();
265
        return 1;
266
}
267
 
268
int GoodyNextID()
269
{
270
        Cur_goody_id++;
271
        if (Cur_goody_type == OBJ_ROBOT) {
272
                if (Cur_goody_id >= LevelSharedRobotInfoState.N_robot_types)
273
                        Cur_goody_id=0;
274
        } else {
275
                if (Cur_goody_id >= N_powerup_types)
276
                        Cur_goody_id=0;
277
        }
278
 
279
        update_goody_info();
280
        return 1;
281
}
282
 
283
int GoodyPrevID()
284
{
285
        Cur_goody_id--;
286
        if (Cur_goody_type == OBJ_ROBOT) {
287
                if (Cur_goody_id < 0)
288
                        Cur_goody_id = LevelSharedRobotInfoState.N_robot_types - 1;
289
        } else {
290
                if (Cur_goody_id < 0)
291
                        Cur_goody_id = N_powerup_types-1;
292
        }
293
 
294
        update_goody_info();
295
        return 1;
296
}
297
 
298
static int GoodyNextCount()
299
{
300
        Cur_goody_count++;
301
        if (Cur_goody_count > GOODY_COUNT_MAX)
302
                Cur_goody_count=0;
303
 
304
        update_goody_info();
305
        return 1;
306
}
307
 
308
static int GoodyPrevCount()
309
{
310
        Cur_goody_count--;
311
        if (Cur_goody_count < 0)
312
                Cur_goody_count=GOODY_COUNT_MAX;
313
 
314
        update_goody_info();
315
        return 1;
316
}
317
 
318
static int is_legal_type(int the_type)
319
{
320
        return (the_type == OBJ_ROBOT) || (the_type == OBJ_CLUTTER);
321
}
322
 
323
static int is_legal_type_for_this_window(const imobjidx_t objnum)
324
{
325
        auto &Objects = LevelUniqueObjectState.Objects;
326
        auto &vmobjptr = Objects.vmptr;
327
        if (objnum == object_none)
328
                return 1;
329
        else
330
                return is_legal_type(vmobjptr(objnum)->type);
331
}
332
 
333
static int LocalObjectSelectNextinSegment(void)
334
{
335
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
336
        auto &Objects = LevelUniqueObjectState.Objects;
337
        auto &Vertices = LevelSharedVertexState.get_vertices();
338
        auto &vmobjptr = Objects.vmptr;
339
        int     rval, first_obj;
340
 
341
        rval = ObjectSelectNextinSegment();
342
        first_obj = Cur_object_index;
343
 
344
        if (Cur_object_index != object_none) {
345
                while (!is_legal_type_for_this_window(Cur_object_index)) {
346
                        rval = ObjectSelectNextinSegment();
347
                        if (first_obj == Cur_object_index)
348
                                break;
349
                }
350
 
351
                const auto &&objp = vmobjptr(Cur_object_index);
352
                Cur_goody_type = objp->contains_type;
353
                Cur_goody_id = objp->contains_id;
354
                if (objp->contains_count < 0)
355
                        objp->contains_count = 0;
356
                Cur_goody_count = objp->contains_count;
357
        }
358
 
359
        if (Cur_object_index != first_obj)
360
        {
361
                auto &vcvertptr = Vertices.vcptr;
362
                set_view_target_from_segment(vcvertptr, Cursegp);
363
        }
364
 
365
        return rval;
366
}
367
 
368
static int LocalObjectSelectNextinMine(void)
369
{
370
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
371
        auto &Objects = LevelUniqueObjectState.Objects;
372
        auto &Vertices = LevelSharedVertexState.get_vertices();
373
        auto &vmobjptr = Objects.vmptr;
374
        int     rval, first_obj;
375
 
376
        rval = ObjectSelectNextInMine();
377
 
378
        first_obj = Cur_object_index;
379
 
380
        if (Cur_object_index != object_none) {
381
                while (!is_legal_type_for_this_window(Cur_object_index)) {
382
                        ObjectSelectNextInMine();
383
                        if (Cur_object_index == first_obj)
384
                                break;
385
                }
386
 
387
                const auto &&objp = vmobjptr(Cur_object_index);
388
                Cur_goody_type = objp->contains_type;
389
                Cur_goody_id = objp->contains_id;
390
                if (objp->contains_count < 0)
391
                        objp->contains_count = 0;
392
                Cur_goody_count = objp->contains_count;
393
        }
394
 
395
        if (Cur_object_index != first_obj)
396
        {
397
                auto &vcvertptr = Vertices.vcptr;
398
                set_view_target_from_segment(vcvertptr, Cursegp);
399
        }
400
 
401
        return rval;
402
}
403
 
404
static int LocalObjectSelectPrevinMine(void)
405
{
406
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
407
        auto &Objects = LevelUniqueObjectState.Objects;
408
        auto &Vertices = LevelSharedVertexState.get_vertices();
409
        auto &vmobjptr = Objects.vmptr;
410
        int     rval, first_obj;
411
 
412
        rval = ObjectSelectPrevInMine();
413
 
414
        first_obj = Cur_object_index;
415
 
416
        if (Cur_object_index != object_none) {
417
                while (!is_legal_type_for_this_window(Cur_object_index)) {
418
                        ObjectSelectPrevInMine();
419
                        if (first_obj == Cur_object_index)
420
                                break;
421
                }
422
 
423
                const auto &&objp = vmobjptr(Cur_object_index);
424
                Cur_goody_type = objp->contains_type;
425
                Cur_goody_id = objp->contains_id;
426
                if (objp->contains_count < 0)
427
                        objp->contains_count = 0;
428
                Cur_goody_count = objp->contains_count;
429
        }
430
 
431
        if (Cur_object_index != first_obj)
432
        {
433
                auto &vcvertptr = Vertices.vcptr;
434
                set_view_target_from_segment(vcvertptr, Cursegp);
435
        }
436
 
437
        return rval;
438
}
439
 
440
static int LocalObjectDelete(void)
441
{
442
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
443
        auto &Objects = LevelUniqueObjectState.Objects;
444
        auto &Vertices = LevelSharedVertexState.get_vertices();
445
        auto &vcobjptr = Objects.vcptr;
446
        int     rval;
447
 
448
        rval = ObjectDelete();
449
 
450
        if (Cur_object_index != object_none) {
451
                auto &objp = *vcobjptr(Cur_object_index);
452
                Cur_goody_type = objp.contains_type;
453
                Cur_goody_id = objp.contains_id;
454
                Cur_goody_count = objp.contains_count;
455
        }
456
 
457
        auto &vcvertptr = Vertices.vcptr;
458
        set_view_target_from_segment(vcvertptr, Cursegp);
459
 
460
        return rval;
461
}
462
 
463
static int LocalObjectPlaceObject(void)
464
{
465
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
466
        auto &Objects = LevelUniqueObjectState.Objects;
467
        auto &Vertices = LevelSharedVertexState.get_vertices();
468
        auto &vmobjptr = Objects.vmptr;
469
        int     rval;
470
 
471
        Cur_goody_count = 0;
472
 
473
        if (Cur_object_type != OBJ_ROBOT)
474
        {
475
                Cur_object_type = OBJ_ROBOT;
476
                Cur_object_id = 3;      // class 1 drone
477
                Num_object_subtypes = LevelSharedRobotInfoState.N_robot_types;
478
        }
479
 
480
        rval = ObjectPlaceObject();
481
        if (rval == -1)
482
                return -1;
483
 
484
        const auto &&objp = vmobjptr(Cur_object_index);
485
        objp->contains_type = Cur_goody_type;
486
        objp->contains_id = Cur_goody_id;
487
        objp->contains_count = Cur_goody_count;
488
 
489
        auto &vcvertptr = Vertices.vcptr;
490
        set_view_target_from_segment(vcvertptr, Cursegp);
491
 
492
        return rval;
493
}
494
 
495
void close_all_windows(void)
496
{
497
        close_trigger_window();
498
        close_wall_window();
499
        close_centers_window();
500
        hostage_close_window();
501
        robot_close_window();
502
}
503
 
504
 
505
//-------------------------------------------------------------------------
506
// Called from the editor... does one instance of the robot dialog box
507
//-------------------------------------------------------------------------
508
int do_robot_dialog()
509
{
510
        // Only open 1 instance of this window...
511
        if ( MainWindow != NULL ) return 0;
512
 
513
        auto r = std::make_unique<robot_dialog>();
514
        // Close other windows
515
        close_all_windows();
516
        Cur_goody_count = 0;
517
        memset(&r->angles, 0, sizeof(vms_angvec));
518
        memset(&r->goody_angles, 0, sizeof(vms_angvec));
519
 
520
        // Open a window with a quit button
521
        MainWindow = ui_create_dialog(TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, robot_dialog_handler, std::move(r));
522
        return 1;
523
}
524
 
525
static window_event_result robot_dialog_created(UI_DIALOG *const w, robot_dialog *const r)
526
{
527
        r->quitButton = ui_add_gadget_button(w, 20, 286, 40, 32, "Done", NULL);
528
        r->prev_powerup_type = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y-3, 25, 22, "<<", GoodyPrevType);
529
        r->next_powerup_type = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y-3, 25, 22, ">>", GoodyNextType);
530
        r->prev_powerup_id = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y+21, 25, 22, "<<", GoodyPrevID);
531
        r->next_powerup_id = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y+21, 25, 22, ">>", GoodyNextID);
532
        r->prev_powerup_count = ui_add_gadget_button(w, GOODY_X+50, GOODY_Y+45, 25, 22, "<<", GoodyPrevCount);
533
        r->next_powerup_count = ui_add_gadget_button(w, GOODY_X+80, GOODY_Y+45, 25, 22, ">>", GoodyNextCount);
534
        r->initialMode[0] = ui_add_gadget_radio(w,  6, 58, 16, 16, 0, "Hover");
535
        r->initialMode[1] = ui_add_gadget_radio(w, 76, 58, 16, 16, 0, "Normal");
536
        r->initialMode[2] = ui_add_gadget_radio(w,  6, 78, 16, 16, 0, "(hide)");
537
        r->initialMode[3] = ui_add_gadget_radio(w, 76, 78, 16, 16, 0, "Avoid");
538
        r->initialMode[4] = ui_add_gadget_radio(w,  6, 98, 16, 16, 0, "Follow");
539
        r->initialMode[5] = ui_add_gadget_radio(w, 76, 98, 16, 16, 0, "Station");
540
        // The little box the robots will spin in.
541
        r->robotViewBox = ui_add_gadget_userbox(w, 155, 5, 150, 125);
542
        // The little box the robots will spin in.
543
        r->containsViewBox = ui_add_gadget_userbox(w, 10, 202, 100, 80);
544
        // A bunch of buttons...
545
        int i = 135;
546
        r->prev_robot_type = ui_add_gadget_button(w, 190, i, 53, 26, "<<Typ",                   RobotPrevType);
547
        r->next_robot_type = ui_add_gadget_button(w, 247, i, 53, 26, "Typ>>",                   RobotNextType);                                                 i += 29;               
548
        r->next_segment = ui_add_gadget_button(w, 190, i, 110, 26, "Next in Seg", LocalObjectSelectNextinSegment);      i += 29;               
549
        r->prev_object = ui_add_gadget_button(w, 190, i, 53, 26, "<<Obj",                       LocalObjectSelectPrevinMine);
550
        r->next_object = ui_add_gadget_button(w, 247, i, 53, 26, ">>Obj",                       LocalObjectSelectNextinMine);           i += 29;               
551
        r->delete_object = ui_add_gadget_button(w, 190, i, 110, 26, "Delete",           LocalObjectDelete);                                             i += 29;               
552
        r->new_object = ui_add_gadget_button(w, 190, i, 110, 26, "Create New",  LocalObjectPlaceObject);                                i += 29;               
553
        r->set_path = ui_add_gadget_button(w, 190, i, 110, 26, "Set Path",      med_set_ai_path);
554
        r->time = timer_query();
555
        r->old_object = -2;             // Set to some dummy value so everything works ok on the first frame.
556
        if ( Cur_object_index == object_none)
557
                LocalObjectSelectNextinMine();
558
        return window_event_result::handled;
559
}
560
 
561
void robot_close_window()
562
{
563
        if ( MainWindow!=NULL ) {
564
                ui_close_dialog( MainWindow );
565
                MainWindow = NULL;
566
        }
567
 
568
}
569
 
570
namespace dsx {
571
window_event_result robot_dialog_handler(UI_DIALOG *dlg,const d_event &event, robot_dialog *r)
572
{
573
        auto &Objects = LevelUniqueObjectState.Objects;
574
        auto &vcobjptr = Objects.vcptr;
575
        auto &vmobjptr = Objects.vmptr;
576
        auto &vmobjptridx = Objects.vmptridx;
577
        switch(event.type)
578
        {
579
                case EVENT_WINDOW_CREATED:
580
                        return robot_dialog_created(dlg, r);
581
                case EVENT_WINDOW_CLOSE:
582
                        std::default_delete<robot_dialog>()(r);
583
                        MainWindow = NULL;
584
                        return window_event_result::ignored;
585
                default:
586
                        break;
587
        }
588
        fix     DeltaTime;
589
        fix64   Temp;
590
        int     first_object_index;
591
        int keypress = 0;
592
        window_event_result rval = window_event_result::ignored;
593
 
594
        if (event.type == EVENT_KEY_COMMAND)
595
                keypress = event_key_get(event);
596
 
597
        Assert(MainWindow != NULL);
598
 
599
        first_object_index = Cur_object_index;
600
        while (!is_legal_type_for_this_window(Cur_object_index)) {
601
                LocalObjectSelectNextinMine();
602
                if (first_object_index == Cur_object_index) {
603
                        break;
604
                }
605
        }
606
 
607
        //------------------------------------------------------------
608
        // Call the ui code..
609
        //------------------------------------------------------------
610
        ui_button_any_drawn = 0;
611
 
612
        //------------------------------------------------------------
613
        // If we change objects, we need to reset the ui code for all
614
        // of the radio buttons that control the ai mode.  Also makes
615
        // the current AI mode button be flagged as pressed down.
616
        //------------------------------------------------------------
617
        if (r->old_object != Cur_object_index ) {
618
                range_for (auto &i, r->initialMode)
619
                        ui_radio_set_value(i.get(), 0);
620
                if ( Cur_object_index != object_none ) {
621
                        auto &behavior = vmobjptr(Cur_object_index)->ctype.ai_info.behavior;
622
                        switch (behavior)
623
                        {
624
                                case ai_behavior::AIB_STILL:
625
                                case ai_behavior::AIB_NORMAL:
626
                                case ai_behavior::AIB_RUN_FROM:
627
                                case ai_behavior::AIB_STATION:
628
#if defined(DXX_BUILD_DESCENT_I)
629
                                case ai_behavior::AIB_HIDE:
630
                                case ai_behavior::AIB_FOLLOW_PATH:
631
#elif defined(DXX_BUILD_DESCENT_II)
632
                                case ai_behavior::AIB_BEHIND:
633
                                case ai_behavior::AIB_SNIPE:
634
                                case ai_behavior::AIB_FOLLOW:
635
#endif
636
                                        break;
637
                                default:
638
                                        behavior = ai_behavior::AIB_NORMAL;
639
                                        break;
640
                        }
641
                        ui_radio_set_value(r->initialMode[static_cast<std::size_t>(behavior) - MIN_BEHAVIOR].get(), 1);
642
                }
643
        }
644
 
645
        //------------------------------------------------------------
646
        // If any of the radio buttons that control the mode are set, then
647
        // update the cooresponding AI state.
648
        //------------------------------------------------------------
649
        range_for (auto &&eim, enumerate(r->initialMode))
650
        {
651
                auto &im = eim.value;
652
                if (GADGET_PRESSED(im.get()))
653
                {
654
                        const auto i = eim.idx;
655
                        const auto b = static_cast<ai_behavior>(MIN_BEHAVIOR + i);
656
                        const auto &&objp = vmobjptridx(Cur_object_index);
657
                        auto &behavior = objp->ctype.ai_info.behavior;
658
                        if (behavior != b) {
659
                                behavior = b;           // Set the ai_state to the cooresponding radio button
660
                                call_init_ai_object(objp, b);
661
                                rval = window_event_result::handled;
662
                        }
663
                }
664
        }
665
 
666
        //------------------------------------------------------------
667
        // Redraw the object in the little 64x64 box
668
        //------------------------------------------------------------
669
        if (event.type == EVENT_UI_DIALOG_DRAW)
670
        {
671
                // A simple frame time counter for spinning the objects...
672
                Temp = timer_query();
673
                DeltaTime = Temp - r->time;
674
                r->time = Temp;
675
 
676
                if (Cur_object_index != object_none )   {
677
                        const auto &&obj = vmobjptr(Cur_object_index);
678
 
679
                        gr_set_current_canvas( r->robotViewBox->canvas );
680
                        draw_object_picture(*grd_curcanv, obj->id, r->angles, obj->type);
681
                        r->angles.h += fixmul(0x1000, DeltaTime );
682
                } else {
683
                        // no object, so just blank out
684
                        gr_set_current_canvas( r->robotViewBox->canvas );
685
                        gr_clear_canvas(*grd_curcanv, CGREY);
686
 
687
        //              LocalObjectSelectNextInMine();
688
                }
689
 
690
        //------------------------------------------------------------
691
        // Redraw the contained object in the other little box
692
        //------------------------------------------------------------
693
                if ((Cur_object_index != object_none ) && (Cur_goody_count > 0))        {
694
                        gr_set_current_canvas( r->containsViewBox->canvas );
695
                        if ( Cur_goody_id > -1 )
696
                                draw_object_picture(*grd_curcanv, Cur_goody_id, r->goody_angles, Cur_goody_type);
697
                        else
698
                                gr_clear_canvas(*grd_curcanv, CGREY);
699
                        r->goody_angles.h += fixmul(0x1000, DeltaTime );
700
                } else {
701
                        // no object, so just blank out
702
                        gr_set_current_canvas( r->containsViewBox->canvas );
703
                        gr_clear_canvas(*grd_curcanv, CGREY);
704
 
705
        //              LocalObjectSelectNextInMine();
706
                }
707
        //------------------------------------------------------------
708
        // If anything changes in the ui system, redraw all the text that
709
        // identifies this robot.
710
        //------------------------------------------------------------
711
 
712
                const char *id_text;
713
                const char *type_text;
714
 
715
                if (Cur_object_index != object_none) {
716
                        const auto &&obj = vmobjptr(Cur_object_index);
717
                        Cur_goody_type = obj->contains_type;
718
                        Cur_goody_id = obj->contains_id;
719
                        if (obj->contains_count < 0)
720
                                obj->contains_count = 0;
721
                        Cur_goody_count = obj->contains_count;
722
                }
723
 
724
                ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y,    " Type:");
725
                ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+24, "   ID:");
726
                ui_dprintf_at( MainWindow, GOODY_X, GOODY_Y+48, "Count:");
727
 
728
                switch (Cur_goody_type) {
729
                        case OBJ_ROBOT:
730
                                type_text = "Robot  ";
731
                                id_text = Robot_names[Cur_goody_id].data();
732
                                break;
733
                        default:
734
                                editor_status_fmt("Illegal contained object type (%i), changing to powerup.", Cur_goody_type);
735
                                Cur_goody_type = OBJ_POWERUP;
736
                                Cur_goody_id = 0;
737
                                DXX_BOOST_FALLTHROUGH;
738
                        case OBJ_POWERUP:
739
                                type_text = "Powerup";
740
                                id_text = Powerup_names[Cur_goody_id].data();
741
                                break;
742
                }
743
 
744
                ui_dputs_at( MainWindow, GOODY_X+108, GOODY_Y, type_text);
745
                ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+24, "%-8s", id_text);
746
                ui_dprintf_at( MainWindow, GOODY_X+108, GOODY_Y+48, "%i", Cur_goody_count);
747
 
748
                if ( Cur_object_index != object_none )  {
749
                        const auto id = get_robot_id(vcobjptr(Cur_object_index));
750
 
751
                        ui_dprintf_at( MainWindow, 12,  6, "Robot: %3d ", Cur_object_index );
752
                        ui_dprintf_at( MainWindow, 12, 22, "   Id: %3d", id);
753
                        ui_dprintf_at( MainWindow, 12, 38, " Name: %-8s", Robot_names[id].data());
754
 
755
                }       else {
756
                        ui_dprintf_at( MainWindow, 12,  6, "Robot: none" );
757
                        ui_dprintf_at( MainWindow, 12, 22, " Type: ?  "  );
758
                        ui_dprintf_at( MainWindow, 12, 38, " Name: ________" );
759
                }
760
        }
761
 
762
        if (ui_button_any_drawn || (r->old_object != Cur_object_index) )
763
                Update_flags |= UF_WORLD_CHANGED;
764
        if (GADGET_PRESSED(r->quitButton.get()) || keypress == KEY_ESC)
765
        {
766
                return window_event_result::close;
767
        }              
768
 
769
        r->old_object = Cur_object_index;
770
 
771
        return rval;
772
}
773
}
774
 
775
//      --------------------------------------------------------------------------------------------------------------------------
776
#define NUM_MATT_THINGS 2
777
 
778
#define MATT_LEN                                20
779
 
780
static UI_DIALOG                                *MattWindow = NULL;
781
 
782
namespace {
783
 
784
struct object_dialog
785
{
786
        struct creation_context
787
        {
788
                vmobjptr_t obj;
789
                creation_context(vmobjptr_t o) :
790
                        obj(o)
791
                {
792
                }
793
        };
794
        std::unique_ptr<UI_GADGET_INPUTBOX> xtext, ytext, ztext;
795
        std::array<std::unique_ptr<UI_GADGET_RADIO>, 2> initialMode;
796
        std::unique_ptr<UI_GADGET_BUTTON> quitButton;
797
};
798
 
799
}
800
 
801
static window_event_result object_dialog_handler(UI_DIALOG *dlg,const d_event &event, object_dialog *o);
802
 
803
void object_close_window()
804
{
805
        if ( MattWindow!=NULL ) {
806
                ui_close_dialog( MattWindow );
807
                MattWindow = NULL;
808
        }
809
 
810
}
811
 
812
 
813
//-------------------------------------------------------------------------
814
// Called from the editor... does one instance of the object dialog box
815
//-------------------------------------------------------------------------
816
int do_object_dialog()
817
{
818
        auto &Objects = LevelUniqueObjectState.Objects;
819
        auto &vmobjptr = Objects.vmptr;
820
        if (Cur_object_index == object_none)
821
                Cur_object_index = object_first;
822
 
823
        auto obj = vmobjptr(Cur_object_index);
824
        if (obj->type == OBJ_ROBOT)             //don't do this for robots
825
                return 0;
826
 
827
        // Only open 1 instance of this window...
828
        if ( MattWindow != NULL )
829
                return 0;
830
 
831
        auto o = std::make_unique<object_dialog>();
832
        Cur_goody_count = 0;
833
 
834
        // Open a window with a quit button
835
        object_dialog::creation_context c(obj);
836
        MattWindow = ui_create_dialog( TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, object_dialog_handler, std::move(o), &c);
837
        return 1;
838
}
839
 
840
static window_event_result object_dialog_created(UI_DIALOG *const w, object_dialog *const o, const object_dialog::creation_context *const c)
841
{
842
        o->quitButton = ui_add_gadget_button(w, 20, 286, 40, 32, "Done", NULL );
843
        o->quitButton->hotkey = KEY_ENTER;
844
        // These are the radio buttons for each mode
845
        o->initialMode[0] = ui_add_gadget_radio(w, 10, 50, 16, 16, 0, "None" );
846
        o->initialMode[1] = ui_add_gadget_radio(w, 80, 50, 16, 16, 0, "Spinning" );
847
        o->initialMode[c->obj->movement_type == MT_SPINNING?1:0]->flag = 1;
848
        char message[MATT_LEN];
849
        snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.x));
850
        o->xtext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 132, message);
851
        snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.y));
852
        o->ytext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 162, message);
853
        snprintf(message, sizeof(message), "%.2f", f2fl(c->obj->mtype.spin_rate.z));
854
        o->ztext = ui_add_gadget_inputbox<MATT_LEN>(w, 30, 192, message);
855
        ui_gadget_calc_keys(w);
856
        w->keyboard_focus_gadget = o->initialMode[0].get();
857
 
858
        return window_event_result::handled;
859
}
860
 
861
static window_event_result object_dialog_handler(UI_DIALOG *dlg,const d_event &event, object_dialog *o)
862
{
863
        auto &Objects = LevelUniqueObjectState.Objects;
864
        auto &vmobjptr = Objects.vmptr;
865
        switch(event.type)
866
        {
867
                case EVENT_WINDOW_CREATED:
868
                        return object_dialog_created(dlg, o, reinterpret_cast<const object_dialog::creation_context *>(static_cast<const d_create_event &>(event).createdata));
869
                case EVENT_WINDOW_CLOSE:
870
                        std::default_delete<object_dialog>()(o);
871
                        MattWindow = NULL;
872
                        return window_event_result::ignored;
873
                default:
874
                        break;
875
        }
876
        const auto &&obj = vmobjptr(Cur_object_index);
877
        int keypress = 0;
878
        window_event_result rval = window_event_result::ignored;
879
 
880
        if (event.type == EVENT_KEY_COMMAND)
881
                keypress = event_key_get(event);
882
 
883
        Assert(MattWindow != NULL);
884
 
885
        //------------------------------------------------------------
886
        // Call the ui code..
887
        //------------------------------------------------------------
888
        ui_button_any_drawn = 0;
889
 
890
 
891
        if (event.type == EVENT_UI_DIALOG_DRAW)
892
        {
893
                ui_dprintf_at( MattWindow, 10, 132,"&X:" );
894
                ui_dprintf_at( MattWindow, 10, 162,"&Y:" );
895
                ui_dprintf_at( MattWindow, 10, 192,"&Z:" );
896
        }
897
 
898
        if (GADGET_PRESSED(o->quitButton.get()) || keypress == KEY_ESC)
899
        {
900
 
901
                if (o->initialMode[0]->flag) obj->movement_type = MT_NONE;
902
                if (o->initialMode[1]->flag) obj->movement_type = MT_SPINNING;
903
 
904
                obj->mtype.spin_rate.x = fl2f(atof(o->xtext->text.get()));
905
                obj->mtype.spin_rate.y = fl2f(atof(o->ytext->text.get()));
906
                obj->mtype.spin_rate.z = fl2f(atof(o->ztext->text.get()));
907
 
908
                return window_event_result::close;
909
        }
910
 
911
        return rval;
912
}