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
 * Functions for refueling centers.
23
 *
24
 */
25
 
26
#include <stdio.h>
27
#include <stdlib.h>
28
#include <math.h>
29
#include <string.h>
30
 
31
#include "fuelcen.h"
32
#include "gameseg.h"
33
#include "game.h"               // For FrameTime
34
#include "dxxerror.h"
35
#include "gauges.h"
36
#include "vclip.h"
37
#include "fireball.h"
38
#include "robot.h"
39
#include "powerup.h"
40
 
41
#include "sounds.h"
42
#include "morph.h"
43
#include "3d.h"
44
#include "physfs-serial.h"
45
#include "bm.h"
46
#include "polyobj.h"
47
#include "ai.h"
48
#include "gamemine.h"
49
#include "gamesave.h"
50
#include "player.h"
51
#include "collide.h"
52
#include "laser.h"
53
#include "multi.h"
54
#include "multibot.h"
55
#include "escort.h"
56
#include "compiler-poison.h"
57
#include "d_enumerate.h"
58
#include "compiler-range_for.h"
59
#include "partial_range.h"
60
#include "segiter.h"
61
 
62
// The max number of fuel stations per mine.
63
 
64
namespace dcx {
65
constexpr fix Fuelcen_give_amount = i2f(25);
66
constexpr fix Fuelcen_max_amount = i2f(100);
67
 
68
// Every time a robot is created in the morphing code, it decreases capacity of the morpher
69
// by this amount... when capacity gets to 0, no more morphers...
70
constexpr fix EnergyToCreateOneRobot = i2f(1);
71
 
72
static int Num_extry_robots = 15;
73
}
74
namespace dsx {
75
#if DXX_USE_EDITOR
76
const char      Special_names[MAX_CENTER_TYPES][11] = {
77
        "NOTHING   ",
78
        "FUELCEN   ",
79
        "REPAIRCEN ",
80
        "CONTROLCEN",
81
        "ROBOTMAKER",
82
#if defined(DXX_BUILD_DESCENT_II)
83
        "GOAL_RED",
84
        "GOAL_BLUE",
85
#endif
86
};
87
#endif
88
 
89
//------------------------------------------------------------
90
// Resets all fuel center info
91
void fuelcen_reset()
92
{
93
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
94
        auto &Station = LevelUniqueFuelcenterState.Station;
95
        DXX_MAKE_MEM_UNDEFINED(Station.begin(), Station.end());
96
        DXX_MAKE_MEM_UNDEFINED(RobotCenters.begin(), RobotCenters.end());
97
        LevelSharedRobotcenterState.Num_robot_centers = 0;
98
        LevelUniqueFuelcenterState.Num_fuelcenters = 0;
99
        range_for (shared_segment &i, Segments)
100
                i.special = SEGMENT_IS_NOTHING;
101
}
102
 
103
#ifndef NDEBUG          //this is sometimes called by people from the debugger
104
static void reset_all_robot_centers() __attribute_used;
105
static void reset_all_robot_centers()
106
{
107
        // Remove all materialization centers
108
        range_for (shared_segment &i, partial_range(Segments, LevelSharedSegmentState.Num_segments))
109
                if (i.special == SEGMENT_IS_ROBOTMAKER)
110
                {
111
                        i.special = SEGMENT_IS_NOTHING;
112
                        i.matcen_num = -1;
113
                }
114
}
115
#endif
116
 
117
//------------------------------------------------------------
118
// Turns a segment into a fully charged up fuel center...
119
void fuelcen_create(const vmsegptridx_t segp)
120
{
121
        auto &Station = LevelUniqueFuelcenterState.Station;
122
        int     station_type;
123
 
124
        station_type = segp->special;
125
 
126
        switch( station_type )  {
127
        case SEGMENT_IS_NOTHING:
128
#if defined(DXX_BUILD_DESCENT_II)
129
        case SEGMENT_IS_GOAL_BLUE:
130
        case SEGMENT_IS_GOAL_RED:
131
#endif
132
                return;
133
        case SEGMENT_IS_FUELCEN:
134
        case SEGMENT_IS_REPAIRCEN:
135
        case SEGMENT_IS_CONTROLCEN:
136
        case SEGMENT_IS_ROBOTMAKER:
137
                break;
138
        default:
139
                Error( "Invalid station type %d in fuelcen.c\n", station_type );
140
        }
141
 
142
        const auto next_fuelcenter_idx = LevelUniqueFuelcenterState.Num_fuelcenters++;
143
        segp->station_idx = next_fuelcenter_idx;
144
        auto &station = Station.at(next_fuelcenter_idx);
145
        station.Type = station_type;
146
        station.Capacity = Fuelcen_max_amount;
147
        station.segnum = segp;
148
        station.Timer = -1;
149
        station.Flag = 0;
150
}
151
 
152
//------------------------------------------------------------
153
// Adds a matcen that already is a special type into the Station array.
154
// This function is separate from other fuelcens because we don't want values reset.
155
static void matcen_create(const vmsegptridx_t segp)
156
{
157
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
158
        auto &Station = LevelUniqueFuelcenterState.Station;
159
        int     station_type = segp->special;
160
 
161
        Assert(station_type == SEGMENT_IS_ROBOTMAKER);
162
 
163
        const auto next_fuelcenter_idx = LevelUniqueFuelcenterState.Num_fuelcenters++;
164
        segp->station_idx = next_fuelcenter_idx;
165
        auto &station = Station.at(next_fuelcenter_idx);
166
 
167
        station.Type = station_type;
168
        station.Capacity = i2f(GameUniqueState.Difficulty_level + 3);
169
        station.segnum = segp;
170
        station.Timer = -1;
171
        station.Flag = 0;
172
 
173
        const auto next_robot_center_idx = LevelSharedRobotcenterState.Num_robot_centers++;
174
        segp->matcen_num = next_robot_center_idx;
175
        auto &robotcenter = RobotCenters[next_robot_center_idx];
176
        robotcenter.fuelcen_num = next_fuelcenter_idx;
177
        robotcenter.segnum = segp;
178
        robotcenter.fuelcen_num = next_fuelcenter_idx;
179
}
180
 
181
//------------------------------------------------------------
182
// Adds a segment that already is a special type into the Station array.
183
void fuelcen_activate(const vmsegptridx_t segp)
184
{
185
        if (segp->special == SEGMENT_IS_ROBOTMAKER)
186
                matcen_create( segp);
187
        else
188
                fuelcen_create( segp);
189
}
190
 
191
//      The lower this number is, the more quickly the center can be re-triggered.
192
//      If it's too low, it can mean all the robots won't be put out, but for about 5
193
//      robots, that's not real likely.
194
#define MATCEN_LIFE (i2f(30-2*Difficulty_level))
195
 
196
//------------------------------------------------------------
197
//      Trigger (enable) the materialization center in segment segnum
198
void trigger_matcen(const vmsegptridx_t segp)
199
{
200
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
201
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
202
        auto &Station = LevelUniqueFuelcenterState.Station;
203
        auto &Vertices = LevelSharedVertexState.get_vertices();
204
        FuelCenter      *robotcen;
205
 
206
        Assert(segp->special == SEGMENT_IS_ROBOTMAKER);
207
        assert(segp->matcen_num < LevelUniqueFuelcenterState.Num_fuelcenters);
208
        Assert((segp->matcen_num >= 0) && (segp->matcen_num <= Highest_segment_index));
209
 
210
        robotcen = &Station[RobotCenters[segp->matcen_num].fuelcen_num];
211
 
212
        if (robotcen->Enabled == 1)
213
                return;
214
 
215
        if (!robotcen->Lives)
216
                return;
217
 
218
        const auto Difficulty_level = GameUniqueState.Difficulty_level;
219
#if defined(DXX_BUILD_DESCENT_II)
220
        //      MK: 11/18/95, At insane, matcens work forever!
221
        if (Difficulty_level+1 < NDL)
222
#endif
223
                robotcen->Lives--;
224
 
225
        robotcen->Timer = F1_0*1000;    //      Make sure the first robot gets emitted right away.
226
        robotcen->Enabled = 1;                  //      Say this center is enabled, it can create robots.
227
        robotcen->Capacity = i2f(Difficulty_level + 3);
228
        robotcen->Disable_time = MATCEN_LIFE;
229
 
230
        //      Create a bright object in the segment.
231
        auto &vcvertptr = Vertices.vcptr;
232
        auto &&pos = compute_segment_center(vcvertptr, segp);
233
        const auto &&delta = vm_vec_sub(vcvertptr(segp->verts[0]), pos);
234
        vm_vec_scale_add2(pos, delta, F1_0/2);
235
        auto objnum = obj_create( OBJ_LIGHT, 0, segp, pos, NULL, 0, CT_LIGHT, MT_NONE, RT_NONE );
236
        if (objnum != object_none) {
237
                objnum->lifeleft = MATCEN_LIFE;
238
                objnum->ctype.light_info.intensity = i2f(8);    //      Light cast by a fuelcen.
239
        } else {
240
                Int3();
241
        }
242
}
243
 
244
#if DXX_USE_EDITOR
245
//------------------------------------------------------------
246
// Takes away a segment's fuel center properties.
247
//      Deletes the segment point entry in the FuelCenter list.
248
void fuelcen_delete(const vmsegptr_t segp)
249
{
250
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
251
        auto &Station = LevelUniqueFuelcenterState.Station;
252
        auto Num_fuelcenters = LevelUniqueFuelcenterState.Num_fuelcenters;
253
Restart: ;
254
        segp->special = 0;
255
 
256
        for (uint_fast32_t i = 0; i < Num_fuelcenters; i++ )    {
257
                FuelCenter &fi = Station[i];
258
                if (vmsegptr(fi.segnum) == segp)
259
                {
260
 
261
                        auto &Num_robot_centers = LevelSharedRobotcenterState.Num_robot_centers;
262
                        // If Robot maker is deleted, fix Segments and RobotCenters.
263
                        if (fi.Type == SEGMENT_IS_ROBOTMAKER) {
264
                                if (!Num_robot_centers)
265
                                {
266
                                        con_printf(CON_URGENT, "%s:%u: error: Num_robot_centers=0 while deleting robot maker", __FILE__, __LINE__);
267
                                        return;
268
                                }
269
                                const auto &&range = partial_range(RobotCenters, static_cast<unsigned>(segp->matcen_num), Num_robot_centers--);
270
 
271
                                std::move(std::next(range.begin()), range.end(), range.begin());
272
                                range_for (auto &fj, partial_const_range(Station, Num_fuelcenters))
273
                                {
274
                                        if ( fj.Type == SEGMENT_IS_ROBOTMAKER )
275
                                                if ( Segments[fj.segnum].matcen_num > segp->matcen_num )
276
                                                        Segments[fj.segnum].matcen_num--;
277
                                }
278
                        }
279
 
280
                        //fix RobotCenters so they point to correct fuelcenter
281
                        range_for (auto &j, partial_range(RobotCenters, Num_robot_centers))
282
                                if (j.fuelcen_num > i)          //this robotcenter's fuelcen is changing
283
                                        j.fuelcen_num--;
284
                        Assert(Num_fuelcenters > 0);
285
                        Num_fuelcenters--;
286
                        for (uint_fast32_t j = i; j < Num_fuelcenters; j++ )    {
287
                                Station[j] = Station[j+1];
288
                                Segments[Station[j].segnum].station_idx = j;
289
                        }
290
                        goto Restart;
291
                }
292
        }
293
        LevelUniqueFuelcenterState.Num_fuelcenters = Num_fuelcenters;
294
}
295
#endif
296
 
297
#define ROBOT_GEN_TIME (i2f(5))
298
 
299
imobjptridx_t create_morph_robot(const vmsegptridx_t segp, const vms_vector &object_pos, const unsigned object_id)
300
{
301
        ai_behavior default_behavior;
302
        auto &Robot_info = LevelSharedRobotInfoState.Robot_info;
303
#if defined(DXX_BUILD_DESCENT_I)
304
        default_behavior = ai_behavior::AIB_NORMAL;
305
        if (object_id == 10)                                            //      This is a toaster guy!
306
                default_behavior = ai_behavior::AIB_RUN_FROM;
307
#elif defined(DXX_BUILD_DESCENT_II)
308
        default_behavior = Robot_info[object_id].behavior;
309
#endif
310
 
311
        auto &Polygon_models = LevelSharedPolygonModelState.Polygon_models;
312
        auto obj = robot_create(object_id, segp, object_pos,
313
                                &vmd_identity_matrix, Polygon_models[Robot_info[object_id].model_num].rad,
314
                                default_behavior);
315
 
316
        if (obj == object_none)
317
        {
318
                Int3();
319
                return object_none;
320
        }
321
 
322
        ++LevelUniqueObjectState.accumulated_robots;
323
        ++GameUniqueState.accumulated_robots;
324
        //Set polygon-object-specific data
325
 
326
        obj->rtype.pobj_info.model_num = Robot_info[get_robot_id(obj)].model_num;
327
        obj->rtype.pobj_info.subobj_flags = 0;
328
 
329
        //set Physics info
330
 
331
        obj->mtype.phys_info.mass = Robot_info[get_robot_id(obj)].mass;
332
        obj->mtype.phys_info.drag = Robot_info[get_robot_id(obj)].drag;
333
 
334
        obj->mtype.phys_info.flags |= (PF_LEVELLING);
335
 
336
        obj->shields = Robot_info[get_robot_id(obj)].strength;
337
 
338
        create_n_segment_path(obj, 6, segment_none);            //      Create a 6 segment path from creation point.
339
 
340
#if defined(DXX_BUILD_DESCENT_I)
341
        if (default_behavior == ai_behavior::AIB_RUN_FROM)
342
                obj->ctype.ai_info.ail.mode = ai_mode::AIM_RUN_FROM_OBJECT;
343
#elif defined(DXX_BUILD_DESCENT_II)
344
        obj->ctype.ai_info.ail.mode = ai_behavior_to_mode(default_behavior);
345
#endif
346
 
347
        return obj;
348
}
349
 
350
}
351
 
