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
 * Editor switch functions.
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <math.h>
29
#include <string.h>
30
 
31
#include "inferno.h"
32
#include "editor.h"
33
#include "editor/esegment.h"
34
#include "editor/kdefs.h"
35
#include "eswitch.h"
36
#include "segment.h"
37
#include "dxxerror.h"
38
#include "event.h"
39
#include "gameseg.h"
40
#include "wall.h"
41
#include "medwall.h"
42
#include "screens.h"
43
#include "textures.h"
44
#include "texmerge.h"
45
#include "medrobot.h"
46
#include "timer.h"
47
#include "key.h"
48
#include "ehostage.h"
49
#include "centers.h"
50
#include "piggy.h"
51
#include "u_mem.h"
52
 
53
#include "compiler-range_for.h"
54
#include "partial_range.h"
55
#include <memory>
56
 
57
//-------------------------------------------------------------------------
58
// Variables for this module...
59
//-------------------------------------------------------------------------
60
#define NUM_TRIGGER_FLAGS 10
61
 
62
static UI_DIALOG                                *MainWindow = NULL;
63
 
64
namespace {
65
 
66
struct trigger_dialog
67
{
68
        std::unique_ptr<UI_GADGET_USERBOX> wallViewBox;
69
        std::unique_ptr<UI_GADGET_BUTTON> quitButton, remove_trigger, bind_wall, bind_matcen, enable_all_triggers;
70
#if defined(DXX_BUILD_DESCENT_I)
71
        std::array<std::unique_ptr<UI_GADGET_CHECKBOX>, NUM_TRIGGER_FLAGS> triggerFlag;
72
#endif
73
        int old_trigger_num;
74
};
75
 
76
}
77
 
78
#if defined(DXX_BUILD_DESCENT_I)
79
//-----------------------------------------------------------------
80
// Adds a trigger to wall, and returns the trigger number. 
81
// If there is a trigger already present, it returns the trigger number. (To be replaced)
82
static trgnum_t add_trigger(trigger_array &Triggers, fvcvertptr &vcvertptr, wall_array &Walls, const shared_segment &seg, const unsigned side)
83
{
84
        trgnum_t trigger_num = Triggers.get_count();
85
 
86
        Assert(trigger_num < MAX_TRIGGERS);
87
        if (trigger_num>=MAX_TRIGGERS) return trigger_none;
88
 
89
        auto wall_num = seg.sides[side].wall_num;
90
        wall *wp;
91
        auto &vmwallptr = Walls.vmptr;
92
        if (wall_num == wall_none) {
93
                wall_add_to_markedside(vcvertptr, Walls, WALL_OPEN);
94
                wall_num = seg.sides[side].wall_num;
95
                wp = vmwallptr(wall_num);
96
                // Set default values first time trigger is added
97
        } else {
98
                auto &w = *vmwallptr(wall_num);
99
                if (w.trigger != trigger_none)
100
                        return w.trigger;
101
 
102
                wp = &w;
103
                // Create new trigger.
104
        }
105
        wp->trigger = trigger_num;
106
        auto &t = *Triggers.vmptr(trigger_num);
107
        t.flags = {};
108
        t.value = F1_0*5;
109
        t.num_links = 0;
110
        Triggers.set_count(trigger_num + 1);
111
        return trigger_num;
112
}              
113
 
114
//-----------------------------------------------------------------
115
// Adds a specific trigger flag to Markedsegp/Markedside if it is possible.
116
// Automatically adds flag to Connectside if possible unless it is a control trigger.
117
// Returns 1 if trigger flag added.
118
// Returns 0 if trigger flag cannot be added.
119
static int trigger_flag_Markedside(const TRIGGER_FLAG flag, const int value)
120
{
121
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
122
        auto &Vertices = LevelSharedVertexState.get_vertices();
123
        if (!Markedsegp) {
124
                editor_status("No Markedside.");
125
                return 0;
126
        }
127
 
128
        // If no child on Markedside return
129
        if (!IS_CHILD(Markedsegp->children[Markedside])) return 0;
130
 
131
        // If no wall just return
132
        const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
133
        if (!value && wall_num == wall_none) return 0;
134
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
135
        auto &vcvertptr = Vertices.vcptr;
136
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
137
        auto &vcwallptr = Walls.vcptr;
138
        const auto trigger_num = value ? add_trigger(Triggers, vcvertptr, Walls, Markedsegp, Markedside) : vcwallptr(wall_num)->trigger;
139
 
140
        if (trigger_num == trigger_none) {
141
                editor_status(value ? "Cannot add trigger at Markedside." : "No trigger at Markedside.");
142
                return 0;
143
        }
144
 
145
        auto &vmtrgptr = Triggers.vmptr;
146
        auto &flags = vmtrgptr(trigger_num)->flags;
147
        if (value)
148
                flags |= flag;
149
        else
150
                flags &= ~flag;
151
 
152
        return 1;
153
}
154
#endif
155
 
