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