352
//      ----------------------------------------------------------------------------------------------------------
353
static void robotmaker_proc(const d_vclip_array &Vclip, fvmsegptridx &vmsegptridx, FuelCenter *const robotcen, const unsigned numrobotcen)
354
{
355
        auto &LevelSharedVertexState = LevelSharedSegmentState.get_vertex_state();
356
        auto &LevelUniqueMorphObjectState = LevelUniqueObjectState.MorphObjectState;
357
        auto &Objects = LevelUniqueObjectState.Objects;
358
        auto &Vertices = LevelSharedVertexState.get_vertices();
359
        auto &vcobjptr = Objects.vcptr;
360
        auto &vmobjptridx = Objects.vmptridx;
361
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
362
        int             matcen_num;
363
        fix             top_time;
364
 
365
        if (robotcen->Enabled == 0)
366
                return;
367
 
368
        if (robotcen->Disable_time > 0) {
369
                robotcen->Disable_time -= FrameTime;
370
                if (robotcen->Disable_time <= 0) {
371
                        robotcen->Enabled = 0;
372
                }
373
        }
374
 
375
        //      No robot making in multiplayer mode.
376
        if ((Game_mode & GM_MULTI) && (!(Game_mode & GM_MULTI_ROBOTS) || !multi_i_am_master()))
377
                return;
378
 
379
        // Wait until transmorgafier has capacity to make a robot...
380
        if ( robotcen->Capacity <= 0 ) {
381
                return;
382
        }
383
 
384
        const auto &&segp = vmsegptr(robotcen->segnum);
385
        matcen_num = segp->matcen_num;
386
 
387
        if ( matcen_num == -1 ) {
388
                return;
389
        }
390
 
391
        matcen_info *mi = &RobotCenters[matcen_num];
392
        for (unsigned i = 0;; ++i)
393
        {
394
                if (i >= mi->robot_flags.size())
395
                        return;
396
                if (mi->robot_flags[i])
397
                        break;
398
        }
399
 
400
        // Wait until we have a free slot for this puppy...
401
   //     <<<<<<<<<<<<<<<< Num robots in mine >>>>>>>>>>>>>>>>>>>>>>>>>>    <<<<<<<<<<<< Max robots in mine >>>>>>>>>>>>>>>
402
        {
403
                auto &plr = get_local_player();
404
                if (LevelUniqueObjectState.accumulated_robots - plr.num_kills_level >= Gamesave_num_org_robots + Num_extry_robots)
405
                {
406
                return;
407
                }
408
        }
409
 
410
        robotcen->Timer += FrameTime;
411
 
412
        auto &vcvertptr = Vertices.vcptr;
413
        switch( robotcen->Flag )        {
414
        case 0:         // Wait until next robot can generate
415
                if (Game_mode & GM_MULTI)
416
                {
417
                        top_time = ROBOT_GEN_TIME;     
418
                }
419
                else
420
                {
421
                        const auto center = compute_segment_center(vcvertptr, segp);
422
                        const auto dist_to_player = vm_vec_dist_quick( ConsoleObject->pos, center );
423
                        top_time = dist_to_player/64 + d_rand() * 2 + F1_0*2;
424
                        if ( top_time > ROBOT_GEN_TIME )
425
                                top_time = ROBOT_GEN_TIME + d_rand();
426
                        if ( top_time < F1_0*2 )
427
                                top_time = F1_0*3/2 + d_rand()*2;
428
                }
429
 
430
                if (robotcen->Timer > top_time )        {
431
                        int     count=0;
432
                        int     my_station_num = numrobotcen;
433
 
434
                        //      Make sure this robotmaker hasn't put out its max without having any of them killed.
435
                        range_for (const auto &&objp, vcobjptr)
436
                        {
437
                                auto &obj = *objp;
438
                                if (obj.type == OBJ_ROBOT)
439
                                        if ((obj.matcen_creator ^ 0x80) == my_station_num)
440
                                                count++;
441
                        }
442
                        if (count > GameUniqueState.Difficulty_level + 3) {
443
                                robotcen->Timer /= 2;
444
                                return;
445
                        }
446
 
447
                        //      Whack on any robot or player in the matcen segment.
448
                        count=0;
449
                        auto segnum = robotcen->segnum;
450
                        const auto &&csegp = vmsegptr(segnum);
451
                        range_for (const auto objp, objects_in(csegp, vmobjptridx, vmsegptr))
452
                        {
453
                                count++;
454
                                if ( count > MAX_OBJECTS )      {
455
                                        Int3();
456
                                        return;
457
                                }
458
                                if (objp->type==OBJ_ROBOT) {
459
                                        collide_robot_and_materialization_center(objp);
460
                                        robotcen->Timer = top_time/2;
461
                                        return;
462
                                } else if (objp->type==OBJ_PLAYER ) {
463
                                        collide_player_and_materialization_center(objp);
464
                                        robotcen->Timer = top_time/2;
465
                                        return;
466
                                }
467
                        }
468
 
469
                        const auto &&cur_object_loc = compute_segment_center(vcvertptr, csegp);
470
                        const auto &&robotcen_segp = vmsegptridx(robotcen->segnum);
471
                        // HACK!!! The 10 under here should be something equal to the 1/2 the size of the segment.
472
                        auto obj = object_create_explosion(robotcen_segp, cur_object_loc, i2f(10), VCLIP_MORPHING_ROBOT);
473
 
474
                        if (obj != object_none)
475
                                extract_orient_from_segment(vcvertptr, obj->orient, robotcen_segp);
476
 
477
                        if ( Vclip[VCLIP_MORPHING_ROBOT].sound_num > -1 )               {
478
                                digi_link_sound_to_pos(Vclip[VCLIP_MORPHING_ROBOT].sound_num, robotcen_segp, 0, cur_object_loc, 0, F1_0);
479
                        }
480
                        robotcen->Flag  = 1;
481
                        robotcen->Timer = 0;
482
 
483
                }
484
                break;
485
        case 1:                 // Wait until 1/2 second after VCLIP started.
486
                if (robotcen->Timer > (Vclip[VCLIP_MORPHING_ROBOT].play_time/2) )       {
487
 
488
                        robotcen->Capacity -= EnergyToCreateOneRobot;
489
                        robotcen->Flag = 0;
490
 
491
                        robotcen->Timer = 0;
492
                        const auto &&cur_object_loc = compute_segment_center(vcvertptr, vcsegptr(robotcen->segnum));
493
 
494
                        // If this is the first materialization, set to valid robot.
495
                        {
496
                                int     type;
497
                                ubyte   legal_types[sizeof(mi->robot_flags) * 8];   // the width of robot_flags[].
498
                                int     num_types;
499
 
500
                                num_types = 0;
501
                                for (unsigned i = 0;; ++i)
502
                                {
503
                                        if (i >= mi->robot_flags.size())
504
                                                break;
505
                                        uint32_t flags = mi->robot_flags[i];
506
                                        for (unsigned j = 0; flags && j < 8 * sizeof(flags); ++j)
507
                                        {
508
                                                if (flags & 1)
509
                                                        legal_types[num_types++] = (i * 32) + j;
510
                                                flags >>= 1;
511
                                        }
512
                                }
513
 
514
                                if (num_types == 1)
515
                                        type = legal_types[0];
516
                                else
517
                                        type = legal_types[(d_rand() * num_types) / 32768];
518
 
519
                                const auto &&obj = create_morph_robot(vmsegptridx(robotcen->segnum), cur_object_loc, type );
520
                                if (obj != object_none) {
521
                                        if (Game_mode & GM_MULTI)
522
                                                multi_send_create_robot(numrobotcen, obj, type);
523
                                        obj->matcen_creator = (numrobotcen) | 0x80;
524
 
525
                                        // Make object faces player...
526
                                        const auto direction = vm_vec_sub(ConsoleObject->pos,obj->pos );
527
                                        vm_vector_2_matrix( obj->orient, direction, &obj->orient.uvec, nullptr);
528
 
529
                                        morph_start(LevelUniqueMorphObjectState, LevelSharedPolygonModelState, obj);
530
                                        //robotcen->last_created_obj = obj;
531
                                        //robotcen->last_created_sig = robotcen->last_created_obj->signature;
532
                                }
533
                        }
534
 
535
                }
536
                break;
537
        default:
538
                robotcen->Flag = 0;
539
                robotcen->Timer = 0;
540
        }
541
}
542
 
