/* * Copyright (C) 1999 Peter Amstutz * * 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., 59 Temple Place, Suite 330, Boston, MA * 02111-1307 USA */ #include #include #include #include #include #include "player.h" #include "relay.h" #include "game.h" #include "weapons/weapon.h" #include "packets.h" #include "kclient.h" #include "log.h" #include "aiexterns.h" #define MAX_Y_VELOCITY 160 /* Most a tank should fall per cycle. */ Player_pl *pl_begin = NULL, *pl_end = NULL; int pl_barrelen = 30; int pl_tankheight, pl_tankwidth; int server_color = MAX_TANKS; int observer_color = MAX_TANKS + 1; /* Last color */ /* Searches the player list for some id and returns the player structure. */ Player_pl *plLookupPlayer(int id) { Player_pl *pcur; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(id == pcur->id) { return pcur; } } return NULL; } Player_pl *plCreatePlayer() { if(pl_begin == NULL) { pl_begin = (Player_pl *) malloc(sizeof(Player_pl)); pl_end = pl_begin; pl_end->prev = NULL; } else { pl_end->next = (Player_pl *) malloc(sizeof(Player_pl)); pl_end->next->prev = pl_end; pl_end = pl_end->next; } pl_end->next = NULL; pl_end->fire_angle = 135; pl_end->fire_velocity = 250; pl_end->barreloff_left = -10; pl_end->barreloff_right = 10; pl_end->barreloff_x = pl_end->barreloff_left; pl_end->barreloff_y = 15; pl_end->armor = 0; pl_end->shield = 0; pl_end->shield_influence = NULL; pl_end->shield_impact = NULL; pl_end->itemstock = (ItemStock_pl *) malloc(sizeof(ItemStock_pl)); pl_end->itemstock->next = pl_end->itemstock; pl_end->itemstock->prev = pl_end->itemstock; pl_end->itemstock->type = WEAPON; pl_end->itemstock->info = wepLookupWeapon("Basic Shell"); pl_end->itemstock->count = 0x0FFFFFFF; pl_end->last_hit = -1; pl_end->fired = 0; return pl_end; } void plDestroyTank(Player_pl * pl, int srcid, int causeid) { pl->ready = NOTREADY; if(gm_iAmServer) { if(gm_gametype == TAKETURNS) { register unsigned i; for(i = 0; i < gm_activeplayers && gm_firing_order[i] != pl; i++) ; for(; i < gm_activeplayers - 1; i++) gm_firing_order[i] = gm_firing_order[i + 1]; } gm_activeplayers--; } gm_death_queue[gm_dq_pos++] = pl->id; gm_death_queue[gm_dq_pos++] = srcid; gm_death_queue[gm_dq_pos++] = causeid; } /* causeid is the id of weapon that caused the explosion or it is * the id of last player that hit the tank before the falling damage */ int plDamageTank(Player_pl * pl, DamageSource_pl ds, int srcid, int causeid, int amt) { logPrintf(SPAM, "Damage of tank %s is %i; source=%i; srcid=%i; amt=%i\n", pl->name, pl->armor, ds, srcid, amt); if(amt <= 0) { return (0); /* Can't hurt me with no damage */ } /* * Ignore those who shoot themselves. */ if(srcid != pl->id) { Player_pl *src; src = plLookupPlayer(srcid); if(src != NULL) { int adj; /* Beware of negative values for badly damaged tanks */ if((adj = pl->shield) < 0) { adj = 0; } if(pl->armor > 0) { adj += pl->armor; } if(adj > amt) { adj = amt; } if(gm_iAmServer) { assert(adj >= 0); src->money += adj*SCORE_DAMAGE; /* Get $SCORE_DAMAGE for every damage point. */ src->roundScore += adj*SCORE_DAMAGE; } } /* Hook for smart AIs to see who's hurting who. */ aihDamageReport(pl, srcid, amt); } if(amt > pl->shield) { amt -= pl->shield; pl->shield = 0; } else { pl->shield -= amt; amt = 0; } pl->armor -= amt; if(pl == gm_myplstruct) gm_tank_damaged = 1; logPrintf(SPAM, "Damage of tank %s is %i; source=%i; srcid=%i; amt=%i\n", pl->name, pl->armor, ds, srcid, amt); if(pl->armor <= 0) { plDestroyTank(pl, srcid, causeid); logPrintf(DEBUG, "Destroying %s's tank\n", pl->name); return 1; } else return 0; } int plAddWeaponToStock(Player_pl * pl, Weapon_wep * wpn, int count, void (*activate) (void *)) { ItemStock_pl *wscur; if(pl->itemstock == NULL) { wscur = (ItemStock_pl *) malloc(sizeof(ItemStock_pl)); wscur->next = wscur; wscur->prev = wscur; wscur->info = wpn; wscur->count = count; wscur->activate = activate; wscur->type = WEAPON; pl->itemstock = wscur; } else { for(wscur = pl->itemstock->next; wscur != pl->itemstock && wscur->info != wpn; wscur = wscur->next) ; if(wscur->info == wpn) { if(wscur->count < 99) { wscur->count += count; return 1; } else return 0; } else { wscur = (ItemStock_pl *) malloc(sizeof(ItemStock_pl)); wscur->next = pl->itemstock; wscur->prev = pl->itemstock->prev; wscur->next->prev = wscur; wscur->prev->next = wscur; wscur->info = wpn; wscur->count = count; wscur->activate = activate; wscur->type = WEAPON; pl->itemstock = wscur; } } return 1; } /* this returns not the new count but basically the count BEFORE using - it may seem a bit counter-intuitive but it's more efficient for the other parts of the program that use this function this way... */ int plUseWeaponInStock(Player_pl * pl, Weapon_wep * wpn, int count) { ItemStock_pl *wscur; int n; if(pl->itemstock == NULL) return 0; for(wscur = pl->itemstock->next; wscur != pl->itemstock && wscur->info != wpn; wscur = wscur->next) ; if(wscur == pl->itemstock && wpn != pl->itemstock->info) return 0; if(wscur->count <= 0) return 0; n = wscur->count; wscur->count -= count; return n; } int plCountWeaponInStock(Player_pl * pl, Weapon_wep * wpn) { ItemStock_pl *wscur; if(pl->itemstock == NULL) return 0; for(wscur = pl->itemstock->next; wscur != pl->itemstock && wscur->info != wpn; wscur = wscur->next) ; if(wscur == pl->itemstock && wpn != pl->itemstock->info) return 0; return wscur->count; } int plBuyWeapon(int id, char *wpn, int count, void (*activate) (void *)) { Weapon_wep *ws; Player_pl *pl; /* No warnings with -Wall... */ int f = 0; pl = plLookupPlayer(id); ws = wepLookupWeapon(wpn); if(pl == NULL || ws == NULL) return 3; if(pl->money >= (ws->cost * count) && (f = plAddWeaponToStock(pl, ws, count, activate))) { pl->money -= (ws->cost * count); return 0; } if(pl->money < (ws->cost * count)) return 1; if(f == 0) return 2; return 3; } int plSellWeapon(int id, char *wpn, int count) { Weapon_wep *ws; Player_pl *pl; pl = plLookupPlayer(id); assert(pl); ws = wepLookupWeapon(wpn); assert(ws); if(plCountWeaponInStock(pl, ws) < count) return 1; if(pl == NULL || ws == NULL) return 1; if(plUseWeaponInStock(pl, ws, count) > 0) { pl->money += (ws->cost * count); return 0; } return 1; } void plClearAllWeapon(int id) { Player_pl *pl; ItemStock_pl *iscur; Weapon_wep *w; pl = plLookupPlayer(id); assert(pl); /* if no items, get outta here */ if(pl->itemstock == NULL) { return; } /* go through all the items (except basic shell) and set the count to 0 */ iscur = pl->itemstock; w = wepLookupWeapon("Basic Shell"); do { if(iscur->info != w) iscur->count = 0; iscur = iscur->next; } while(iscur != pl->itemstock); return; } /* a little trig */ int plPlayerInCircleArea(Player_pl * pl, int x, int y, int r) { double m = pl->x - x; double n = pl->y - y; double c, d, e; int w = pl_tankwidth / 2; if(sqrt(m * m + n * n) <= r) return 1; else { c = atan2(n, m); d = r * cos(c); e = r * sin(c); if((x + d) >= (pl->x - w) && (x + d) <= (pl->x + w) && (y + e) >= pl->y && (y + e) <= pl->y + pl_tankheight) return 1; else return 0; } } /* * plCalcTankFall -- make tanks fall. * * Returns non-zero if tanks are still falling, or 0 if they've all landed. */ int plCalcTankFall(void) { Player_pl *pcur, *tmp; int i, nx, ny, ret; int x_max; char has_hit; ret = 0; for(pcur = pl_begin; pcur; pcur = pcur->next) { if(pcur->ready != READY) { continue; } pcur->ox = pcur->x; /* remember prev coords */ pcur->oy = pcur->y; has_hit = 0; x_max = pcur->x + pl_tankwidth / 2; for(i = pcur->x - pl_tankwidth / 2; i < x_max; i++) { if(terCheckPos(ter_data, i, pcur->y)) { hit: has_hit = 1; if(pcur->vy < 0) { plDamageTank(pcur, FALLING, 0, pcur->last_hit, pcur->vy / -10); } pcur->vy = 0; break; } } if(!has_hit && pcur->y > 0) { logPrintf(SPAM, "CalcTankFall %s: pcur->x=%i; pcur->y=%i; pcur->vy=%lf\n", pcur->name, pcur->x, pcur->y, pcur->vy); pcur->vy -= bal_grav / bal_lerp_tweak; if(pcur->vy < -MAX_Y_VELOCITY) { pcur->vy = -MAX_Y_VELOCITY; /* clip to max */ } else if(pcur->vy > 0) { pcur->vy = 0; /* Huh? */ } nx = pcur->x; ny = pcur->y + pcur->vy; (void) balCheckIntersect(pcur->x, pcur->y, nx, ny, &tmp, &nx, &ny); pcur->y = ny; ret = 1; /* still falling */ } if(pcur->y < 0) { /* check for underflow */ pcur->y = 0; goto hit; } } return ret; }