1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on the
6  * source.
7  *
8 */
9 
10 
11 
12 
13 #include "globalincs/pstypes.h"
14 #include "globalincs/globals.h"
15 #include "globalincs/linklist.h"
16 #include "io/key.h"
17 #include "io/joy.h"
18 #include "io/timer.h"
19 #include "ship/ship.h"
20 #include "playerman/player.h"
21 #include "weapon/weapon.h"
22 #include "hud/hud.h"
23 #include "gamesequence/gamesequence.h"
24 #include "mission/missiongoals.h"
25 #include "hud/hudets.h"
26 #include "gamesnd/gamesnd.h"
27 #include "hud/hudsquadmsg.h"
28 #include "gamesnd/eventmusic.h"
29 #include "freespace2/freespace.h"
30 #include "mission/missionhotkey.h"
31 #include "hud/hudescort.h"
32 #include "hud/hudshield.h"
33 #include "io/keycontrol.h"
34 #include "ship/shiphit.h"
35 #include "ship/shipfx.h"
36 #include "mission/missionlog.h"
37 #include "hud/hudtargetbox.h"
38 #include "popup/popup.h"
39 #include "object/objcollide.h"
40 #include "object/object.h"
41 #include "hud/hudconfig.h"
42 #include "hud/hudmessage.h"
43 #include "network/multi_pmsg.h"
44 #include "starfield/supernova.h"
45 #include "mission/missionmessage.h"
46 #include "menuui/mainhallmenu.h"
47 #include "missionui/missionpause.h"
48 #include "hud/hudgauges.h"
49 #include "freespace2/freespace.h"	//For time compression stuff
50 #include "species_defs/species_defs.h"
51 #include "asteroid/asteroid.h"
52 #include "iff_defs/iff_defs.h"
53 #include "network/multi.h"
54 #include "network/multiutil.h"
55 #include "network/multimsgs.h"
56 #include "network/multi_pause.h"
57 #include "network/multi_observer.h"
58 #include "network/multi_endgame.h"
59 #include "autopilot/autopilot.h"
60 #include "cmdline/cmdline.h"
61 #include "object/objectshield.h"
62 
63 #define MAX_NUM_SLOTS 6
64 
65 struct ftable
66 {
67 int count;
68 int table[ MAX_NUM_SLOTS ];
69 };
70 
71 #define MAX_SLOT_COUNT 25
72 
73 class factor_table
74 	{
75 	public:
76 		factor_table();
~factor_table()77 		~factor_table(){ delete[] table; };
78 		int getNextSlots( int slots_on_ship, int cur_slots );
79 
80 	private:
81 		ftable * table;
82 	};
83 
84 factor_table ftables;
85 
isAPrimeFactor(int factor,int product)86 static bool isAPrimeFactor( int factor, int product )
87 {
88 return ( (float)product / (float)factor ) == (product / factor);
89 }
90 
factor_table()91 factor_table::factor_table()
92 {
93 table = new ftable[ MAX_NUM_SLOTS ];
94 
95 memset( table, 0x00, sizeof( ftable ) * MAX_NUM_SLOTS );
96 for( int i = 0 ; i < MAX_NUM_SLOTS; ++i )
97 	{
98 	table[ i ].count = 0;
99 	for( int j = 1; j <= i; ++j )
100 		{
101 		if( isAPrimeFactor( j, i ) )
102 			{
103 			table[ i ].table[ table[ i ].count ] = j;
104 			table[ i ].count ++;
105 			}
106 		}
107 	}
108 }
109 
getNextSlots(int slots_on_ship,int cur_slots)110 int factor_table::getNextSlots(int slots_on_ship, int cur_slots)
111 {
112 Assert( slots_on_ship <= MAX_NUM_SLOTS );
113 Assert( slots_on_ship >= 0 );
114 
115 for( int i = 0; i < table[ slots_on_ship ].count; ++i )
116 	{
117 	if( table[ slots_on_ship ].table[ i ] == cur_slots )
118 		{
119 		if( table[ slots_on_ship ].count == i + 1 )
120 			{
121 			//Overflow back to 1
122 			return 1;
123 			}
124 		else
125 			{
126 			//Next block in the table
127 			return table[ slots_on_ship ].table[ i + 1 ];
128 			}
129 		}
130 	}
131 //Did not find cur_slots, try and get back on track
132 
133 Assert( 0 );
134 return 1;
135 }
136 
137 // --------------------------------------------------------------
138 // Global to file
139 // --------------------------------------------------------------
140 
141 // time compression/dilation values - Goober5000
142 // (Volition sez "can't compress below 0.25"... not sure if
143 // this is arbitrary or dictated by code)
144 #define MAX_TIME_MULTIPLIER		64
145 #define MAX_TIME_DIVIDER		4
146 
147 #define CHEAT_BUFFER_LEN	17
148 char CheatBuffer[CHEAT_BUFFER_LEN+1];
149 
150 enum cheatCode {
151 	CHEAT_CODE_NONE = 0,
152 	CHEAT_CODE_FREESPACE,
153 	CHEAT_CODE_FISH,
154 	CHEAT_CODE_HEADZ,
155 	CHEAT_CODE_TOOLED,
156 	CHEAT_CODE_PIRATE,
157 	CHEAT_CODE_SKIP
158 };
159 
160 struct Cheat {
161 	cheatCode code;
162 	char* data;
163 };
164 
165 static struct Cheat cheatsTable[] = {
166   { CHEAT_CODE_FREESPACE, "www.freespace2.com" },
167   { CHEAT_CODE_FISH,      "vasudanswuvfishes" },
168   { CHEAT_CODE_HEADZ,     "humanheadsinside." },
169   { CHEAT_CODE_TOOLED,    "tooledworkedowned" },
170   { CHEAT_CODE_PIRATE,    "arrrrwalktheplank" },
171   { CHEAT_CODE_SKIP,      "skipmemymissionyo" }
172 };
173 
174 #define CHEATS_TABLE_LEN	6
175 
176 int Tool_enabled = 0;
177 bool Perspective_locked=false;
178 bool quit_mission_popup_shown = false;
179 
180 extern int AI_watch_object;
181 extern int Countermeasures_enabled;
182 
183 extern float do_subobj_hit_stuff(object *ship_obj, object *other_obj, vec3d *hitpos, int submodel_num, float damage, bool *hull_should_apply_armor);
184 
185 extern void mission_goal_mark_all_true( int type );
186 
187 int Normal_key_set[] = {
188 	TARGET_NEXT,
189 	TARGET_PREV,
190 	TARGET_NEXT_CLOSEST_HOSTILE,
191 	TARGET_PREV_CLOSEST_HOSTILE,
192 	TARGET_NEXT_CLOSEST_FRIENDLY,
193 	TARGET_PREV_CLOSEST_FRIENDLY,
194 	TARGET_TARGETS_TARGET,
195 	TARGET_SHIP_IN_RETICLE,
196 	TARGET_LAST_TRANMISSION_SENDER,
197 	TARGET_CLOSEST_SHIP_ATTACKING_TARGET,
198 	TARGET_CLOSEST_SHIP_ATTACKING_SELF,
199 	STOP_TARGETING_SHIP,
200 	TOGGLE_AUTO_TARGETING,
201 	TARGET_SUBOBJECT_IN_RETICLE,
202 	TARGET_PREV_SUBOBJECT,
203 	TARGET_NEXT_SUBOBJECT,
204 	STOP_TARGETING_SUBSYSTEM,
205 
206 	TARGET_NEXT_UNINSPECTED_CARGO,
207 	TARGET_PREV_UNINSPECTED_CARGO,
208 	TARGET_NEWEST_SHIP,
209 	TARGET_NEXT_LIVE_TURRET,
210 	TARGET_PREV_LIVE_TURRET,
211 	TARGET_NEXT_BOMB,
212 	TARGET_PREV_BOMB,
213 
214 	ATTACK_MESSAGE,
215 	DISARM_MESSAGE,
216 	DISABLE_MESSAGE,
217 	ATTACK_SUBSYSTEM_MESSAGE,
218 	CAPTURE_MESSAGE,
219 	ENGAGE_MESSAGE,
220 	FORM_MESSAGE,
221 	PROTECT_MESSAGE,
222 	COVER_MESSAGE,
223 	WARP_MESSAGE,
224 	REARM_MESSAGE,
225 	IGNORE_MESSAGE,
226 	SQUADMSG_MENU,
227 
228 	VIEW_CHASE,
229 	VIEW_OTHER_SHIP,
230 	VIEW_TOPDOWN,
231 	VIEW_TRACK_TARGET,
232 
233 	SHOW_GOALS,
234 	END_MISSION,
235 
236 	ADD_REMOVE_ESCORT,
237 	ESCORT_CLEAR,
238 	TARGET_NEXT_ESCORT_SHIP,
239 
240 	XFER_SHIELD,
241 	XFER_LASER,
242 	INCREASE_SHIELD,
243 	INCREASE_WEAPON,
244 	INCREASE_ENGINE,
245 	DECREASE_SHIELD,
246 	DECREASE_WEAPON,
247 	DECREASE_ENGINE,
248 	ETS_EQUALIZE,
249 	SHIELD_EQUALIZE,
250 	SHIELD_XFER_TOP,
251 	SHIELD_XFER_BOTTOM,
252 	SHIELD_XFER_RIGHT,
253 	SHIELD_XFER_LEFT,
254 
255 	CYCLE_NEXT_PRIMARY,
256 	CYCLE_PREV_PRIMARY,
257 	CYCLE_SECONDARY,
258 	CYCLE_NUM_MISSLES,
259 	RADAR_RANGE_CYCLE,
260 
261 	MATCH_TARGET_SPEED,
262 	TOGGLE_AUTO_MATCH_TARGET_SPEED,
263 
264 	VIEW_EXTERNAL,
265 	VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK,
266 	LAUNCH_COUNTERMEASURE,
267 	ONE_THIRD_THROTTLE,
268 	TWO_THIRDS_THROTTLE,
269 	PLUS_5_PERCENT_THROTTLE,
270 	MINUS_5_PERCENT_THROTTLE,
271 	ZERO_THROTTLE,
272 	MAX_THROTTLE,
273 
274 	TARGET_CLOSEST_REPAIR_SHIP,
275 
276 	MULTI_MESSAGE_ALL,
277 	MULTI_MESSAGE_FRIENDLY,
278 	MULTI_MESSAGE_HOSTILE,
279 	MULTI_MESSAGE_TARGET,
280 	MULTI_OBSERVER_ZOOM_TO,
281 
282 	TIME_SPEED_UP,
283 	TIME_SLOW_DOWN,
284 
285 	TOGGLE_HUD_CONTRAST,
286 
287 	MULTI_TOGGLE_NETINFO,
288 	MULTI_SELF_DESTRUCT,
289 
290 	TOGGLE_HUD,
291 
292 	HUD_TARGETBOX_TOGGLE_WIREFRAME,
293 	AUTO_PILOT_TOGGLE,
294 	NAV_CYCLE,
295 
296 	TOGGLE_GLIDING,
297 	CYCLE_PRIMARY_WEAPON_SEQUENCE
298 };
299 
300 int Dead_key_set[] = {
301 	TARGET_NEXT,
302 	TARGET_PREV,
303 	TARGET_NEXT_CLOSEST_HOSTILE,
304 	TARGET_PREV_CLOSEST_HOSTILE,
305 	TARGET_NEXT_CLOSEST_FRIENDLY,
306 	TARGET_PREV_CLOSEST_FRIENDLY,
307 	TARGET_TARGETS_TARGET,
308 	TARGET_CLOSEST_SHIP_ATTACKING_TARGET,
309 	STOP_TARGETING_SHIP,
310 	TOGGLE_AUTO_TARGETING,
311 	TARGET_SUBOBJECT_IN_RETICLE,
312 	TARGET_PREV_SUBOBJECT,
313 	TARGET_NEXT_SUBOBJECT,
314 	STOP_TARGETING_SUBSYSTEM,
315 	TARGET_NEWEST_SHIP,
316 	TARGET_NEXT_LIVE_TURRET,
317 	TARGET_PREV_LIVE_TURRET,
318 	TARGET_NEXT_BOMB,
319 	TARGET_PREV_BOMB,
320 
321 	VIEW_CHASE,
322 	VIEW_OTHER_SHIP,
323 	VIEW_TOPDOWN,
324 
325 	SHOW_GOALS,
326 
327 	ADD_REMOVE_ESCORT,
328 	ESCORT_CLEAR,
329 	TARGET_NEXT_ESCORT_SHIP,
330 	TARGET_CLOSEST_REPAIR_SHIP,
331 
332 	MULTI_MESSAGE_ALL,
333 	MULTI_MESSAGE_FRIENDLY,
334 	MULTI_MESSAGE_HOSTILE,
335 	MULTI_MESSAGE_TARGET,
336 	MULTI_OBSERVER_ZOOM_TO,
337 
338 	TIME_SPEED_UP,
339 	TIME_SLOW_DOWN
340 };
341 
342 int Critical_key_set[] = {
343 	CYCLE_NEXT_PRIMARY,
344 	CYCLE_PREV_PRIMARY,
345 	CYCLE_SECONDARY,
346 	CYCLE_NUM_MISSLES,
347 	INCREASE_WEAPON,
348 	DECREASE_WEAPON,
349 	INCREASE_SHIELD,
350 	DECREASE_SHIELD,
351 	INCREASE_ENGINE,
352 	DECREASE_ENGINE,
353 	ETS_EQUALIZE,
354 	SHIELD_EQUALIZE,
355 	SHIELD_XFER_TOP,
356 	SHIELD_XFER_BOTTOM,
357 	SHIELD_XFER_LEFT,
358 	SHIELD_XFER_RIGHT,
359 	XFER_SHIELD,
360 	XFER_LASER,
361 };
362 
363 int Non_critical_key_set[] = {
364 	MATCH_TARGET_SPEED,
365 	TOGGLE_AUTO_MATCH_TARGET_SPEED,
366 	TARGET_NEXT,
367 	TARGET_PREV,
368 	TARGET_NEXT_CLOSEST_HOSTILE,
369 	TARGET_PREV_CLOSEST_HOSTILE,
370 	TOGGLE_AUTO_TARGETING,
371 	TARGET_NEXT_CLOSEST_FRIENDLY,
372 	TARGET_PREV_CLOSEST_FRIENDLY,
373 	TARGET_SHIP_IN_RETICLE,
374 	TARGET_LAST_TRANMISSION_SENDER,
375 	TARGET_CLOSEST_REPAIR_SHIP,
376 	TARGET_CLOSEST_SHIP_ATTACKING_TARGET,
377 	STOP_TARGETING_SHIP,
378 	TARGET_CLOSEST_SHIP_ATTACKING_SELF,
379 	TARGET_TARGETS_TARGET,
380 	TARGET_SUBOBJECT_IN_RETICLE,
381 	TARGET_PREV_SUBOBJECT,
382 	TARGET_NEXT_SUBOBJECT,
383 	STOP_TARGETING_SUBSYSTEM,
384 	TARGET_NEXT_BOMB,
385 	TARGET_PREV_BOMB,
386 	TARGET_NEXT_UNINSPECTED_CARGO,
387 	TARGET_PREV_UNINSPECTED_CARGO,
388 	TARGET_NEWEST_SHIP,
389 	TARGET_NEXT_LIVE_TURRET,
390 	TARGET_PREV_LIVE_TURRET,
391 	ATTACK_MESSAGE,
392 	DISARM_MESSAGE,
393 	DISABLE_MESSAGE,
394 	ATTACK_SUBSYSTEM_MESSAGE,
395 	CAPTURE_MESSAGE,
396 	ENGAGE_MESSAGE,
397 	FORM_MESSAGE,
398 	PROTECT_MESSAGE,
399 	COVER_MESSAGE,
400 	WARP_MESSAGE,
401 	IGNORE_MESSAGE,
402 	REARM_MESSAGE,
403 	VIEW_CHASE,
404 	VIEW_EXTERNAL,
405 	VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK,
406 	VIEW_OTHER_SHIP,
407 	VIEW_TOPDOWN,
408 	VIEW_TRACK_TARGET,
409 	RADAR_RANGE_CYCLE,
410 	SQUADMSG_MENU,
411 	SHOW_GOALS,
412 	END_MISSION,
413 	ADD_REMOVE_ESCORT,
414 	ESCORT_CLEAR,
415 	TARGET_NEXT_ESCORT_SHIP,
416 	MULTI_MESSAGE_ALL,
417 	MULTI_MESSAGE_FRIENDLY,
418 	MULTI_MESSAGE_HOSTILE,
419 	MULTI_MESSAGE_TARGET,
420 	MULTI_OBSERVER_ZOOM_TO,
421 	TOGGLE_HUD_CONTRAST,
422 
423 	MULTI_TOGGLE_NETINFO,
424 	MULTI_SELF_DESTRUCT,
425 
426 	TOGGLE_HUD,
427 
428 	HUD_TARGETBOX_TOGGLE_WIREFRAME,
429 	AUTO_PILOT_TOGGLE,
430 	NAV_CYCLE,
431 	TOGGLE_GLIDING,
432 	CYCLE_PRIMARY_WEAPON_SEQUENCE
433 };
434 
435 int Ignored_keys[CCFG_MAX];
436 
437 // set sizes of the key sets automatically
438 int Normal_key_set_size = sizeof(Normal_key_set) / sizeof(int);
439 int Dead_key_set_size = sizeof(Dead_key_set) / sizeof(int);
440 int Critical_key_set_size = sizeof(Critical_key_set) / sizeof(int);
441 int Non_critical_key_set_size = sizeof(Non_critical_key_set) / sizeof(int);
442 
443 // --------------------------------------------------------------
444 // routine to process keys used only for debugging
445 // --------------------------------------------------------------
446 
debug_cycle_player_ship(int delta)447 void debug_cycle_player_ship(int delta)
448 {
449 	if ( Player_obj == NULL )
450 		return;
451 
452 	int si_index = Ships[Player_obj->instance].ship_info_index;
453 	int sanity = 0;
454 	ship_info	*sip;
455 	while ( TRUE ) {
456 		si_index += delta;
457 		if ( si_index > Num_ship_classes ){
458 			si_index = 0;
459 		}
460 		if ( si_index < 0 ){
461 			si_index = Num_ship_classes - 1;
462 		}
463 		sip = &Ship_info[si_index];
464 		if ( sip->flags & SIF_PLAYER_SHIP ){
465 			break;
466 		}
467 
468 		// just in case
469 		sanity++;
470 		if ( sanity > Num_ship_classes ){
471 			break;
472 		}
473 	}
474 
475 	change_ship_type(Player_obj->instance, si_index);
476 	HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player ship changed to %s", 0), Ship_info[si_index].name);
477 }
478 
479 /**
480  * Cycle targeted ship to next ship in that species
481  * @param delta Increment
482  */
debug_cycle_targeted_ship(int delta)483 void debug_cycle_targeted_ship(int delta)
484 {
485 	object		*objp;
486 	ship_info	*sip;
487 	int			si_index, species;
488 	char			name[NAME_LENGTH];
489 
490 	if ( Player_ai->target_objnum == -1 )
491 		return;
492 
493 	objp = &Objects[Player_ai->target_objnum];
494 	if ( objp->type != OBJ_SHIP )
495 		return;
496 
497 	si_index = Ships[objp->instance].ship_info_index;
498 	Assert(si_index != -1 );
499 	species = Ship_info[si_index].species;
500 
501 	int sanity = 0;
502 
503 	while ( TRUE ) {
504 		si_index += delta;
505 		if ( si_index > Num_ship_classes )
506 			si_index = 0;
507 		if ( si_index < 0 )
508 			si_index = Num_ship_classes-1;
509 
510 
511 		sip = &Ship_info[si_index];
512 
513 		// if it has test in the name, jump over it
514 		strcpy_s(name, sip->name);
515 		_strlwr(name);
516 		if ( strstr(name,NOX("test")) != NULL )
517 			continue;
518 
519 		if ( sip->species == species && (sip->flags & (SIF_FIGHTER | SIF_BOMBER | SIF_TRANSPORT) ) )
520 			break;
521 
522 		// just in case
523 		sanity++;
524 		if ( sanity > Num_ship_classes )
525 			break;
526 	}
527 
528 	change_ship_type(objp->instance, si_index);
529 	HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Changed player target to %s", 1), Ship_info[si_index].name);
530 }
531 
debug_max_secondary_weapons(object * objp)532 void debug_max_secondary_weapons(object *objp)
533 {
534 	int index;
535 	ship *shipp = &Ships[objp->instance];
536 	ship_info *sip = &Ship_info[shipp->ship_info_index];
537 	ship_weapon *swp = &shipp->weapons;
538 
539 	for ( index = 0; index < MAX_SHIP_SECONDARY_BANKS; index++ )
540 	{
541 		swp->secondary_bank_ammo[index] = sip->secondary_bank_ammo_capacity[index];
542 	}
543 }
544 
debug_max_primary_weapons(object * objp)545 void debug_max_primary_weapons(object *objp)	// Goober5000
546 {
547 	Assert(objp);	// Goober5000
548 
549 	int index;
550 	ship *shipp = &Ships[objp->instance];
551 	ship_info *sip = &Ship_info[shipp->ship_info_index];
552 	ship_weapon *swp = &shipp->weapons;
553 	weapon_info *wip;
554 
555 	if (sip->flags & SIF_BALLISTIC_PRIMARIES)
556 	{
557 		for ( index = 0; index < MAX_SHIP_PRIMARY_BANKS; index++ )
558 		{
559 			wip = &Weapon_info[swp->primary_bank_weapons[index]];
560 			if (wip->wi_flags2 & WIF2_BALLISTIC)
561 			{
562 				float capacity, size;
563 				capacity = (float) sip->primary_bank_ammo_capacity[index];
564 				size = (float) wip->cargo_size;
565 				swp->primary_bank_ammo[index] = fl2i((capacity / size)+0.5f);
566 			}
567 		}
568 	}
569 }
570 
debug_change_song(int delta)571 void debug_change_song(int delta)
572 {
573 	char buf[256];
574 	if ( event_music_next_soundtrack(delta) != -1 ) {
575 		event_music_get_soundtrack_name(buf);
576 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Soundtrack changed to: %s", 2), buf);
577 
578 	} else {
579 		HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music is not playing", 3));
580 	}
581 }
582 
583 extern void hud_target_asteroid();
584 extern int Framerate_delay;
585 
586 extern void snd_stop_any_sound();
587 
588 extern vec3d Eye_position;
589 extern matrix Eye_matrix;
590 extern void g3_set_view_matrix(vec3d *view_pos,matrix *view_matrix,float zoom);
591 
592 extern int Show_cpu;
593 
get_prev_weapon_looped(int current_weapon,int subtype)594 int get_prev_weapon_looped(int current_weapon, int subtype)
595 {
596 	int i, new_index;
597 
598 	for (i = 1; i < Num_weapon_types; i++)
599 	{
600 		new_index = (Num_weapon_types + current_weapon - i) % Num_weapon_types;
601 
602 		if(Weapon_info[new_index].subtype == subtype)
603 		{
604 			return new_index;
605 		}
606 	}
607 
608 	return current_weapon;
609 }
610 
get_next_weapon_looped(int current_weapon,int subtype)611 int get_next_weapon_looped(int current_weapon, int subtype)
612 {
613 	int i, new_index;
614 
615 	for (i = 1; i < Num_weapon_types; i++)
616 	{
617 		new_index = (current_weapon + i) % Num_weapon_types;
618 
619 		if(Weapon_info[new_index].subtype == subtype)
620 		{
621 			return new_index;
622 		}
623 	}
624 
625 	return current_weapon;
626 }
627 
process_debug_keys(int k)628 void process_debug_keys(int k)
629 {
630 	// Kazan -- NO CHEATS IN MULTI
631 	if (Game_mode & GM_MULTIPLAYER)
632 	{
633 		Cheats_enabled = 0;
634 		return;
635 	}
636 
637 	switch (k) {
638 		case KEY_DEBUGGED + KEY_Q:
639 			Snapshot_all_events = true;
640 			break;
641 
642 		case KEY_DEBUGGED + KEY_H:
643 			hud_target_toggle_hidden_from_sensors();
644 			break;
645 
646 		case KEY_DEBUGGED + KEY_F:
647 			extern int wacky_scheme;
648 			if(wacky_scheme == 3){
649 				wacky_scheme = 0;
650 			} else {
651 				wacky_scheme++;
652 			}
653 			break;
654 
655 		case KEY_DEBUGGED + KEY_ALTED + KEY_F:
656 			Framerate_delay += 10;
657 			HUD_printf(XSTR( "Framerate delay increased to %i milliseconds per frame.", 4), Framerate_delay);
658 			break;
659 
660 		case KEY_DEBUGGED + KEY_ALTED + KEY_SHIFTED + KEY_F:
661 			Framerate_delay -= 10;
662 			if (Framerate_delay < 0)
663 				Framerate_delay = 0;
664 
665 			HUD_printf(XSTR( "Framerate delay decreased to %i milliseconds per frame.", 5), Framerate_delay);
666 			break;
667 
668 		case KEY_DEBUGGED + KEY_X:
669 		case KEY_DEBUGGED1 + KEY_X:
670 			HUD_printf("Cloaking has been disabled, thank you for playing fs2_open, %s", Player->callsign);
671 			break;
672 
673 		case KEY_DEBUGGED + KEY_C:
674 		case KEY_DEBUGGED1 + KEY_C:
675 			if(Player_obj->flags & OF_COLLIDES){
676 				obj_set_flags(Player_obj, Player_obj->flags & ~(OF_COLLIDES));
677 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player no longer collides");
678 			} else {
679 				obj_set_flags(Player_obj, Player_obj->flags | OF_COLLIDES);
680 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Player collides");
681 			}
682 			break;
683 
684 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_C:
685 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_C:
686 			Countermeasures_enabled = !Countermeasures_enabled;
687 			HUD_printf(XSTR( "Countermeasure firing: %s", 6), Countermeasures_enabled ? XSTR( "ENABLED", 7) : XSTR( "DISABLED", 8));
688 			break;
689 
690 // Goober5000: this should only be compiled in debug builds, since it crashes release
691 #ifndef NDEBUG
692 		case KEY_DEBUGGED + KEY_E:
693 			gameseq_post_event(GS_EVENT_EVENT_DEBUG);
694 			break;
695 #endif
696 
697 		// Goober5000: handle time dilation in cheat section
698 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_COMMA:
699 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_COMMA:
700 			if ( Game_mode & GM_NORMAL ) {
701 				if ( Game_time_compression > (F1_0/MAX_TIME_DIVIDER) ) {
702 					change_time_compression(0.5);
703 				} else {
704 					gamesnd_play_error_beep();
705 				}
706 			} else {
707 				gamesnd_play_error_beep();
708 			}
709 			break;
710 
711 		// Goober5000: handle as normal here
712 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_PERIOD:
713 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_PERIOD:
714 			if ( Game_mode & GM_NORMAL ) {
715 				if ( Game_time_compression < (F1_0*MAX_TIME_MULTIPLIER) ) {
716 					change_time_compression(2);
717 				} else {
718 					gamesnd_play_error_beep();
719 				}
720 			} else {
721 				gamesnd_play_error_beep();
722 			}
723 			break;
724 
725 		//	Kill! the currently targeted ship.
726 		case KEY_DEBUGGED + KEY_K:
727 		case KEY_DEBUGGED1 + KEY_K:
728 			if (Player_ai->target_objnum != -1) {
729 				object	*objp = &Objects[Player_ai->target_objnum];
730 
731 				switch (objp->type) {
732 				case OBJ_SHIP:
733 
734 					// remove guardian flag -- kazan
735 					Ships[objp->instance].ship_guardian_threshold = 0;
736 
737 					ship_apply_local_damage( objp, Player_obj, &objp->pos, 100000.0f, MISS_SHIELDS, CREATE_SPARKS);
738 					ship_apply_local_damage( objp, Player_obj, &objp->pos, 1.0f, MISS_SHIELDS, CREATE_SPARKS);
739 					break;
740 				case OBJ_WEAPON:
741 					Weapons[objp->instance].lifeleft = 0.01f;
742 					break;
743 				}
744 			}
745 
746 			break;
747 
748 		// play the next mission message
749 		case KEY_DEBUGGED + KEY_V:
750 			extern int Message_debug_index;
751 			extern int Num_messages_playing;
752 			// stop any other messages
753 			if(Num_messages_playing){
754 				message_kill_all(1);
755 			}
756 
757 			// next message
758 			if(Message_debug_index >= Num_messages - 1){
759 				Message_debug_index = Num_builtin_messages;
760 			} else {
761 				Message_debug_index++;
762 			}
763 
764 			// play the message
765 			message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 );
766 			if (Messages[Message_debug_index].avi_info.index == -1) {
767 				HUD_printf("No anim set for message \"%s\"; None will play!", Messages[Message_debug_index].name);
768 			}
769 			break;
770 
771 		// play the previous mission message
772 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_V:
773 			extern int Message_debug_index;
774 			extern int Num_messages_playing;
775 			// stop any other messages
776 			if(Num_messages_playing){
777 				message_kill_all(1);
778 			}
779 
780 			// go maybe go down one
781 			if(Message_debug_index == Num_builtin_messages - 1){
782 				Message_debug_index = Num_builtin_messages;
783 			} else if(Message_debug_index > Num_builtin_messages){
784 				Message_debug_index--;
785 			}
786 
787 			// play the message
788 			message_send_unique_to_player( Messages[Message_debug_index].name, Message_waves[Messages[Message_debug_index].wave_info.index].name, MESSAGE_SOURCE_SPECIAL, MESSAGE_PRIORITY_HIGH, 0, 0 );
789 			if (Messages[Message_debug_index].avi_info.index == -1) {
790 				HUD_printf("No avi associated with this message; None will play!");
791 			}
792 			break;
793 
794 		// reset to the beginning of mission messages
795 		case KEY_DEBUGGED + KEY_ALTED + KEY_V:
796 			extern int Message_debug_index;
797 			Message_debug_index = Num_builtin_messages - 1;
798 			HUD_printf("Resetting to first mission message");
799 			break;
800 
801 		//	Kill! the currently targeted ship.
802 		case KEY_DEBUGGED + KEY_ALTED + KEY_SHIFTED + KEY_K:
803 		case KEY_DEBUGGED1 + KEY_ALTED + KEY_SHIFTED + KEY_K:
804 			if (Player_ai->target_objnum != -1) {
805 				object	*objp = &Objects[Player_ai->target_objnum];
806 
807 				if (objp->type == OBJ_SHIP) {
808 					ship_apply_local_damage( objp, Player_obj, &objp->pos, Ships[objp->instance].ship_max_hull_strength * 0.1f + 10.0f, MISS_SHIELDS, CREATE_SPARKS);
809 				}
810 			}
811 			break;
812 
813 			//	Kill the currently targeted subsystem.
814 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_K:
815 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_K:
816 			if ((Player_ai->target_objnum != -1) && (Player_ai->targeted_subsys != NULL)) {
817 				object	*objp = &Objects[Player_ai->target_objnum];
818 				if ( objp->type == OBJ_SHIP ) {
819 					ship		*sp = &Ships[objp->instance];
820 					vec3d	g_subobj_pos;
821 
822 					get_subsystem_world_pos(objp, Player_ai->targeted_subsys, &g_subobj_pos);
823 
824 					do_subobj_hit_stuff(objp, Player_obj, &g_subobj_pos, Player_ai->targeted_subsys->system_info->subobj_num, (float) -Player_ai->targeted_subsys->system_info->type, NULL); //100.0f);
825 
826 					if ( sp->subsys_info[SUBSYSTEM_ENGINE].aggregate_current_hits <= 0.0f ) {
827 						mission_log_add_entry(LOG_SHIP_DISABLED, sp->ship_name, NULL );
828 						sp->flags |= SF_DISABLED;				// add the disabled flag
829 					}
830 
831 					if ( sp->subsys_info[SUBSYSTEM_TURRET].aggregate_current_hits <= 0.0f ) {
832 						mission_log_add_entry(LOG_SHIP_DISARMED, sp->ship_name, NULL );
833 					}
834 				}
835 			}
836 			break;
837 
838 		case KEY_DEBUGGED + KEY_ALTED + KEY_K:
839 		case KEY_DEBUGGED1 + KEY_ALTED + KEY_K:
840 			{
841 				float	shield, integrity;
842 				vec3d	pos, randvec;
843 
844 				vm_vec_rand_vec_quick(&randvec);
845 				vm_vec_scale_add(&pos, &Player_obj->pos, &randvec, Player_obj->radius);
846 			ship_apply_local_damage(Player_obj, Player_obj, &pos, 25.0f, MISS_SHIELDS, CREATE_SPARKS);
847 			hud_get_target_strength(Player_obj, &shield, &integrity);
848 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You knocked yourself down to %7.3f percent hull.\n", 9), 100.0f * integrity);
849 			break;
850 			}
851 
852 		//	Whack down the player's shield and hull by a little more than 50%
853 		//	Select next object to be viewed by AI.
854 		case KEY_DEBUGGED + KEY_I:
855 		case KEY_DEBUGGED1 + KEY_I:
856 			Player_obj->flags ^= OF_INVULNERABLE;
857 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "You are %s", 10), Player_obj->flags & OF_INVULNERABLE ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12));
858 			break;
859 
860 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_I:
861 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_I:
862 			if (Player_ai->target_objnum != -1) {
863 				object	*objp = &Objects[Player_ai->target_objnum];
864 
865 				objp->flags ^= OF_INVULNERABLE;
866 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Player's target [%s] is %s", 13), Ships[objp->instance].ship_name, objp->flags & OF_INVULNERABLE ? XSTR( "now INVULNERABLE!", 11) : XSTR( "no longer invulnerable...", 12));
867 			}
868 			break;
869 
870 		case KEY_DEBUGGED + KEY_N:
871 			AI_watch_object++;
872 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Spewing debug info about object #%d", 14), AI_watch_object);
873 			break;
874 
875 		case KEY_DEBUGGED + KEY_O:
876 		case KEY_DEBUGGED1 + KEY_O:
877 			toggle_player_object();
878 			break;
879 
880 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_O:
881 			extern int Debug_octant;
882 			if(Debug_octant == 7){
883 				Debug_octant = -1;
884 			} else {
885 				Debug_octant++;
886 			}
887 			nprintf(("General", "Debug_octant == %d\n", Debug_octant));
888 			break;
889 
890 		case KEY_DEBUGGED + KEY_P:
891 			supernova_start(20);
892 			break;
893 
894 		case KEY_DEBUGGED + KEY_W:
895 		case KEY_DEBUGGED1 + KEY_W:
896 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_W:
897 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_W:
898 			// temp code for testing purposes, toggles weapon energy cheat
899 			Weapon_energy_cheat = !Weapon_energy_cheat;
900 			if (Weapon_energy_cheat) {
901 				if (k & KEY_SHIFTED)
902 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Weapon energy and missile count will always be at full ALL SHIPS!", 15));
903 				else
904 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Weapon energy and missile count will always be at full for player", 16));
905 
906 				debug_max_secondary_weapons(Player_obj);
907 				debug_max_primary_weapons(Player_obj);
908 				if (k & KEY_SHIFTED) {
909 					object	*objp;
910 
911 					for ( objp = GET_FIRST(&obj_used_list); objp !=END_OF_LIST(&obj_used_list); objp = GET_NEXT(objp) )
912 						if (objp->type == OBJ_SHIP) {
913 							debug_max_secondary_weapons(objp);
914 							debug_max_primary_weapons(objp);
915 						}
916 				}
917 
918 			} else
919 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Normal weapon energy system / missile count restored", 17));
920 
921 			break;
922 
923 		case KEY_DEBUGGED + KEY_G:
924 		case KEY_DEBUGGED1 + KEY_G:
925 			mission_goal_mark_all_true( PRIMARY_GOAL );
926 			break;
927 
928 		case KEY_DEBUGGED + KEY_G + KEY_SHIFTED:
929 		case KEY_DEBUGGED1 + KEY_G + KEY_SHIFTED:
930 			mission_goal_mark_all_true( SECONDARY_GOAL );
931 			break;
932 
933 		case KEY_DEBUGGED + KEY_G + KEY_ALTED:
934 		case KEY_DEBUGGED1 + KEY_G + KEY_ALTED:
935 			mission_goal_mark_all_true( BONUS_GOAL );
936 			break;
937 
938 		case KEY_DEBUGGED + KEY_9: {
939 		case KEY_DEBUGGED1 + KEY_9:
940 			ship* shipp;
941 
942 			shipp = &Ships[Player_obj->instance];
943 			int *weap = &shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank];
944 			*weap = get_next_weapon_looped(*weap, WP_MISSILE);
945 
946 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[*weap].name);
947 			break;
948 		}
949 
950 
951 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_9: {
952 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_9:
953 			ship* shipp;
954 
955 			shipp = &Ships[Player_obj->instance];
956 			int *weap = &shipp->weapons.secondary_bank_weapons[shipp->weapons.current_secondary_bank];
957 			*weap = get_prev_weapon_looped(*weap, WP_MISSILE);
958 
959 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary Weapon forced to %s", 18), Weapon_info[*weap].name);
960 			break;
961 		}
962 
963 		case KEY_DEBUGGED + KEY_U: {
964 		case KEY_DEBUGGED1 + KEY_U:
965 			// launch asteroid
966 			object *asteroid_create(asteroid_field *asfieldp, int asteroid_type, int subtype);
967 			object *objp = asteroid_create(&Asteroid_field, 0, 0);
968 			if(objp == NULL) {
969 				break;
970 			}
971 			vec3d vel;
972 			vm_vec_copy_scale(&vel, &Player_obj->orient.vec.fvec, 50.0f);
973 			objp->phys_info.vel = vel;
974 			objp->phys_info.desired_vel = vel;
975 			objp->pos = Player_obj->pos;
976 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Asteroid launched", 1595));
977 			break;
978 		}
979 
980 		case KEY_DEBUGGED + KEY_0: {
981 		case KEY_DEBUGGED1 + KEY_0:
982 			ship* shipp;
983 
984 			shipp = &Ships[Player_obj->instance];
985 			int *weap = &shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank];
986 			*weap = get_next_weapon_looped(*weap, WP_LASER);
987 
988 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[*weap].name);
989 			break;
990 		}
991 
992 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_0: {
993 		case KEY_DEBUGGED1 + KEY_SHIFTED + KEY_0:
994 			ship* shipp;
995 
996 			shipp = &Ships[Player_obj->instance];
997 			int *weap = &shipp->weapons.primary_bank_weapons[shipp->weapons.current_primary_bank];
998 			*weap = get_prev_weapon_looped(*weap, WP_LASER);
999 
1000 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Primary Weapon forced to %s", 19), Weapon_info[*weap].name);
1001 			break;
1002 		}
1003 
1004 		case KEY_DEBUGGED + KEY_J: {
1005 			int new_pattern = event_music_return_current_pattern();
1006 
1007 			new_pattern++;
1008 			if ( new_pattern >= MAX_PATTERNS )
1009 				new_pattern = 0;
1010 
1011 			event_music_change_pattern(new_pattern);
1012 			break;
1013 		}
1014 
1015 		case KEY_DEBUGGED + KEY_M: {
1016 			if ( Event_music_enabled ) {
1017 				event_music_disable();
1018 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music disabled", 20));
1019 
1020 			} else {
1021 				event_music_enable();
1022 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Event music enabled", 21));
1023 			}
1024 
1025 			break;
1026 		}
1027 
1028 		case KEY_DEBUGGED + KEY_R:
1029 		case KEY_DEBUGGED1 + KEY_R:
1030 		{
1031 			// rearm the target, if we have one
1032 			object *obj_to_rearm = (Player_ai->target_objnum >= 0) ? &Objects[Player_ai->target_objnum] : Player_obj;
1033 
1034 			if (is_support_allowed(obj_to_rearm))
1035 			{
1036 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Issuing rearm request for %s", -1), Ships[obj_to_rearm->instance].ship_name);
1037 				ai_issue_rearm_request(obj_to_rearm);
1038 			}
1039 			else
1040 			{
1041 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR("Cannot issue rearm request for %s", -1), Ships[obj_to_rearm->instance].ship_name);
1042 			}
1043 
1044 			break;
1045 		}
1046 
1047 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_UP:
1048 			Game_detail_level++;
1049 			HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level );
1050 			break;
1051 
1052 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_DOWN:
1053 			Game_detail_level--;
1054 			HUD_printf( XSTR( "Detail level set to %+d\n", 22), Game_detail_level );
1055 			break;
1056 
1057 #ifndef NDEBUG
1058 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_T:	{
1059 			extern int Test_begin;
1060 
1061 			if ( Test_begin == 1 )
1062 				break;
1063 
1064 			Test_begin = 1;
1065 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Frame Rate test started", 23));
1066 
1067 			break;
1068 		}
1069 #endif
1070 		case KEY_DEBUGGED + KEY_A:	{
1071 
1072 			HUD_printf("frame rate currently is %0.2 FPS", 1/flFrametime);
1073 
1074 			break;
1075 		}
1076 
1077 
1078 		case KEY_DEBUGGED + KEY_D:
1079 			extern int OO_update_index;
1080 
1081 			if(MULTIPLAYER_MASTER){
1082 				do {
1083 					OO_update_index++;
1084 				} while((OO_update_index < (MAX_PLAYERS-1)) && !MULTI_CONNECTED(Net_players[OO_update_index]));
1085 				if(OO_update_index >= MAX_PLAYERS-1){
1086 					OO_update_index = -1;
1087 				}
1088 			} else {
1089 				if(OO_update_index < 0){
1090 					OO_update_index = MY_NET_PLAYER_NUM;
1091 				} else {
1092 					OO_update_index = -1;
1093 				}
1094 			}
1095 			break;
1096 
1097 		// change player ship to next flyable type
1098 		case KEY_DEBUGGED + KEY_RIGHT:
1099 			debug_cycle_player_ship(1);
1100 			break;
1101 
1102 		// change player ship to previous flyable ship
1103 		case KEY_DEBUGGED + KEY_LEFT:
1104 			debug_cycle_player_ship(-1);
1105 			break;
1106 
1107 		// cycle target to ship
1108 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_RIGHT:
1109 			debug_cycle_targeted_ship(1);
1110 			break;
1111 
1112 		// cycle target to previous ship
1113 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_LEFT:
1114 			debug_cycle_targeted_ship(-1);
1115 			break;
1116 
1117 		// change species of the targeted ship
1118 		case KEY_DEBUGGED + KEY_S: {
1119 			if ( Player_ai->target_objnum < 0 )
1120 				break;
1121 
1122 			object		*objp;
1123 			ship_info	*sip;
1124 
1125 			objp = &Objects[Player_ai->target_objnum];
1126 			if ( objp->type != OBJ_SHIP )
1127 				return;
1128 
1129 			sip = &Ship_info[Ships[objp->instance].ship_info_index];
1130 			sip->species++;
1131 
1132 			if (sip->species >= (int)Species_info.size())
1133 				sip->species = 0;
1134 
1135 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Species of target changed to: %s", 24), Species_info[sip->species].species_name);
1136 			break;
1137 		}
1138 
1139 		case KEY_DEBUGGED + KEY_SHIFTED + KEY_S:
1140 			game_increase_skill_level();
1141 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Skill level set to %s.", 25), Skill_level_names(Game_skill_level));
1142 			break;
1143 
1144 		case KEY_DEBUGGED + KEY_T: {
1145 			char buf[256];
1146 			event_music_get_info(buf);
1147 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, buf);
1148 			break;
1149 		}
1150 
1151 		case KEY_DEBUGGED + KEY_UP:
1152 		case KEY_DEBUGGED1 + KEY_UP:
1153 			debug_change_song(1);
1154 			break;
1155 
1156 		case KEY_DEBUGGED + KEY_DOWN:
1157 		case KEY_DEBUGGED1 + KEY_DOWN:
1158 			debug_change_song(-1);
1159 			break;
1160 
1161 		case KEY_PADMINUS: {
1162 			int init_flag = 0;
1163 
1164 			if ( keyd_pressed[KEY_1] )	{
1165 				init_flag = 1;
1166 				HUD_color_red -= 4;
1167 			}
1168 
1169 			if ( keyd_pressed[KEY_2] )	{
1170 				init_flag = 1;
1171 				HUD_color_green -= 4;
1172 			}
1173 
1174 			if ( keyd_pressed[KEY_3] )	{
1175 				init_flag = 1;
1176 				HUD_color_blue -= 4;
1177 			}
1178 
1179 			if (init_flag)
1180 				HUD_init_colors();
1181 
1182 			break;
1183 		}
1184 
1185 		case KEY_DEBUGGED + KEY_Y:
1186 			extern int tst;
1187 			tst = 2;
1188 			break;
1189 
1190 		case KEY_PADPLUS: {
1191 			int init_flag = 0;
1192 
1193 			if ( keyd_pressed[KEY_1] )	{
1194 				init_flag = 1;
1195 				HUD_color_red += 4;
1196 			}
1197 
1198 			if ( keyd_pressed[KEY_2] )	{
1199 				init_flag = 1;
1200 				HUD_color_green += 4;
1201 			}
1202 
1203 			if ( keyd_pressed[KEY_3] )	{
1204 				init_flag = 1;
1205 				HUD_color_blue += 4;
1206 			}
1207 
1208 			if (init_flag)
1209 				HUD_init_colors();
1210 
1211 			break;
1212 		}
1213 		case KEY_DEBUGGED + KEY_ALTED + KEY_EQUAL:
1214 		case KEY_DEBUGGED1 + KEY_ALTED + KEY_EQUAL:
1215 			{
1216 			camera *cam = Main_camera.getCamera();
1217 			if(cam == NULL)
1218 			{
1219 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Couldn't get camera FOV");
1220 				break;
1221 			}
1222 			cam->set_fov(cam->get_fov() + 0.1f);
1223 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov raised to %0.2f" , cam->get_fov());
1224 			}
1225 			break;
1226 
1227 		case KEY_DEBUGGED + KEY_ALTED + KEY_MINUS:
1228 		case KEY_DEBUGGED1 + KEY_ALTED + KEY_MINUS:
1229 			{
1230 			camera *cam = Main_camera.getCamera();
1231 			if(cam == NULL)
1232 			{
1233 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Couldn't get camera FOV");
1234 				break;
1235 			}
1236 			cam->set_fov(cam->get_fov() - 0.1f);
1237 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, "Camera fov lowered to %0.2f" , cam->get_fov());
1238 			}
1239 			break;
1240 		case KEY_DEBUGGED + KEY_Z:
1241 		case KEY_DEBUGGED1 + KEY_Z:
1242 			{
1243 				Show_cpu = !Show_cpu;
1244 			}
1245 			break;
1246 
1247 	}	// end switch
1248 }
1249 
ppsk_hotkeys(int k)1250 void ppsk_hotkeys(int k)
1251 {
1252 	// use k to check for keys that can have Shift,Ctrl,Alt,Del status
1253 	int hotkey_set;
1254 
1255 #ifndef NDEBUG
1256 	k &= ~KEY_DEBUGGED;			// since hitting F11 will set this bit
1257 #endif
1258 
1259 	switch (k) {
1260 		case KEY_F5:
1261 		case KEY_F6:
1262 		case KEY_F7:
1263 		case KEY_F8:
1264 		case KEY_F9:
1265 		case KEY_F10:
1266 		case KEY_F11:
1267 		case KEY_F12:
1268 			hotkey_set = mission_hotkey_get_set_num(k);
1269 			if ( !(Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE) )
1270 				hud_target_hotkey_select( hotkey_set );
1271 			else
1272 				hud_squadmsg_hotkey_select( hotkey_set );
1273 
1274 			break;
1275 
1276 		case KEY_F5 + KEY_SHIFTED:
1277 		case KEY_F6 + KEY_SHIFTED:
1278 		case KEY_F7 + KEY_SHIFTED:
1279 		case KEY_F8 + KEY_SHIFTED:
1280 		case KEY_F9 + KEY_SHIFTED:
1281 		case KEY_F10 + KEY_SHIFTED:
1282 		case KEY_F11 + KEY_SHIFTED:
1283 		case KEY_F12 + KEY_SHIFTED:
1284 			hotkey_set = mission_hotkey_get_set_num(k&(~KEY_SHIFTED));
1285 			mprintf(("Adding to set %d\n", hotkey_set+1));
1286 			if ( Player_ai->target_objnum == -1)
1287 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "No target to add/remove from set %d.", 26), hotkey_set+1);
1288 			else  {
1289 				hud_target_hotkey_add_remove( hotkey_set, &Objects[Player_ai->target_objnum], HOTKEY_USER_ADDED);
1290 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "%s added to set %d. (F%d)", 27), Ships[Objects[Player_ai->target_objnum].instance].ship_name, hotkey_set, 4+hotkey_set+1);
1291 			}
1292 
1293 			break;
1294 
1295 		case KEY_F5 + KEY_SHIFTED + KEY_ALTED:
1296 		case KEY_F6 + KEY_SHIFTED + KEY_ALTED:
1297 		case KEY_F7 + KEY_SHIFTED + KEY_ALTED:
1298 		case KEY_F8 + KEY_SHIFTED + KEY_ALTED:
1299 		case KEY_F9 + KEY_SHIFTED + KEY_ALTED:
1300 		case KEY_F10 + KEY_SHIFTED + KEY_ALTED:
1301 		case KEY_F11 + KEY_SHIFTED + KEY_ALTED:
1302 		case KEY_F12 + KEY_SHIFTED + KEY_ALTED:
1303 			hotkey_set = mission_hotkey_get_set_num(k & ~KEY_SHIFTED+KEY_ALTED);
1304 			hud_target_hotkey_clear( hotkey_set );
1305 			break;
1306 
1307 		case KEY_SHIFTED + KEY_MINUS:
1308 			if ( HUD_color_alpha > HUD_COLOR_ALPHA_USER_MIN )	{
1309 				HUD_color_alpha--;
1310 				HUD_init_colors();
1311 			}
1312 			break;
1313 
1314 		case KEY_SHIFTED + KEY_EQUAL:
1315 			if ( HUD_color_alpha < HUD_COLOR_ALPHA_USER_MAX ) {
1316 				HUD_color_alpha++;
1317 				HUD_init_colors();
1318 			}
1319 			break;
1320 	}	// end switch
1321 }
1322 
1323 /**
1324  * Check keypress 'key' against a set of valid controls and mark the match in the
1325  * player's button info bitfield.  Also checks joystick controls in the set.
1326  *
1327  * @param key Scancode (plus modifiers).
1328  * @param count Total size of the list
1329  * @param list List of ::Control_config struct action indices to check for
1330  */
process_set_of_keys(int key,int count,int * list)1331 void process_set_of_keys(int key, int count, int *list)
1332 {
1333 	int i;
1334 
1335 	for (i=0; i<count; i++)
1336 		if (check_control(list[i], key))
1337 			button_info_set(&Player->bi, list[i]);
1338 }
1339 
1340 /**
1341  * Routine to process keys used for player ship stuff (*not* ship movement).
1342  */
process_player_ship_keys(int k)1343 void process_player_ship_keys(int k)
1344 {
1345 	int masked_k;
1346 
1347 	masked_k = k & ~KEY_CTRLED;	// take out CTRL modifier only
1348 
1349 	// moved this line to beginning of function since hotkeys now encompass
1350 	// F5 - F12.  We can return after using F11 as a hotkey.
1351 	ppsk_hotkeys(masked_k);
1352 	if (keyd_pressed[KEY_DEBUG_KEY]){
1353 		return;
1354 	}
1355 
1356 	// if we're in supernova mode. do nothing
1357 	if(Player->control_mode == PCM_SUPERNOVA){
1358 		return;
1359 	}
1360 
1361 	// pass the key to the squadmate messaging code.  If the messaging code took the key, then return
1362 	// from here immediately since we don't want to do further key processing.
1363 	if ( hud_squadmsg_read_key(k) )
1364 		return;
1365 
1366 	if ( Player->control_mode == PCM_NORMAL )	{
1367 		//	The following things are not legal to do while dead.
1368 		if ( !(Game_mode & GM_DEAD) ) {
1369 			process_set_of_keys(masked_k, Normal_key_set_size, Normal_key_set);
1370 		} else	{
1371 			process_set_of_keys(masked_k, Dead_key_set_size, Dead_key_set);
1372 		}
1373 		if (lua_game_control & LGC_B_POLL_ALL) {
1374 			// first clear all
1375 			button_info_clear(&Player->lua_bi_full);
1376 
1377 			// then check the keys.
1378 			int i;
1379 			for(i = 0; i < CCFG_MAX; i++) {
1380 				if (check_control(i, masked_k))
1381 					button_info_set(&Player->lua_bi_full, i);
1382 			}
1383 		}
1384 	} else {
1385 
1386 	}
1387 }
1388 
1389 /**
1390  * Handler for when player hits 'ESC' during the game
1391  */
game_do_end_mission_popup()1392 void game_do_end_mission_popup()
1393 {
1394 	int	pf_flags, choice;
1395 
1396 	// do the multiplayer version of this
1397 	if(Game_mode & GM_MULTIPLAYER){
1398 		multi_quit_game(PROMPT_ALL);
1399 	} else {
1400 		// single player version....
1401 		// do housekeeping things.
1402 		game_stop_time();
1403 		game_stop_looped_sounds();
1404 		snd_stop_all();
1405 
1406 		quit_mission_popup_shown = true;
1407 
1408 		pf_flags = PF_BODY_BIG | PF_USE_AFFIRMATIVE_ICON | PF_USE_NEGATIVE_ICON;
1409 		choice = popup(pf_flags, 3, POPUP_NO, XSTR( "&Yes, Quit", 28), XSTR( "Yes, &Restart", 29), XSTR( "Do you really want to end the mission?", 30));
1410 
1411 		switch (choice) {
1412 		case 1:
1413 			gameseq_post_event(GS_EVENT_END_GAME);
1414 			break;
1415 
1416 		case 2:
1417 			gameseq_post_event(GS_EVENT_ENTER_GAME);
1418 			break;
1419 
1420 		default:
1421 			break;  // do nothing
1422 		}
1423 		quit_mission_popup_shown = false;
1424 
1425 		game_start_time();
1426 		game_flush();
1427 	}
1428 }
1429 
1430 /**
1431  * Handle pause keypress
1432  */
game_process_pause_key()1433 void game_process_pause_key()
1434 {
1435 	// special processing for multiplayer
1436 	if (Game_mode & GM_MULTIPLAYER) {
1437 		if(Multi_pause_status){
1438 			multi_pause_request(0);
1439 		} else {
1440 			multi_pause_request(1);
1441 		}
1442 	} else {
1443 		gameseq_post_event( GS_EVENT_PAUSE_GAME );
1444 	}
1445 }
1446 
1447 /**
1448  * Process cheat codes
1449  */
game_process_cheats(int k)1450 void game_process_cheats(int k)
1451 {
1452 	size_t i;
1453 
1454 	if ( k == 0 ){
1455 		return;
1456 	}
1457 
1458 	// no cheats in multiplayer, ever
1459 	if(Game_mode & GM_MULTIPLAYER){
1460 		Cheats_enabled = 0;
1461 		return;
1462 	}
1463 
1464 	for (i = 0; i < CHEAT_BUFFER_LEN; i++){
1465 		CheatBuffer[i]=CheatBuffer[i+1];
1466 	}
1467 
1468 	CheatBuffer[CHEAT_BUFFER_LEN - 1] = (char)key_to_ascii(k);
1469 
1470 	cheatCode detectedCheatCode = CHEAT_CODE_NONE;
1471 
1472 	for(i=0; i < CHEATS_TABLE_LEN; i++) {
1473 		Cheat cheat = cheatsTable[i];
1474 
1475 		if(!strncmp(cheat.data, CheatBuffer, CHEAT_BUFFER_LEN)){
1476 			detectedCheatCode = cheat.code;
1477 			break;
1478 		}
1479 	}
1480 
1481 	if(detectedCheatCode == CHEAT_CODE_FREESPACE){
1482 		Cheats_enabled = 1;
1483 
1484 		// cheating allows the changing of weapons so we have to grab anything
1485 		// that we don't already have loaded, just in case
1486 		extern void weapons_page_in_cheats();
1487 		weapons_page_in_cheats();
1488 
1489 		HUD_printf("Cheats enabled");
1490 	}
1491 	if(detectedCheatCode == CHEAT_CODE_FISH){
1492 		// only enable in the Vasudan main hall
1493 		if ((gameseq_get_state() == GS_STATE_MAIN_MENU) && main_hall_is_vasudan()) {
1494 			extern void fishtank_start();
1495 			fishtank_start();
1496 		}
1497 	}
1498 	if(detectedCheatCode == CHEAT_CODE_HEADZ){
1499 		// only enable in the Vasudan main hall
1500 		if ((gameseq_get_state() == GS_STATE_MAIN_MENU) && main_hall_is_vasudan()) {
1501 			main_hall_vasudan_funny();
1502 		}
1503 	}
1504 	if(detectedCheatCode ==  CHEAT_CODE_SKIP && (gameseq_get_state() == GS_STATE_MAIN_MENU)){
1505 		extern void main_hall_campaign_cheat();
1506 		main_hall_campaign_cheat();
1507 	}
1508 	if(detectedCheatCode == CHEAT_CODE_TOOLED && (Game_mode & GM_IN_MISSION)){
1509 		Tool_enabled = 1;
1510 		HUD_printf("Prepare to be taken to school");
1511 	}
1512 	if(detectedCheatCode == CHEAT_CODE_PIRATE && (Game_mode & GM_IN_MISSION) && (Player_obj != NULL)){
1513 		extern void prevent_spawning_collision(object *new_obj);
1514 		ship_subsys *ptr;
1515 		char name[NAME_LENGTH];
1516 		int ship_idx, ship_class;
1517 
1518 		// if not found, then don't create it :(
1519 		ship_class = ship_info_lookup("Volition Bravos");
1520 		if (ship_class < 0)
1521 			return;
1522 
1523 		HUD_printf(NOX("Walk the plank"));
1524 
1525 		vec3d pos = Player_obj->pos;
1526 		matrix orient = Player_obj->orient;
1527 		pos.xyz.x += frand_range(-700.0f, 700.0f);
1528 		pos.xyz.y += frand_range(-700.0f, 700.0f);
1529 		pos.xyz.z += frand_range(-700.0f, 700.0f);
1530 
1531 		int objnum = ship_create(&orient, &pos, ship_class);
1532 		if (objnum < 0)
1533 			return;
1534 
1535 		ship *shipp = &Ships[Objects[objnum].instance];
1536 		shipp->ship_name[0] = '\0';
1537 		shipp->orders_accepted = (1<<NUM_COMM_ORDER_ITEMS)-1;
1538 
1539 		// Goober5000 - stolen from support ship creation
1540 		// create a name for the ship.  use "Volition Bravos #".  look for collisions until one isn't found anymore
1541 		ship_idx = 1;
1542 		do {
1543 			sprintf(name, NOX("Volition Bravos %d"), ship_idx);
1544 			if ( (ship_name_lookup(name) == -1) && (ship_find_exited_ship_by_name(name) == -1) )
1545 			{
1546 				strcpy_s(shipp->ship_name, name);
1547 				break;
1548 			}
1549 
1550 			ship_idx++;
1551 		} while(1);
1552 
1553 		shipp->flags |= SF_ESCORT;
1554 		shipp->escort_priority = 1000 - ship_idx;
1555 
1556 		// now make sure we're not colliding with anyone
1557 		prevent_spawning_collision(&Objects[objnum]);
1558 
1559 		// Goober5000 - beam free
1560 		for (ptr = GET_FIRST(&shipp->subsys_list); ptr != END_OF_LIST(&shipp->subsys_list); ptr = GET_NEXT(ptr))
1561 		{
1562 			// mark all turrets as beam free
1563 			if (ptr->system_info->type == SUBSYSTEM_TURRET)
1564 			{
1565 				ptr->weapons.flags |= SW_FLAG_BEAM_FREE;
1566 				ptr->turret_next_fire_stamp = timestamp((int) frand_range(50.0f, 4000.0f));
1567 			}
1568 		}
1569 
1570 		// warpin
1571 		shipfx_warpin_start(&Objects[objnum]);
1572 	}
1573 }
1574 
game_process_keys()1575 void game_process_keys()
1576 {
1577 	int k;
1578 
1579 	button_info_clear(&Player->bi);	// clear out the button info struct for the player
1580     do
1581 	{
1582 		k = game_poll();
1583 
1584 		if ( Game_mode & GM_DEAD_BLEW_UP ) {
1585 			continue;
1586 		}
1587 
1588 		game_process_cheats( k );
1589 		process_player_ship_keys(k);
1590 		process_debug_keys(k);
1591 
1592 		switch (k) {
1593 			case 0:
1594 				// No key
1595 				break;
1596 
1597 			case KEY_ESC:
1598 				if ( Player->control_mode != PCM_NORMAL )	{
1599 					if ( Player->control_mode == PCM_WARPOUT_STAGE1 )	{
1600 						gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_STOP );
1601 					} else {
1602 						// too late to abort warp out!
1603 					}
1604 				} else {
1605 					// let the ESC key break out of messaging mode
1606 					if ( Players[Player_num].flags & PLAYER_FLAGS_MSG_MODE ) {
1607 						hud_squadmsg_toggle();
1608 						break;
1609 					}
1610 
1611 					//If topdown view in non-2D mission, go back to cockpit view.
1612 					if ( (Viewer_mode & VM_TOPDOWN) && !(The_mission.flags & MISSION_FLAG_2D_MISSION) && !(Perspective_locked) ) {
1613 						Viewer_mode &= ~VM_TOPDOWN;
1614 						break;
1615 					}
1616 
1617 					// if in external view or chase view, go back to cockpit view
1618 					if ( (Viewer_mode & (VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP)) && !(Perspective_locked) ) {
1619 						Viewer_mode &= ~(VM_EXTERNAL|VM_CHASE|VM_OTHER_SHIP);
1620 						break;
1621 					}
1622 
1623 					if (!(Game_mode & GM_DEAD_DIED))
1624 						game_do_end_mission_popup();
1625 
1626 				}
1627 				break;
1628 
1629 			case KEY_Y:
1630 				break;
1631 
1632 			case KEY_N:
1633 				break;
1634 
1635 			case KEY_ALTED + KEY_SHIFTED+KEY_J:
1636 				// treat the current joystick position as the center position
1637 				joy_set_cen();
1638 				break;
1639 
1640 			case KEY_DEBUGGED | KEY_PAUSE:
1641 				gameseq_post_event( GS_EVENT_DEBUG_PAUSE_GAME );
1642 				break;
1643 
1644 			case KEY_ALTED + KEY_PAUSE:
1645 				if( Game_mode & GM_DEAD_BLEW_UP ||
1646 					Game_mode & GM_DEAD_DIED) {
1647 					break;
1648 				}
1649 
1650 				pause_set_type(PAUSE_TYPE_VIEWER);
1651 				game_process_pause_key();
1652 				break;
1653 			case KEY_PAUSE:
1654 				if( Game_mode & GM_DEAD_BLEW_UP ||
1655 					Game_mode & GM_DEAD_DIED) {
1656 					break;
1657 				}
1658 
1659 				pause_set_type(PAUSE_TYPE_NORMAL);
1660 				game_process_pause_key();
1661 				break;
1662 
1663 		} // end switch
1664 	}
1665 	while (k);
1666 
1667 	// lua button command override goes here!!
1668 	if (lua_game_control & LGC_B_OVERRIDE) {
1669 		button_info temp = Player->bi;
1670 		Player->bi = Player->lua_bi;
1671 		Player->lua_bi = temp;
1672 	} else if (lua_game_control & LGC_B_ADDITIVE) {
1673 		// add the lua commands to current commands
1674 		int i;
1675 		for (i=0; i<NUM_BUTTON_FIELDS; i++)
1676 			Player->bi.status[i] |= Player->lua_bi.status[i];
1677 		Player->lua_bi = Player->bi;
1678 	} else {
1679 		// just copy over the values
1680 		Player->lua_bi = Player->bi;
1681 	}
1682 	// there.. wasnt that bad hack was it?
1683 
1684 	button_info_do(&Player->bi);	// call functions based on status of button_info bit vectors
1685 }
1686 
button_function_critical(int n,net_player * p=NULL)1687 int button_function_critical(int n, net_player *p = NULL)
1688 {
1689 	object *objp;
1690 	player *pl;
1691 	net_player *npl;
1692 	int at_self;    // flag indicating the object is local (for hud messages, etc)
1693 
1694 	Assert(n >= 0);
1695 
1696 	// multiplayer clients should leave critical button bits alone and pass them to the server instead
1697 	if (MULTIPLAYER_CLIENT) {
1698 		// if this flag is set, we should apply the button itself (came from the server)
1699 		if (!Multi_button_info_ok){
1700 			return 0;
1701 		}
1702 	}
1703 
1704 	// in single player mode make sure we're using the player object and the player himself, otherwise use the object and
1705 	// player pertaining to the passed net_player
1706 	npl = NULL;
1707 	if (p == NULL) {
1708 		objp = Player_obj;
1709 		pl = Player;
1710 
1711 		if(Game_mode & GM_MULTIPLAYER){
1712 			npl = Net_player;
1713 
1714 			// if we're the server in multiplayer and we're an observer, don't process our own critical button functions
1715 			if((Net_player->flags & NETINFO_FLAG_AM_MASTER) && (Net_player->flags & NETINFO_FLAG_OBSERVER)){
1716 				return 0;
1717 			}
1718 		}
1719 
1720 		at_self = 1;
1721 	} else {
1722 		objp = &Objects[p->m_player->objnum];
1723 		pl = p->m_player;
1724 		npl = p;
1725 		at_self = 0;
1726 
1727 		if ( NETPLAYER_IS_DEAD(npl) || (Ships[Objects[pl->objnum].instance].flags & SF_DYING) )
1728 			return 0;
1729 	}
1730 
1731 	switch (n) {
1732 		// cycle num primaries to fire at once
1733 		case CYCLE_PRIMARY_WEAPON_SEQUENCE:
1734 			{
1735 				int count;
1736 				ship * shipp = &Ships[objp->instance];
1737 				ship_weapon *swp = &shipp->weapons;
1738 				ship_info *sip = &Ship_info[shipp->ship_info_index];
1739 				polymodel *pm = model_get( sip->model_num );
1740 				count = ftables.getNextSlots( pm->gun_banks[ swp->current_primary_bank ].num_slots, swp->primary_bank_slot_count[ swp->current_primary_bank ] );
1741 				swp->primary_bank_slot_count[ swp->current_primary_bank ] = count;
1742 				shipp->last_fired_point[ swp->current_primary_bank ] += count - ( shipp->last_fired_point[ swp->current_primary_bank ] % count);
1743 				shipp->last_fired_point[ swp->current_primary_bank ] -= 1;
1744 				shipp->last_fired_point[ swp->current_primary_bank ] %= swp->primary_bank_slot_count[ swp->current_primary_bank ];
1745 			}
1746 			break;
1747 
1748 		// cycle to next primary weapon
1749 		case CYCLE_NEXT_PRIMARY:
1750 			if (at_self) {
1751 				control_used(CYCLE_NEXT_PRIMARY);
1752 			}
1753 
1754 			hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
1755 			if (ship_select_next_primary(objp, CYCLE_PRIMARY_NEXT)) {
1756 				ship* shipp = &Ships[objp->instance];
1757 				if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) {
1758 					shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250);	//	1/4 second delay until can fire
1759 				}
1760 
1761 				// multiplayer server should maintain bank/link status here
1762 				if ( MULTIPLAYER_MASTER ) {
1763 					Assert(npl != NULL);
1764 					multi_server_update_player_weapons(npl,shipp);
1765 				}
1766 			}
1767 			break;
1768 
1769 		// cycle to previous primary weapon
1770 		case CYCLE_PREV_PRIMARY:
1771 			if (at_self) {
1772 				control_used(CYCLE_PREV_PRIMARY);
1773 			}
1774 
1775 			hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
1776 			if (ship_select_next_primary(objp, CYCLE_PRIMARY_PREV)) {
1777 				ship* shipp = &Ships[objp->instance];
1778 				if ( timestamp_elapsed(shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank]) ) {
1779 					shipp->weapons.next_primary_fire_stamp[shipp->weapons.current_primary_bank] = timestamp(250);	//	1/4 second delay until can fire
1780 				}
1781 
1782 				// multiplayer server should maintain bank/link status here
1783 				if ( MULTIPLAYER_MASTER ) {
1784 					Assert(npl != NULL);
1785 					multi_server_update_player_weapons(npl,shipp);
1786 				}
1787 			}
1788 			break;
1789 
1790 		// cycle to next secondary weapon
1791 		case CYCLE_SECONDARY:
1792 			if(at_self)
1793 				control_used(CYCLE_SECONDARY);
1794 
1795 			hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
1796 			if (ship_select_next_secondary(objp)) {
1797 				ship* shipp = &Ships[objp->instance];
1798 				if ( timestamp_elapsed(shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank]) ) {
1799 					shipp->weapons.next_secondary_fire_stamp[shipp->weapons.current_secondary_bank] = timestamp(250);	//	1/4 second delay until can fire
1800 				}
1801 
1802 				// multiplayer server should maintain bank/link status here
1803 				if( MULTIPLAYER_MASTER ){
1804 					Assert(npl != NULL);
1805 					multi_server_update_player_weapons(npl,shipp);
1806 				}
1807 			}
1808 			break;
1809 
1810 		// cycle number of missiles
1811 		case CYCLE_NUM_MISSLES: {
1812 			if(at_self)
1813 				control_used(CYCLE_NUM_MISSLES);
1814 
1815 			if ( objp == Player_obj ) {
1816 				if ( Player_ship->weapons.num_secondary_banks <= 0 ) {
1817 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "This ship has no secondary weapons", 33));
1818 					gamesnd_play_iface(SND_GENERAL_FAIL);
1819 					break;
1820 				}
1821 			}
1822 
1823 			polymodel *pm = model_get(Ship_info[Ships[objp->instance].ship_info_index].model_num);
1824 
1825 			int firepoints = pm->missile_banks[Ships[objp->instance].weapons.current_secondary_bank].num_slots;
1826 
1827 			if ( Ships[objp->instance].flags & SF_SECONDARY_DUAL_FIRE || firepoints < 2) {
1828 				Ships[objp->instance].flags &= ~SF_SECONDARY_DUAL_FIRE;
1829 				if(at_self) {
1830 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary weapon set to normal fire mode", 34));
1831 					snd_play( &Snds[ship_get_sound(Player_obj, SND_SECONDARY_CYCLE)] );
1832 					hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
1833 				}
1834 			} else {
1835 				Ships[objp->instance].flags |= SF_SECONDARY_DUAL_FIRE;
1836 				if(at_self) {
1837 					HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Secondary weapon set to dual fire mode", 35));
1838 					snd_play( &Snds[ship_get_sound(Player_obj, SND_SECONDARY_CYCLE)] );
1839 					hud_gauge_popup_start(HUD_WEAPONS_GAUGE);
1840 				}
1841 			}
1842 
1843 			// multiplayer server should maintain bank/link status here
1844 			if( MULTIPLAYER_MASTER ){
1845 				Assert(npl != NULL);
1846 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1847 			}
1848 			break;
1849 		}
1850 
1851 		// increase weapon recharge rate
1852 		case INCREASE_WEAPON:
1853 			if(at_self)
1854 				control_used(INCREASE_WEAPON);
1855 			increase_recharge_rate(objp, WEAPONS);
1856 
1857 			// multiplayer server should maintain bank/link status here
1858 			if( MULTIPLAYER_MASTER ){
1859 				Assert(npl != NULL);
1860 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1861 			}
1862 			break;
1863 
1864 		// decrease weapon recharge rate
1865 		case DECREASE_WEAPON:
1866 			if(at_self)
1867 				control_used(DECREASE_WEAPON);
1868 			decrease_recharge_rate(objp, WEAPONS);
1869 
1870 			// multiplayer server should maintain bank/link status here
1871 			if( MULTIPLAYER_MASTER ){
1872 				Assert(npl != NULL);
1873 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1874 			}
1875 			break;
1876 
1877 		// increase shield recharge rate
1878 		case INCREASE_SHIELD:
1879 			if(at_self)
1880 				control_used(INCREASE_SHIELD);
1881 			increase_recharge_rate(objp, SHIELDS);
1882 
1883 			// multiplayer server should maintain bank/link status here
1884 			if( MULTIPLAYER_MASTER ){
1885 				Assert(npl != NULL);
1886 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1887 			}
1888 			break;
1889 
1890 		// decrease shield recharge rate
1891 		case DECREASE_SHIELD:
1892 			if(at_self)
1893 				control_used(DECREASE_SHIELD);
1894 			decrease_recharge_rate(objp, SHIELDS);
1895 
1896 			// multiplayer server should maintain bank/link status here
1897 			if( MULTIPLAYER_MASTER ){
1898 				Assert(npl != NULL);
1899 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1900 			}
1901 			break;
1902 
1903 		// increase energy to engines
1904 		case INCREASE_ENGINE:
1905 			if(at_self)
1906 				control_used(INCREASE_ENGINE);
1907 			increase_recharge_rate(objp, ENGINES);
1908 
1909 			// multiplayer server should maintain bank/link status here
1910 			if( MULTIPLAYER_MASTER ){
1911 				Assert(npl != NULL);
1912 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1913 			}
1914 			break;
1915 
1916 		// decrease energy to engines
1917 		case DECREASE_ENGINE:
1918 			if(at_self)
1919    			control_used(DECREASE_ENGINE);
1920 			decrease_recharge_rate(objp, ENGINES);
1921 
1922 			// multiplayer server should maintain bank/link status here
1923 			if( MULTIPLAYER_MASTER ){
1924 				Assert(npl != NULL);
1925 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1926 			}
1927 			break;
1928 
1929 		// equalize recharge rates
1930 		case ETS_EQUALIZE:
1931 			if (at_self) {
1932    			control_used(ETS_EQUALIZE);
1933 			}
1934 
1935 			set_default_recharge_rates(objp);
1936 			snd_play( &Snds[SND_ENERGY_TRANS] );
1937 
1938 			// multiplayer server should maintain bank/link status here
1939 			if( MULTIPLAYER_MASTER ){
1940 				Assert(npl != NULL);
1941 				multi_server_update_player_weapons(npl,&Ships[objp->instance]);
1942 			}
1943 			break;
1944 
1945 		// equalize shield energy to all quadrants
1946 		case SHIELD_EQUALIZE:
1947 			if(at_self){
1948 				control_used(SHIELD_EQUALIZE);
1949 			}
1950 			hud_shield_equalize(objp, pl);
1951 			break;
1952 
1953 		// transfer shield energy to front
1954 		case SHIELD_XFER_TOP:
1955 			if(at_self){
1956    			control_used(SHIELD_XFER_TOP);
1957 			}
1958 			hud_augment_shield_quadrant(objp, FRONT_QUAD);
1959 			break;
1960 
1961 		// transfer shield energy to rear
1962 		case SHIELD_XFER_BOTTOM:
1963 			if(at_self)
1964 				control_used(SHIELD_XFER_BOTTOM);
1965 			hud_augment_shield_quadrant(objp, REAR_QUAD);
1966 			break;
1967 
1968 		// transfer shield energy to left
1969 		case SHIELD_XFER_LEFT:
1970 			if(at_self)
1971 				control_used(SHIELD_XFER_LEFT);
1972 			hud_augment_shield_quadrant(objp, LEFT_QUAD);
1973 			break;
1974 
1975 		// transfer shield energy to right
1976 		case SHIELD_XFER_RIGHT:
1977 			if(at_self)
1978 				control_used(SHIELD_XFER_RIGHT);
1979 			hud_augment_shield_quadrant(objp, RIGHT_QUAD);
1980 			break;
1981 
1982 		// transfer energy to shield from weapons
1983 		case XFER_SHIELD:
1984 			if(at_self)
1985 				control_used(XFER_SHIELD);
1986 			transfer_energy_to_shields(objp);
1987 			break;
1988 
1989 		// transfer energy to weapons from shield
1990 		case XFER_LASER:
1991 			if(at_self)
1992 				control_used(XFER_LASER);
1993 			transfer_energy_to_weapons(objp);
1994 			break;
1995 
1996 		// following are not handled here, but we need to bypass the Int3()
1997 		case LAUNCH_COUNTERMEASURE:
1998 		case VIEW_SLEW:
1999 		case VIEW_EXTERNAL:
2000 		case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK:
2001 		case VIEW_TRACK_TARGET:
2002 		case ONE_THIRD_THROTTLE:
2003 		case TWO_THIRDS_THROTTLE:
2004 		case MINUS_5_PERCENT_THROTTLE:
2005 		case PLUS_5_PERCENT_THROTTLE:
2006 		case ZERO_THROTTLE:
2007 		case MAX_THROTTLE:
2008 		case TOGGLE_GLIDING:
2009 			return 0;
2010 
2011 		default :
2012 			Int3(); // bad bad bad
2013 			break;
2014 	}
2015 
2016 	return 1;
2017 }
2018 
2019 /**
2020  * Execute function corresponding to action n
2021  * Basically, these are actions which don't affect demo playback at all
2022  * @param n Action number
2023  */
button_function_demo_valid(int n)2024 int button_function_demo_valid(int n)
2025 {
2026 	// by default, we'll return "not processed". ret will get set to 1, if this is one of the keys which is always allowed, even in demo
2027 	// playback.
2028 	int ret = 0;
2029 
2030 	//	No keys, not even targeting keys, when player in death roll.  He can press keys after he blows up.
2031 	if (Game_mode & GM_DEAD_DIED){
2032 		return 0;
2033 	}
2034 
2035 	// any of these buttons are valid
2036 	switch(n){
2037 	case VIEW_CHASE:
2038 		control_used(VIEW_CHASE);
2039 		if(!Perspective_locked)
2040 		{
2041 			Viewer_mode ^= VM_CHASE;
2042 		}
2043 		else
2044 		{
2045 			snd_play( &Snds[SND_TARGET_FAIL] );
2046 		}
2047 		ret = 1;
2048 		break;
2049 
2050 	case VIEW_TRACK_TARGET: // Target padlock mode toggle (Swifty)
2051 		control_used(VIEW_TRACK_TARGET);
2052 		if (!Perspective_locked) {
2053 			if(Viewer_mode & VM_TRACK) {
2054 				chase_slew_angles.h = 0;
2055 				chase_slew_angles.p = 0;
2056 			}
2057 			Viewer_mode ^= VM_TRACK;
2058 		} else {
2059 			snd_play( &Snds[SND_TARGET_FAIL] );
2060 		}
2061 		ret = 1;
2062 		break;
2063 
2064 	case VIEW_EXTERNAL:
2065 		control_used(VIEW_EXTERNAL);
2066 		if(!Perspective_locked)
2067 		{
2068 			Viewer_mode ^= VM_EXTERNAL;
2069 			Viewer_mode &= ~VM_EXTERNAL_CAMERA_LOCKED;	// reset camera lock when leaving/entering external view
2070 		}
2071 		else
2072 		{
2073 			snd_play( &Snds[SND_TARGET_FAIL] );
2074 		}
2075 		ret = 1;
2076 		break;
2077 
2078 	case VIEW_TOPDOWN:
2079 		control_used(VIEW_TOPDOWN);
2080 		if(!Perspective_locked)
2081 		{
2082 			Viewer_mode ^= VM_TOPDOWN;
2083 		}
2084 		else
2085 		{
2086 			snd_play( &Snds[SND_TARGET_FAIL] );
2087 		}
2088 		ret = 1;
2089 		break;
2090 
2091 	case VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK:
2092 		control_used(VIEW_EXTERNAL_TOGGLE_CAMERA_LOCK);
2093 		if ( Viewer_mode & VM_EXTERNAL ) {
2094 		Viewer_mode ^= VM_EXTERNAL_CAMERA_LOCKED;
2095 		if ( Viewer_mode & VM_EXTERNAL_CAMERA_LOCKED ) {
2096 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External camera is locked, controls will move ship", 36));
2097 			} else {
2098 				HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "External camera is free, controls will move the camera, not the ship", 37));
2099 			}
2100 		}
2101 		ret = 1;
2102 		break;
2103 
2104 	case VIEW_OTHER_SHIP:
2105 		control_used(VIEW_OTHER_SHIP);
2106 		if ( Player_ai->target_objnum < 0 || Perspective_locked) {
2107 			snd_play( &Snds[SND_TARGET_FAIL] );
2108 		} else {
2109 			if ( Objects[Player_ai->target_objnum].type != OBJ_SHIP )  {
2110 				snd_play( &Snds[SND_TARGET_FAIL] );
2111 			} else {
2112 				Viewer_mode ^= VM_OTHER_SHIP;
2113 			}
2114 		}
2115 		ret = 1;
2116 		break;
2117 
2118 	case TIME_SLOW_DOWN:
2119 		if ( Game_mode & GM_NORMAL ) {
2120 			// Goober5000 - time dilation only available in cheat mode (see above);
2121 			// now you can do it with or without pressing the tilde, per Kazan's request
2122 			if ( ((Game_time_compression > F1_0) || (Cheats_enabled && (Game_time_compression > (F1_0/MAX_TIME_DIVIDER)))) && !Time_compression_locked) {
2123 				change_time_compression(0.5f);
2124 			} else {
2125 				gamesnd_play_error_beep();
2126 			}
2127 		} else {
2128 			gamesnd_play_error_beep();
2129 		}
2130 		ret = 1;
2131 		break;
2132 
2133 	case TIME_SPEED_UP:
2134 		if ( Game_mode & GM_NORMAL ) {
2135 			if ( (Game_time_compression < (F1_0*MAX_TIME_MULTIPLIER)) && !Time_compression_locked ) {
2136 				change_time_compression(2.0f);
2137 			} else {
2138 				gamesnd_play_error_beep();
2139 			}
2140 		} else {
2141 			gamesnd_play_error_beep();
2142 		}
2143 		ret = 1;
2144 		break;
2145 	}
2146 
2147 	// done
2148 	return ret;
2149 }
2150 
key_is_targeting(int n)2151 bool key_is_targeting(int n)
2152 {
2153 	switch(n) {
2154 		case TARGET_NEXT:
2155 		case TARGET_PREV:
2156 		case TARGET_NEXT_CLOSEST_HOSTILE:
2157 		case TARGET_PREV_CLOSEST_HOSTILE:
2158 		case TARGET_NEXT_CLOSEST_FRIENDLY:
2159 		case TARGET_PREV_CLOSEST_FRIENDLY:
2160 		case TARGET_SHIP_IN_RETICLE:
2161 		case TARGET_LAST_TRANMISSION_SENDER:
2162 		case TARGET_CLOSEST_SHIP_ATTACKING_TARGET:
2163 		case TARGET_CLOSEST_SHIP_ATTACKING_SELF:
2164 		case TARGET_TARGETS_TARGET:
2165 		case TARGET_SUBOBJECT_IN_RETICLE:
2166 		case TARGET_PREV_SUBOBJECT:
2167 		case TARGET_NEXT_SUBOBJECT:
2168 			return true;
2169 
2170 		default:
2171 			return false;
2172 	}
2173 }
2174 
2175 /**
2176  * Execute function corresponding to action n (BUTTON_ from KeyControl.h)
2177  * @return 1 when action was taken
2178  */
button_function(int n)2179 int button_function(int n)
2180 {
2181 	Assert(n >= 0);
2182 
2183 	if (Control_config[n].disabled)
2184 		return 0;
2185 
2186 	// check if the button has been set to be ignored by a SEXP
2187 	if (Ignored_keys[n]) {
2188 		if (Ignored_keys[n] > 0) {
2189 			Ignored_keys[n]--;
2190 		}
2191 		return 0;
2192 	}
2193 
2194 	//	No keys, not even targeting keys, when player in death roll.  He can press keys after he blows up.
2195 	if (Game_mode & GM_DEAD_DIED){
2196 		return 0;
2197 	}
2198 
2199 	// Goober5000 - if the ship doesn't have subspace drive, jump key doesn't work: so test and exit early
2200 	if (Player_ship->flags2 & SF2_NO_SUBSPACE_DRIVE)
2201 	{
2202 		switch(n)
2203 		{
2204 			case END_MISSION:
2205 				control_used(n);	// set the timestamp for when we used the control, in case we need it
2206 				return 1;			// pretend we took the action: if we return 0, strange stuff may happen
2207 		}
2208 	}
2209 
2210 	// Goober5000 - if we have primitive sensors, some keys don't work: so test and exit early
2211 	if (Player_ship->flags2 & SF2_PRIMITIVE_SENSORS)
2212 	{
2213 		switch (n)
2214 		{
2215 			case MATCH_TARGET_SPEED:
2216 			case TOGGLE_AUTO_MATCH_TARGET_SPEED:
2217 			case TOGGLE_AUTO_TARGETING:
2218 			case TARGET_NEXT:
2219 			case TARGET_PREV:
2220 			case TARGET_NEXT_CLOSEST_HOSTILE:
2221 			case TARGET_PREV_CLOSEST_HOSTILE:
2222 			case TARGET_NEXT_CLOSEST_FRIENDLY:
2223 			case TARGET_PREV_CLOSEST_FRIENDLY:
2224 			case TARGET_SHIP_IN_RETICLE:
2225 			case TARGET_LAST_TRANMISSION_SENDER:
2226 			case TARGET_CLOSEST_REPAIR_SHIP:
2227 			case TARGET_CLOSEST_SHIP_ATTACKING_TARGET:
2228 			case STOP_TARGETING_SHIP:
2229 			case TARGET_CLOSEST_SHIP_ATTACKING_SELF:
2230 			case TARGET_TARGETS_TARGET:
2231 			case TARGET_SUBOBJECT_IN_RETICLE:
2232 			case TARGET_NEXT_SUBOBJECT:
2233 			case TARGET_PREV_SUBOBJECT:
2234 			case STOP_TARGETING_SUBSYSTEM:
2235 			case TARGET_NEXT_BOMB:
2236 			case TARGET_PREV_BOMB:
2237 			case TARGET_NEXT_UNINSPECTED_CARGO:
2238 			case TARGET_PREV_UNINSPECTED_CARGO:
2239 			case TARGET_NEWEST_SHIP:
2240 			case TARGET_NEXT_LIVE_TURRET:
2241 			case TARGET_PREV_LIVE_TURRET:
2242 			case TARGET_NEXT_ESCORT_SHIP:
2243 				control_used(n);	// set the timestamp for when we used the control, in case we need it
2244 				return 1;			// pretend we took the action: if we return 0, strange stuff may happen
2245 		}
2246 	}
2247 
2248 	switch(n) {
2249 		// following are not handled here, but we need to bypass the Int3()
2250 		case LAUNCH_COUNTERMEASURE:
2251 		case VIEW_SLEW:
2252 		case VIEW_TRACK_TARGET:
2253 		case ONE_THIRD_THROTTLE:
2254 		case TWO_THIRDS_THROTTLE:
2255 		case MINUS_5_PERCENT_THROTTLE:
2256 		case PLUS_5_PERCENT_THROTTLE:
2257 		case ZERO_THROTTLE:
2258 		case MAX_THROTTLE:
2259 		case TOGGLE_GLIDING:
2260 		case GLIDE_WHEN_PRESSED:
2261 			return 0;
2262 	}
2263 
2264 	/**
2265 	 * This switch handles the critical buttons
2266 	 *
2267 	 * button_function_critical is also called from network
2268 	 */
2269 	switch (n) {
2270 		case CYCLE_PRIMARY_WEAPON_SEQUENCE:
2271 		case CYCLE_NEXT_PRIMARY:	// cycle to next primary weapon
2272 		case CYCLE_PREV_PRIMARY:	// cycle to previous primary weapon
2273 		case CYCLE_SECONDARY:		// cycle to next secondary weapon
2274 		case CYCLE_NUM_MISSLES:		// cycle number of missiles fired from secondary bank
2275 		case SHIELD_EQUALIZE:		// equalize shield energy to all quadrants
2276 		case SHIELD_XFER_TOP:		// transfer shield energy to front
2277 		case SHIELD_XFER_BOTTOM:	// transfer shield energy to rear
2278 		case SHIELD_XFER_LEFT:		// transfer shield energy to left
2279 		case SHIELD_XFER_RIGHT:		// transfer shield energy to right
2280 		case XFER_SHIELD:			// transfer energy to shield from weapons
2281 		case XFER_LASER:			// transfer energy to weapons from shield
2282 			return button_function_critical(n);
2283 			break;
2284 
2285 		case INCREASE_WEAPON:		// increase weapon recharge rate
2286 		case DECREASE_WEAPON:		// decrease weapon recharge rate
2287 		case INCREASE_SHIELD:		// increase shield recharge rate
2288 		case DECREASE_SHIELD:		// decrease shield recharge rate
2289 		case INCREASE_ENGINE:		// increase energy to engines
2290 		case DECREASE_ENGINE:		// decrease energy to engines
2291 		case ETS_EQUALIZE:
2292 			if ((Player_ship->flags2 & SF2_NO_ETS) == 0) {
2293 				hud_gauge_popup_start(HUD_ETS_GAUGE);
2294 				return button_function_critical(n);
2295 			}
2296 			return 1;
2297 			break;
2298 	}
2299 
2300 	/**
2301 	 * Assume the switches below will catch the key, if not, set to FALSE in default
2302 	 *
2303 	 * Below, you must not use return in cases,
2304 	 * else the check for invalid keys will fail
2305 	 */
2306 	int keyHasBeenUsed = TRUE;
2307 
2308 	switch(n) {
2309 		// message all netplayers button
2310 		case MULTI_MESSAGE_ALL:
2311 			multi_msg_key_down(MULTI_MSG_ALL);
2312 			break;
2313 
2314 		// message all friendlies button
2315 		case MULTI_MESSAGE_FRIENDLY:
2316 			multi_msg_key_down(MULTI_MSG_FRIENDLY);
2317 			break;
2318 
2319 		// message all hostiles button
2320 		case MULTI_MESSAGE_HOSTILE:
2321 			multi_msg_key_down(MULTI_MSG_HOSTILE);
2322 			break;
2323 
2324 		// message targeted ship (if player)
2325 		case MULTI_MESSAGE_TARGET:
2326 			multi_msg_key_down(MULTI_MSG_TARGET);
2327 			break;
2328 
2329 		// undefined in multiplayer for clients right now
2330 		// toggle auto-match target speed
2331 		case TOGGLE_AUTO_MATCH_TARGET_SPEED:
2332 			// multiplayer observers can't match target speed
2333 			if((Game_mode & GM_MULTIPLAYER) && (Net_player != NULL) && ((Net_player->flags & NETINFO_FLAG_OBSERVER) || (Player_obj->type == OBJ_OBSERVER)) ){
2334 				break;
2335 			}
2336 
2337 			Player->flags ^= PLAYER_FLAGS_AUTO_MATCH_SPEED;
2338 			control_used(TOGGLE_AUTO_MATCH_TARGET_SPEED);
2339 			hud_gauge_popup_start(HUD_AUTO_SPEED);
2340 			if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
2341 				snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f);
2342 				if ( !(Player->flags & PLAYER_FLAGS_MATCH_TARGET) ) {
2343 					player_match_target_speed();
2344 				}
2345 			}
2346 			else
2347 			{
2348 				snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f);
2349 				player_match_target_speed();
2350 			}
2351 			break;
2352 
2353 		case TARGET_NEXT_UNINSPECTED_CARGO:
2354 			hud_target_uninspected_object(1);
2355 			break;
2356 
2357 		case TARGET_PREV_UNINSPECTED_CARGO:
2358 			hud_target_uninspected_object(0);
2359 			break;
2360 
2361 		case TARGET_NEWEST_SHIP:
2362 			hud_target_newest_ship();
2363 			break;
2364 
2365 		case TARGET_NEXT_LIVE_TURRET:
2366 			hud_target_live_turret(1);
2367 			break;
2368 
2369 		case TARGET_PREV_LIVE_TURRET:
2370 			hud_target_live_turret(0);
2371 			break;
2372 
2373 		// end the mission
2374 		case END_MISSION:
2375 			// in multiplayer, all end mission requests should go through the server
2376 			if (Game_mode & GM_MULTIPLAYER) {
2377 				multi_handle_end_mission_request();
2378 				break;
2379 			}
2380 
2381 			control_used(END_MISSION);
2382 
2383 			if (collide_predict_large_ship(Player_obj, 200.0f)
2384 			|| (Ship_info[Ships[Player_obj->instance].ship_info_index].warpout_type == WT_HYPERSPACE
2385 			&& collide_predict_large_ship(Player_obj, 100000.0f)))
2386 			{
2387 				gamesnd_play_iface(SND_GENERAL_FAIL);
2388 				HUD_printf(XSTR( "** WARNING ** Collision danger.  Subspace drive not activated.", 39));
2389 			} else if (!ship_engine_ok_to_warp(Player_ship)) {
2390 				gamesnd_play_iface(SND_GENERAL_FAIL);
2391 				HUD_printf(XSTR("Engine failure.  Cannot engage subspace drive.", 40));
2392 			} else if (!ship_navigation_ok_to_warp(Player_ship)) {
2393 				gamesnd_play_iface(SND_GENERAL_FAIL);
2394 				HUD_printf(XSTR("Navigation failure.  Cannot engage subspace drive.", 1596));
2395 			} else if ( (Player_obj != NULL) && object_get_gliding(Player_obj)) {
2396 				gamesnd_play_iface(SND_GENERAL_FAIL);
2397 				HUD_printf(XSTR("Cannot engage subspace drive while gliding.", 1597));
2398 			} else {
2399 				gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START );
2400 			}
2401 			break;
2402 
2403 		case ADD_REMOVE_ESCORT:
2404 			if ( Player_ai->target_objnum >= 0 ) {
2405 				control_used(ADD_REMOVE_ESCORT);
2406 				hud_add_remove_ship_escort(Player_ai->target_objnum);
2407 			}
2408 			break;
2409 
2410 		// if i'm an observer, zoom to my targeted object
2411 		case MULTI_OBSERVER_ZOOM_TO:
2412 			multi_obs_zoom_to_target();
2413 			break;
2414 
2415 		// toggle between high and low HUD contrast
2416 		case TOGGLE_HUD_CONTRAST:
2417 			gamesnd_play_iface(SND_USER_SELECT);
2418 			hud_toggle_contrast();
2419 			break;
2420 
2421 		// toggle network info
2422 		case MULTI_TOGGLE_NETINFO:
2423 			extern int Multi_display_netinfo;
2424 			Multi_display_netinfo = !Multi_display_netinfo;
2425 			break;
2426 
2427 		// self destruct (multiplayer only)
2428 		case MULTI_SELF_DESTRUCT:
2429 			if (!(Game_mode & GM_MULTIPLAYER)) {
2430 				break;
2431 			}
2432 
2433 			// bogus netplayer
2434 			if ( (Net_player == NULL) || (Net_player->m_player == NULL) ) {
2435 				break;
2436 			}
2437 
2438 			// blow myself up, if I'm the server
2439 			if (Net_player->flags & NETINFO_FLAG_AM_MASTER) {
2440 				if ( (Net_player->m_player->objnum >= 0) &&
2441 					(Net_player->m_player->objnum < MAX_OBJECTS) &&
2442 					(Objects[Net_player->m_player->objnum].type == OBJ_SHIP) &&
2443 					(Objects[Net_player->m_player->objnum].instance >= 0) &&
2444 					(Objects[Net_player->m_player->objnum].instance < MAX_SHIPS) )
2445 				{
2446 
2447 					ship_self_destruct(&Objects[Net_player->m_player->objnum]);
2448 				}
2449 			} else { // otherwise send a packet to the server
2450 				send_self_destruct_packet();
2451 			}
2452 			break;
2453 
2454 		case TOGGLE_HUD:
2455 			gamesnd_play_iface(SND_USER_SELECT);
2456 			hud_toggle_draw();
2457 			break;
2458 
2459 		case HUD_TARGETBOX_TOGGLE_WIREFRAME:
2460 			if (!Lock_targetbox_mode) {
2461 				gamesnd_play_iface(SND_USER_SELECT);
2462 				hud_targetbox_switch_wireframe_mode();
2463 			} else {
2464 				gamesnd_play_iface(SND_GENERAL_FAIL);
2465 			}
2466 			break;
2467 
2468 		// Autopilot key control
2469 		case AUTO_PILOT_TOGGLE:
2470 			if (!(The_mission.flags & MISSION_FLAG_DEACTIVATE_AP)) {
2471 				if (AutoPilotEngaged) {
2472 					if (Cmdline_autopilot_interruptable == 1) //allow WCS to disable autopilot interrupt via commandline
2473 						EndAutoPilot();
2474 				} else {
2475 					if (!StartAutopilot())
2476 						gamesnd_play_iface(SND_GENERAL_FAIL);
2477 				}
2478 			}
2479 			break;
2480 
2481 		case NAV_CYCLE:
2482 			if (!Sel_NextNav())
2483 				gamesnd_play_iface(SND_GENERAL_FAIL);
2484 			break;
2485 		default:
2486 			keyHasBeenUsed = FALSE;
2487 			break;
2488 	}
2489 
2490 	/**
2491 	 * The key has been handled, return early before the timestamp is set
2492 	 */
2493 	if (keyHasBeenUsed) {
2494 		return 1;
2495 	}
2496 
2497 	/**
2498 	 * 	Update the last used timestamp of this key
2499 	 */
2500 	control_used(n);
2501 
2502 	if ( hud_sensors_ok(Player_ship) ) {
2503 		int keyHasBeenUsed = TRUE;
2504 		switch(n) {
2505 			// target next
2506 			case TARGET_NEXT:
2507 				hud_target_next();
2508 				break;
2509 
2510 			// target previous
2511 			case TARGET_PREV:
2512 				hud_target_prev();
2513 				break;
2514 
2515 			// target the next hostile target
2516 			case TARGET_NEXT_CLOSEST_HOSTILE:
2517 				hud_target_next_list();
2518 				break;
2519 
2520 			// target the previous closest hostile
2521 			case TARGET_PREV_CLOSEST_HOSTILE:
2522 				hud_target_next_list(1,0);
2523 				break;
2524 
2525 			// target the next friendly ship
2526 			case TARGET_NEXT_CLOSEST_FRIENDLY:
2527 				hud_target_next_list(0);
2528 				break;
2529 
2530 			// target the closest friendly ship
2531 			case TARGET_PREV_CLOSEST_FRIENDLY:
2532 				hud_target_next_list(0,0);
2533 				break;
2534 
2535 			// target ship closest to center of reticle
2536 			case TARGET_SHIP_IN_RETICLE:
2537 				hud_target_in_reticle_new();
2538 				break;
2539 
2540 			case TARGET_LAST_TRANMISSION_SENDER:
2541 				hud_target_last_transmit();
2542 				break;
2543 
2544 			// target the closest ship attacking current target
2545 			case TARGET_CLOSEST_SHIP_ATTACKING_TARGET:
2546 				if (Player_ai->target_objnum < 0) {
2547 					snd_play(&Snds[SND_TARGET_FAIL]);
2548 					break;
2549 				}
2550 
2551 				hud_target_closest(iff_get_attacker_mask(obj_team(&Objects[Player_ai->target_objnum])), Player_ai->target_objnum);
2552 				break;
2553 
2554 			// target closest ship that is attacking player
2555 			case TARGET_CLOSEST_SHIP_ATTACKING_SELF:
2556 				hud_target_next_list(1, 0, iff_get_attacker_mask(Player_ship->team), OBJ_INDEX(Player_obj), TRUE, 0, 1);
2557 				break;
2558 
2559 			// target your target's target
2560 			case TARGET_TARGETS_TARGET:
2561 				hud_target_targets_target();
2562 				break;
2563 
2564 			// target ships subsystem in reticle
2565 			case TARGET_SUBOBJECT_IN_RETICLE:
2566 				hud_target_subsystem_in_reticle();
2567 				break;
2568 
2569 			case TARGET_PREV_SUBOBJECT:
2570 				hud_target_prev_subobject();
2571 				break;
2572 
2573 			// target next subsystem on current target
2574 			case TARGET_NEXT_SUBOBJECT:
2575 				hud_target_next_subobject();
2576 				break;
2577 
2578 			default:
2579 				keyHasBeenUsed = FALSE;
2580 				break;
2581 		};
2582 		if (keyHasBeenUsed) {
2583 			return 1;
2584 		}
2585 	}
2586 	else
2587 	{
2588 		//if sensors are gone, and the passed key is one of the targeting keys, we need to exit here before we hit the Int3() later in this function
2589 		if (key_is_targeting(n)) {
2590 			return 1;
2591 		}
2592 	}
2593 
2594 	keyHasBeenUsed = TRUE;
2595 	switch(n) {
2596 		// undefined in multiplayer for clients right now
2597 		// match target speed
2598 		case MATCH_TARGET_SPEED:
2599 			// If player is auto-matching, break auto-match speed
2600 			if ( Player->flags & PLAYER_FLAGS_AUTO_MATCH_SPEED ) {
2601 				Player->flags &= ~PLAYER_FLAGS_AUTO_MATCH_SPEED;
2602 			}
2603 			player_match_target_speed();
2604 			break;
2605 
2606 		// toggle auto-targeting
2607 		case TOGGLE_AUTO_TARGETING:
2608 			hud_gauge_popup_start(HUD_AUTO_TARGET);
2609 			Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING;
2610 			if ( Players[Player_num].flags & PLAYER_FLAGS_AUTO_TARGETING ) {
2611 				if (hud_sensors_ok(Player_ship)) {
2612 					hud_target_closest(iff_get_attackee_mask(Player_ship->team), -1, FALSE, TRUE );
2613 					snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f);
2614 					//HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting activated", -1));
2615 				} else {
2616 					Players[Player_num].flags ^= PLAYER_FLAGS_AUTO_TARGETING;
2617 				}
2618 			} else {
2619 				snd_play(&Snds[SND_SHIELD_XFER_OK], 1.0f);
2620 				//HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Auto targeting deactivated", -1));
2621 			}
2622 			break;
2623 
2624 		// target the closest repair ship
2625 		case TARGET_CLOSEST_REPAIR_SHIP:
2626 			// AL: Try to find the closest repair ship coming to repair the player... if no support
2627 			//		 ships are coming to rearm the player, just try for the closest repair ship
2628 			if ( hud_target_closest_repair_ship(OBJ_INDEX(Player_obj)) == 0 ) {
2629 				if ( hud_target_closest_repair_ship() == 0 ) {
2630 					snd_play(&Snds[SND_TARGET_FAIL]);
2631 				}
2632 			}
2633 			break;
2634 
2635 		// stop targeting ship
2636 		case STOP_TARGETING_SHIP:
2637 			hud_cease_targeting();
2638 			break;
2639 
2640 		// stop targeting subsystems on ship
2641 		case STOP_TARGETING_SUBSYSTEM:
2642 			hud_cease_subsystem_targeting();
2643 			break;
2644 
2645 		case TARGET_NEXT_BOMB:
2646 			hud_target_missile(Player_obj, 1);
2647 			break;
2648 
2649 		case TARGET_PREV_BOMB:
2650 			hud_target_missile(Player_obj, 0);
2651 			break;
2652 
2653 		// wingman message: attack current target
2654 		case ATTACK_MESSAGE:
2655 			hud_squadmsg_shortcut( ATTACK_TARGET_ITEM );
2656 			break;
2657 
2658 		// wingman message: disarm current target
2659 		case DISARM_MESSAGE:
2660 			hud_squadmsg_shortcut( DISARM_TARGET_ITEM );
2661 			break;
2662 
2663 		// wingman message: disable current target
2664 		case DISABLE_MESSAGE:
2665 			hud_squadmsg_shortcut( DISABLE_TARGET_ITEM );
2666 			break;
2667 
2668 		// wingman message: disable current target
2669 		case ATTACK_SUBSYSTEM_MESSAGE:
2670 			hud_squadmsg_shortcut( DISABLE_SUBSYSTEM_ITEM );
2671 			break;
2672 
2673 		// wingman message: capture current target
2674 		case CAPTURE_MESSAGE:
2675 			hud_squadmsg_shortcut( CAPTURE_TARGET_ITEM );
2676 			break;
2677 
2678 		// wingman message: engage enemy
2679 		case ENGAGE_MESSAGE:
2680 			hud_squadmsg_shortcut( ENGAGE_ENEMY_ITEM );
2681 			break;
2682 
2683 		// wingman message: form on my wing
2684 		case FORM_MESSAGE:
2685 			hud_squadmsg_shortcut( FORMATION_ITEM );
2686 			break;
2687 
2688 		// wingman message: protect current target
2689 		case PROTECT_MESSAGE:
2690 			hud_squadmsg_shortcut( PROTECT_TARGET_ITEM );
2691 			break;
2692 
2693 		// wingman message: cover me
2694 		case COVER_MESSAGE:
2695 			hud_squadmsg_shortcut( COVER_ME_ITEM );
2696 			break;
2697 
2698 		// wingman message: warp out
2699 		case WARP_MESSAGE:
2700 			hud_squadmsg_shortcut( DEPART_ITEM );
2701 			break;
2702 
2703 		case IGNORE_MESSAGE:
2704 			hud_squadmsg_shortcut( IGNORE_TARGET_ITEM );
2705 			break;
2706 
2707 		// rearm message
2708 		case REARM_MESSAGE:
2709 			hud_squadmsg_rearm_shortcut();
2710 			break;
2711 
2712 		// cycle to next radar range
2713 		case RADAR_RANGE_CYCLE:
2714 			HUD_config.rp_dist++;
2715 			if ( HUD_config.rp_dist >= RR_MAX_RANGES )
2716 				HUD_config.rp_dist = 0;
2717 
2718 			HUD_sourced_printf(HUD_SOURCE_HIDDEN, XSTR( "Radar range set to %s", 38), Radar_range_text(HUD_config.rp_dist));
2719 			break;
2720 
2721 		// toggle the squadmate messaging menu
2722 		case SQUADMSG_MENU:
2723 			hud_squadmsg_toggle();				// leave the details to the messaging code!!!
2724 			break;
2725 
2726 		// show the mission goals screen
2727 		case SHOW_GOALS:
2728 			gameseq_post_event( GS_EVENT_SHOW_GOALS );
2729 			break;
2730 
2731 		// end the mission
2732 		case END_MISSION:
2733 			// in multiplayer, all end mission requests should go through the server
2734 			if(Game_mode & GM_MULTIPLAYER){
2735 				multi_handle_end_mission_request();
2736 				break;
2737 			}
2738 
2739 			control_used(END_MISSION);
2740 
2741 			if (collide_predict_large_ship(Player_obj, 200.0f)
2742 			|| (Ship_info[Ships[Player_obj->instance].ship_info_index].warpout_type == WT_HYPERSPACE
2743 			&& collide_predict_large_ship(Player_obj, 100000.0f)))
2744 			{
2745 				gamesnd_play_iface(SND_GENERAL_FAIL);
2746 				HUD_printf(XSTR( "** WARNING ** Collision danger.  Subspace drive not activated.", 39));
2747 			} else if (!ship_engine_ok_to_warp(Player_ship)) {
2748 				gamesnd_play_iface(SND_GENERAL_FAIL);
2749 				HUD_printf(XSTR("Engine failure.  Cannot engage subspace drive.", 40));
2750 			} else if (!ship_navigation_ok_to_warp(Player_ship)) {
2751 				gamesnd_play_iface(SND_GENERAL_FAIL);
2752 				HUD_printf(XSTR("Navigation failure.  Cannot engage subspace drive.", 1572));
2753 			} else if (Player_obj != NULL && object_get_gliding(Player_obj)) {
2754 				gamesnd_play_iface(SND_GENERAL_FAIL);
2755 				HUD_printf(XSTR("Cannot engage subspace drive while gliding.", 1573));
2756 			} else {
2757 				gameseq_post_event( GS_EVENT_PLAYER_WARPOUT_START );
2758 			}
2759 			break;
2760 
2761 		case ADD_REMOVE_ESCORT:
2762 			if ( Player_ai->target_objnum >= 0 ) {
2763 				control_used(ADD_REMOVE_ESCORT);
2764 				hud_add_remove_ship_escort(Player_ai->target_objnum);
2765 			}
2766 			break;
2767 
2768 		case ESCORT_CLEAR:
2769 			hud_escort_clear_all(true);
2770 			break;
2771 
2772 		case TARGET_NEXT_ESCORT_SHIP:
2773 			hud_escort_target_next();
2774 			break;
2775 
2776 		default:
2777 			keyHasBeenUsed = FALSE;
2778 			break;
2779 	};
2780 	if (keyHasBeenUsed) {
2781 		return 1;
2782 	}
2783 
2784 	/**
2785 	 * All keys should have been handled above, if not panic
2786 	 */
2787 	mprintf(("Unknown key %d at %s:%u\n", n, __FILE__, __LINE__));
2788 	Int3();
2789 
2790 	return 1;
2791 }
2792 
2793 /**
2794  * Calls multiple event handlers for each active button
2795  * @param bi currently active buttons
2796  */
button_info_do(button_info * bi)2797 void button_info_do(button_info *bi)
2798 {
2799 	for (int i = 0; i < CCFG_MAX; i++) {
2800 		if( button_info_query(bi, i) ) {
2801 			int keyHasBeenUsed = FALSE;
2802 
2803 			if( !keyHasBeenUsed ) {
2804 				keyHasBeenUsed = button_function_demo_valid(i);
2805 			}
2806 
2807 			if( !keyHasBeenUsed ) {
2808 				keyHasBeenUsed = button_function(i);
2809 			}
2810 
2811 			if( keyHasBeenUsed ) {
2812 				button_info_unset(bi, i);
2813 			}
2814 		}
2815 	}
2816 }
2817 
2818 
2819 /**
2820  * Set the bit for the corresponding action n (BUTTON_ from KeyControl.h)
2821  */
button_info_set(button_info * bi,int n)2822 void button_info_set(button_info *bi, int n)
2823 {
2824 	int field_num, bit_num;
2825 
2826 	field_num = n / 32;
2827 	bit_num = n % 32;
2828 
2829 	bi->status[field_num] |= (1 << bit_num);
2830 }
2831 
2832 /**
2833  * Unset the bit for the corresponding action n (BUTTON_ from KeyControl.h)
2834  */
button_info_unset(button_info * bi,int n)2835 void button_info_unset(button_info *bi, int n)
2836 {
2837 	int field_num, bit_num;
2838 
2839 	field_num = n / 32;
2840 	bit_num = n % 32;
2841 
2842 	bi->status[field_num] &= ~(1 << bit_num);
2843 }
2844 
button_info_query(button_info * bi,int n)2845 int button_info_query(button_info *bi, int n)
2846 {
2847 	return bi->status[n / 32] & (1 << (n % 32));
2848 }
2849 
2850 /**
2851  * Clear out the ::button_info struct
2852  */
button_info_clear(button_info * bi)2853 void button_info_clear(button_info *bi)
2854 {
2855 	int i;
2856 
2857 	for (i=0; i<NUM_BUTTON_FIELDS; i++) {
2858 		bi->status[i] = 0;
2859 	}
2860 }
2861 
2862 /**
2863  * Strip out all noncritical keys from the ::button_info struct
2864  */
button_strip_noncritical_keys(button_info * bi)2865 void button_strip_noncritical_keys(button_info *bi)
2866 {
2867 	int idx;
2868 
2869 	// clear out all noncritical keys
2870 	for(idx=0;idx<Non_critical_key_set_size;idx++){
2871 		button_info_unset(bi,Non_critical_key_set[idx]);
2872 	}
2873 }
2874