/* * 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 "terrain.h" #include "ballistics.h" #include "player.h" #include "game.h" #include "weapons/weapon.h" #include "cfgfile.h" #include "log.h" #define DEFAULT_GRAVITY 58.8 const double SPEED_RATIO[] = { /* used for calculation wall effects */ 0.6, /* WALL_PADDED */ 0.8, /* WALL_RUBBER */ 0.99 }; /* WALL_SPRING */ double bal_grav = DEFAULT_GRAVITY; double bal_lerp_tweak = 10.0; struct Projectilelist_bal *bal_Projectiles = NULL; WindModes_bal bal_wind_mode; WallTypes_bal bal_wall_type, bal_wall; int bal_wind, bal_wind_maxspeed; void balInit() { const char *wind_modes_str[] = { "off", "constant", "random", NULL }; const WindModes_bal wind_modes_val[] = { WND_OFF, WND_CONST, WND_RANDOM }; const char *wall_types_str[] = { "off", "concrete", "padded", "rubber", "spring", "wraparound", "random" }; const WallTypes_bal wall_types_val[] = { WALL_OFF, WALL_CONCRETE, WALL_PADDED, WALL_RUBBER, WALL_SPRING, WALL_WRAPAROUND, WALL_RANDOM }; bal_grav = DEFAULT_GRAVITY; bal_wind_mode = WND_OFF; bal_wind_maxspeed = 0; bal_wall_type = bal_wall = WALL_OFF; cfgLoadConfigItemDoubleD(cfg_configuration, "nature.gravity", &bal_grav, DEFAULT_GRAVITY); cfgLoadConfigItemOptionD(cfg_configuration, "nature.wind", wind_modes_str, (int *) wind_modes_val, (int *) &bal_wind_mode, (int) WND_OFF); cfgLoadConfigItemIntD(cfg_configuration, "nature.maxwind", &bal_wind_maxspeed, 0); cfgLoadConfigItemOptionD(cfg_configuration, "landscape.walls", wall_types_str, (int *) wall_types_val, (int *) &bal_wall_type, (int) WALL_OFF); } void balSetWall() { switch (bal_wall_type) { case WALL_OFF: case WALL_CONCRETE: case WALL_PADDED: case WALL_RUBBER: case WALL_SPRING: case WALL_WRAPAROUND: bal_wall = bal_wall_type; break; case WALL_RANDOM: bal_wall = (WallTypes_bal) (random() % (int) WALL_LAST); break; case WALL_LAST: /* just to satisfy gcc -Wall */ ; } } void balCalcWind() { switch (bal_wind_mode) { case WND_OFF: bal_wind = 0; break; case WND_CONST: case WND_RANDOM: bal_wind = random() % (bal_wind_maxspeed * 2 + 1) - bal_wind_maxspeed; break; } } void balRecalcWind() { if(bal_wind_mode == WND_RANDOM) { bal_wind += (random() % (bal_wind_maxspeed * 2 + 1) - bal_wind_maxspeed) / 2; if(bal_wind > bal_wind_maxspeed) bal_wind = bal_wind_maxspeed; if(bal_wind < -bal_wind_maxspeed) bal_wind = -bal_wind_maxspeed; } } void balPrintWind(char *buf) { if(bal_wind > 0) sprintf(buf, "wind %i \020", bal_wind); else if(bal_wind < 0) sprintf(buf, "\021 wind %i", -bal_wind); else sprintf(buf, "no wind"); } Shellstat_bal balEnvironmentAdjustProjPos(struct Projectilepos_bal *prjpos) { prjpos->x += (bal_wind / bal_lerp_tweak); logPrintf(SPAM, "AdjustProjPos: prjpos->x=%f\n", prjpos->x); logPrintf(SPAM, "bal_grav=%f bal_lerp_tweak=%f bal_wind=%d\n", bal_grav, bal_lerp_tweak, bal_wind); if(prjpos->x <= 0 || prjpos->x >= ter_sizex || prjpos->y <= 0 || prjpos->y >= ter_sizey) { switch (bal_wall) { case WALL_OFF: /* TODO: don't free if wind is enough to bring it back ... */ if(prjpos->x <= 0 || prjpos->x >= ter_sizex) { prjpos->x = -1; prjpos->y = -1; return FREEING; } break; case WALL_CONCRETE: /* Explodes on contact */ if(prjpos->x < 0) prjpos->x = 0; if(prjpos->y < 0) prjpos->y = 0; if(prjpos->x > ter_sizex) prjpos->x = ter_sizex; if(prjpos->y > ter_sizey) prjpos->y = ter_sizey; return EXPLODING; break; case WALL_PADDED: /* Mirrored, but speed decreased by 40% */ case WALL_RUBBER: /* Mirrored, but speed decresed by 20% */ case WALL_SPRING: /* Mirror, but speed decreased by 1% */ if(prjpos->x <= 0 || prjpos->x >= ter_sizex) { prjpos->vx = -SPEED_RATIO[bal_wall - WALL_PADDED] * prjpos->vx; if(prjpos->x <= 0) prjpos->x = 1; else prjpos->x = ter_sizex - 1; } if(prjpos->y <= 0 || prjpos->y >= ter_sizey) { logPrintf(SPAM, "terain y: %f\n", prjpos->y); prjpos->vy = -SPEED_RATIO[bal_wall - WALL_PADDED] * prjpos->vy; if(prjpos->y <= 0) prjpos->y = 1; else prjpos->y = ter_sizey - 1; if(prjpos->vy < (bal_grav / bal_lerp_tweak)) return EXPLODING; } break; case WALL_WRAPAROUND: /* wrap around the screen */ if(prjpos->x <= 0) { prjpos->x += ter_sizex; prjpos->ox = prjpos->x; } else { if(prjpos->x >= ter_sizex) { prjpos->x -= ter_sizex; prjpos->ox = prjpos->x; } } break; case WALL_RANDOM: /* both of this can't happen, */ case WALL_LAST: /* so they are here to satisfy gcc -Wall */ ; } } return FLYING; } int balCheckTankIntersect(int x, int y, Player_pl ** pl, int *ix, int *iy) { Player_pl *tmp; *pl = NULL; for(tmp = pl_begin; tmp; tmp = tmp->next) { if(tmp->ready != READY) continue; if(x > (tmp->x - (pl_tankwidth / 2)) && x < (tmp->x + (pl_tankwidth / 2)) && y > (tmp->y) && y < (tmp->y + (pl_tankheight))) { logPrintf(DEBUG, "Intersecting tank: x=%i y=%i bullet.x=%i bullet.y=%i\n", tmp->x, tmp->y, x, y); *pl = tmp; *ix = x; *iy = y; return 1; } } return 0; } int balCheckIntersect(int x1, int y1, int x2, int y2, Player_pl ** pl, int *ix, int *iy) { int y_unit, x_unit; int x = x1, y = y1; int ydiff = y2 - y1; int xdiff = x2 - x1; int error_term = 0; int length, i; logPrintf(SPAM, "CheckIntersect: x1=%d; y1=%d; x2=%d; y2=%d\n", x1, y1, x2, y2); if(ydiff < 0) { ydiff = -ydiff; y_unit = -1; } else y_unit = 1; if(xdiff < 0) { xdiff = -xdiff; x_unit = -1; } else x_unit = 1; if(xdiff > ydiff) { length = xdiff + 1; for(i = 0; i < length; i++) { if(terCheckPos(ter_data, x, y)) { *pl = NULL; *ix = x; *iy = y; return TERRAIN_ISECT; } if(y < 0) { *pl = NULL; *ix = x; *iy = 0; return WALL_ISECT; } if(x < 0) { *pl = NULL; *ix = 0; *iy = y; return WALL_ISECT; } if(x > ter_sizex - 1) { *pl = NULL; *ix = ter_sizex - 1; *iy = y; return WALL_ISECT; } if(balCheckTankIntersect(x, y, pl, ix, iy)) return TANK_ISECT; x += x_unit; error_term += ydiff; if(error_term > xdiff) { error_term -= xdiff; y += y_unit; } } } else { length = ydiff + 1; for(i = 0; i < length; i++) { if(terCheckPos(ter_data, x, y)) { *pl = NULL; *ix = x; *iy = y; return TERRAIN_ISECT; } if(y < 0) { *pl = NULL; *ix = x; *iy = 0; return WALL_ISECT; } if(x < 0) { *pl = NULL; *ix = 0; *iy = y; return WALL_ISECT; } if(x > ter_sizex - 1) { *pl = NULL; *ix = ter_sizex - 1; *iy = y; return WALL_ISECT; } if(balCheckTankIntersect(x, y, pl, ix, iy)) return TANK_ISECT; y += y_unit; error_term += xdiff; if(error_term > 0) { error_term -= ydiff; x += x_unit; } } } return NO_ISECT; } struct Projectilelist_bal *balNewShotAV(int id, int gen, int x, int y, double a, double v, Weapon_wep * w) { struct Projectilelist_bal *newshot = (struct Projectilelist_bal *) malloc(sizeof(struct Projectilelist_bal)); v /= bal_lerp_tweak; logPrintf(DEBUG, "Added shot for %s, gen:%i (%i, %i), %f/%f\n", plLookupPlayer(id)->name, gen, x, y, a, v); newshot->prjpos.id = id; newshot->prjpos.wid = w->id; newshot->gen = gen; newshot->prjpos.x = x; newshot->prjpos.y = y; newshot->prjpos.ox = x; newshot->prjpos.oy = y; newshot->prjpos.rox = x; newshot->prjpos.roy = y; newshot->prjpos.vx = v * cos((a / 180.0) * M_PI); newshot->prjpos.vy = v * sin((a / 180.0) * M_PI); newshot->stat = HOLDING; newshot->guidanceinfo = NULL; newshot->explosioninfo = NULL; newshot->wpn = w; newshot->prev = NULL; newshot->next = bal_Projectiles; if(bal_Projectiles) bal_Projectiles->prev = newshot; bal_Projectiles = newshot; return newshot; } struct Projectilelist_bal *balNewShotXY(int id, int gen, int x, int y, double vx, double vy, Weapon_wep * w) { struct Projectilelist_bal *newshot = (struct Projectilelist_bal *) malloc(sizeof(struct Projectilelist_bal)); newshot->prjpos.id = id; newshot->prjpos.wid = w->id; newshot->gen = gen; newshot->prjpos.x = x; newshot->prjpos.y = y; newshot->prjpos.ox = x; newshot->prjpos.oy = y; newshot->prjpos.vx = vx; newshot->prjpos.vy = vy; newshot->stat = HOLDING; newshot->guidanceinfo = NULL; newshot->explosioninfo = NULL; newshot->wpn = w; newshot->prev = NULL; newshot->next = bal_Projectiles; if(bal_Projectiles) bal_Projectiles->prev = newshot; bal_Projectiles = newshot; return newshot; } int balAdvanceProjectiles() { struct Projectilelist_bal *prj, *nxt; int ret = 0; for(prj = bal_Projectiles; prj; prj = nxt) { switch (prj->stat) { case FLYING: if(prj->wpn->drawshot) { prj->wpn->drawshot(&(prj->prjpos), prj->guidanceinfo); } prj->stat = prj->wpn->doguidance(prj->guidanceinfo, &(prj->prjpos), prj->wpn->initexplosion, &(prj->explosioninfo)); if(prj->stat > FLYING && prj->wpn->drawshot) { prj->wpn->drawshot(&(prj->prjpos), prj->guidanceinfo); } ret = 1; nxt = prj->next; break; case EXPLODING: if(prj->wpn->drawexplosion) { prj->wpn->drawexplosion(prj->explosioninfo); } prj->stat = prj->wpn->doexplosion(prj->explosioninfo); nxt = prj->next; ret = 1; break; case FREEING: nxt = prj->next; if(prj->prev) prj->prev->next = prj->next; if(prj->next) prj->next->prev = prj->prev; if(bal_Projectiles == prj) bal_Projectiles = prj->next; free(prj); break; default: nxt = prj->next; } } return ret; } /* Free the projectiles list */ void balClearAllProjectiles() { struct Projectilelist_bal *prj, *nxt; for(prj = bal_Projectiles; prj; prj = nxt) { nxt = prj->next; free(prj); } bal_Projectiles = NULL; }