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
 * Code for powerup objects.
23
 *
24
 */
25
 
26
 
27
#include <stdarg.h>
28
#include <stdio.h>
29
#include <string.h>
30
 
31
#include "maths.h"
32
#include "vecmat.h"
33
#include "gr.h"
34
#include "3d.h"
35
#include "dxxerror.h"
36
#include "inferno.h"
37
#include "object.h"
38
#include "game.h"
39
#include "key.h"
40
#include "fireball.h"
41
#include "powerup.h"
42
#include "gauges.h"
43
#include "sounds.h"
44
#include "player.h"
45
#include "physfs-serial.h"
46
#include "text.h"
47
#include "weapon.h"
48
#include "laser.h"
49
#include "scores.h"
50
#include "multi.h"
51
#include "segment.h"
52
#include "lighting.h"
53
#include "controls.h"
54
#include "kconfig.h"
55
#include "newdemo.h"
56
#include "escort.h"
57
#if DXX_USE_EDITOR
58
#include "gr.h" //      for powerup outline drawing
59
#include "editor/editor.h"
60
#endif
61
#include "hudmsg.h"
62
#include "playsave.h"
63
#include "partial_range.h"
64
 
65
namespace dcx {
66
unsigned N_powerup_types;
67
}
68
namespace dsx {
69
d_powerup_info_array Powerup_info;
70
 
71
//process this powerup for this frame
72
void do_powerup_frame(const d_vclip_array &Vclip, const vmobjptridx_t obj)
73
{
74
        vclip_info *vci = &obj->rtype.vclip_info;
75
 
76
#if defined(DXX_BUILD_DESCENT_I)
77
        const fix fudge = 0;
78
#elif defined(DXX_BUILD_DESCENT_II)
79
        long objnum = obj;
80
        const fix fudge = (FrameTime * (objnum&3)) >> 4;
81
#endif
82
 
83
        auto &vc = Vclip[vci->vclip_num];
84
        const auto vc_frame_time = vc.frame_time;
85
        if (vc_frame_time > 0)
86
        {
87
        const auto vc_num_frames1 = vc.num_frames - 1;
88
        vci->frametime -= FrameTime+fudge;
89
 
90
        while (vci->frametime < 0 ) {
91
 
92
                vci->frametime += vc_frame_time;
93
                if (vci->framenum > vc_num_frames1)
94
                        vci->framenum=0;
95
 
96
#if defined(DXX_BUILD_DESCENT_II)
97
                if (objnum&1)
98
                {
99
                        if (-- vci->framenum > vc_num_frames1)
100
                                vci->framenum = vc_num_frames1;
101
                }
102
                else
103
#endif
104
                {
105
                        if (vci->framenum >= vc_num_frames1)
106
                                vci->framenum=0;
107
                        else
108
                                vci->framenum++;
109
                }
110
        }
111
        }
112
 
113
        if (obj->lifeleft <= 0) {
114
                object_create_explosion(vmsegptridx(obj->segnum), obj->pos, F1_0*7/2, VCLIP_POWERUP_DISAPPEARANCE);
115
 
116
                if ( Vclip[VCLIP_POWERUP_DISAPPEARANCE].sound_num > -1 )
117
                        digi_link_sound_to_object(Vclip[VCLIP_POWERUP_DISAPPEARANCE].sound_num, obj, 0, F1_0, sound_stack::allow_stacking);
118
        }
119
}
120
 
121
}
122
 
123
namespace dcx {
124
 
125
void draw_powerup(const d_vclip_array &Vclip, grs_canvas &canvas, const object_base &obj)
126
{
127
        auto &vci = obj.rtype.vclip_info;
128
        draw_object_blob(canvas, obj, Vclip[vci.vclip_num].frames[vci.framenum]);
129
}
130
 
131
}
132
 
133
static void _powerup_basic_nonhud(int redadd, int greenadd, int blueadd, int score)
134
{
135
        PALETTE_FLASH_ADD(redadd,greenadd,blueadd);
136
        add_points_to_score(ConsoleObject->ctype.player_info, score);
137
}
138
 
139
void (powerup_basic)(int redadd, int greenadd, int blueadd, int score, const char *format, ...)
140
{
141
        va_list args;
142
 
143
        va_start(args, format );
144
        HUD_init_message_va(HM_DEFAULT, format, args);
145
        va_end(args);
146
        _powerup_basic_nonhud(redadd, greenadd, blueadd, score);
147
}
148
 
