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 | } |