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-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
18
*/
19
 
20
/*
21
 *
22
 * object system definitions
23
 *
24
 */
25
 
26
#pragma once
27
 
28
#include <type_traits>
29
#include "dsx-ns.h"
30
#ifdef dsx
31
 
32
#include "pstypes.h"
33
#include "vecmat.h"
34
#include "aistruct.h"
35
#include "polyobj.h"
36
#include "laser.h"
37
 
38
#ifdef __cplusplus
39
#include <cassert>
40
#include <cstdint>
41
#include "dxxsconf.h"
42
#include "valptridx.h"
43
#include "objnum.h"
44
#include "fwd-segment.h"
45
#include <vector>
46
#include <stdexcept>
47
#include "fwd-object.h"
48
#include "weapon.h"
49
#include "powerup.h"
50
#include "compiler-poison.h"
51
#include "physics_info.h"
52
#include "player-flags.h"
53
#if defined(DXX_BUILD_DESCENT_II)
54
#include "escort.h"
55
#endif
56
#include <array>
57
#include <utility>
58
 
59
namespace dcx {
60
 
61
// Object types
62
enum object_type_t : uint8_t
63
{
64
        OBJ_NONE        = 255, // unused object
65
        OBJ_WALL        = 0,   // A wall... not really an object, but used for collisions
66
        OBJ_FIREBALL    = 1,   // a fireball, part of an explosion
67
        OBJ_ROBOT       = 2,   // an evil enemy
68
        OBJ_HOSTAGE     = 3,   // a hostage you need to rescue
69
        OBJ_PLAYER      = 4,   // the player on the console
70
        OBJ_WEAPON      = 5,   // a laser, missile, etc
71
        OBJ_CAMERA      = 6,   // a camera to slew around with
72
        OBJ_POWERUP     = 7,   // a powerup you can pick up
73
        OBJ_DEBRIS      = 8,   // a piece of robot
74
        OBJ_CNTRLCEN    = 9,   // the control center
75
        OBJ_CLUTTER     = 11,  // misc objects
76
        OBJ_GHOST       = 12,  // what the player turns into when dead
77
        OBJ_LIGHT       = 13,  // a light source, & not much else
78
        OBJ_COOP        = 14,  // a cooperative player object.
79
        OBJ_MARKER      = 15,  // a map marker
80
};
81
 
82
enum movement_type_t : uint8_t
83
{
84
        MT_NONE = 0,   // doesn't move
85
        MT_PHYSICS = 1,   // moves by physics
86
        MT_SPINNING = 3,   // this object doesn't move, just sits and spins
87
};
88
 
89
enum render_type_t : uint8_t
90
{
91
        RT_NONE = 0,   // does not render
92
        RT_POLYOBJ = 1,   // a polygon model
93
        RT_FIREBALL = 2,   // a fireball
94
        RT_LASER = 3,   // a laser
95
        RT_HOSTAGE = 4,   // a hostage
96
        RT_POWERUP = 5,   // a powerup
97
        RT_MORPH = 6,   // a robot being morphed
98
        RT_WEAPON_VCLIP = 7,   // a weapon that renders as a vclip
99
};
100
 
101
static inline bool valid_render_type(const uint8_t r)
102
{
103
        switch (r)
104
        {
105
                case RT_NONE:
106
                case RT_POLYOBJ:
107
                case RT_FIREBALL:
108
                case RT_LASER:
109
                case RT_HOSTAGE:
110
                case RT_POWERUP:
111
                case RT_MORPH:
112
                case RT_WEAPON_VCLIP:
113
                        return true;
114
                default:
115
                        return false;
116
        }
117
}
118
 
119
}
120
 