149
void powerup_basic_str(int redadd, int greenadd, int blueadd, int score, const char *str)
150
{
151
        HUD_init_message_literal(HM_DEFAULT, str);
152
        _powerup_basic_nonhud(redadd, greenadd, blueadd, score);
153
}
154
 
155
//#ifndef RELEASE
156
//      Give the megawow powerup!
157
namespace dsx {
158
void do_megawow_powerup(int quantity)
159
{
160
        auto &Objects = LevelUniqueObjectState.Objects;
161
        auto &vmobjptr = Objects.vmptr;
162
        powerup_basic(30, 0, 30, 1, "MEGA-WOWIE-ZOWIE!");
163
        auto &player_info = get_local_plrobj().ctype.player_info;
164
#if defined(DXX_BUILD_DESCENT_I)
165
        player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG);
166
#elif defined(DXX_BUILD_DESCENT_II)
167
        player_info.primary_weapon_flags = (HAS_LASER_FLAG | HAS_VULCAN_FLAG | HAS_SPREADFIRE_FLAG | HAS_PLASMA_FLAG | HAS_FUSION_FLAG) | (HAS_GAUSS_FLAG | HAS_HELIX_FLAG | HAS_PHOENIX_FLAG | HAS_OMEGA_FLAG);
168
#endif
169
        player_info.vulcan_ammo = VULCAN_AMMO_MAX;
170
 
171
        auto &secondary_ammo = player_info.secondary_ammo;
172
        range_for (auto &i, partial_range(secondary_ammo, 3u))
173
                i = quantity;
174
 
175
        range_for (auto &i, partial_range(secondary_ammo, 3u, secondary_ammo.size()))
176
                i = quantity/5;
177
 
178
        player_info.energy = F1_0*200;
179
        get_local_plrobj().shields = F1_0*200;
180
        player_info.powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
181
#if defined(DXX_BUILD_DESCENT_I)
182
        const auto laser_level = MAX_LASER_LEVEL;
183
#elif defined(DXX_BUILD_DESCENT_II)
184
        player_info.Omega_charge = MAX_OMEGA_CHARGE;
185
        if (game_mode_hoard())
186
                player_info.hoard.orbs = player_info.max_hoard_orbs;
187
        const auto laser_level = MAX_SUPER_LASER_LEVEL;
188
#endif
189
        if (Newdemo_state == ND_STATE_RECORDING)
190
                newdemo_record_laser_level(player_info.laser_level, laser_level);
191
        player_info.laser_level = laser_level;
192
}
193
}
194
//#endif
195
 
196
namespace dsx {
197
 
198
static int pick_up_energy(player_info &player_info)
199
{
200
        int     used=0;
201
 
202
        auto &energy = player_info.energy;
203
        if (energy < MAX_ENERGY) {
204
                fix boost;
205
                const auto Difficulty_level = GameUniqueState.Difficulty_level;
206
                boost = 3*F1_0 + 3*F1_0*(NDL - Difficulty_level);
207
#if defined(DXX_BUILD_DESCENT_II)
208
                if (Difficulty_level == 0)
209
                        boost += boost/2;
210
#endif
211
                energy += boost;
212
                if (energy > MAX_ENERGY)
213
                        energy = MAX_ENERGY;
214
                powerup_basic(15, 15, 7, ENERGY_SCORE, "%s %s %d", TXT_ENERGY, TXT_BOOSTED_TO, f2ir(energy));
215
                used=1;
216
        } else
217
                HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_ENERGY);
218
 
219
        return used;
220
}
221
 
222
static int pick_up_primary_or_energy(player_info &player_info, int weapon_index)
223
{
224
        const auto used = pick_up_primary(player_info, weapon_index);
225
        if (used || (Game_mode & GM_MULTI))
226
                return used;
227
        return pick_up_energy(player_info);
228
}
229
 
230
static int pick_up_vulcan_ammo(player_info &player_info)
231
{
232
        int     used=0;
233
        if (pick_up_vulcan_ammo(player_info, VULCAN_AMMO_AMOUNT, false)) {
234
                powerup_basic(7, 14, 21, VULCAN_AMMO_SCORE, "%s!", TXT_VULCAN_AMMO);
235
                used = 1;
236
        } else {
237
                const auto max = PLAYER_MAX_AMMO(player_info.powerup_flags, VULCAN_AMMO_MAX);
238
                HUD_init_message(HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL, "%s %d %s!", TXT_ALREADY_HAVE, vulcan_ammo_scale(max), TXT_VULCAN_ROUNDS);
239
                used = 0;
240
        }
241
        return used;
242
}
243
 