156
static int bind_matcen_to_trigger() {
157
 
158
        if (!Markedsegp) {
159
                editor_status("No marked segment.");
160
                return 0;
161
        }
162
 
163
        const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
164
        if (wall_num == wall_none) {
165
                editor_status("No wall at Markedside.");
166
                return 0;
167
        }
168
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
169
        auto &vcwallptr = Walls.vcptr;
170
        const auto trigger_num = vcwallptr(wall_num)->trigger;
171
        if (trigger_num == trigger_none) {
172
                editor_status("No trigger at Markedside.");
173
                return 0;
174
        }
175
 
176
        if (!(Cursegp->special & SEGMENT_IS_ROBOTMAKER))
177
        {
178
                editor_status("No Matcen at Cursegp.");
179
                return 0;
180
        }
181
 
182
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
183
        auto &vmtrgptr = Triggers.vmptr;
184
        const auto &&t = vmtrgptr(trigger_num);
185
        const auto link_num = t->num_links;
186
        for (int i=0;i<link_num;i++)
187
                if (Cursegp == t->seg[i])
188
                {
189
                        editor_status("Matcen already bound to Markedside.");
190
                        return 0;
191
                }
192
 
193
        // Error checking completed, actual binding begins
194
        t->seg[link_num] = Cursegp;
195
        t->num_links++;
196
 
197
        editor_status("Matcen linked to trigger");
198
 
199
        return 1;
200
}
201
 
202
 
203
int bind_wall_to_trigger() {
204
 
205
        if (!Markedsegp) {
206
                editor_status("No marked segment.");
207
                return 0;
208
        }
209
 
210
        const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
211
        if (wall_num == wall_none) {
212
                editor_status("No wall at Markedside.");
213
                return 0;
214
        }
215
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
216
        auto &vcwallptr = Walls.vcptr;
217
        const auto trigger_num = vcwallptr(wall_num)->trigger;
218
        if (trigger_num == trigger_none) {
219
                editor_status("No trigger at Markedside.");
220
                return 0;
221
        }
222
 
223
        if (Cursegp->shared_segment::sides[Curside].wall_num == wall_none)
224
        {
225
                editor_status("No wall at Curside.");
226
                return 0;
227
        }
228
 
229
        if ((Cursegp==Markedsegp) && (Curside==Markedside)) {
230
                editor_status("Cannot bind wall to itself.");
231
                return 0;
232
        }
233
 
234
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
235
        auto &vmtrgptr = Triggers.vmptr;
236
        const auto &&t = vmtrgptr(trigger_num);
237
        const auto link_num = t->num_links;
238
        for (int i=0;i<link_num;i++)
239
                if (Cursegp == t->seg[i] && Curside == t->side[i])
240
                {
241
                        editor_status("Curside already bound to Markedside.");
242
                        return 0;
243
                }
244
 
245
        // Error checking completed, actual binding begins
246
        t->seg[link_num] = Cursegp;
247
        t->side[link_num] = Curside;
248
        t->num_links++;
249
 
250
        editor_status("Wall linked to trigger");
251
 
252
        return 1;
253
}
254
 
255
int remove_trigger_num(int trigger_num)
256
{
257
        if (trigger_num != trigger_none)
258
        {
259
                auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
260
                auto r = partial_range(Triggers, static_cast<unsigned>(trigger_num), Triggers.get_count());
261
                Triggers.set_count(Triggers.get_count() - 1);
262
                std::move(std::next(r.begin()), r.end(), r.begin());
263
 
264
                auto &Walls = LevelUniqueWallSubsystemState.Walls;
265
                auto &vmwallptr = Walls.vmptr;
266
                range_for (const auto &&w, vmwallptr)
267
                {
268
                        auto &trigger = w->trigger;
269
                        if (trigger == trigger_num)
270
                                trigger = trigger_none; // a trigger can be shared by multiple walls
271
                        else if (trigger > trigger_num && trigger != trigger_none)
272
                                --trigger;
273
                }
274
 
275
                return 1;
276
        }
277
 
278
        editor_status("No trigger to remove");
279
        return 0;
280
}
281
 