121
namespace dsx {
122
 
123
/*
124
 * STRUCTURES
125
 */
126
 
127
struct reactor_static {
128
        /* Location of the gun on the reactor object */
129
        std::array<vms_vector, MAX_CONTROLCEN_GUNS>     gun_pos,
130
        /* Orientation of the gun on the reactor object */
131
                gun_dir;
132
};
133
 
134
struct player_info
135
{
136
        fix     energy;                 // Amount of energy remaining.
137
        fix     homing_object_dist;     // Distance of nearest homing object.
138
        fix Fusion_charge;
139
#if defined(DXX_BUILD_DESCENT_II)
140
        fix Omega_charge;
141
        fix Omega_recharge_delay;
142
#endif
143
        player_flags powerup_flags;
144
        objnum_t   killer_objnum;          // Who killed me.... (-1 if no one)
145
        uint16_t vulcan_ammo;
146
#if defined(DXX_BUILD_DESCENT_I)
147
        using primary_weapon_flag_type = uint8_t;
148
#elif defined(DXX_BUILD_DESCENT_II)
149
        using primary_weapon_flag_type = uint16_t;
150
#endif
151
        primary_weapon_flag_type primary_weapon_flags;
152
        bool Player_eggs_dropped;
153
        bool FakingInvul;
154
        bool lavafall_hiss_playing;
155
        uint8_t missile_gun;
156
        player_selected_weapon<primary_weapon_index_t> Primary_weapon;
157
        player_selected_weapon<secondary_weapon_index_t> Secondary_weapon;
158
        stored_laser_level laser_level;
159
        std::array<uint8_t, MAX_SECONDARY_WEAPONS>  secondary_ammo; // How much ammo of each type.
160
        uint8_t Spreadfire_toggle;
161
#if defined(DXX_BUILD_DESCENT_II)
162
        uint8_t Primary_last_was_super;
163
        uint8_t Secondary_last_was_super;
164
        uint8_t Helix_orientation;
165
#endif
166
        int16_t net_killed_total;               // Number of times killed total
167
        int16_t net_kills_total;                // Number of net kills total
168
        int16_t KillGoalCount;                  // Num of players killed this level
169
        union {
170
                struct {
171
                        int score;                              // Current score.
172
                        int last_score;                 // Score at beginning of current level.
173
                        uint16_t hostages_rescued_total; // Total number of hostages rescued.
174
                        uint8_t hostages_on_board;
175
                } mission;
176
                struct {
177
                        uint8_t orbs;
178
                } hoard;
179
        };
180
        enum
181
        {
182
                max_hoard_orbs = 12,
183
        };
184
        fix64   cloak_time;             // Time cloaked
185
        fix64   invulnerable_time;      // Time invulnerable
186
        fix64 Next_flare_fire_time;
187
        fix64 Next_laser_fire_time;
188
        fix64 Next_missile_fire_time;
189
        fix64 Last_bumped_local_player;
190
        fix64 Auto_fire_fusion_cannon_time;
191
};
192
 
193
}
194
 