244
static int pick_up_key(const int r, const int g, const int b, player_flags &player_flags, const PLAYER_FLAG key_flag, const char *const key_name, const powerup_type_t id)
245
{
246
        if (player_flags & key_flag)
247
                return 0;
248
        player_flags |= key_flag;
249
        powerup_basic(r, g, b, KEY_SCORE, "%s %s", key_name, TXT_ACCESS_GRANTED);
250
        multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
251
#if defined(DXX_BUILD_DESCENT_II)
252
        auto &BuddyState = LevelUniqueObjectState.BuddyState;
253
        invalidate_escort_goal(BuddyState);
254
#endif
255
        return (Game_mode & GM_MULTI) ? 0 : 1;
256
}
257
 
258
//      returns true if powerup consumed
259
namespace {
260
 
261
#if defined(DXX_BUILD_DESCENT_II)
262
template <int r, int g, int b>
263
struct player_hit_basic_silent_powerup
264
{
265
        const char *const desc_pickup;
266
        player_hit_basic_silent_powerup(const char *const p) :
267
                desc_pickup(p)
268
        {
269
        }
270
        void report() const
271
        {
272
                powerup_basic_str(r, g, b, 0, desc_pickup);
273
        }
274
        template <PLAYER_FLAG player_flag>
275
                void pickup(player_flags &powerup_flags) const
276
                {
277
                        powerup_flags |= player_flag;
278
                        report();
279
                }
280
};
281
 
282
template <int r, int g, int b, powerup_type_t id>
283
struct player_hit_basic_sound_powerup : player_hit_basic_silent_powerup<r, g, b>
284
{
285
        using base_type = player_hit_basic_silent_powerup<r, g, b>;
286
        DXX_INHERIT_CONSTRUCTORS(player_hit_basic_sound_powerup, base_type);
287
        template <PLAYER_FLAG player_flag>
288
                void pickup(player_flags &powerup_flags) const
289
                {
290
                        multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
291
                        base_type::template pickup<player_flag>(powerup_flags);
292
                }
293
};
294
 
295
using player_hit_silent_rb_powerup = player_hit_basic_silent_powerup<15, 0, 15>;
296
 
297
struct player_hit_afterburner_powerup : player_hit_basic_sound_powerup<15, 15, 15, POW_AFTERBURNER>
298
{
299
        using base_type = player_hit_basic_sound_powerup<15, 15, 15, POW_AFTERBURNER>;
300
        DXX_INHERIT_CONSTRUCTORS(player_hit_afterburner_powerup, base_type);
301
        template <PLAYER_FLAG player_flag>
302
                void pickup(player_flags &powerup_flags) const
303
                {
304
                        Afterburner_charge = f1_0;
305
                        base_type::template pickup<player_flag>(powerup_flags);
306
                }
307
};
308
 
309
struct player_hit_headlight_powerup
310
{
311
        /* Template parameter unused, but required for signature
312
         * compatibility with the other player_hit_* structures.
313
         */
314
        template <PLAYER_FLAG>
315
                void pickup(player_flags &powerup_flags) const
316
                {
317
                        process(powerup_flags);
318
                }
319
        void process(player_flags &powerup_flags) const
320
        {
321
                const auto active = PlayerCfg.HeadlightActiveDefault;
322
                powerup_flags |= active
323
                        ? PLAYER_FLAG::HEADLIGHT_PRESENT_AND_ON
324
                        : PLAYER_FLAG::HEADLIGHT;
325
                powerup_basic(15, 0, 15, 0, "HEADLIGHT BOOST! (Headlight is O%s)", active ? "N" : "FF");
326
                multi_digi_play_sample(Powerup_info[POW_HEADLIGHT].hit_sound, F1_0);
327
                if (active && (Game_mode & GM_MULTI))
328
                        multi_send_flags (Player_num);
329
        }
330
};
331
 
332
template <unsigned TEAM>
333
static int player_hit_flag_powerup(player_info &player_info, const char *const desc)
334
{
335
        if (!game_mode_capture_flag())
336
                return 0;
337
        const auto pnum = Player_num;
338
        if (get_team(pnum) == TEAM)
339
        {
340
                player_info.powerup_flags |= PLAYER_FLAGS_FLAG;
341
                powerup_basic_str(15, 0, 15, 0, desc);
342
                multi_send_got_flag(pnum);
343
                return 1;
344
        }
345
        return 0;
346
}
347
#endif
348
 
349
struct player_hit_quadlaser_powerup
350
{
351
        /* Template parameter unused, but required for signature
352
         * compatibility with the other player_hit_* structures.
353
         */
354
        template <PLAYER_FLAG>
355
                void pickup(player_flags &powerup_flags) const
356
                {
357
                        process(powerup_flags);
358
                }
359
        void process(player_flags &powerup_flags) const
360
        {
361
                powerup_flags |= PLAYER_FLAGS_QUAD_LASERS;
362
                powerup_basic(15, 15, 7, QUAD_FIRE_SCORE, "%s!", TXT_QUAD_LASERS);
363
                update_laser_weapon_info();
364
        }
365
};
366
 
367
}
368
 
