/* $Id: event.c,v 5.12 2002/04/13 18:26:03 bertg Exp $ * * XPilot, a multiplayer gravity war game. Copyright (C) 1991-2001 by * * Bjørn Stabell * Ken Ronny Schouten * Bert Gijsbers * Dick Balaska * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #ifdef _WINDOWS # include "NT/winServer.h" #endif #define SERVER #include "version.h" #include "config.h" #include "serverconst.h" #include "global.h" #include "proto.h" #include "score.h" #include "map.h" #include "saudio.h" #include "bit.h" #include "netserver.h" char event_version[] = VERSION; #define SWAP(_a, _b) {DFLOAT _tmp = _a; _a = _b; _b = _tmp;} /* * Globals. */ static char msg[MSG_LEN]; static void Refuel(int ind) { player *pl = Players[ind]; int i; DFLOAT l, dist = 1e9; if (!BIT(pl->have, HAS_REFUEL)) return; CLR_BIT(pl->used, HAS_REFUEL); for (i=0; ipos.x - World.fuel[i].pix_pos.x, pl->pos.y - World.fuel[i].pix_pos.y); if (BIT(pl->used, HAS_REFUEL) == 0 || l < dist) { SET_BIT(pl->used, HAS_REFUEL); pl->fs = i; dist = l; } } } } static void Repair(int ind) { player *pl = Players[ind]; int i; DFLOAT l, dist = 1e9; DFLOAT x, y; target_t *targ = World.targets; if (!BIT(pl->have, HAS_REPAIR)) return; CLR_BIT(pl->used, HAS_REPAIR); for (i = 0; i < World.NumTargets; i++, targ++) { if (targ->team == pl->team && targ->dead_time <= 0) { x = targ->pos.x*BLOCK_SZ + BLOCK_SZ/2; y = targ->pos.y*BLOCK_SZ + BLOCK_SZ/2; l = Wrap_length(pl->pos.x - x, pl->pos.y - y); if (BIT(pl->used, HAS_REPAIR) == 0 || l < dist) { SET_BIT(pl->used, HAS_REPAIR); pl->repair_target = i; dist = l; } } } } bool team_dead(int team) { int i; bool alive = false; for (i = 0; i < NumPlayers; i++) { if (Players[i]->team == team && BIT(Players[i]->status, PLAYING|GAME_OVER) == PLAYING) { alive = true; break; } } return (!alive); } /* * Return true if a lock is allowed. */ static bool Player_lock_allowed(int ind, int lock) { player *pl = Players[ind]; /* we can never lock on ourselves, nor on -1. */ if (ind == lock || lock == -1) { return false; } /* if we are actively playing then we can lock since we are not viewing. */ if (BIT(pl->status, PLAYING|PAUSE|GAME_OVER) == PLAYING) { return true; } /* if there is no team play then we can always lock on anyone. */ if (!BIT(World.rules->mode, TEAM_PLAY)) { return true; } /* we can always lock on players from our own team. */ if (TEAM(ind, lock)) { return true; } /* if lockOtherTeam is true then we can always lock on other teams. */ if (lockOtherTeam) { return true; } /* if our own team is dead then we can lock on anyone. */ if (team_dead(pl->team)) { return true; } /* can't find any reason why this lock should be allowed. */ return false; } /* * Sven Mascheck: * If all _opponents are paused, then even LOCK_NEXT (ot LOCK_PREV) * might not lock_next (or lock_prev), as Player_lock_closest() might * be called [ "event.c" line 466 ] : * This happens when the player is not locked on any one anymore - * and this happens if he tried to lock_closest before (if all * opponents are paused). * Player_lock_closest() is called with (ind, 0) and that means that * the lock is cleared in _any case_ with the current code - that could * be done without calling Player_lock_closest(). * (btw, code in Player_lock_closest() looks like 'evolutionary code :) * I am not sure where to fix that locking problem * ( in "case KEY_LOCK_NEXT" or in Player_lock_closest() ). * I tried to find a solution but now i am bit screwed up.. :) */ int Player_lock_closest(int ind, int next) { player *pl = Players[ind]; int lock, i, newpl; DFLOAT dist, best, l; if (!next) { CLR_BIT(pl->lock.tagged, LOCK_PLAYER); } if (BIT(pl->lock.tagged, LOCK_PLAYER)) { lock = GetInd[pl->lock.pl_id]; dist = Wrap_length(Players[lock]->pos.x - pl->pos.x, Players[lock]->pos.y - pl->pos.y); } else { lock = -1; dist = 0.0; } newpl = -1; best = FLT_MAX; for (i = 0; i < NumPlayers; i++) { if (i == lock || (BIT(Players[i]->status, PLAYING|PAUSE|GAME_OVER) != PLAYING) || !Player_lock_allowed(ind, i) || OWNS_TANK(ind, i) || TEAM(ind,i) || ALLIANCE(ind, i)) { continue; } l = Wrap_length(Players[i]->pos.x - pl->pos.x, Players[i]->pos.y - pl->pos.y); if (l >= dist && l < best) { best = l; newpl = i; } } if (newpl == -1) { return 0; } SET_BIT(pl->lock.tagged, LOCK_PLAYER); pl->lock.pl_id = Players[newpl]->id; return 1; } void Pause_player(int ind, int onoff) { player *pl = Players[ind]; int i; if (onoff != 0 && !BIT(pl->status, PAUSE)) { /* Turn pause mode on */ pl->count = 10*FPS; pl->updateVisibility = 1; CLR_BIT(pl->status, SELF_DESTRUCT|PLAYING); SET_BIT(pl->status, PAUSE); pl->mychar = 'P'; updateScores = true; if (BIT(pl->have, HAS_BALL)) Detach_ball(ind, -1); } else if (onoff == 0 && BIT(pl->status, PAUSE)) { /* Turn pause mode off */ if (pl->count <= 0) { bool toolate = false; CLR_BIT(pl->status, PAUSE); updateScores = true; if (BIT(World.rules->mode, LIMITED_LIVES)) { for (i = 0; i < NumPlayers; i++) { /* If a non-team member has lost a life, * then it's too late to join. */ if (i == ind) { continue; } if (Players[i]->life < World.rules->lives && !TEAM(ind, i)) { toolate = true; break; } } } if (toolate) { pl->life = 0; pl->mychar = 'W'; SET_BIT(pl->status, GAME_OVER); } else { pl->mychar = ' '; Go_home(ind); SET_BIT(pl->status, PLAYING); if (BIT(World.rules->mode, LIMITED_LIVES)) { pl->life = World.rules->lives; } } if (BIT(World.rules->mode, TIMING)) { pl->round = 0; pl->check = 0; pl->time = 0; pl->best_lap = 0; pl->last_lap = 0; pl->last_lap_time = 0; } } } } int Handle_keyboard(int ind) { player *pl = Players[ind]; int i, j, k, key, pressed, xi, yi; DFLOAT minv; for (key = 0; key < NUM_KEYS; key++) { if (pl->last_keyv[key / BITV_SIZE] == pl->prev_keyv[key / BITV_SIZE]) { key |= (BITV_SIZE - 1); /* Skip to next keyv element */ continue; } while (BITV_ISSET(pl->last_keyv, key) == BITV_ISSET(pl->prev_keyv, key)) { if (++key >= NUM_KEYS) { break; } } if (key >= NUM_KEYS) { break; } pressed = BITV_ISSET(pl->last_keyv, key) != 0; BITV_TOGGLE(pl->prev_keyv, key); if (key != KEY_SHIELD) /* would interfere with auto-idle-pause.. */ pl->frame_last_busy = frame_loops; /* due to client auto-shield */ /* * Allow these functions before a round has started. */ if (round_delay > 0 && pressed) { switch (key) { case KEY_LOCK_NEXT: case KEY_LOCK_PREV: case KEY_LOCK_CLOSE: case KEY_TOGGLE_NUCLEAR: case KEY_PAUSE: case KEY_TANK_NEXT: case KEY_TANK_PREV: case KEY_TOGGLE_VELOCITY: case KEY_TOGGLE_CLUSTER: case KEY_SWAP_SETTINGS: case KEY_INCREASE_POWER: case KEY_DECREASE_POWER: case KEY_INCREASE_TURNSPEED: case KEY_DECREASE_TURNSPEED: case KEY_LOCK_NEXT_CLOSE: case KEY_TOGGLE_COMPASS: case KEY_TOGGLE_MINI: case KEY_TOGGLE_SPREAD: case KEY_TOGGLE_POWER: case KEY_TOGGLE_LASER: case KEY_CLEAR_MODIFIERS: case KEY_LOAD_MODIFIERS_1: case KEY_LOAD_MODIFIERS_2: case KEY_LOAD_MODIFIERS_3: case KEY_LOAD_MODIFIERS_4: case KEY_SELECT_ITEM: case KEY_TOGGLE_IMPLOSION: case KEY_REPROGRAM: case KEY_LOAD_LOCK_1: case KEY_LOAD_LOCK_2: case KEY_LOAD_LOCK_3: case KEY_LOAD_LOCK_4: break; default: continue; } } /* * Allow these functions while you're 'dead'. */ if (BIT(pl->status, PLAYING|GAME_OVER|PAUSE|HOVERPAUSE) != PLAYING) { switch (key) { case KEY_PAUSE: case KEY_LOCK_NEXT: case KEY_LOCK_PREV: case KEY_LOCK_CLOSE: case KEY_LOCK_NEXT_CLOSE: case KEY_TOGGLE_NUCLEAR: case KEY_TOGGLE_CLUSTER: case KEY_TOGGLE_IMPLOSION: case KEY_TOGGLE_VELOCITY: case KEY_TOGGLE_MINI: case KEY_TOGGLE_SPREAD: case KEY_TOGGLE_POWER: case KEY_TOGGLE_LASER: case KEY_TOGGLE_COMPASS: case KEY_CLEAR_MODIFIERS: case KEY_LOAD_MODIFIERS_1: case KEY_LOAD_MODIFIERS_2: case KEY_LOAD_MODIFIERS_3: case KEY_LOAD_MODIFIERS_4: case KEY_LOAD_LOCK_1: case KEY_LOAD_LOCK_2: case KEY_LOAD_LOCK_3: case KEY_LOAD_LOCK_4: case KEY_REPROGRAM: case KEY_SWAP_SETTINGS: case KEY_INCREASE_POWER: case KEY_DECREASE_POWER: case KEY_INCREASE_TURNSPEED: case KEY_DECREASE_TURNSPEED: case KEY_TANK_NEXT: case KEY_TANK_PREV: case KEY_TURN_LEFT: /* Needed so that we don't get */ case KEY_TURN_RIGHT: /* out-of-sync with the turnacc */ break; default: continue; } } /* allow these functions while you're phased */ if (BIT(pl->used, HAS_PHASING_DEVICE) && pressed) { switch (key) { case KEY_LOCK_NEXT: case KEY_LOCK_PREV: case KEY_LOCK_CLOSE: case KEY_SHIELD: case KEY_TOGGLE_NUCLEAR: case KEY_TURN_LEFT: case KEY_TURN_RIGHT: case KEY_SELF_DESTRUCT: case KEY_LOSE_ITEM: case KEY_PAUSE: case KEY_TANK_NEXT: case KEY_TANK_PREV: case KEY_TOGGLE_VELOCITY: case KEY_TOGGLE_CLUSTER: case KEY_SWAP_SETTINGS: case KEY_INCREASE_POWER: case KEY_DECREASE_POWER: case KEY_INCREASE_TURNSPEED: case KEY_DECREASE_TURNSPEED: case KEY_THRUST: case KEY_CLOAK: case KEY_DROP_BALL: case KEY_TALK: case KEY_LOCK_NEXT_CLOSE: case KEY_TOGGLE_COMPASS: case KEY_TOGGLE_MINI: case KEY_TOGGLE_SPREAD: case KEY_TOGGLE_POWER: case KEY_TOGGLE_LASER: case KEY_EMERGENCY_THRUST: case KEY_CLEAR_MODIFIERS: case KEY_LOAD_MODIFIERS_1: case KEY_LOAD_MODIFIERS_2: case KEY_LOAD_MODIFIERS_3: case KEY_LOAD_MODIFIERS_4: case KEY_SELECT_ITEM: case KEY_PHASING: case KEY_TOGGLE_IMPLOSION: case KEY_REPROGRAM: case KEY_LOAD_LOCK_1: case KEY_LOAD_LOCK_2: case KEY_LOAD_LOCK_3: case KEY_LOAD_LOCK_4: case KEY_EMERGENCY_SHIELD: case KEY_TOGGLE_AUTOPILOT: break; default: continue; } } if (pressed) { /* --- KEYPRESS --- */ switch (key) { case KEY_TANK_NEXT: case KEY_TANK_PREV: if (pl->fuel.num_tanks) { pl->fuel.current += (key==KEY_TANK_NEXT) ? 1 : -1; if (pl->fuel.current < 0) pl->fuel.current = pl->fuel.num_tanks; else if (pl->fuel.current > pl->fuel.num_tanks) pl->fuel.current = 0; } break; case KEY_TANK_DETACH: Tank_handle_detach(pl); break; case KEY_LOCK_NEXT: case KEY_LOCK_PREV: j = i = GetInd[pl->lock.pl_id]; if (!BIT(pl->lock.tagged, LOCK_PLAYER) || j < 0 || j >= NumPlayers) { /* better jump to KEY_LOCK_CLOSE... */ Player_lock_closest(ind, 0); break; } do { if (key == KEY_LOCK_PREV) { if (--i < 0) { i = NumPlayers - 1; } } else { if (++i >= NumPlayers) { i = 0; } } if (i == j) break; } while (i == ind || BIT(Players[i]->status, GAME_OVER|PAUSE) || !Player_lock_allowed(ind, i)); if (i == ind) { CLR_BIT(pl->lock.tagged, LOCK_PLAYER); } else { pl->lock.pl_id = Players[i]->id; SET_BIT(pl->lock.tagged, LOCK_PLAYER); } break; case KEY_TOGGLE_COMPASS: if (!BIT(pl->have, HAS_COMPASS)) break; TOGGLE_BIT(pl->used, HAS_COMPASS); if (BIT(pl->used, HAS_COMPASS) == 0) { break; } /* * Verify if the lock has ever been initialized at all * and if the lock is still valid. */ if (BIT(pl->lock.tagged, LOCK_PLAYER) && NumPlayers > 1 && (k = pl->lock.pl_id) > 0 && (i = GetInd[k]) > 0 && i < NumPlayers && Players[i]->id == k && i != ind) { break; } Player_lock_closest(ind, 0); break; case KEY_LOCK_NEXT_CLOSE: if (!Player_lock_closest(ind, 1)) { Player_lock_closest(ind, 0); } break; case KEY_LOCK_CLOSE: Player_lock_closest(ind, 0); break; case KEY_CHANGE_HOME: xi = OBJ_X_IN_BLOCKS(pl); yi = OBJ_Y_IN_BLOCKS(pl); if (World.block[xi][yi] == BASE) { msg[0] = '\0'; for (i=0; ihome_base) { break; } if (World.base[i].team != TEAM_NOT_SET && World.base[i].team != pl->team) break; pl->home_base = i; sprintf(msg, "%s has changed home base.", pl->name); break; } } for (i=0; ihome_base == Players[i]->home_base) { Pick_startpos(i); sprintf(msg, "%s has taken over %s's home base.", pl->name, Players[i]->name); } if (msg[0]) { sound_play_all(CHANGE_HOME_SOUND); Set_message(msg); } for (i = 0; i < NumPlayers; i++) { if (Players[i]->conn != NOT_CONNECTED) { Send_base(Players[i]->conn, pl->id, pl->home_base); } } } break; case KEY_SHIELD: if (BIT(pl->have, HAS_SHIELD)) { SET_BIT(pl->used, HAS_SHIELD); CLR_BIT(pl->used, HAS_LASER); /* don't remove! */ } break; case KEY_DROP_BALL: Detach_ball(ind, -1); break; case KEY_FIRE_SHOT: if (!BIT(pl->used, HAS_SHIELD|HAS_SHOT) && BIT(pl->have, HAS_SHOT)) { SET_BIT(pl->used, HAS_SHOT); Fire_normal_shots(ind); } break; case KEY_FIRE_MISSILE: if (pl->item[ITEM_MISSILE] > 0) Fire_shot(ind, OBJ_SMART_SHOT, pl->dir); break; case KEY_FIRE_HEAT: if (pl->item[ITEM_MISSILE] > 0) Fire_shot(ind, OBJ_HEAT_SHOT, pl->dir); break; case KEY_FIRE_TORPEDO: if (pl->item[ITEM_MISSILE] > 0) Fire_shot(ind, OBJ_TORPEDO, pl->dir); break; case KEY_FIRE_LASER: if (pl->item[ITEM_LASER] > 0 && BIT(pl->used, HAS_SHIELD) == 0) { SET_BIT(pl->used, HAS_LASER); } break; case KEY_TOGGLE_NUCLEAR: if (BIT(World.rules->mode, ALLOW_NUKES)) { switch (pl->mods.nuclear) { case NUCLEAR: pl->mods.nuclear |= FULLNUCLEAR; break; case 0: pl->mods.nuclear = NUCLEAR; break; default: pl->mods.nuclear = 0; break; } } break; case KEY_TOGGLE_CLUSTER: if (BIT(World.rules->mode, ALLOW_CLUSTERS)) { TOGGLE_BIT(pl->mods.warhead, CLUSTER); } break; case KEY_TOGGLE_IMPLOSION: if (BIT(World.rules->mode, ALLOW_MODIFIERS)) { TOGGLE_BIT(pl->mods.warhead, IMPLOSION); } break; case KEY_TOGGLE_VELOCITY: if (BIT(World.rules->mode, ALLOW_MODIFIERS)) { /* NB. These may be bit fields, dont modify this code */ if (pl->mods.velocity == MODS_VELOCITY_MAX) pl->mods.velocity = 0; else pl->mods.velocity++; } break; case KEY_TOGGLE_MINI: if (BIT(World.rules->mode, ALLOW_MODIFIERS)) { /* NB. These may be bit fields, dont modify this code */ if (pl->mods.mini == MODS_MINI_MAX) pl->mods.mini = 0; else pl->mods.mini++; } break; case KEY_TOGGLE_SPREAD: if (BIT(World.rules->mode, ALLOW_MODIFIERS)) { /* NB. These may be bit fields, dont modify this code */ if (pl->mods.spread == MODS_SPREAD_MAX) pl->mods.spread = 0; else pl->mods.spread++; } break; case KEY_TOGGLE_LASER: if (BIT(World.rules->mode, ALLOW_LASER_MODIFIERS)) { /* NB. These may be bit fields, dont modify this code */ if (pl->mods.laser == MODS_LASER_MAX) pl->mods.laser = 0; else pl->mods.laser++; } break; case KEY_TOGGLE_POWER: if (BIT(World.rules->mode, ALLOW_MODIFIERS)) { /* NB. These may be bit fields, dont modify this code */ if (pl->mods.power == MODS_POWER_MAX) pl->mods.power = 0; else pl->mods.power++; } break; case KEY_CLEAR_MODIFIERS: CLEAR_MODS(pl->mods); break; case KEY_REPROGRAM: SET_BIT(pl->status, REPROGRAM); break; case KEY_LOAD_MODIFIERS_1: case KEY_LOAD_MODIFIERS_2: case KEY_LOAD_MODIFIERS_3: case KEY_LOAD_MODIFIERS_4: { modifiers *m = &(pl->modbank[key - KEY_LOAD_MODIFIERS_1]); if (BIT(pl->status, REPROGRAM)) { *m = pl->mods; } else { pl->mods = *m; filter_mods(&pl->mods); } break; } case KEY_LOAD_LOCK_1: case KEY_LOAD_LOCK_2: case KEY_LOAD_LOCK_3: case KEY_LOAD_LOCK_4: { int *l = &(pl->lockbank[key - KEY_LOAD_LOCK_1]); if (BIT(pl->status, REPROGRAM)) { if (BIT(pl->lock.tagged, LOCK_PLAYER)) { *l = pl->lock.pl_id; } } else { if (*l != NOT_CONNECTED && Player_lock_allowed(ind, GetInd[*l])) { pl->lock.pl_id = *l; SET_BIT(pl->lock.tagged, LOCK_PLAYER); } } break; } case KEY_TOGGLE_AUTOPILOT: if (BIT(pl->have, HAS_AUTOPILOT)) Autopilot(ind, !BIT(pl->used, HAS_AUTOPILOT)); break; case KEY_EMERGENCY_THRUST: if (BIT(pl->have, HAS_EMERGENCY_THRUST)) Emergency_thrust(ind,!BIT(pl->used, HAS_EMERGENCY_THRUST)); break; case KEY_EMERGENCY_SHIELD: if (BIT(pl->have, HAS_EMERGENCY_SHIELD)) Emergency_shield(ind,!BIT(pl->used, HAS_EMERGENCY_SHIELD)); break; case KEY_DROP_MINE: Place_mine(ind); break; case KEY_DETACH_MINE: Place_moving_mine(ind); break; case KEY_DETONATE_MINES: Detonate_mines(ind); break; case KEY_TURN_LEFT: case KEY_TURN_RIGHT: if (BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, 0); pl->turnacc = 0; if (BITV_ISSET(pl->last_keyv, KEY_TURN_LEFT)) { pl->turnacc += pl->turnspeed; } if (BITV_ISSET(pl->last_keyv, KEY_TURN_RIGHT)) { pl->turnacc -= pl->turnspeed; } break; case KEY_SELF_DESTRUCT: TOGGLE_BIT(pl->status, SELF_DESTRUCT); if (BIT(pl->status, SELF_DESTRUCT)) pl->count = 150; break; case KEY_PAUSE: if (BIT(pl->status, PAUSE)) { i = PAUSE; } else if (BIT(pl->status, HOVERPAUSE)) { i = HOVERPAUSE; } else { xi = OBJ_X_IN_BLOCKS(pl); yi = OBJ_Y_IN_BLOCKS(pl); j = World.base[pl->home_base].pos.x; k = World.base[pl->home_base].pos.y; if (j == xi && k == yi) { minv = 3.0f; i = PAUSE; } else { /* * Hover pause doesn't work within two squares of the * players home base, they would want the better pause. */ if (ABS(j - xi) <= 2 && ABS(k - yi) <= 2) break; minv = 5.0f; i = HOVERPAUSE; } minv += VECTOR_LENGTH(World.gravity[xi][yi]); if (pl->velocity > minv) break; } switch (i) { case PAUSE: if (BIT(pl->status, HOVERPAUSE)) break; if (BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, 0); /* toggle pause mode */ Pause_player(ind, !BIT(pl->status, PAUSE)); if (BIT(pl->status, PLAYING)) { BITV_SET(pl->last_keyv, key); BITV_SET(pl->prev_keyv, key); return 1; } break; case HOVERPAUSE: if (BIT(pl->status, PAUSE)) break; if (!BIT(pl->status, HOVERPAUSE)) { /* * Turn hover pause on, together with shields. */ pl->count = 5*FPS; CLR_BIT(pl->status, SELF_DESTRUCT); SET_BIT(pl->status, HOVERPAUSE); if (BIT(pl->used, HAS_EMERGENCY_THRUST)) Emergency_thrust(ind, false); if (BIT(pl->used, HAS_EMERGENCY_SHIELD)) Emergency_shield(ind, false); if (!BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, true); if (BIT(pl->used, HAS_PHASING_DEVICE)) Phasing(ind, false); /* * Don't allow firing while paused. Similar * reasons exist for refueling, connector and * tractor beams. Other items are allowed (esp. * cloaking). */ Player_used_kill(ind); if (BIT(pl->have, HAS_SHIELD)) SET_BIT(pl->used, HAS_SHIELD); } else if (pl->count <= 0) { Autopilot(ind, 0); CLR_BIT(pl->status, HOVERPAUSE); if (!BIT(pl->have, HAS_SHIELD)) { CLR_BIT(pl->used, HAS_SHIELD); } } break; } break; case KEY_SWAP_SETTINGS: if ( BIT(pl->status, HOVERPAUSE) || BIT(pl->used, HAS_AUTOPILOT)) break; if (pl->turnacc == 0.0) { SWAP(pl->power, pl->power_s); SWAP(pl->turnspeed, pl->turnspeed_s); SWAP(pl->turnresistance, pl->turnresistance_s); } break; case KEY_REFUEL: Refuel(ind); break; case KEY_REPAIR: Repair(ind); break; case KEY_CONNECTOR: if (BIT(pl->have, HAS_CONNECTOR)) SET_BIT(pl->used, HAS_CONNECTOR); break; case KEY_PRESSOR_BEAM: if (BIT(pl->have, HAS_TRACTOR_BEAM)) { pl->tractor_is_pressor = true; SET_BIT(pl->used, HAS_TRACTOR_BEAM); } break; case KEY_TRACTOR_BEAM: if (BIT(pl->have, HAS_TRACTOR_BEAM)) { pl->tractor_is_pressor = false; SET_BIT(pl->used, HAS_TRACTOR_BEAM); } break; case KEY_INCREASE_POWER: if ( BIT(pl->status, HOVERPAUSE) || BIT(pl->used, HAS_AUTOPILOT)) break; pl->power *= 1.10; pl->power = MIN(pl->power, MAX_PLAYER_POWER); break; case KEY_DECREASE_POWER: if ( BIT(pl->status, HOVERPAUSE) || BIT(pl->used, HAS_AUTOPILOT)) break; pl->power *= 0.90; pl->power = MAX(pl->power, MIN_PLAYER_POWER); break; case KEY_INCREASE_TURNSPEED: if ( BIT(pl->status, HOVERPAUSE) || BIT(pl->used, HAS_AUTOPILOT)) break; if (pl->turnacc == 0.0) pl->turnspeed *= 1.05; pl->turnspeed = MIN(pl->turnspeed, MAX_PLAYER_TURNSPEED); break; case KEY_DECREASE_TURNSPEED: if ( BIT(pl->status, HOVERPAUSE) || BIT(pl->used, HAS_AUTOPILOT)) break; if (pl->turnacc == 0.0) pl->turnspeed *= 0.95; pl->turnspeed = MAX(pl->turnspeed, MIN_PLAYER_TURNSPEED); break; case KEY_THRUST: if (BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, 0); SET_BIT(pl->status, THRUSTING); break; case KEY_CLOAK: if (pl->item[ITEM_CLOAK] > 0) { Cloak(ind, !BIT(pl->used, HAS_CLOAKING_DEVICE)); } break; case KEY_ECM: Fire_ecm(ind); break; case KEY_TRANSPORTER: Do_transporter(ind); break; case KEY_DEFLECTOR: if (pl->item[ITEM_DEFLECTOR] > 0) { Deflector(ind, !BIT(pl->used, HAS_DEFLECTOR)); } break; case KEY_HYPERJUMP: if (pl->item[ITEM_HYPERJUMP] > 0 && pl->fuel.sum > -ED_HYPERJUMP) { pl->item[ITEM_HYPERJUMP]--; Add_fuel(&(pl->fuel), ED_HYPERJUMP); do_hyperjump(pl); } break; case KEY_PHASING: if (BIT(pl->have, HAS_PHASING_DEVICE)) { Phasing(ind, !BIT(pl->used, HAS_PHASING_DEVICE)); } break; case KEY_SELECT_ITEM: for (i = 0; i < NUM_ITEMS; i++) { if (++pl->lose_item >= NUM_ITEMS) { pl->lose_item = 0; } if (BIT(1U << pl->lose_item, ITEM_BIT_FUEL | ITEM_BIT_TANK)) { /* can't lose fuel or tanks. */ continue; } if (pl->item[pl->lose_item] > 0) { pl->lose_item_state = 2; /* 2: key down; 1: key up */ break; } } break; case KEY_LOSE_ITEM: do_lose_item(ind); break; default: break; } } else { /* --- KEYRELEASE --- */ switch (key) { case KEY_TURN_LEFT: case KEY_TURN_RIGHT: if (BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, 0); pl->turnacc = 0; if (BITV_ISSET(pl->last_keyv, KEY_TURN_LEFT)) { pl->turnacc += pl->turnspeed; } if (BITV_ISSET(pl->last_keyv, KEY_TURN_RIGHT)) { pl->turnacc -= pl->turnspeed; } break; case KEY_REFUEL: CLR_BIT(pl->used, HAS_REFUEL); break; case KEY_REPAIR: CLR_BIT(pl->used, HAS_REPAIR); break; case KEY_CONNECTOR: CLR_BIT(pl->used, HAS_CONNECTOR); break; case KEY_TRACTOR_BEAM: case KEY_PRESSOR_BEAM: CLR_BIT(pl->used, HAS_TRACTOR_BEAM); break; case KEY_SHIELD: if (BIT(pl->used, HAS_SHIELD) && round_delay == 0) { CLR_BIT(pl->used, HAS_SHIELD|HAS_LASER); /* * Insert the default fireRepeatRate between lowering * shields and firing in order to prevent macros * and hacked clients. */ pl->shot_time = frame_loops; } break; case KEY_FIRE_SHOT: CLR_BIT(pl->used, HAS_SHOT); break; case KEY_FIRE_LASER: CLR_BIT(pl->used, HAS_LASER); break; case KEY_THRUST: if (BIT(pl->used, HAS_AUTOPILOT)) Autopilot(ind, 0); CLR_BIT(pl->status, THRUSTING); break; case KEY_REPROGRAM: CLR_BIT(pl->status, REPROGRAM); break; case KEY_SELECT_ITEM: pl->lose_item_state = 1; break; default: break; } } } memcpy(pl->prev_keyv, pl->last_keyv, sizeof(pl->last_keyv)); return 1; } void filter_mods(modifiers * mods) { if (!BIT(World.rules->mode, ALLOW_NUKES)) mods->nuclear = 0; if (!BIT(World.rules->mode, ALLOW_CLUSTERS)) CLR_BIT(mods->warhead, CLUSTER); if (!BIT(World.rules->mode, ALLOW_MODIFIERS)) { CLR_BIT(mods->warhead, IMPLOSION); mods->velocity = 0; mods->mini = 0; mods->spread = 0; mods->power = 0; } if (!BIT(World.rules->mode, ALLOW_LASER_MODIFIERS)) mods->laser = 0; }