1 /*
2 * XPilot NG, a multiplayer space war game.
3 *
4 * Copyright (C) 2000-2004 by
5 *
6 * Uoti Urpala <uau@users.sourceforge.net>
7 * Kristian S�derblom <kps@users.sourceforge.net>
8 *
9 * Copyright (C) 1991-2001 by
10 *
11 * Bj�rn Stabell <bjoern@xpilot.org>
12 * Ken Ronny Schouten <ken@xpilot.org>
13 * Bert Gijsbers <bert@xpilot.org>
14 * Dick Balaska <dick@xpilot.org>
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
29 */
30
31 #include "xpserver.h"
32
team_dead(int team)33 bool team_dead(int team)
34 {
35 int i;
36
37 for (i = 0; i < NumPlayers; i++) {
38 player_t *pl = Player_by_index(i);
39
40 if (pl->team != team)
41 continue;
42
43 if (Player_is_alive(pl)
44 || Player_is_appearing(pl)
45 || Player_is_killed(pl))
46 return false;
47 }
48 return true;
49 }
50
51 /*
52 * Return true if a lock is allowed.
53 */
Player_lock_allowed(player_t * pl,player_t * lock_pl)54 static bool Player_lock_allowed(player_t *pl, player_t *lock_pl)
55 {
56 /* we can never lock on ourselves, nor on NULL. */
57 if (lock_pl == NULL || pl->id == lock_pl->id)
58 return false;
59
60 /* Spectators can watch freely */
61 if (pl->rectype == 2)
62 return true;
63
64 /* if we are actively playing then we can lock since we are not viewing. */
65 if (Player_is_active(pl))
66 return true;
67
68 /* if there is no team play then we can always lock on anyone. */
69 if (!BIT(world->rules->mode, TEAM_PLAY))
70 return true;
71
72 /* we can always lock on players from our own team. */
73 if (Players_are_teammates(pl, lock_pl))
74 return true;
75
76 /* if lockOtherTeam is true then we can always lock on other teams. */
77 if (options.lockOtherTeam)
78 return true;
79
80 /* if our own team is dead then we can lock on anyone. */
81 if (team_dead(pl->team))
82 return true;
83
84 /* can't find any reason why this lock should be allowed. */
85 return false;
86 }
87
Player_lock_next_or_prev(player_t * pl,int key)88 static void Player_lock_next_or_prev(player_t *pl, int key)
89 {
90 int i, j, ind = GetInd(pl->id);
91 player_t *pl_i;
92
93 if (NumPlayers == 0) /* Spectator? */
94 return;
95
96 j = i = GetInd(pl->lock.pl_id);
97 if (!BIT(pl->lock.tagged, LOCK_PLAYER))
98 i = j = 0;
99 if (j < 0 || j >= NumPlayers)
100 /* kps - handle this some other way */
101 fatal("Illegal player lock target");
102
103 do {
104 if (key == KEY_LOCK_PREV) {
105 if (--i < 0)
106 i = NumPlayers - 1;
107 } else {
108 if (++i >= NumPlayers)
109 i = 0;
110 }
111 if (i == j)
112 return;
113
114 pl_i = Player_by_index(i);
115 } while (i == ind
116 || Player_is_paused(pl_i)
117 || Player_is_dead(pl_i)
118 || Player_is_waiting(pl_i)
119 || !Player_lock_allowed(pl, pl_i));
120
121 if (i == ind)
122 CLR_BIT(pl->lock.tagged, LOCK_PLAYER);
123 else {
124 pl->lock.pl_id = pl_i->id;
125 SET_BIT(pl->lock.tagged, LOCK_PLAYER);
126 }
127 }
128
Player_lock_closest(player_t * pl,bool next)129 int Player_lock_closest(player_t *pl, bool next)
130 {
131 int i;
132 double dist = 0.0, best, l;
133 player_t *lock_pl = NULL, *new_pl = NULL;
134
135 if (!next)
136 CLR_BIT(pl->lock.tagged, LOCK_PLAYER);
137
138 if (BIT(pl->lock.tagged, LOCK_PLAYER)) {
139 lock_pl = Player_by_id(pl->lock.pl_id);
140 dist = Wrap_length(lock_pl->pos.cx - pl->pos.cx,
141 lock_pl->pos.cy - pl->pos.cy);
142 }
143 best = FLT_MAX;
144 for (i = 0; i < NumPlayers; i++) {
145 player_t *pl_i = Player_by_index(i);
146
147 if (pl_i == lock_pl
148 || !Player_is_active(pl_i)
149 || !Player_lock_allowed(pl, pl_i)
150 || Player_owns_tank(pl, pl_i)
151 || Players_are_teammates(pl, pl_i)
152 || Players_are_allies(pl, pl_i))
153 continue;
154
155 l = Wrap_length(pl_i->pos.cx - pl->pos.cx,
156 pl_i->pos.cy - pl->pos.cy);
157 if (l >= dist && l < best) {
158 best = l;
159 new_pl = pl_i;
160 }
161 }
162 if (new_pl == NULL)
163 return 0;
164
165 SET_BIT(pl->lock.tagged, LOCK_PLAYER);
166 pl->lock.pl_id = new_pl->id;
167
168 return 1;
169 }
170
Player_change_home(player_t * pl)171 static void Player_change_home(player_t *pl)
172 {
173 player_t *pl2 = NULL;
174 base_t *base2 = NULL;
175 base_t *enemybase = NULL;
176 double l, dist = 1e19;
177 int i;
178
179 for (i = 0; i < Num_bases(); i++) {
180 base_t *base = Base_by_index(i);
181
182 l = Wrap_length(pl->pos.cx - base->pos.cx,
183 pl->pos.cy - base->pos.cy);
184 if (l < dist
185 && l < 1.5 * BLOCK_CLICKS) {
186 if (base->team != TEAM_NOT_SET
187 && base->team != pl->team) {
188 enemybase = base;
189 continue;
190 }
191 base2 = base;
192 dist = l;
193 }
194 }
195
196 if (base2 == NULL) {
197 if (enemybase)
198 Set_player_message_f(pl, "Base belongs to team %d. "
199 "Enemy home bases can't be occupied. "
200 "[*Server notice*]", enemybase->team);
201 else
202 Set_player_message(pl, "You are too far away from "
203 "a suitable base to change home. "
204 "[*Server notice*]");
205 return;
206 }
207
208 /* Maybe the base is our own base? */
209 if (base2 == pl->home_base)
210 return;
211
212 /* Let's see if someone else in our has this base. */
213 for (i = 0; i < NumPlayers; i++) {
214 player_t *pl_i = Player_by_index(i);
215
216 if (pl_i->id != pl->id
217 && !Player_is_tank(pl_i)
218 && base2 == pl_i->home_base) {
219 pl2 = pl_i;
220 break;
221 }
222 }
223
224 #if 0
225 /* kps - perhaps this isn't a good idea. */
226 if (pl2 != NULL
227 && Players_are_teammates(pl, pl2)
228 && Get_Score(pl) <= Get_Score(pl2)) {
229 Set_player_message(pl, "You must have a higher score than your "
230 "teammate to take over their base. "
231 "[*Server notice*]");
232 return;
233 }
234 #endif
235
236 pl->home_base = base2;
237 sound_play_all(CHANGE_HOME_SOUND);
238
239 if (pl2 != NULL) {
240 Pick_startpos(pl2);
241 Set_message_f("%s has taken over %s's home base.",
242 pl->name, pl2->name);
243 } else
244 Set_message_f("%s has changed home base.", pl->name);
245
246 /*
247 * Send info about new bases.
248 */
249 for (i = 0; i < NumPlayers; i++) {
250 player_t *pl_i = Player_by_index(i);
251
252 if (pl_i->conn != NULL)
253 Send_base(pl_i->conn, pl->id, pl->home_base->ind);
254 }
255 for (i = 0; i < NumSpectators; i++)
256 Send_base(Player_by_index(i + spectatorStart)->conn,
257 pl->id, pl->home_base->ind);
258
259 if (pl2) {
260 for (i = 0; i < NumPlayers; i++) {
261 player_t *pl_i = Player_by_index(i);
262
263 if (pl_i->conn != NULL)
264 Send_base(pl_i->conn, pl2->id, pl2->home_base->ind);
265 }
266 for (i = 0; i < NumSpectators; i++)
267 Send_base(Player_by_index(i + spectatorStart)->conn,
268 pl2->id, pl2->home_base->ind);
269 }
270 }
271
Player_refuel(player_t * pl)272 static void Player_refuel(player_t *pl)
273 {
274 int i;
275 double l, dist = 1e19;
276
277 if (!BIT(pl->have, HAS_REFUEL))
278 return;
279
280 CLR_BIT(pl->used, USES_REFUEL);
281 for (i = 0; i < Num_fuels(); i++) {
282 fuel_t *fs = Fuel_by_index(i);
283
284 l = Wrap_length(pl->pos.cx - fs->pos.cx,
285 pl->pos.cy - fs->pos.cy);
286 if (!Player_is_refueling(pl)
287 || l < dist) {
288 SET_BIT(pl->used, USES_REFUEL);
289 pl->fs = i;
290 dist = l;
291 }
292 }
293 }
294
295 /* Repair target or possibly something else. */
Player_repair(player_t * pl)296 static void Player_repair(player_t *pl)
297 {
298 int i;
299 double l, dist = 1e19;
300
301 if (!BIT(pl->have, HAS_REPAIR))
302 return;
303
304 CLR_BIT(pl->used, USES_REPAIR);
305 for (i = 0; i < Num_targets(); i++) {
306 target_t *targ = Target_by_index(i);
307
308 if (targ->team == pl->team
309 && targ->dead_ticks <= 0) {
310 l = Wrap_length(pl->pos.cx - targ->pos.cx,
311 pl->pos.cy - targ->pos.cy);
312 if (!Player_is_repairing(pl)
313 || l < dist) {
314 SET_BIT(pl->used, USES_REPAIR);
315 pl->repair_target = i;
316 dist = l;
317 }
318 }
319 }
320 }
321
322 /* Player pressed pause key. */
Player_toggle_pause(player_t * pl)323 static void Player_toggle_pause(player_t *pl)
324 {
325 enum pausetype {
326 unknown, paused, hoverpaused
327 } pausetype = unknown;
328
329 if (Player_is_paused(pl))
330 pausetype = paused;
331 else if (Player_is_hoverpaused(pl))
332 pausetype = hoverpaused;
333 else if (Player_is_appearing(pl))
334 pausetype = paused;
335 else {
336 base_t *base = pl->home_base;
337 double dist = Wrap_length(pl->pos.cx - base->pos.cx,
338 pl->pos.cy - base->pos.cy);
339 double minv;
340
341 if (dist < 1.5 * BLOCK_CLICKS) {
342 minv = 3.0;
343 pausetype = paused;
344 } else {
345 minv = 5.0;
346 pausetype = hoverpaused;
347 }
348 minv += VECTOR_LENGTH(World_gravity(pl->pos));
349 if (pl->velocity > minv) {
350 Set_player_message(pl,
351 "You need to slow down to pause. [*Server notice*]");
352 return;
353 }
354 }
355
356 switch (pausetype) {
357 case paused:
358 if (Player_is_hoverpaused(pl))
359 break;
360
361 if (Player_uses_autopilot(pl))
362 Autopilot(pl, false);
363
364 Pause_player(pl, !Player_is_paused(pl));
365 break;
366
367 case hoverpaused:
368 if (Player_is_paused(pl))
369 break;
370
371 if (!Player_is_hoverpaused(pl)) {
372 /*
373 * Turn hover pause on, together with shields.
374 */
375 pl->pause_count = 5 * 12;
376 Player_self_destruct(pl, false);
377 SET_BIT(pl->pl_status, HOVERPAUSE);
378
379 if (Player_uses_emergency_thrust(pl))
380 Emergency_thrust(pl, false);
381
382 if (BIT(pl->used, HAS_EMERGENCY_SHIELD))
383 Emergency_shield(pl, false);
384
385 if (!Player_uses_autopilot(pl))
386 Autopilot(pl, true);
387
388 if (Player_is_phasing(pl))
389 Phasing(pl, false);
390
391 /*
392 * Don't allow firing while paused. Similar
393 * reasons exist for refueling, connector and
394 * tractor beams. Other items are allowed (esp.
395 * cloaking).
396 */
397 Player_used_kill(pl);
398 if (BIT(pl->have, HAS_SHIELD))
399 SET_BIT(pl->used, HAS_SHIELD);
400 } else if (pl->pause_count <= 0) {
401 Autopilot(pl, false);
402 CLR_BIT(pl->pl_status, HOVERPAUSE);
403 if (!BIT(pl->have, HAS_SHIELD))
404 CLR_BIT(pl->used, HAS_SHIELD);
405 }
406 break;
407 default:
408 warn("Player_toggle_pause: BUG: unknown pause type.");
409 break;
410 }
411 }
412
413 #define FOOBARSWAP(a, b) {double tmp = a; a = b; b = tmp;}
414
Player_swap_settings(player_t * pl)415 static void Player_swap_settings(player_t *pl)
416 {
417 if (Player_is_hoverpaused(pl)
418 || Player_uses_autopilot(pl))
419 return;
420
421 /* kps - turnacc == 0.0 ? */
422 if (pl->turnacc == 0.0) {
423 FOOBARSWAP(pl->power, pl->power_s);
424 FOOBARSWAP(pl->turnspeed, pl->turnspeed_s);
425 FOOBARSWAP(pl->turnresistance, pl->turnresistance_s);
426 }
427 }
428 #undef FOOBARSWAP
429
430
Player_toggle_compass(player_t * pl)431 static void Player_toggle_compass(player_t *pl)
432 {
433 int i, k, ind = GetInd(pl->id);
434
435 if (!BIT(pl->have, HAS_COMPASS))
436 return;
437 TOGGLE_BIT(pl->used, USES_COMPASS);
438
439 if (!Player_uses_compass(pl))
440 return;
441
442 /*
443 * Verify if the lock has ever been initialized at all
444 * and if the lock is still valid.
445 */
446 if (BIT(pl->lock.tagged, LOCK_PLAYER)
447 && NumPlayers > 1
448 && (k = pl->lock.pl_id) > 0
449 && (i = GetInd(k)) > 0
450 && i < NumPlayers
451 && Player_by_index(i)->id == k
452 && i != ind)
453 return;
454
455 Player_lock_closest(pl, false);
456 }
457
458
Pause_player(player_t * pl,bool on)459 void Pause_player(player_t *pl, bool on)
460 {
461 int i;
462
463 /* kps - add support for pausing robots ? */
464 if (!Player_is_human(pl))
465 return;
466 if (on && !Player_is_paused(pl)) { /* Turn pause mode on */
467 if (pl->team != TEAM_NOT_SET)
468 world->teams[pl->team].SwapperId = NO_ID;
469 /* Minimum pause time is 10 seconds at gamespeed 12. */
470 pl->pause_count = 10 * 12;
471 /* player might have paused when recovering */
472 pl->recovery_count = 0;
473 pl->updateVisibility = true;
474 Player_set_state(pl, PL_STATE_PAUSED);
475 pl->pauseTime = 0;
476 if (options.baselessPausing) {
477 if (pl->team != TEAM_NOT_SET)
478 world->teams[pl->team].NumMembers--;
479 pl->pl_prev_team = pl->team;
480 /* kps - probably broken if no team play */
481 pl->team = 0;
482 for (i = 0; i < NumPlayers; i++) {
483 player_t *pl_i = Player_by_index(i);
484
485 if (pl_i->conn != NULL) {
486 Send_base(pl_i->conn, NO_ID, pl->home_base->ind);
487 Send_team(pl_i->conn, pl->id, 0);
488 }
489 }
490 for (i = spectatorStart; i < spectatorStart + NumSpectators; i++) {
491 player_t *pl_i = Player_by_index(i);
492
493 Send_base(pl_i->conn, NO_ID, pl->home_base->ind);
494 Send_team(pl_i->conn, pl->id, 0);
495 }
496 pl->home_base = NULL;
497 }
498 updateScores = true;
499
500 Detach_ball(pl, NULL);
501 if (Player_uses_autopilot(pl)
502 || Player_is_hoverpaused(pl)) {
503 CLR_BIT(pl->pl_status, HOVERPAUSE);
504 Autopilot(pl, false);
505 }
506
507 pl->vel.x = pl->vel.y = 0.0;
508 pl->acc.x = pl->acc.y = 0.0;
509
510 pl->obj_status &= ~(KILL_OBJ_BITS);
511
512 /*
513 * kps - possibly add option to make items reset
514 * to initial when pausing
515 */
516
517 pl->forceVisible = 0;
518 pl->ecmcount = 0;
519 pl->emergency_thrust_left = 0;
520 pl->emergency_shield_left = 0;
521 pl->phasing_left = 0;
522 pl->self_destruct_count = 0;
523 pl->damaged = 0;
524 pl->stunned = 0;
525 pl->lock.distance = 0;
526 pl->used = DEF_USED;
527
528 for (i = 0; i < MAX_TEAMS ; i++) {
529 if (world->teams[i].SwapperId == pl->id)
530 world->teams[i].SwapperId = NO_ID;
531 }
532 }
533 else if (!on && Player_is_paused(pl)) { /* Turn pause mode off */
534
535 if (pl->pause_count > 0) {
536 Set_player_message(pl, "You can't unpause so soon after pausing. "
537 "[*Server notice*]");
538 return;
539 }
540 /* there seems to be a race condition if idleTime is set later */
541 pl->idleTime = 0;
542
543 if (pl->home_base == NULL) {
544 int team = pl->pl_prev_team;
545
546 /* kps - code copied from Cmd_team() */
547 if (team > 0 && team < MAX_TEAMS
548 && (world->teams[team].NumBases
549 > world->teams[team].NumMembers)) {
550 pl->team = team;
551 world->teams[pl->team].NumMembers++;
552 Set_swapper_state(pl);
553 Pick_startpos(pl);
554 Send_info_about_player(pl);
555 }
556 else {
557 Set_player_message(pl, "You don't have a base. "
558 "Select team to unpause. "
559 "[*Server notice*]");
560 return;
561 }
562 }
563
564 updateScores = true;
565 if (BIT(world->rules->mode, LIMITED_LIVES)) {
566 /* too late, wait for next round */
567 Player_set_state(pl, PL_STATE_WAITING);
568 } else {
569 Player_set_state(pl, PL_STATE_APPEARING);
570 Go_home(pl);
571 }
572 Player_reset_timing(pl);
573 }
574 }
575
576
Handle_keyboard(player_t * pl)577 int Handle_keyboard(player_t *pl)
578 {
579 int i, key;
580 bool pressed;
581
582 /*assert(!Player_is_killed(pl));*/
583
584 for (key = 0; key < NUM_KEYS; key++) {
585 /* Find first keyv element where last_keyv isn't equal to prev_keyv. */
586 if (pl->last_keyv[key / BITV_SIZE] == pl->prev_keyv[key / BITV_SIZE]) {
587 /* Skip to next keyv element. */
588 key |= (BITV_SIZE - 1);
589 continue;
590 }
591 /* Now check which specific key it is that has changed state. */
592 while (BITV_ISSET(pl->last_keyv, key)
593 == BITV_ISSET(pl->prev_keyv, key)) {
594 if (++key >= NUM_KEYS)
595 break;
596 }
597 if (key >= NUM_KEYS)
598 break;
599
600 pressed = (BITV_ISSET(pl->last_keyv, key) != 0) ? true : false;
601 BITV_TOGGLE(pl->prev_keyv, key);
602 /*
603 * KEY_SHIELD would interfere with auto-idle-pause
604 * due to client auto-shield hack.
605 */
606 if (key != KEY_SHIELD)
607 pl->idleTime = 0;
608
609 /*
610 * Allow these functions while you're 'dead'.
611 */
612 if (!Player_is_alive(pl)
613 || Player_is_hoverpaused(pl)) {
614 switch (key) {
615 case KEY_PAUSE:
616 case KEY_LOCK_NEXT:
617 case KEY_LOCK_PREV:
618 case KEY_LOCK_CLOSE:
619 case KEY_LOCK_NEXT_CLOSE:
620 case KEY_TOGGLE_NUCLEAR:
621 case KEY_TOGGLE_CLUSTER:
622 case KEY_TOGGLE_IMPLOSION:
623 case KEY_TOGGLE_VELOCITY:
624 case KEY_TOGGLE_MINI:
625 case KEY_TOGGLE_SPREAD:
626 case KEY_TOGGLE_POWER:
627 case KEY_TOGGLE_LASER:
628 case KEY_TOGGLE_COMPASS:
629 case KEY_CLEAR_MODIFIERS:
630 case KEY_LOAD_MODIFIERS_1:
631 case KEY_LOAD_MODIFIERS_2:
632 case KEY_LOAD_MODIFIERS_3:
633 case KEY_LOAD_MODIFIERS_4:
634 case KEY_LOAD_LOCK_1:
635 case KEY_LOAD_LOCK_2:
636 case KEY_LOAD_LOCK_3:
637 case KEY_LOAD_LOCK_4:
638 case KEY_REPROGRAM:
639 case KEY_SWAP_SETTINGS:
640 case KEY_TANK_NEXT:
641 case KEY_TANK_PREV:
642 case KEY_TURN_LEFT: /* Needed so that we don't get */
643 case KEY_TURN_RIGHT: /* out-of-sync with the turnacc */
644 break;
645 default:
646 continue;
647 }
648 }
649
650 /* allow these functions while you're phased */
651 if (Player_is_phasing(pl) && pressed) {
652 switch (key) {
653 case KEY_LOCK_NEXT:
654 case KEY_LOCK_PREV:
655 case KEY_LOCK_CLOSE:
656 case KEY_SHIELD:
657 case KEY_TOGGLE_NUCLEAR:
658 case KEY_TURN_LEFT:
659 case KEY_TURN_RIGHT:
660 case KEY_SELF_DESTRUCT:
661 case KEY_LOSE_ITEM:
662 case KEY_PAUSE:
663 case KEY_TANK_NEXT:
664 case KEY_TANK_PREV:
665 case KEY_TOGGLE_VELOCITY:
666 case KEY_TOGGLE_CLUSTER:
667 case KEY_SWAP_SETTINGS:
668 case KEY_THRUST:
669 case KEY_CLOAK:
670 case KEY_DROP_BALL:
671 case KEY_TALK:
672 case KEY_LOCK_NEXT_CLOSE:
673 case KEY_TOGGLE_COMPASS:
674 case KEY_TOGGLE_MINI:
675 case KEY_TOGGLE_SPREAD:
676 case KEY_TOGGLE_POWER:
677 case KEY_TOGGLE_LASER:
678 case KEY_EMERGENCY_THRUST:
679 case KEY_CLEAR_MODIFIERS:
680 case KEY_LOAD_MODIFIERS_1:
681 case KEY_LOAD_MODIFIERS_2:
682 case KEY_LOAD_MODIFIERS_3:
683 case KEY_LOAD_MODIFIERS_4:
684 case KEY_SELECT_ITEM:
685 case KEY_PHASING:
686 case KEY_TOGGLE_IMPLOSION:
687 case KEY_REPROGRAM:
688 case KEY_LOAD_LOCK_1:
689 case KEY_LOAD_LOCK_2:
690 case KEY_LOAD_LOCK_3:
691 case KEY_LOAD_LOCK_4:
692 case KEY_EMERGENCY_SHIELD:
693 case KEY_TOGGLE_AUTOPILOT:
694 break;
695 default:
696 continue;
697 }
698 }
699
700 if (pressed) { /* --- KEYPRESS --- */
701 switch (key) {
702
703 case KEY_TANK_NEXT:
704 case KEY_TANK_PREV:
705 if (pl->fuel.num_tanks) {
706 pl->fuel.current += (key==KEY_TANK_NEXT) ? 1 : -1;
707 if (pl->fuel.current < 0)
708 pl->fuel.current = pl->fuel.num_tanks;
709 else if (pl->fuel.current > pl->fuel.num_tanks)
710 pl->fuel.current = 0;
711 }
712 break;
713
714 case KEY_TANK_DETACH:
715 Tank_handle_detach(pl);
716 break;
717
718 case KEY_LOCK_NEXT:
719 case KEY_LOCK_PREV:
720 Player_lock_next_or_prev(pl, key);
721 break;
722
723 case KEY_TOGGLE_COMPASS:
724 Player_toggle_compass(pl);
725 break;
726
727 case KEY_LOCK_NEXT_CLOSE:
728 if (!Player_lock_closest(pl, true))
729 Player_lock_closest(pl, false);
730 break;
731
732 case KEY_LOCK_CLOSE:
733 Player_lock_closest(pl, false);
734 break;
735
736 case KEY_CHANGE_HOME:
737 Player_change_home(pl);
738 break;
739
740 case KEY_SHIELD:
741 if (BIT(pl->have, HAS_SHIELD)) {
742 SET_BIT(pl->used, HAS_SHIELD);
743 CLR_BIT(pl->used, HAS_LASER); /* don't remove! */
744 }
745 break;
746
747 case KEY_DROP_BALL:
748 Detach_ball(pl, NULL);
749 break;
750
751 case KEY_FIRE_SHOT:
752 if (!BIT(pl->used, HAS_SHIELD|HAS_SHOT)
753 && BIT(pl->have, HAS_SHOT)) {
754 SET_BIT(pl->used, HAS_SHOT);
755 /* Set this so that one shot is fired even if player
756 * releases the key during the same frame */
757 pl->did_shoot = true;
758 }
759 break;
760
761 case KEY_FIRE_MISSILE:
762 if (pl->item[ITEM_MISSILE] > 0)
763 Fire_shot(pl, OBJ_SMART_SHOT, pl->dir);
764 break;
765
766 case KEY_FIRE_HEAT:
767 if (pl->item[ITEM_MISSILE] > 0)
768 Fire_shot(pl, OBJ_HEAT_SHOT, pl->dir);
769 break;
770
771 case KEY_FIRE_TORPEDO:
772 if (pl->item[ITEM_MISSILE] > 0)
773 Fire_shot(pl, OBJ_TORPEDO, pl->dir);
774 break;
775
776 case KEY_FIRE_LASER:
777 if (pl->item[ITEM_LASER] > 0 && BIT(pl->used, HAS_SHIELD) == 0)
778 SET_BIT(pl->used, HAS_LASER);
779 break;
780
781 case KEY_TOGGLE_NUCLEAR:
782 switch (Mods_get(pl->mods, ModsNuclear)) {
783 case MODS_NUCLEAR:
784 Mods_set(&pl->mods, ModsNuclear,
785 MODS_NUCLEAR|MODS_FULLNUCLEAR);
786 break;
787 case 0:
788 Mods_set(&pl->mods, ModsNuclear, MODS_NUCLEAR);
789 break;
790 default:
791 Mods_set(&pl->mods, ModsNuclear, 0);
792 break;
793 }
794
795 break;
796
797 case KEY_TOGGLE_CLUSTER:
798 {
799 int cluster = Mods_get(pl->mods, ModsCluster);
800
801 Mods_set(&pl->mods, ModsCluster, !cluster);
802 }
803 break;
804
805 case KEY_TOGGLE_IMPLOSION:
806 {
807 int implosion = Mods_get(pl->mods, ModsImplosion);
808
809 Mods_set(&pl->mods, ModsImplosion, !implosion);
810 }
811 break;
812
813 case KEY_TOGGLE_VELOCITY:
814 {
815 int velocity = Mods_get(pl->mods, ModsVelocity);
816
817 if (velocity == MODS_VELOCITY_MAX)
818 velocity = 0;
819 else
820 velocity++;
821 Mods_set(&pl->mods, ModsVelocity, velocity);
822 }
823 break;
824
825 case KEY_TOGGLE_MINI:
826 {
827 int mini = Mods_get(pl->mods, ModsMini);
828
829 if (mini == MODS_MINI_MAX)
830 mini = 0;
831 else
832 mini++;
833 Mods_set(&pl->mods, ModsMini, mini);
834 }
835 break;
836
837 case KEY_TOGGLE_SPREAD:
838 {
839 int spread = Mods_get(pl->mods, ModsSpread);
840
841 if (spread == MODS_SPREAD_MAX)
842 spread = 0;
843 else
844 spread++;
845 Mods_set(&pl->mods, ModsSpread, spread);
846 }
847 break;
848
849 case KEY_TOGGLE_LASER:
850 {
851 int laser = Mods_get(pl->mods, ModsLaser);
852
853 if (laser == MODS_LASER_MAX)
854 laser = 0;
855 else
856 laser++;
857 Mods_set(&pl->mods, ModsLaser, laser);
858 }
859 break;
860
861 case KEY_TOGGLE_POWER:
862 {
863 int power = Mods_get(pl->mods, ModsPower);
864
865 if (power == MODS_POWER_MAX)
866 power = 0;
867 else
868 power++;
869 Mods_set(&pl->mods, ModsPower, power);
870 }
871 break;
872
873 case KEY_CLEAR_MODIFIERS:
874 Mods_clear(&pl->mods);
875 break;
876
877 case KEY_REPROGRAM:
878 SET_BIT(pl->pl_status, REPROGRAM);
879 break;
880
881 case KEY_LOAD_MODIFIERS_1:
882 case KEY_LOAD_MODIFIERS_2:
883 case KEY_LOAD_MODIFIERS_3:
884 case KEY_LOAD_MODIFIERS_4: {
885 modifiers_t *m = &(pl->modbank[key - KEY_LOAD_MODIFIERS_1]);
886
887 if (BIT(pl->pl_status, REPROGRAM))
888 *m = pl->mods;
889 else {
890 pl->mods = *m;
891 Mods_filter(&pl->mods);
892 }
893 break;
894 }
895
896 case KEY_LOAD_LOCK_1:
897 case KEY_LOAD_LOCK_2:
898 case KEY_LOAD_LOCK_3:
899 case KEY_LOAD_LOCK_4: {
900 int *l = &(pl->lockbank[key - KEY_LOAD_LOCK_1]);
901
902 if (BIT(pl->pl_status, REPROGRAM)) {
903 if (BIT(pl->lock.tagged, LOCK_PLAYER))
904 *l = pl->lock.pl_id;
905 } else {
906 if (*l != -1
907 && Player_lock_allowed(pl, Player_by_id(*l))) {
908 pl->lock.pl_id = *l;
909 SET_BIT(pl->lock.tagged, LOCK_PLAYER);
910 }
911 }
912 break;
913 }
914
915 case KEY_TOGGLE_AUTOPILOT:
916 if (Player_has_autopilot(pl))
917 Autopilot(pl, !Player_uses_autopilot(pl));
918 break;
919
920 case KEY_EMERGENCY_THRUST:
921 if (Player_has_emergency_thrust(pl))
922 Emergency_thrust(pl,
923 !Player_uses_emergency_thrust(pl));
924 break;
925
926 case KEY_EMERGENCY_SHIELD:
927 if (BIT(pl->have, HAS_EMERGENCY_SHIELD))
928 Emergency_shield(pl,
929 !BIT(pl->used, HAS_EMERGENCY_SHIELD));
930 break;
931
932 case KEY_DROP_MINE:
933 Place_mine(pl);
934 break;
935
936 case KEY_DETACH_MINE:
937 Place_moving_mine(pl);
938 break;
939
940 case KEY_DETONATE_MINES:
941 Detonate_mines(pl);
942 break;
943
944 case KEY_TURN_LEFT:
945 case KEY_TURN_RIGHT:
946 if (Player_uses_autopilot(pl))
947 Autopilot(pl, false);
948 pl->turnacc = 0;
949 #if 0
950 if (frame_loops % 50 == 0)
951 Set_player_message(pl, "You should use the mouse to turn."
952 " [*Server notice*]");
953 #endif
954 if (BITV_ISSET(pl->last_keyv, KEY_TURN_LEFT))
955 pl->turnacc += pl->turnspeed;
956 if (BITV_ISSET(pl->last_keyv, KEY_TURN_RIGHT))
957 pl->turnacc -= pl->turnspeed;
958 break;
959
960 case KEY_SELF_DESTRUCT:
961 if (Player_is_self_destructing(pl))
962 Player_self_destruct(pl, false);
963 else
964 Player_self_destruct(pl, true);
965 break;
966
967 case KEY_PAUSE:
968 Player_toggle_pause(pl);
969 break;
970
971 case KEY_SWAP_SETTINGS:
972 Player_swap_settings(pl);
973 break;
974
975 case KEY_REFUEL:
976 Player_refuel(pl);
977 break;
978
979 case KEY_REPAIR:
980 Player_repair(pl);
981 break;
982
983 case KEY_CONNECTOR:
984 if (BIT(pl->have, HAS_CONNECTOR))
985 SET_BIT(pl->used, USES_CONNECTOR);
986 break;
987
988 case KEY_PRESSOR_BEAM:
989 if (Player_has_tractor_beam(pl)) {
990 pl->tractor_is_pressor = true;
991 SET_BIT(pl->used, USES_TRACTOR_BEAM);
992 }
993 break;
994
995 case KEY_TRACTOR_BEAM:
996 if (Player_has_tractor_beam(pl)) {
997 pl->tractor_is_pressor = false;
998 SET_BIT(pl->used, USES_TRACTOR_BEAM);
999 }
1000 break;
1001
1002 case KEY_THRUST:
1003 if (Player_uses_autopilot(pl))
1004 Autopilot(pl, false);
1005 Thrust(pl, true);
1006 break;
1007
1008 case KEY_CLOAK:
1009 if (pl->item[ITEM_CLOAK] > 0)
1010 Cloak(pl, !Player_is_cloaked(pl));
1011 break;
1012
1013 case KEY_ECM:
1014 Fire_ecm(pl);
1015 break;
1016
1017 case KEY_TRANSPORTER:
1018 Do_transporter(pl);
1019 break;
1020
1021 case KEY_DEFLECTOR:
1022 if (pl->item[ITEM_DEFLECTOR] > 0)
1023 Deflector(pl, !BIT(pl->used, USES_DEFLECTOR));
1024 break;
1025
1026 case KEY_HYPERJUMP:
1027 Initiate_hyperjump(pl);
1028 break;
1029
1030 case KEY_PHASING:
1031 if (Player_has_phasing_device(pl))
1032 Phasing(pl, !Player_is_phasing(pl));
1033 break;
1034
1035 case KEY_SELECT_ITEM:
1036 for (i = 0; i < NUM_ITEMS; i++) {
1037 if (++pl->lose_item >= NUM_ITEMS)
1038 pl->lose_item = 0;
1039 if (pl->lose_item == ITEM_FUEL
1040 || pl->lose_item == ITEM_TANK)
1041 /* can't lose fuel or tanks. */
1042 continue;
1043 if (pl->item[pl->lose_item] > 0) {
1044 /* 2: key down; 1: key up */
1045 pl->lose_item_state = 2;
1046 break;
1047 }
1048 }
1049 break;
1050
1051 case KEY_LOSE_ITEM:
1052 do_lose_item(pl);
1053 break;
1054
1055 default:
1056 break;
1057 }
1058 } else {
1059 /* --- KEYRELEASE --- */
1060 switch (key) {
1061 case KEY_TURN_LEFT:
1062 case KEY_TURN_RIGHT:
1063 if (Player_uses_autopilot(pl))
1064 Autopilot(pl, false);
1065 pl->turnacc = 0;
1066 if (BITV_ISSET(pl->last_keyv, KEY_TURN_LEFT))
1067 pl->turnacc += pl->turnspeed;
1068 if (BITV_ISSET(pl->last_keyv, KEY_TURN_RIGHT))
1069 pl->turnacc -= pl->turnspeed;
1070 break;
1071
1072 case KEY_REFUEL:
1073 CLR_BIT(pl->used, USES_REFUEL);
1074 break;
1075
1076 case KEY_REPAIR:
1077 CLR_BIT(pl->used, USES_REPAIR);
1078 break;
1079
1080 case KEY_CONNECTOR:
1081 CLR_BIT(pl->used, USES_CONNECTOR);
1082 break;
1083
1084 case KEY_TRACTOR_BEAM:
1085 case KEY_PRESSOR_BEAM:
1086 CLR_BIT(pl->used, USES_TRACTOR_BEAM);
1087 break;
1088
1089 case KEY_SHIELD:
1090 if (BIT(pl->used, HAS_SHIELD)) {
1091 CLR_BIT(pl->used, HAS_SHIELD|HAS_LASER);
1092 /*
1093 * Insert the default fireRepeatRate between lowering
1094 * shields and firing in order to prevent macros
1095 * and hacked clients.
1096 */
1097 pl->shot_time = frame_time;
1098 pl->laser_time = frame_time;
1099 }
1100 break;
1101
1102 case KEY_FIRE_SHOT:
1103 CLR_BIT(pl->used, HAS_SHOT);
1104 break;
1105
1106 case KEY_FIRE_LASER:
1107 CLR_BIT(pl->used, HAS_LASER);
1108 break;
1109
1110 case KEY_THRUST:
1111 if (Player_uses_autopilot(pl))
1112 Autopilot(pl, false);
1113 Thrust(pl, false);
1114 break;
1115
1116 case KEY_REPROGRAM:
1117 CLR_BIT(pl->pl_status, REPROGRAM);
1118 break;
1119
1120 case KEY_SELECT_ITEM:
1121 pl->lose_item_state = 1;
1122 break;
1123
1124 default:
1125 break;
1126 }
1127 }
1128 }
1129 memcpy(pl->prev_keyv, pl->last_keyv, sizeof(pl->last_keyv));
1130
1131 return 1;
1132 }
1133