369
static int player_has_powerup(player_info &player_info, const char *const desc_have)
370
{
371
        HUD_init_message(HM_DEFAULT | HM_REDUNDANT | HM_MAYDUPL, "%s %s!", TXT_ALREADY_HAVE, desc_have);
372
        return (Game_mode & GM_MULTI) ? 0 : pick_up_energy(player_info);
373
}
374
 
375
template <PLAYER_FLAG player_flag, typename F>
376
static int player_hit_powerup(player_info &player_info, const char *const desc_have, const F &&pickup)
377
{
378
        auto &powerup_flags = player_info.powerup_flags;
379
        return (powerup_flags & player_flag)
380
                ? player_has_powerup(player_info, desc_have)
381
                : (pickup.template pickup<player_flag>(powerup_flags), 1);
382
}
383
 
384
int do_powerup(const vmobjptridx_t obj)
385
{
386
        auto &Objects = LevelUniqueObjectState.Objects;
387
        auto &vcobjptr = Objects.vcptr;
388
        auto &vmobjptr = Objects.vmptr;
389
        int used=0;
390
#if defined(DXX_BUILD_DESCENT_I)
391
        int vulcan_ammo_to_add_with_cannon;
392
#endif
393
        int special_used=0;             //for when hitting vulcan cannon gets vulcan ammo
394
 
395
        if (Player_dead_state != player_dead_state::no ||
396
                ConsoleObject->type == OBJ_GHOST ||
397
                get_local_plrobj().shields < 0)
398
                return 0;
399
 
400
        if ((obj->ctype.powerup_info.flags & PF_SPAT_BY_PLAYER) && obj->ctype.powerup_info.creation_time>0 && GameTime64<obj->ctype.powerup_info.creation_time+i2f(2))
401
                return 0;               //not enough time elapsed
402
 
403
        if (Game_mode & GM_MULTI)
404
        {
405
                /*
406
                 * The fact: Collecting a powerup is decided Client-side and due to PING it takes time for other players to know if one collected a powerup actually. This may lead to the case two players collect the same powerup!
407
                 * The solution: Let us check if someone else is closer to a powerup and if so, do not collect it.
408
                 * NOTE: Player positions computed by 'shortpos' and PING can still cause a small margin of error.
409
                 */
410
                vms_vector tvec;
411
                fix mydist = vm_vec_normalized_dir(tvec, obj->pos, ConsoleObject->pos);
412
 
413
                for (unsigned i = 0; i < MAX_PLAYERS; ++i)
414
                {
415
                        if (i == Player_num)
416
                                continue;
417
                        auto &plr = *vcplayerptr(i);
418
                        if (plr.connected != CONNECT_PLAYING)
419
                                continue;
420
                        auto &o = *vcobjptr(plr.objnum);
421
                        if (o.type == OBJ_GHOST)
422
                                continue;
423
                        if (mydist > vm_vec_normalized_dir(tvec, obj->pos, o.pos))
424
                                return 0;
425
                }
426
        }
427
 
428
        auto &plrobj = get_local_plrobj();
429
        auto &player_info = plrobj.ctype.player_info;
430
        auto id = get_powerup_id(obj);
431
        switch (id)
432
        {
433
                case POW_EXTRA_LIFE:
434
                        get_local_player().lives++;
435
                        powerup_basic_str(15, 15, 15, 0, TXT_EXTRA_LIFE);
436
                        used=1;
437
                        break;
438
                case POW_ENERGY:
439
                        used = pick_up_energy(player_info);
440
                        break;
441
                case POW_SHIELD_BOOST:
442
                        {
443
                                auto &shields = plrobj.shields;
444
                        if (shields < MAX_SHIELDS) {
445
                                const auto Difficulty_level = GameUniqueState.Difficulty_level;
446
                                fix boost = 3*F1_0 + 3*F1_0*(NDL - Difficulty_level);
447
#if defined(DXX_BUILD_DESCENT_II)
448
                                if (Difficulty_level == 0)
449
                                        boost += boost/2;
450
#endif
451
                                shields += boost;
452
                                if (shields > MAX_SHIELDS)
453
                                        shields = MAX_SHIELDS;
454
                                powerup_basic(0, 0, 15, SHIELD_SCORE, "%s %s %d", TXT_SHIELD, TXT_BOOSTED_TO, f2ir(shields));
455
                                used=1;
456
                        } else
457
                                HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_SHIELD);
458
                        break;
459
                        }
460
                case POW_LASER:
461
                        if (player_info.laser_level >= MAX_LASER_LEVEL) {
462
#if defined(DXX_BUILD_DESCENT_I)
463
                                player_info.laser_level = MAX_LASER_LEVEL;
464
#endif
465
                                HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, TXT_MAXED_OUT,TXT_LASER);
466
                        } else {
467
                                if (Newdemo_state == ND_STATE_RECORDING)
468
                                        newdemo_record_laser_level(player_info.laser_level, player_info.laser_level + 1);
469
                                ++ player_info.laser_level;
470
                                powerup_basic(10, 0, 10, LASER_SCORE, "%s %s %d",TXT_LASER,TXT_BOOSTED_TO, player_info.laser_level+1);
471
                                pick_up_primary(player_info, primary_weapon_index_t::LASER_INDEX);
472
                                used=1;
473
                        }