543
//-------------------------------------------------------------
544
// Called once per frame, replenishes fuel supply.
545
void fuelcen_update_all()
546
{
547
        auto &Station = LevelUniqueFuelcenterState.Station;
548
        range_for (auto &&e, enumerate(partial_range(Station, LevelUniqueFuelcenterState.Num_fuelcenters)))
549
        {
550
                auto &i = e.value;
551
                if (i.Type == SEGMENT_IS_ROBOTMAKER)
552
                {
553
                        if (! (Game_suspended & SUSP_ROBOTS))
554
                                robotmaker_proc(Vclip, vmsegptridx, &i, e.idx);
555
                }
556
        }
557
}
558
 
559
namespace dsx {
560
 
561
#if defined(DXX_BUILD_DESCENT_I)
562
constexpr std::integral_constant<unsigned, F1_0 / 3> FUELCEN_SOUND_DELAY{};
563
#elif defined(DXX_BUILD_DESCENT_II)
564
//play every half second
565
constexpr std::integral_constant<unsigned, F1_0 / 4> FUELCEN_SOUND_DELAY{};
566
#endif
567
 
568
//-------------------------------------------------------------
569
fix fuelcen_give_fuel(const shared_segment &segp, fix MaxAmountCanTake)
570
{
571
        static fix64 last_play_time = 0;
572
 
573
        if (segp.special == SEGMENT_IS_FUELCEN)
574
        {
575
                fix amount;
576
 
577
#if defined(DXX_BUILD_DESCENT_II)
578
                detect_escort_goal_fuelcen_accomplished();
579
#endif
580
 
581
                if (MaxAmountCanTake <= 0 )     {
582
                        return 0;
583
                }
584
 
585
                amount = fixmul(FrameTime,Fuelcen_give_amount);
586
 
587
                if (amount > MaxAmountCanTake )
588
                        amount = MaxAmountCanTake;
589
 
590
                if (last_play_time + FUELCEN_SOUND_DELAY < GameTime64 || last_play_time > GameTime64)
591
                {
592
                        last_play_time = GameTime64;
593
                        multi_digi_play_sample(SOUND_REFUEL_STATION_GIVING_FUEL, F1_0/2);
594
                }
595
                return amount;
596
        } else {
597
                return 0;
598
        }
599
}
600
 
601
#if defined(DXX_BUILD_DESCENT_II)
602
//-------------------------------------------------------------
603
// DM/050904
604
// Repair centers
605
// use same values as fuel centers
606
fix repaircen_give_shields(const shared_segment &segp, const fix MaxAmountCanTake)
607
{
608
        static fix last_play_time=0;
609
 
610
        if (segp.special == SEGMENT_IS_REPAIRCEN)
611
        {
612
                fix amount;
613
                if (MaxAmountCanTake <= 0 ) {
614
                        return 0;
615
                }
616
                amount = fixmul(FrameTime,Fuelcen_give_amount);
617
                if (amount > MaxAmountCanTake )
618
                        amount = MaxAmountCanTake;
619
                if (last_play_time > GameTime64)
620
                        last_play_time = 0;
621
                if (GameTime64 > last_play_time+FUELCEN_SOUND_DELAY) {
622
                        multi_digi_play_sample(SOUND_REFUEL_STATION_GIVING_FUEL, F1_0/2);
623
                        last_play_time = GameTime64;
624
                }
625
                return amount;
626
        } else {
627
                return 0;
628
        }
629
}
630
#endif
631
 
632
//      --------------------------------------------------------------------------------------------
633
void disable_matcens(void)
634
{
635
        auto &Station = LevelUniqueFuelcenterState.Station;
636
        range_for (auto &s, partial_range(Station, LevelUniqueFuelcenterState.Num_fuelcenters))
637
                if (s.Type == SEGMENT_IS_ROBOTMAKER)
638
                {
639
                        s.Enabled = 0;
640
                        s.Disable_time = 0;
641
                }
642
}
643
 
644
//      --------------------------------------------------------------------------------------------
645
//      Initialize all materialization centers.
646
//      Give them all the right number of lives.
647
void init_all_matcens(void)
648
{
649
        auto &RobotCenters = LevelSharedRobotcenterState.RobotCenters;
650
        auto &Station = LevelUniqueFuelcenterState.Station;
651
        const auto Num_fuelcenters = LevelUniqueFuelcenterState.Num_fuelcenters;
652
        const auto &&robot_range = partial_const_range(RobotCenters, LevelSharedRobotcenterState.Num_robot_centers);
653
        for (uint_fast32_t i = 0; i < Num_fuelcenters; i++)
654
                if (Station[i].Type == SEGMENT_IS_ROBOTMAKER) {
655
                        Station[i].Lives = 3;
656
                        Station[i].Enabled = 0;
657
                        Station[i].Disable_time = 0;
658
                        //      Make sure this fuelcen is pointed at by a matcen.
659
                        if (std::find_if(robot_range.begin(), robot_range.end(), [i](const matcen_info &mi) {
660
                                return mi.fuelcen_num == i;
661
                        }) == robot_range.end())
662
                        {
663
                                Station[i].Lives = 0;
664
                                LevelError("Station %" PRIuFAST32 " has type robotmaker, but no robotmaker uses it; ignoring.", i);
665
                        }
666
                }
667
 
668
#ifndef NDEBUG
669
        //      Make sure all matcens point at a fuelcen
670
        range_for (auto &i, robot_range)
671
        {
672
                auto    fuelcen_num = i.fuelcen_num;
673
                Assert(fuelcen_num < Num_fuelcenters);
674
                Assert(Station[fuelcen_num].Type == SEGMENT_IS_ROBOTMAKER);
675
        }
676
#endif
677
 
678
}
679
 
680
}
681
 