282
unsigned remove_trigger(shared_segment &seg, const unsigned side)
283
{      
284
        const auto wall_num = seg.sides[side].wall_num;
285
        if (wall_num == wall_none)
286
        {
287
                return 0;
288
        }
289
 
290
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
291
        auto &vcwallptr = Walls.vcptr;
292
        return remove_trigger_num(vcwallptr(wall_num)->trigger);
293
}
294
 
295
static int trigger_remove()
296
{
297
        remove_trigger(Markedsegp, Markedside);
298
        Update_flags = UF_WORLD_CHANGED;
299
        return 1;
300
}
301
 
302
#if defined(DXX_BUILD_DESCENT_II)
303
static int trigger_turn_all_ON()
304
{
305
        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
306
        auto &vmtrgptr = Triggers.vmptr;
307
        range_for (const auto t, vmtrgptr)
308
                t->flags &= ~trigger_behavior_flags::disabled;
309
        return 1;
310
}
311
#endif
312
 
313
static window_event_result trigger_dialog_handler(UI_DIALOG *dlg,const d_event &event, trigger_dialog *t);
314
 
315
//-------------------------------------------------------------------------
316
// Called from the editor... does one instance of the trigger dialog box
317
//-------------------------------------------------------------------------
318
int do_trigger_dialog()
319
{
320
        if (!Markedsegp) {
321
                editor_status("Trigger requires Marked Segment & Side.");
322
                return 0;
323
        }
324
 
325
        // Only open 1 instance of this window...
326
        if ( MainWindow != NULL ) return 0;
327
 
328
        auto t = std::make_unique<trigger_dialog>();
329
        // Close other windows. 
330
        robot_close_window();
331
        close_wall_window();
332
        close_centers_window();
333
        hostage_close_window();
334
 
335
        // Open a window with a quit button
336
        MainWindow = ui_create_dialog(TMAPBOX_X+20, TMAPBOX_Y+20, 765-TMAPBOX_X, 545-TMAPBOX_Y, DF_DIALOG, trigger_dialog_handler, std::move(t));
337
        return 1;
338
}
339
 
340
static window_event_result trigger_dialog_created(UI_DIALOG *const w, trigger_dialog *const t)
341
{
342
        // These are the checkboxes for each door flag.
343
        int i = 44;
344
#if defined(DXX_BUILD_DESCENT_I)
345
        t->triggerFlag[0] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Door Control");        i+=22;
346
        t->triggerFlag[1] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Shield damage");       i+=22;
347
        t->triggerFlag[2] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Energy drain");                i+=22;
348
        t->triggerFlag[3] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Exit");                                        i+=22;
349
        t->triggerFlag[4] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "One-shot");                    i+=22;
350
        t->triggerFlag[5] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Illusion ON");         i+=22;
351
        t->triggerFlag[6] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Illusion OFF");                i+=22;
352
        t->triggerFlag[7] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Unused");                      i+=22;
353
        t->triggerFlag[8] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Matcen Trigger");      i+=22;
354
        t->triggerFlag[9] = ui_add_gadget_checkbox(w, 22, i, 16, 16, 0, "Secret Exit");                 i+=22;
355
#endif
356
 
357
        t->quitButton = ui_add_gadget_button( w, 20, i, 48, 40, "Done", NULL );
358
 
359
        // The little box the wall will appear in.
360
        t->wallViewBox = ui_add_gadget_userbox( w, 155, 5, 64, 64 );
361
 
362
        // A bunch of buttons...
363
        i = 80;
364
        t->remove_trigger = ui_add_gadget_button(w, 155, i, 140, 26, "Remove Trigger", trigger_remove); i += 29;
365
        t->bind_wall = ui_add_gadget_button(w, 155, i, 140, 26, "Bind Wall", bind_wall_to_trigger); i += 29;
366
        t->bind_matcen = ui_add_gadget_button(w, 155, i, 140, 26, "Bind Matcen", bind_matcen_to_trigger); i += 29;
367
#if defined(DXX_BUILD_DESCENT_II)
368
        t->enable_all_triggers = ui_add_gadget_button(w, 155, i, 140, 26, "All Triggers ON", trigger_turn_all_ON); i += 29;