474
                        if (!used && !(Game_mode & GM_MULTI) )
475
                                used = pick_up_energy(player_info);
476
                        break;
477
                case POW_MISSILE_1:
478
                        used=pick_up_secondary(player_info, CONCUSSION_INDEX, 1);
479
                        break;
480
                case POW_MISSILE_4:
481
                        used=pick_up_secondary(player_info, CONCUSSION_INDEX, 4);
482
                        break;
483
 
484
                case POW_KEY_BLUE:
485
                        used = pick_up_key(0, 0, 15, player_info.powerup_flags, PLAYER_FLAGS_BLUE_KEY, TXT_BLUE, id);
486
                        break;
487
                case POW_KEY_RED:
488
                        used = pick_up_key(15, 0, 0, player_info.powerup_flags, PLAYER_FLAGS_RED_KEY, TXT_RED, id);
489
                        break;
490
                case POW_KEY_GOLD:
491
                        used = pick_up_key(15, 15, 7, player_info.powerup_flags, PLAYER_FLAGS_GOLD_KEY, TXT_YELLOW, id);
492
                        break;
493
                case POW_QUAD_FIRE:
494
                        used = player_hit_powerup<PLAYER_FLAGS_QUAD_LASERS>(player_info, TXT_QUAD_LASERS, player_hit_quadlaser_powerup());
495
                        break;
496
 
497
                case    POW_VULCAN_WEAPON:
498
#if defined(DXX_BUILD_DESCENT_I)
499
                        if ((used = pick_up_primary(player_info, primary_weapon_index_t::VULCAN_INDEX)) != 0) {
500
                                vulcan_ammo_to_add_with_cannon = obj->ctype.powerup_info.count;
501
                                if (vulcan_ammo_to_add_with_cannon < VULCAN_WEAPON_AMMO_AMOUNT) vulcan_ammo_to_add_with_cannon = VULCAN_WEAPON_AMMO_AMOUNT;
502
                                pick_up_vulcan_ammo(player_info, vulcan_ammo_to_add_with_cannon);
503
                        }
504
 
505
//added/edited 8/3/98 by Victor Rachels to fix vulcan multi bug
506
//check if multi, if so, pick up ammo w/o using, set ammo left. else, normal
507
 