682
struct d1mi_v25
683
{
684
        matcen_info *m;
685
        d1mi_v25(matcen_info &mi) : m(&mi) {}
686
};
687
 
688
struct d1cmi_v25
689
{
690
        const matcen_info *m;
691
        d1cmi_v25(const matcen_info &mi) : m(&mi) {}
692
};
693
 
694
#define D1_MATCEN_V25_MEMBERLIST        (p.m->robot_flags[0], serial::pad<sizeof(fix) * 2>(), p.m->segnum, p.m->fuelcen_num)
695
DEFINE_SERIAL_UDT_TO_MESSAGE(d1mi_v25, p, D1_MATCEN_V25_MEMBERLIST);
696
DEFINE_SERIAL_UDT_TO_MESSAGE(d1cmi_v25, p, D1_MATCEN_V25_MEMBERLIST);
697
ASSERT_SERIAL_UDT_MESSAGE_SIZE(d1mi_v25, 16);
698
 
699
namespace dsx {
700
#if defined(DXX_BUILD_DESCENT_I)
701
struct d1mi_v26
702
{
703
        matcen_info *m;
704
        d1mi_v26(matcen_info &mi) : m(&mi) {}
705
};
706
 
707
struct d1cmi_v26
708
{
709
        const matcen_info *m;
710
        d1cmi_v26(const matcen_info &mi) : m(&mi) {}
711
};
712
 
713
#define D1_MATCEN_V26_MEMBERLIST        (p.m->robot_flags[0], serial::pad<sizeof(uint32_t)>(), serial::pad<sizeof(fix) * 2>(), p.m->segnum, p.m->fuelcen_num)
714
DEFINE_SERIAL_UDT_TO_MESSAGE(d1mi_v26, p, D1_MATCEN_V26_MEMBERLIST);
715
DEFINE_SERIAL_UDT_TO_MESSAGE(d1cmi_v26, p, D1_MATCEN_V26_MEMBERLIST);
716
ASSERT_SERIAL_UDT_MESSAGE_SIZE(d1mi_v26, 20);
717
 
718
void matcen_info_read(PHYSFS_File *fp, matcen_info &mi, int version)
719
{
720
        if (version > 25)
721
                PHYSFSX_serialize_read<const d1mi_v26>(fp, mi);
722
        else
723
                PHYSFSX_serialize_read<const d1mi_v25>(fp, mi);
724
}
725
#elif defined(DXX_BUILD_DESCENT_II)
726
void fuelcen_check_for_goal(object &plrobj, const shared_segment &segp)
727
{
728
        Assert (game_mode_capture_flag());
729
 
730
        unsigned check_team;
731
        powerup_type_t powerup_to_drop;
732
        switch(segp.special)
733
        {
734
                case SEGMENT_IS_GOAL_BLUE:
735
                        check_team = TEAM_BLUE;
736
                        powerup_to_drop = POW_FLAG_RED;
737
                        break;
738
                case SEGMENT_IS_GOAL_RED:
739
                        check_team = TEAM_RED;
740
                        powerup_to_drop = POW_FLAG_BLUE;
741
                        break;
742
                default:
743
                        return;
744
        }
745
        if (get_team(Player_num) != check_team)
746
                return;
747
        auto &player_info = plrobj.ctype.player_info;
748
        if (player_info.powerup_flags & PLAYER_FLAGS_FLAG)
749
        {
750
                player_info.powerup_flags &= ~PLAYER_FLAGS_FLAG;
751
                                multi_send_capture_bonus (Player_num);
752
                maybe_drop_net_powerup(powerup_to_drop, 1, 0);
753
        }
754
}
755
 
756
void fuelcen_check_for_hoard_goal(object &plrobj, const shared_segment &segp)
757
{
758
        Assert (game_mode_hoard());
759
 
760
   if (Player_dead_state != player_dead_state::no)
761
                return;
762
 
763
        const auto special = segp.special;
764
        if (special==SEGMENT_IS_GOAL_BLUE || special==SEGMENT_IS_GOAL_RED)
765
        {
766
                auto &player_info = plrobj.ctype.player_info;
767
                if (auto &hoard_orbs = player_info.hoard.orbs)
768
                {
769
                                player_info.powerup_flags &= ~PLAYER_FLAGS_FLAG;
770
                        multi_send_orb_bonus(Player_num, std::exchange(hoard_orbs, 0));
771
      }
772
        }
773
 
774
}
775
 
776
 
777
/*
778
 * reads an d1_matcen_info structure from a PHYSFS_File
779
 */
780
void d1_matcen_info_read(PHYSFS_File *fp, matcen_info &mi)
781
{
782
        PHYSFSX_serialize_read<const d1mi_v25>(fp, mi);
783
        mi.robot_flags[1] = 0;
784
}
785
 
786
DEFINE_SERIAL_UDT_TO_MESSAGE(matcen_info, m, (m.robot_flags, serial::pad<sizeof(fix) * 2>(), m.segnum, m.fuelcen_num));
787
ASSERT_SERIAL_UDT_MESSAGE_SIZE(matcen_info, 20);
788
 
789
void matcen_info_read(PHYSFS_File *fp, matcen_info &mi)
790
{
791
        PHYSFSX_serialize_read(fp, mi);
792
}
793
#endif
794
 
795
void matcen_info_write(PHYSFS_File *fp, const matcen_info &mi, short version)
796
{
797
        if (version >= 27)
798
#if defined(DXX_BUILD_DESCENT_I)
799
                PHYSFSX_serialize_write<d1cmi_v26>(fp, mi);
800
#elif defined(DXX_BUILD_DESCENT_II)
801
                PHYSFSX_serialize_write(fp, mi);
802
#endif
803
        else
804
                PHYSFSX_serialize_write<d1cmi_v25>(fp, mi);
805
}
806
}
807
 
808
DEFINE_SERIAL_UDT_TO_MESSAGE(FuelCenter, fc, (serial::sign_extend<int>(fc.Type), serial::sign_extend<int>(fc.segnum), fc.Flag, fc.Enabled, fc.Lives, serial::pad<1>(), fc.Capacity, serial::pad<sizeof(fix)>(), fc.Timer, fc.Disable_time, serial::pad<3 * sizeof(fix)>()));
809
ASSERT_SERIAL_UDT_MESSAGE_SIZE(FuelCenter, 40);
810
 
811
void fuelcen_read(PHYSFS_File *fp, FuelCenter &fc)
812
{
813
        PHYSFSX_serialize_read(fp, fc);
814
}
815
 
816
void fuelcen_write(PHYSFS_File *fp, const FuelCenter &fc)
817
{
818
        PHYSFSX_serialize_write(fp, fc);
819
}