195
namespace dcx {
196
 
197
// A compressed form for sending crucial data
198
struct shortpos : prohibit_void_ptr<shortpos>
199
{
200
        std::array<int8_t, 9> bytemat;
201
        int16_t xo, yo, zo;
202
        segnum_t segment;
203
        int16_t velx, vely, velz;
204
};
205
 
206
// Another compressed form for object position, velocity, orientation and rotvel using quaternion
207
struct quaternionpos : prohibit_void_ptr<quaternionpos>
208
{
209
        using packed_size = std::integral_constant<std::size_t, sizeof(vms_quaternion) + sizeof(segnum_t) + (sizeof(vms_vector) * 3)>;
210
        vms_quaternion orient;
211
        vms_vector pos;
212
        segnum_t segment;
213
        vms_vector vel;
214
        vms_vector rotvel;
215
};
216
 
217
// stuctures for different kinds of simulation
218
 
219
struct laser_parent
220
{
221
        int16_t parent_type = {};        // The type of the parent of this object
222
        objnum_t parent_num = {};         // The object's parent's number
223
        object_signature_t parent_signature = object_signature_t{0};   // The object's parent's signature...
224
};
225
 
226
struct laser_info : prohibit_void_ptr<laser_info>, laser_parent
227
{
228
        fix64 creation_time = 0;      // Absolute time of creation.
229
        /* hitobj_pos specifies the next position to which a value should be
230
         * written.  That position may have a defined value if the array has
231
         * wrapped, but should be treated as write-only in the general case.
232
         *
233
         * hitobj_count tells how many elements in hitobj_values[] are
234
         * valid.  Its valid values are [0, hitobj_values.size()].  When
235
         * hitobj_count == hitobj_values.size(), hitobj_pos wraps around and
236
         * begins erasing the oldest elements first.
237
         */
238
        uint8_t hitobj_pos = 0, hitobj_count = 0;
239
        std::array<objnum_t, 83> hitobj_values = {};
240
        objnum_t track_goal = 0;         // Object this object is tracking.
241
        fix multiplier = 0;         // Power if this is a fusion bolt (or other super weapon to be added).
242
        uint_fast8_t test_set_hitobj(const vcobjidx_t o);
243
        uint_fast8_t test_hitobj(const vcobjidx_t o) const;
244
        icobjidx_t get_last_hitobj() const;
245
        void clear_hitobj()
246
        {
247
                hitobj_pos = hitobj_count = 0;
248
        }
249
        void reset_hitobj(const icobjidx_t o)
250
        {
251
                if (o == object_none)
252
                {
253
                        /* Adding object_none to the array is harmless, since
254
                         * get_last_hitobj can return object_none for empty arrays.
255
                         * However, by filtering it here (which is called only when
256
                         * loading data into new objects), test_hitobj (which is
257
                         * called every time the object strikes a potential target)
258
                         * will not need to read a slot that is guaranteed not to
259
                         * match.
260
                         */
261
                        clear_hitobj();
262
                        return;
263
                }
264
                /* Assume caller already poisoned the unused array elements */
265
                hitobj_pos = hitobj_count = 1;
266
                hitobj_values[0] = o;
267
        }
268
};
269
 
270
// Same as above but structure Savegames/Multiplayer objects expect
271
struct laser_info_rw
272
{
273
        short   parent_type;        // The type of the parent of this object
274
        short   parent_num;         // The object's parent's number
275
        int     parent_signature;   // The object's parent's signature...
276
        fix     creation_time;      // Absolute time of creation.
277
        short   last_hitobj;        // For persistent weapons (survive object collision), object it most recently hit.
278
        short   track_goal;         // Object this object is tracking.
279
        fix     multiplier;         // Power if this is a fusion bolt (or other super weapon to be added).
280
} __pack__;
281
 
282
struct explosion_info : prohibit_void_ptr<explosion_info>
283
{
284
    fix     spawn_time;         // when lifeleft is < this, spawn another
285
    fix     delete_time;        // when to delete object
286
    objnum_t   delete_objnum;      // and what object to delete
287
    objnum_t   attach_parent;      // explosion is attached to this object
288
    objnum_t   prev_attach;        // previous explosion in attach list
289
    objnum_t   next_attach;        // next explosion in attach list
290
};
291
 
292
struct explosion_info_rw
293
{
294
    fix     spawn_time;         // when lifeleft is < this, spawn another
295
    fix     delete_time;        // when to delete object
296
    short   delete_objnum;      // and what object to delete
297
    short   attach_parent;      // explosion is attached to this object
298
    short   prev_attach;        // previous explosion in attach list
299
    short   next_attach;        // next explosion in attach list
300
} __pack__;
301
 
302
struct light_info : prohibit_void_ptr<light_info>
303
{
304
    fix     intensity;          // how bright the light is
305
};
306
 
307
struct light_info_rw
308
{
309
    fix     intensity;          // how bright the light is
310
} __pack__;
311
 
312
struct powerup_info : prohibit_void_ptr<powerup_info>
313
{
314
        int     count;          // how many/much we pick up (vulcan cannon only?)
315
        int     flags;          // spat by player?
316
        fix64   creation_time;  // Absolute time of creation.
317
};
318
 
319
}
320
 
321
namespace dsx {
322
 
323
struct powerup_info_rw
324
{
325
        int     count;          // how many/much we pick up (vulcan cannon only?)
326
#if defined(DXX_BUILD_DESCENT_II)
327
// Same as above but structure Savegames/Multiplayer objects expect
328
        fix     creation_time;  // Absolute time of creation.
329
        int     flags;          // spat by player?
330
#endif
331
} __pack__;
332
 
333
}
334
 