508
//killed 8/27/98 by Victor Rachels to fix vulcan ammo multiplying.  new way
509
// is by spewing the current held ammo when dead.
510
//-killed                        if (!used && (Game_mode & GM_MULTI))
511
//-killed                        {
512
//-killed                         int tempcount;                          
513
//-killed                           tempcount=Players[Player_num].primary_ammo[VULCAN_INDEX];
514
//-killed                            if (pick_up_ammo(CLASS_PRIMARY, VULCAN_INDEX, obj->ctype.powerup_info.count))
515
//-killed                             obj->ctype.powerup_info.count -= Players[Player_num].primary_ammo[VULCAN_INDEX]-tempcount;
516
//-killed                        }
517
//end kill - Victor Rachels
518
 
519
                        if (!used && !(Game_mode & GM_MULTI) )
520
//end addition/edit - Victor Rachels
521
                                used = pick_up_vulcan_ammo(player_info);
522
                        break;
523
#elif defined(DXX_BUILD_DESCENT_II)
524
                case    POW_GAUSS_WEAPON: {
525
                        int ammo = obj->ctype.powerup_info.count;
526
 
527
                        used = pick_up_primary(player_info, (get_powerup_id(obj) == POW_VULCAN_WEAPON)
528
                                ? primary_weapon_index_t::VULCAN_INDEX
529
                                : primary_weapon_index_t::GAUSS_INDEX
530
                        );
531
 
532
                        //didn't get the weapon (because we already have it), but
533
                        //maybe snag some of the ammo.  if single-player, grab all the ammo
534
                        //and remove the powerup.  If multi-player take ammo in excess of
535
                        //the amount in a powerup, and leave the rest.
536
                        if (! used)
537
                                if ((Game_mode & GM_MULTI) )
538
                                        ammo -= VULCAN_AMMO_AMOUNT;     //don't let take all ammo
539
 
540
                        if (ammo > 0) {
541
                                int ammo_used;
542
                                ammo_used = pick_up_vulcan_ammo(player_info, ammo);
543
                                obj->ctype.powerup_info.count -= ammo_used;
544
                                if (!used && ammo_used) {
545
                                        powerup_basic(7, 14, 21, VULCAN_AMMO_SCORE, "%s!", TXT_VULCAN_AMMO);
546
                                        special_used = 1;
547
                                        id = POW_VULCAN_AMMO;           //set new id for making sound at end of this function
548
                                        if (obj->ctype.powerup_info.count == 0)
549
                                                used = 1;               //say used if all ammo taken
550
                                        if (Game_mode & GM_MULTI)
551
                                                multi_send_vulcan_weapon_ammo_adjust(obj); // let other players know how much ammo we took.
552
                                }
553
                        }
554
 
555
                        break;
556
                }
557
#endif
558
 
559
                case    POW_SPREADFIRE_WEAPON:
560
                        used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::SPREADFIRE_INDEX);
561
                        break;
562
                case    POW_PLASMA_WEAPON:
563
                        used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::PLASMA_INDEX);
564
                        break;
565
                case    POW_FUSION_WEAPON:
566
                        used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::FUSION_INDEX);
567
                        break;
568
 
569
#if defined(DXX_BUILD_DESCENT_II)
570
                case    POW_HELIX_WEAPON:
571
                        used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::HELIX_INDEX);
572
                        break;
573
 
574
                case    POW_PHOENIX_WEAPON:
575
                        used = pick_up_primary_or_energy(player_info, primary_weapon_index_t::PHOENIX_INDEX);
576
                        break;
577
 
578
                case    POW_OMEGA_WEAPON:
579
                        used = pick_up_primary(player_info, primary_weapon_index_t::OMEGA_INDEX);
580
                        if (used)
581
                                player_info.Omega_charge = obj->ctype.powerup_info.count;
582
                        if (!used && !(Game_mode & GM_MULTI) )
583
                                used = pick_up_energy(player_info);
584
                        break;
585
#endif
586
 
587
                case    POW_PROXIMITY_WEAPON:
588
                        used=pick_up_secondary(player_info, PROXIMITY_INDEX, 4);
589
                        break;
590
                case    POW_SMARTBOMB_WEAPON:
591
                        used=pick_up_secondary(player_info, SMART_INDEX, 1);
592
                        break;
593
                case    POW_MEGA_WEAPON:
594
                        used=pick_up_secondary(player_info, MEGA_INDEX, 1);
595
                        break;