369
#endif
370
 
371
        t->old_trigger_num = -2;                // Set to some dummy value so everything works ok on the first frame.
372
        return window_event_result::handled;
373
}
374
 
375
void close_trigger_window()
376
{
377
        if ( MainWindow!=NULL ) {
378
                ui_close_dialog( MainWindow );
379
                MainWindow = NULL;
380
        }
381
}
382
 
383
window_event_result trigger_dialog_handler(UI_DIALOG *dlg,const d_event &event, trigger_dialog *t)
384
{
385
        switch(event.type)
386
        {
387
                case EVENT_WINDOW_CREATED:
388
                        return trigger_dialog_created(dlg, t);
389
                case EVENT_WINDOW_CLOSE:
390
                        std::default_delete<trigger_dialog>()(t);
391
                        MainWindow = NULL;
392
                        return window_event_result::ignored;
393
                default:
394
                        break;
395
        }
396
        int keypress = 0;
397
        window_event_result rval = window_event_result::ignored;
398
 
399
        Assert(MainWindow != NULL);
400
        if (!Markedsegp) {
401
                return window_event_result::close;
402
        }
403
 
404
        //------------------------------------------------------------
405
        // Call the ui code..
406
        //------------------------------------------------------------
407
        ui_button_any_drawn = 0;
408
 
409
        if (event.type == EVENT_KEY_COMMAND)
410
                keypress = event_key_get(event);
411
 
412
        //------------------------------------------------------------
413
        // If we change walls, we need to reset the ui code for all
414
        // of the checkboxes that control the wall flags.  
415
        //------------------------------------------------------------
416
        const auto Markedwall = Markedsegp->shared_segment::sides[Markedside].wall_num;
417
        auto &Walls = LevelUniqueWallSubsystemState.Walls;
418
        auto &vcwallptr = Walls.vcptr;
419
        const auto trigger_num = (Markedwall != wall_none) ? vcwallptr(Markedwall)->trigger : trigger_none;
420
 
421
        if (t->old_trigger_num != trigger_num)
422
        {
423
                if (trigger_num != trigger_none)
424
                {
425
#if defined(DXX_BUILD_DESCENT_I)
426
                        auto &Triggers = LevelUniqueWallSubsystemState.Triggers;
427
                        auto &vctrgptr = Triggers.vcptr;
428
                        const auto &&trig = vctrgptr(trigger_num);
429
 
430
                        ui_checkbox_check(t->triggerFlag[0].get(), trig->flags & TRIGGER_CONTROL_DOORS);
431
                        ui_checkbox_check(t->triggerFlag[1].get(), trig->flags & TRIGGER_SHIELD_DAMAGE);
432
                        ui_checkbox_check(t->triggerFlag[2].get(), trig->flags & TRIGGER_ENERGY_DRAIN);
433
                        ui_checkbox_check(t->triggerFlag[3].get(), trig->flags & TRIGGER_EXIT);
434
                        ui_checkbox_check(t->triggerFlag[4].get(), trig->flags & TRIGGER_ONE_SHOT);
435
                        ui_checkbox_check(t->triggerFlag[5].get(), trig->flags & TRIGGER_ILLUSION_ON);
436
                        ui_checkbox_check(t->triggerFlag[6].get(), trig->flags & TRIGGER_ILLUSION_OFF);
437
                        ui_checkbox_check(t->triggerFlag[7].get(), 0);
438
                        ui_checkbox_check(t->triggerFlag[8].get(), trig->flags & TRIGGER_MATCEN);
439
                        ui_checkbox_check(t->triggerFlag[9].get(), trig->flags & TRIGGER_SECRET_EXIT);
440
#endif
441
                }
442
        }
443
 
444
        //------------------------------------------------------------
445
        // If any of the checkboxes that control the wallflags are set, then
446
        // update the cooresponding wall flag.
447
        //------------------------------------------------------------
448
        if (IS_CHILD(Markedsegp->children[Markedside]))
449
        {
450
#if defined(DXX_BUILD_DESCENT_I)
451
                rval = window_event_result::handled;
452
                if (GADGET_PRESSED(t->triggerFlag[0].get()))
453
                        trigger_flag_Markedside(TRIGGER_CONTROL_DOORS, t->triggerFlag[0]->flag);
454
                else if (GADGET_PRESSED(t->triggerFlag[1].get()))
455
                        trigger_flag_Markedside(TRIGGER_SHIELD_DAMAGE, t->triggerFlag[1]->flag);
456
                else if (GADGET_PRESSED(t->triggerFlag[2].get()))
457
                        trigger_flag_Markedside(TRIGGER_ENERGY_DRAIN, t->triggerFlag[2]->flag);
458
                else if (GADGET_PRESSED(t->triggerFlag[3].get()))
459
                        trigger_flag_Markedside(TRIGGER_EXIT, t->triggerFlag[3]->flag);
460
                else if (GADGET_PRESSED(t->triggerFlag[4].get()))
461
                        trigger_flag_Markedside(TRIGGER_ONE_SHOT, t->triggerFlag[4]->flag);
462
                else if (GADGET_PRESSED(t->triggerFlag[5].get()))
463
                        trigger_flag_Markedside(TRIGGER_ILLUSION_ON, t->triggerFlag[5]->flag);
464
                else if (GADGET_PRESSED(t->triggerFlag[6].get()))
465
                        trigger_flag_Markedside(TRIGGER_ILLUSION_OFF, t->triggerFlag[6]->flag);
466
                else if (GADGET_PRESSED(t->triggerFlag[7].get()))
467
                {
468
                }
469
                else if (GADGET_PRESSED(t->triggerFlag[8].get()))
470
                        trigger_flag_Markedside(TRIGGER_MATCEN, t->triggerFlag[8]->flag);
471
                else if (GADGET_PRESSED(t->triggerFlag[9].get()))
472
                        trigger_flag_Markedside(TRIGGER_SECRET_EXIT, t->triggerFlag[9]->flag);
473
                else
474
#endif
475
                        rval = window_event_result::ignored;
476
 
477
        } else
478
        {
479
#if defined(DXX_BUILD_DESCENT_I)
480
                range_for (auto &i, t->triggerFlag)
481
                        ui_checkbox_check(i.get(), 0);
482
#endif
483
        }
484
 
485
        //------------------------------------------------------------
486
        // Draw the wall in the little 64x64 box
487
        //------------------------------------------------------------
488
        if (event.type == EVENT_UI_DIALOG_DRAW)
489
        {
490
                gr_set_current_canvas( t->wallViewBox->canvas );
491
                auto &canvas = *grd_curcanv;
492
 
493
                const auto wall_num = Markedsegp->shared_segment::sides[Markedside].wall_num;
494
                if (wall_num == wall_none || vcwallptr(wall_num)->trigger == trigger_none)
495
                        gr_clear_canvas(canvas, CBLACK);
496
                else {
497
                        auto &us = Markedsegp->unique_segment::sides[Markedside];
498
                        if (us.tmap_num2 > 0)
499
                        {
500
                                gr_ubitmap(canvas, texmerge_get_cached_bitmap(us.tmap_num, us.tmap_num2));
501
                        } else {
502
                                const auto tmap_num = us.tmap_num;
503
                                if (tmap_num > 0)
504
                                {
505
                                        auto &t = Textures[tmap_num];
506
                                        PIGGY_PAGE_IN(t);
507
                                        gr_ubitmap(canvas, GameBitmaps[t.index]);
508
                                } else
509
                                        gr_clear_canvas(canvas, CGREY);
510
                        }
511
                }
512
        }
513
 
514
        //------------------------------------------------------------
515
        // If anything changes in the ui system, redraw all the text that
516
        // identifies this robot.
517
        //------------------------------------------------------------
518
        if (event.type == EVENT_UI_DIALOG_DRAW)
519
        {
520
                if (Markedsegp->shared_segment::sides[Markedside].wall_num != wall_none)
521
                {
522
                        ui_dprintf_at( MainWindow, 12, 6, "Trigger: %d    ", trigger_num);
523
                }       else {
524
                        ui_dprintf_at( MainWindow, 12, 6, "Trigger: none ");
525
                }
526
        }
527
 
528
        if (ui_button_any_drawn || (t->old_trigger_num != trigger_num) )
529
                Update_flags |= UF_WORLD_CHANGED;
530
        if (GADGET_PRESSED(t->quitButton.get()) || keypress == KEY_ESC)
531
        {
532
                return window_event_result::close;
533
        }              
534
 
535
        t->old_trigger_num = trigger_num;
536
 
537
        return rval;
538
}
539
 
540
 
541
 
542
 
543