335
namespace dcx {
336
 
337
struct vclip_info : prohibit_void_ptr<vclip_info>
338
{
339
        int     vclip_num;
340
        fix     frametime;
341
        uint8_t framenum;
342
};
343
 
344
struct vclip_info_rw
345
{
346
        int     vclip_num;
347
        fix     frametime;
348
        sbyte   framenum;
349
} __pack__;
350
 
351
// structures for different kinds of rendering
352
 
353
struct polyobj_info : prohibit_void_ptr<polyobj_info>
354
{
355
        int     model_num = 0;          // which polygon model
356
        std::array<vms_angvec, MAX_SUBMODELS> anim_angles{}; // angles for each subobject
357
        int     subobj_flags = 0;       // specify which subobjs to draw
358
        int     tmap_override = 0;      // if this is not -1, map all face to this
359
        int     alt_textures = 0;       // if not -1, use these textures instead
360
};
361
 
362
struct polyobj_info_rw
363
{
364
        int     model_num;          // which polygon model
365
        vms_angvec anim_angles[MAX_SUBMODELS]; // angles for each subobject
366
        int     subobj_flags;       // specify which subobjs to draw
367
        int     tmap_override;      // if this is not -1, map all face to this
368
        int     alt_textures;       // if not -1, use these textures instead
369
} __pack__;
370
 
371
struct object_base
372
{
373
        object_signature_t signature;
374
        object_type_t   type;           // what type of object this is... robot, weapon, hostage, powerup, fireball
375
        ubyte   id;             // which form of object...which powerup, robot, etc.
376
        objnum_t   next,prev;      // id of next and previous connected object in Objects, -1 = no connection
377
        ubyte   control_type;   // how this object is controlled
378
        movement_type_t   movement_type;  // how this object moves
379
        render_type_t render_type;    // how this object renders
380
        ubyte   flags;          // misc flags
381
        segnum_t   segnum;         // segment number containing object
382
        objnum_t   attached_obj;   // number of attached fireball object
383
        vms_vector pos;         // absolute x,y,z coordinate of center of object
384
        vms_matrix orient;      // orientation of object in world
385
        fix     size;           // 3d size of object - for collision detection
386
        fix     shields;        // Starts at maximum, when <0, object dies..
387
        sbyte   contains_type;  // Type of object this object contains (eg, spider contains powerup)
388
        sbyte   contains_id;    // ID of object this object contains (eg, id = blue type = key)
389
        sbyte   contains_count; // number of objects of type:id this object contains
390
        sbyte   matcen_creator; // Materialization center that created this object, high bit set if matcen-created
391
        fix     lifeleft;       // how long until goes away, or 7fff if immortal
392
        // -- Removed, MK, 10/16/95, using lifeleft instead: int     lightlevel;
393
 
394
        // movement info, determined by MOVEMENT_TYPE
395
        union movement_info {
396
                physics_info phys_info; // a physics object
397
                vms_vector   spin_rate; // for spinning objects
398
                constexpr movement_info() :
399
                        phys_info{}
400
                {
401
                        static_assert(sizeof(phys_info) == sizeof(*this), "insufficient initialization");
402
                }
403
        } mtype;
404
 
405
        // render info, determined by RENDER_TYPE
406
        union render_info {
407
                struct polyobj_info    pobj_info;      // polygon model
408
                struct vclip_info      vclip_info;     // vclip
409
                constexpr render_info() :
410
                        pobj_info{}
411
                {
412
                        static_assert(sizeof(pobj_info) == sizeof(*this), "insufficient initialization");
413
                }
414
        } rtype;
415
};
416
 
417
}
418
 
419
namespace dsx {
420
 
421
#if defined(DXX_BUILD_DESCENT_II)
422
struct laser_info : public ::dcx::laser_info
423
{
424
        fix64 last_afterburner_time = 0;        //      Time at which this object last created afterburner blobs.
425
};
426
#endif
427
 
428
struct object : public ::dcx::object_base
429
{
430
        // control info, determined by CONTROL_TYPE
431
        union control_info {
432
                constexpr control_info() :
433
                        ai_info{}
434
                {
435
                        static_assert(sizeof(ai_info) == sizeof(*this), "insufficient initialization");
436
                }
437
                struct laser_info      laser_info;
438
                struct explosion_info  expl_info;      // NOTE: debris uses this also
439
                struct light_info      light_info;     // why put this here?  Didn't know what else to do with it.
440
                struct powerup_info    powerup_info;
441
                struct ai_static       ai_info;
442
                struct reactor_static  reactor_info;
443
                struct player_info     player_info;
444
        } ctype;
445
};
446
 
447
}
448
 