596
#if defined(DXX_BUILD_DESCENT_II)
597
                case    POW_SMISSILE1_1:
598
                        used=pick_up_secondary(player_info, SMISSILE1_INDEX, 1);
599
                        break;
600
                case    POW_SMISSILE1_4:
601
                        used=pick_up_secondary(player_info, SMISSILE1_INDEX, 4);
602
                        break;
603
                case    POW_GUIDED_MISSILE_1:
604
                        used=pick_up_secondary(player_info, GUIDED_INDEX, 1);
605
                        break;
606
                case    POW_GUIDED_MISSILE_4:
607
                        used=pick_up_secondary(player_info, GUIDED_INDEX, 4);
608
                        break;
609
                case    POW_SMART_MINE:
610
                        used=pick_up_secondary(player_info, SMART_MINE_INDEX, 4);
611
                        break;
612
                case    POW_MERCURY_MISSILE_1:
613
                        used=pick_up_secondary(player_info, SMISSILE4_INDEX, 1);
614
                        break;
615
                case    POW_MERCURY_MISSILE_4:
616
                        used=pick_up_secondary(player_info, SMISSILE4_INDEX, 4);
617
                        break;
618
                case    POW_EARTHSHAKER_MISSILE:
619
                        used=pick_up_secondary(player_info, SMISSILE5_INDEX, 1);
620
                        break;
621
#endif
622
                case    POW_VULCAN_AMMO:
623
                        used = pick_up_vulcan_ammo(player_info);
624
#if defined(DXX_BUILD_DESCENT_I)
625
                        if (!used && !(Game_mode & GM_MULTI) )
626
                                used = pick_up_vulcan_ammo(player_info);
627
#endif
628
                        break;
629
                case    POW_HOMING_AMMO_1:
630
                        used=pick_up_secondary(player_info, HOMING_INDEX, 1);
631
                        break;
632
                case    POW_HOMING_AMMO_4:
633
                        used=pick_up_secondary(player_info, HOMING_INDEX, 4);
634
                        break;
635
                case    POW_CLOAK:
636
                        if (player_info.powerup_flags & PLAYER_FLAGS_CLOAKED) {
637
                                HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "%s %s!",TXT_ALREADY_ARE,TXT_CLOAKED);
638
                                break;
639
                        } else {
640
                                player_info.cloak_time = GameTime64;    //      Not! changed by awareness events (like player fires laser).
641
                                player_info.powerup_flags |= PLAYER_FLAGS_CLOAKED;
642
                                ai_do_cloak_stuff();
643
                                if (Game_mode & GM_MULTI)
644
                                        multi_send_cloak();
645
                                powerup_basic(-10,-10,-10, CLOAK_SCORE, "%s!",TXT_CLOAKING_DEVICE);
646
                                used = 1;
647
                                break;
648
                        }
649
                case    POW_INVULNERABILITY:
650
                        {
651
                                auto &pl_flags = player_info.powerup_flags;
652
                                if (pl_flags & PLAYER_FLAGS_INVULNERABLE) {
653
                                        if (!player_info.FakingInvul)
654
                                        {
655
                                HUD_init_message(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "%s %s!",TXT_ALREADY_ARE,TXT_INVULNERABLE);
656
                                break;
657
                                        }
658
                        }
659
                                player_info.FakingInvul = 0;
660
                                pl_flags |= PLAYER_FLAGS_INVULNERABLE;
661
                                player_info.invulnerable_time = GameTime64;
662
                                powerup_basic(7, 14, 21, INVULNERABILITY_SCORE, "%s!",TXT_INVULNERABILITY);
663
                                used = 1;
664
                                break;
665
                        }
666
        #ifndef RELEASE
667
                case    POW_MEGAWOW:
668
                        do_megawow_powerup(50);
669
                        used = 1;
670
                        break;
671
        #endif
672
 
673
#if defined(DXX_BUILD_DESCENT_II)
674
                case POW_FULL_MAP:
675
                        used = player_hit_powerup<PLAYER_FLAGS_MAP_ALL>(player_info, "the FULL MAP", player_hit_silent_rb_powerup("FULL MAP!"));
676
                        break;
677
 
678
                case POW_CONVERTER:
679
                        used = player_hit_powerup<PLAYER_FLAGS_CONVERTER>(player_info, "the Converter", player_hit_silent_rb_powerup("Energy -> shield converter!"));
680
                        break;
681
 
682
                case POW_SUPER_LASER:
683
                        if (player_info.laser_level >= MAX_SUPER_LASER_LEVEL)
684
                        {
685
                                player_info.laser_level = MAX_SUPER_LASER_LEVEL;
686
                                HUD_init_message_literal(HM_DEFAULT|HM_REDUNDANT|HM_MAYDUPL, "SUPER LASER MAXED OUT!");
687
                        } else {
688
                                const auto old_level = player_info.laser_level;
689
 
690
                                if (player_info.laser_level <= MAX_LASER_LEVEL)
691
                                        player_info.laser_level = MAX_LASER_LEVEL;
692
                                ++ player_info.laser_level;
693
                                if (Newdemo_state == ND_STATE_RECORDING)
694
                                        newdemo_record_laser_level(old_level, player_info.laser_level);
695
                                powerup_basic(10, 0, 10, LASER_SCORE, "Super Boost to Laser level %d", player_info.laser_level + 1);
696
                                if (player_info.Primary_weapon != primary_weapon_index_t::LASER_INDEX)
697
                                        check_to_use_primary_super_laser(player_info);
698
                                used=1;
699
                        }
700
                        if (!used && !(Game_mode & GM_MULTI) )
701
                                used = pick_up_energy(player_info);
702
                        break;
703
 
704
                case POW_AMMO_RACK:
705
                        used = player_hit_powerup<PLAYER_FLAGS_AMMO_RACK>(player_info, "the Ammo rack", player_hit_basic_sound_powerup<15, 0, 15, POW_AMMO_RACK>("AMMO RACK!"));
706
                        break;
707
 
708
                case POW_AFTERBURNER:
709
                        used = player_hit_powerup<PLAYER_FLAGS_AFTERBURNER>(player_info, "the Afterburner", player_hit_afterburner_powerup("AFTERBURNER!"));
710
                        break;
711
 
712
                case POW_HEADLIGHT:
713
                        used = player_hit_powerup<PLAYER_FLAGS_HEADLIGHT>(player_info, "the Headlight boost", player_hit_headlight_powerup());
714
                        break;
715
 
716
                case POW_FLAG_BLUE:
717
                        used = player_hit_flag_powerup<TEAM_RED>(player_info, "BLUE FLAG!");
718
                   break;
719
 
720
                case POW_HOARD_ORB:
721
                        if (game_mode_hoard())                 
722
                        {
723
                                auto &proximity = player_info.hoard.orbs;
724
                                if (proximity < player_info.max_hoard_orbs)
725
                                {
726
                                        ++ proximity;
727
                                        powerup_basic(15, 0, 15, 0, "Orb!!!");
728
                                        player_info.powerup_flags |= PLAYER_FLAGS_FLAG;
729
                                        used=1;
730
                                        multi_send_got_orb (Player_num);
731
                                }
732
                        }
733
                  break;       
734
 
735
                case POW_FLAG_RED:
736
                        used = player_hit_flag_powerup<TEAM_BLUE>(player_info, "RED FLAG!");
737
                   break;
738
 
739
//              case POW_HOARD_ORB:
740
 
741
#endif
742
                default:
743
                        break;
744
                }
745
 
746
//always say used, until physics problem (getting stuck on unused powerup)
747
//is solved.  Note also the break statements above that are commented out
748
//!!    used=1;
749
 
750
        if ((used || special_used) && Powerup_info[id].hit_sound  > -1 ) {
751
                multi_digi_play_sample(Powerup_info[id].hit_sound, F1_0);
752
                detect_escort_goal_accomplished(obj);
753
        }
754
 
755
        return used;
756
 
757
}
758
}
759
 
760
DEFINE_SERIAL_UDT_TO_MESSAGE(powerup_type_info, pti, (pti.vclip_num, pti.hit_sound, pti.size, pti.light));
761
ASSERT_SERIAL_UDT_MESSAGE_SIZE(powerup_type_info, 16);
762
 
763
namespace dcx {
764
 
765
void powerup_type_info_read(PHYSFS_File *fp, powerup_type_info &pti)
766
{
767
        PHYSFSX_serialize_read(fp, pti);
768
}
769
 
770
void powerup_type_info_write(PHYSFS_File *fp, const powerup_type_info &pti)
771
{
772
        PHYSFSX_serialize_write(fp, pti);
773
}
774
 
775
}