449
namespace dcx {
450
 
451
// Same as above but structure Savegames/Multiplayer objects expect
452
struct object_rw
453
{
454
        int     signature;      // Every object ever has a unique signature...
455
        ubyte   type;           // what type of object this is... robot, weapon, hostage, powerup, fireball
456
        ubyte   id;             // which form of object...which powerup, robot, etc.
457
        short   next,prev;      // id of next and previous connected object in Objects, -1 = no connection
458
        ubyte   control_type;   // how this object is controlled
459
        ubyte   movement_type;  // how this object moves
460
        ubyte   render_type;    // how this object renders
461
        ubyte   flags;          // misc flags
462
        short   segnum;         // segment number containing object
463
        short   attached_obj;   // number of attached fireball object
464
        vms_vector pos;         // absolute x,y,z coordinate of center of object
465
        vms_matrix orient;      // orientation of object in world
466
        fix     size;           // 3d size of object - for collision detection
467
        fix     shields;        // Starts at maximum, when <0, object dies..
468
        vms_vector last_pos;    // where object was last frame
469
        sbyte   contains_type;  // Type of object this object contains (eg, spider contains powerup)
470
        sbyte   contains_id;    // ID of object this object contains (eg, id = blue type = key)
471
        sbyte   contains_count; // number of objects of type:id this object contains
472
        sbyte   matcen_creator; // Materialization center that created this object, high bit set if matcen-created
473
        fix     lifeleft;       // how long until goes away, or 7fff if immortal
474
        // -- Removed, MK, 10/16/95, using lifeleft instead: int     lightlevel;
475
 
476
        // movement info, determined by MOVEMENT_TYPE
477
        union {
478
                physics_info_rw phys_info; // a physics object
479
                vms_vector   spin_rate; // for spinning objects
480
        } __pack__ mtype ;
481
 
482
        // control info, determined by CONTROL_TYPE
483
        union {
484
                laser_info_rw   laser_info;
485
                explosion_info_rw  expl_info;      // NOTE: debris uses this also
486
                ai_static_rw    ai_info;
487
                light_info_rw      light_info;     // why put this here?  Didn't know what else to do with it.
488
                powerup_info_rw powerup_info;
489
        } __pack__ ctype ;
490
 
491
        // render info, determined by RENDER_TYPE
492
        union {
493
                polyobj_info_rw    pobj_info;      // polygon model
494
                vclip_info_rw      vclip_info;     // vclip
495
        } __pack__ rtype;
496
} __pack__;
497
 
498
struct obj_position
499
{
500
        vms_vector  pos;        // absolute x,y,z coordinate of center of object
501
        vms_matrix  orient;     // orientation of object in world
502
        segnum_t       segnum;     // segment number containing object
503
};
504
 
505
#define set_object_type(O,T)    \
506
        ( DXX_BEGIN_COMPOUND_STATEMENT {        \
507
                object_base &dxx_object_type_ref = (O); \
508
                const uint8_t &dxx_object_type_value = (T);     \
509
                assert( \
510
                        dxx_object_type_value == OBJ_NONE ||    \
511
                        dxx_object_type_value == OBJ_FIREBALL ||        \
512
                        dxx_object_type_value == OBJ_ROBOT ||   \
513
                        dxx_object_type_value == OBJ_HOSTAGE || \
514
                        dxx_object_type_value == OBJ_PLAYER ||  \
515
                        dxx_object_type_value == OBJ_WEAPON ||  \
516
                        dxx_object_type_value == OBJ_CAMERA ||  \
517
                        dxx_object_type_value == OBJ_POWERUP || \
518
                        dxx_object_type_value == OBJ_DEBRIS ||  \
519
                        dxx_object_type_value == OBJ_CNTRLCEN ||        \
520
                        dxx_object_type_value == OBJ_CLUTTER || \
521
                        dxx_object_type_value == OBJ_GHOST ||   \
522
                        dxx_object_type_value == OBJ_LIGHT ||   \
523
                        dxx_object_type_value == OBJ_COOP ||    \
524
                        dxx_object_type_value == OBJ_MARKER     \
525
                );      \
526
                dxx_object_type_ref.type = static_cast<object_type_t>(dxx_object_type_value);   \
527
        } DXX_END_COMPOUND_STATEMENT )
528
 
529
#define set_object_movement_type(O,T)   \
530
        ( DXX_BEGIN_COMPOUND_STATEMENT {        \
531
                object_base &dxx_object_movement_type_ref = (O);        \
532
                const uint8_t &dxx_object_movement_type_value = (T);    \
533
                assert( \
534
                        dxx_object_movement_type_value == MT_NONE ||    \
535
                        dxx_object_movement_type_value == MT_PHYSICS || \
536
                        dxx_object_movement_type_value == MT_SPINNING   \
537
                );      \
538
                dxx_object_movement_type_ref.movement_type = static_cast<movement_type_t>(dxx_object_movement_type_value);      \
539
        } DXX_END_COMPOUND_STATEMENT )
540
 
541
template <typename T, std::size_t... N>
542
constexpr std::array<T, sizeof...(N)> init_object_number_array(std::index_sequence<N...>)
543
{
544
        return {{((void)N, object_none)...}};
545
}
546
 
547
template <typename T, std::size_t N>
548
struct object_number_array : std::array<T, N>
549
{
550
        constexpr object_number_array() :
551
                std::array<T, N>(init_object_number_array<T>(std::make_index_sequence<N>()))
552
        {
553
        }
554
};
555
 
556
unsigned laser_parent_is_matching_signature(const laser_parent &l, const object_base &o);
557
 
558
struct d_level_unique_control_center_state
559
{
560
        uint8_t Control_center_destroyed;
561
        uint8_t Control_center_been_hit;
562
        uint8_t Control_center_present;
563
        player_visibility_state Control_center_player_been_seen;
564
        objnum_t Dead_controlcen_object_num;
565
        int Countdown_seconds_left;
566
        fix Countdown_timer;
567
        fix Frametime_until_next_fire;
568
        /* If the player is not dead, this stays 0.  If the player is dead,
569
         * this accumulates FrameTime, up until it saturates.  When it
570
         * saturates, the reactor stops firing.
571
         *
572
         * FIXME: The original game defined this in terms of "the player",
573
         * but reactors are present in multiplayer games.
574
         */
575
        fix Frametime_since_player_died;
576
        int Total_countdown_time;               //in whole seconds
577
};
578
 
579
struct d_level_unique_boss_state
580
{
581
        fix64 Last_gate_time;
582
        fix64 Boss_cloak_start_time;
583
        fix64 Last_teleport_time;
584
        fix64 Boss_dying_start_time;
585
        int8_t Boss_hit_this_frame;
586
        int8_t Boss_dying;
587
        int8_t Boss_dying_sound_playing;
588
};
589
 
590
}
591
 
592
#define Highest_object_index (Objects.get_count() - 1)
593
 
594
#include "morph.h"
595
 
596
namespace dcx {
597
 
598
struct d_level_unique_morph_object_state
599
{
600
        std::array<morph_data::ptr, 5> morph_objects;
601
        ~d_level_unique_morph_object_state();
602
};
603
 
604
}
605
 
606
namespace dsx {
607
 
608
#if defined(DXX_BUILD_DESCENT_II)
609
 
610
/* game_marker_index values are assigned as the object.id field for
611
 * marker objects, and are used as an index into most marker-related
612
 * arrays.  These values are scoped to a single level.  Each player
613
 * marker has a unique value for game_marker_index, computed based on
614
 * the player's ID and the player_marker_index active for that player
615
 * when the marker was created.
616
 */
617
enum class game_marker_index : uint8_t
618
{
619
        GuidebotDeathSite = 10,
620
        None = UINT8_MAX
621
};
622
 
623
/* player_marker_index are per-player marker indexes.  Each player has
624
 * their own private number space.  Valid values range from [0,
625
 * maxdrop), where maxdrop depends on the game type (single player,
626
 * competitive multiplayer, or cooperative multiplayer).
627
 */
628
enum class player_marker_index : uint8_t
629
{
630
        _0,
631
        None = UINT8_MAX
632
};
633
 
634
static inline game_marker_index &operator++(game_marker_index &i)
635
{
636
        auto u = static_cast<unsigned>(i);
637
        ++ u;
638
        i = static_cast<game_marker_index>(u);
639
        return i;
640
}
641
 
642
static inline player_marker_index &operator++(player_marker_index &i)
643
{
644
        auto u = static_cast<unsigned>(i);
645
        ++ u;
646
        i = static_cast<player_marker_index>(u);
647
        return i;
648
}
649
 
650
playernum_t get_marker_owner(unsigned, game_marker_index, unsigned max_numplayers);
651
 
652
struct d_unique_buddy_state
653
{
654
        enum class Escort_goal_reachability : uint8_t
655
        {
656
                unreachable,
657
                reachable,
658
        };
659
        icobjidx_t Buddy_objnum = object_none;
660
        icobjidx_t Escort_goal_objidx = object_none;
661
        Escort_goal_reachability Escort_goal_reachable = Escort_goal_reachability::unreachable;
662
        uint8_t Buddy_allowed_to_talk;
663
        uint8_t Buddy_messages_suppressed;
664
        uint8_t Buddy_gave_hint_count;
665
        game_marker_index Looking_for_marker;
666
        int Last_buddy_key;
667
        int Last_buddy_polish_path_tick;
668
        escort_goal_t Escort_goal_object;
669
        escort_goal_t Escort_special_goal;
670
        fix64 Buddy_sorry_time;
671
        fix64 Buddy_last_seen_player;
672
        fix64 Buddy_last_missile_time;
673
        fix64 Last_time_buddy_gave_hint;
674
        fix64 Last_come_back_message_time;
675
        fix64 Escort_last_path_created;
676
        fix64 Buddy_last_player_path_created;
677
        fix64 Last_buddy_message_time;
678
};
679
 
680
struct d_level_unique_control_center_state :
681
        ::dcx::d_level_unique_control_center_state
682
{
683
        fix64 Last_time_cc_vis_check;
684
};
685
 
686
class d_guided_missile_indices : object_number_array<imobjidx_t, MAX_PLAYERS>
687
{
688
        template <typename R, typename F>
689
                R get_player_active_guided_missile_tmpl(F &fvcobj, unsigned pnum) const;
690
        static bool debug_check_current_object(const object_base &);
691
public:
692
        imobjidx_t get_player_active_guided_missile(unsigned pnum) const;
693
        imobjptr_t get_player_active_guided_missile(fvmobjptr &vmobjptr, unsigned pnum) const;
694
        imobjptridx_t get_player_active_guided_missile(fvmobjptridx &vmobjptridx, unsigned pnum) const;
695
        void set_player_active_guided_missile(vmobjidx_t, unsigned pnum);
696
        void clear_player_active_guided_missile(unsigned pnum);
697
};
698
 
699
struct d_level_unique_boss_state : ::dcx::d_level_unique_boss_state
700
{
701
        fix64 Boss_hit_time;
702
};
703
 
704
const player &get_player_controlling_guidebot(const d_unique_buddy_state & /* reserved for future use */, const valptridx<player>::array_managed_type &Players);
705
#endif
706
 
707
struct d_level_unique_object_state
708
{
709
        unsigned num_objects = 0;
710
        /* `accumulated_robots` counts robots present at level entry and
711
         * robots added later via materialization centers / boss gating.  It
712
         * never decreases, so it is not a shortcut for counting the number
713
         * of currently live objects with type OBJ_ROBOT.
714
         */
715
        unsigned accumulated_robots;
716
        unsigned total_hostages;
717
        unsigned Debris_object_count = 0;
718
#if defined(DXX_BUILD_DESCENT_II)
719
        d_unique_buddy_state BuddyState;
720
        d_thief_unique_state ThiefState;
721
        d_guided_missile_indices Guided_missile;
722
#endif
723
        object_number_array<imobjidx_t, MAX_OBJECTS> free_obj_list;
724
        object_array Objects;
725
        d_level_unique_boss_state BossState;
726
        d_level_unique_control_center_state ControlCenterState;
727
        vms_vector last_console_player_position;
728
        d_level_unique_morph_object_state MorphObjectState;
729
        auto &get_objects()
730
        {
731
                return Objects;
732
        }
733
        const auto &get_objects() const
734
        {
735
                return Objects;
736
        }
737
};
738
 
739
extern d_level_unique_object_state LevelUniqueObjectState;
740
 
741
unsigned laser_parent_is_player(fvcobjptr &, const laser_parent &, const object_base &);
742
unsigned laser_parent_is_object(fvcobjptr &, const laser_parent &, const object_base &);
743
unsigned laser_parent_is_object(const laser_parent &, vcobjptridx_t);
744
unsigned laser_parent_object_exists(fvcobjptr &, const laser_parent &);
745
 
746
static inline powerup_type_t get_powerup_id(const object_base &o)
747
{
748
        return static_cast<powerup_type_t>(o.id);
749
}
750
 
751
static inline weapon_id_type get_weapon_id(const object_base &o)
752
{
753
        return static_cast<weapon_id_type>(o.id);
754
}
755
 
756
#if defined(DXX_BUILD_DESCENT_II)
757
static inline game_marker_index get_marker_id(const object_base &o)
758
{
759
        return game_marker_index{o.id};
760
}
761
#endif
762
 
763
void set_powerup_id(const d_powerup_info_array &Powerup_info, const d_vclip_array &Vclip, object_base &o, powerup_type_t id);
764
 
765
static inline void set_weapon_id(object_base &o, weapon_id_type id)
766
{
767
        o.id = static_cast<uint8_t>(id);
768
}
769
 
770
}
771
 
772
namespace dcx {
773
 
774
static inline uint8_t get_player_id(const object_base &o)
775
{
776
        return o.id;
777
}
778
 
779
static inline uint8_t get_reactor_id(const object_base &o)
780
{
781
        return o.id;
782
}
783
 
784
static inline uint8_t get_fireball_id(const object_base &o)
785
{
786
        return o.id;
787
}
788
 
789
static inline uint8_t get_robot_id(const object_base &o)
790
{
791
        return o.id;
792
}
793
 
794
static inline void set_player_id(object_base &o, const uint8_t id)
795
{
796
        o.id = id;
797
}
798
 
799
static inline void set_reactor_id(object_base &o, const uint8_t id)
800
{
801
        o.id = id;
802
}
803
 
804
static inline void set_robot_id(object_base &o, const uint8_t id)
805
{
806
        o.id = id;
807
}
808
 
809
void check_warn_object_type(const object_base &, object_type_t, const char *file, unsigned line);
810
#define get_player_id(O)        (check_warn_object_type(O, OBJ_PLAYER, __FILE__, __LINE__), get_player_id(O))
811
#define get_powerup_id(O)       (check_warn_object_type(O, OBJ_POWERUP, __FILE__, __LINE__), get_powerup_id(O))
812
#define get_reactor_id(O)       (check_warn_object_type(O, OBJ_CNTRLCEN, __FILE__, __LINE__), get_reactor_id(O))
813
#define get_ghost_id(O) (check_warn_object_type(O, OBJ_GHOST, __FILE__, __LINE__), (get_player_id)(O))
814
#define get_fireball_id(O)      (check_warn_object_type(O, OBJ_FIREBALL, __FILE__, __LINE__), get_fireball_id(O))
815
#define get_robot_id(O) (check_warn_object_type(O, OBJ_ROBOT, __FILE__, __LINE__), get_robot_id(O))
816
#define get_weapon_id(O)        (check_warn_object_type(O, OBJ_WEAPON, __FILE__, __LINE__), get_weapon_id(O))
817
#if defined(DXX_BUILD_DESCENT_II)
818
#define get_marker_id(O)        (check_warn_object_type(O, OBJ_MARKER, __FILE__, __LINE__), get_marker_id(O))
819
#endif
820
#define set_player_id(O,I)      (check_warn_object_type(O, OBJ_PLAYER, __FILE__, __LINE__), set_player_id(O, I))
821
#define set_reactor_id(O,I)     (check_warn_object_type(O, OBJ_CNTRLCEN, __FILE__, __LINE__), set_reactor_id(O, I))
822
#define set_robot_id(O,I)       (check_warn_object_type(O, OBJ_ROBOT, __FILE__, __LINE__), set_robot_id(O, I))
823
#define set_weapon_id(O,I)      (check_warn_object_type(O, OBJ_WEAPON, __FILE__, __LINE__), set_weapon_id(O, I))
824
#ifdef DXX_CONSTANT_TRUE
825
#define check_warn_object_type(O,T,F,L) \
826
        ( DXX_BEGIN_COMPOUND_STATEMENT {        \
827
                const object_base &dxx_check_warn_o = (O);      \
828
                const auto dxx_check_warn_actual_type = dxx_check_warn_o.type;  \
829
                const auto dxx_check_warn_expected_type = (T);  \
830
                /* If the type is always right, omit the runtime check. */      \
831
                DXX_CONSTANT_TRUE(dxx_check_warn_actual_type == dxx_check_warn_expected_type) || (      \
832
                        /* If the type is always wrong, force a compile-time error. */  \
833
                        DXX_CONSTANT_TRUE(dxx_check_warn_actual_type != dxx_check_warn_expected_type)   \
834
                        ? DXX_ALWAYS_ERROR_FUNCTION(dxx_error_object_type_mismatch, "object type mismatch")     \
835
                        : (     \
836
                                check_warn_object_type(dxx_check_warn_o, dxx_check_warn_expected_type, F, L)    \
837
                        )       \
838
                , 0);   \
839
        } DXX_END_COMPOUND_STATEMENT )
840
#endif
841
 
842
}
843
#endif
844
 
845
#endif