1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
|
/* $Header: /fridge/cvs/xscorch/sgame/strack.c,v 1.27 2009-04-26 17:39:45 jacob Exp $ */
/*
xscorch - strack.c Copyright(c) 2000-2003 Justin David Smith
Copyright(c) 2003 Jacob Luna Lundberg
justins(at)chaos2.org http://chaos2.org/
jacob(at)chaos2.org http://chaos2.org/~jacob
Scorched basic weapon tracking
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, version 2 of the License ONLY.
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <strack.h>
#include <scolor.h>
#include <sconfig.h>
#include <sexplosion.h>
#include <sland.h>
#include <sphoenix.h>
#include <sphysics.h>
#include <splayer.h>
#include <sshield.h>
#include <sspill.h>
#include <swindow.h>
#include <sutil/srand.h>
/*** Weapon tracking ***/
static void _sc_traj_landfall(sc_trajectory *tr, int x) {
/* sc_traj_landfall
This function is called with an x coordinate indicating that at least one
land tile in column x was cleared. A window is maintained indicating the
minimum and maximum X coordinates where landfall might occur, to optimize
performance during landfall. A hint in tr is updated to indicate that
landfall may occur in column x. */
/* Update the landfall for this trajectory */
if(tr->landfall_x1 > x) tr->landfall_x1 = x;
if(tr->landfall_x2 < x) tr->landfall_x2 = x;
}
static sc_trajectory_result _sc_weapon_track_point(sc_config *c, sc_trajectory *tr, void *data) {
/* sc_weapon_track_point
This function takes a current trajectory tr, and a weapon (passed in
using the extra data argument). This function is called when a
weapon passes through a particular coordinate; this function clears
any land that may have occupied the coordinate, applies "smoke" to
the land mask if necessary, and updates hints indicating landfall
should occur and that the screen needs to be redrawn.
This function takes the trajectory's current x, y coordinates and
rounds them to the nearest integer values to determine what coordinate
the weapon currently occupies. It will update the land mask and hints
in tr, but it will not adjust the weapon.
This function has no effect if the weapon is out-of-bounds. This
will always return SC_TRAJ_CONTINUE, indicating tracking should
proceed. */
const int *gradient; /* Sky gradient */
int gradientflag; /* Sky gradient flag */
bool dither; /* Sky: dithering allowed? */
bool repaint = false; /* Is a repaint needed? */
sc_weapon *wp = (sc_weapon *)data; /* Weapon data */
int *trace; /* Trace location on land */
int x; /* Weapon's current X */
int y; /* Weapon's current Y */
/* Get the current land pointer at (x, y) */
x = rint(tr->curx);
y = rint(tr->cury);
if(!sc_land_translate_xy(c->land, &x, &y)) return(SC_TRAJ_CONTINUE);
trace = SC_LAND_XY(c->land, x, y);
/* Check if the weapon just clobbered some land... */
if(SC_LAND_IS_GROUND(*trace)) {
/* Get the sky gradient */
gradient = sc_land_sky_index(c);
gradientflag = sc_land_sky_flag(c);
dither = c->graphics.gfxdither;
*trace = gradientflag | sc_color_gradient_index(dither, gradient, y);
_sc_traj_landfall(tr, x);
repaint = true;
} /* Obliterated some land? */
/* Check if the weapon is leaving a smoke trail */
if((c->weapons->tracepaths || SC_WEAPON_IS_SMOKING(wp)) && !SC_CONFIG_GFX_FAST(c)) {
*trace = SC_LAND_SMOKE | wp->playerid;
repaint = true;
}
if(repaint) {
sc_window_paint(c->window, x, y, x, y, SC_REGENERATE_LAND | SC_PAINT_EVERYTHING);
}
/* Nothing interesting ever happens... */
return(SC_TRAJ_CONTINUE);
}
static sc_weapon_track_result _sc_weapon_track(sc_config *c, sc_weapon **wp, sc_explosion **e) {
/* sc_weapon_track
Track a single weapon component. This function undraws the weapon at
its current position ((*wp)->tr->cur{x,y}). Then, it tracks the weapon
to its new location, or to the point of impact if the weapon hit any-
thing. Impacts are dealt with, deleting the current weapon if needed,
and adding an explosion to e.
wp is a list; we are only tracking the weapon at the HEAD of the list
but we may append extra warheads to the tail if needed (phoenix code
will do this). Similarly, e is a list; we may append new explosions
to it at the tail of the list.
A fair amount of phoenix processing is done here to determine if the
weapon hit its apex and should break apart (for MIRVs, etc). */
sc_trajectory_result stepres; /* Indicates result of sc_traj_step */
sc_trajectory *traj; /* Shortcut to (*wp)->traj. */
bool goingup; /* True if weapon initially ascending */
int flags; /* Flags for if we are tunnelling, etc */
assert(c != NULL && wp != NULL && e != NULL);
assert(*wp != NULL);
/* We don't want to process this weapon now if it's deferring.
Instead, we'll mark it to be processed next time and let the
tracking state know it needs to keep processing the weapon. */
if(SC_WEAPON_IS_DEFERRING(*wp)) {
(*wp)->state &= ~SC_WEAPON_STATE_DEFER;
return(SC_WEAPON_TRACK_NEED_RECURSE);
}
/* At this point, traj->cur{x,y} indicate the original control
point; the point that the weapon was at upon entering this
function. */
traj = (*wp)->tr;
sc_window_undraw_weapon(c->window, *wp);
goingup = sc_traj_get_velocity_y(traj) >= 0;
if(traj->timestep == 0) goingup = true;
/* Are we tunnelling? This is important,
as we must set the trajectory flags now! */
flags = SC_WEAPON_TRAJ_FLAGS(c, *wp);
/* WEAPON collision check; since the weapon is likely to be skipping
more than one pixel away per iteration, we need to make sure it
wouldn't have collided with something along the way. We need to
check all points between the previous and current position. We do
this with a line approximation, which is well, good enough... */
stepres = sc_traj_step(c, traj, flags, _sc_weapon_track_point, *wp);
/* At this point, the values traj->cur{x,y} either indicate the new
control point, or the point of impact if the weapon hit something. */
/* Deal damage to shields if appropriate */
if(stepres == SC_TRAJ_IMPACT_SHIELD && !SC_WEAPON_IS_LIQUID(*wp)) {
sc_shield_absorb_hit(c->players[traj->victim], SC_WEAPON_IS_SAPPER(*wp) ? 1 : 0);
stepres = SC_TRAJ_SIZZLE;
}
/* This code checks if we just detonated. If we did, it checks whether
the weapon is a LAND phoenix. If so, it calls sc_phoenix() which
checks which phoenix subroutine to run on the weapon. */
if(SC_TRAJ_IS_IMPACT(stepres) && SC_PHOENIX_IS_AT_LAND((*wp)->weaponinfo)) {
switch(sc_phoenix(SC_PHOENIX_AT_LAND, c, wp, e)) {
/* Weapon hit something, exploded, and also created children */
case SC_PHOENIX_DETONATE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_DETONATE);
/* Weapon hit something and created children but we call it a sizzle
because we don't detonate the original weapon. Note that this is
never done in the original Scorched Earth. */
case SC_PHOENIX_SIZZLE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_SIZZLE);
/* We need to skip tracking the weapon this turn */
case SC_PHOENIX_RESET:
/* Draw the weapon at its new position */
sc_window_draw_weapon(c->window, *wp);
/* Defer to next tracking round */
return(SC_WEAPON_TRACK_NEED_RECURSE);
/* There was an error (oom, for example) in sc_phoenix() */
case SC_PHOENIX_FAILURE:
/* The default case should not arise (we hope) */
default:
/* do nothing */;
}
}
/* Decide what to do based on the return value of the path traversal */
if(SC_TRAJ_IS_IMPACT(stepres)) {
/* Weapon hit something, and detonated */
if(*e == NULL) {
sc_expl_add(e, sc_weapon_get_explosion(c, *wp, traj->curx, traj->cury, SC_EXPL_DEFAULT_DIR));
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_DETONATE);
} else {
/* Draw the weapon at its new position. Probably not needed... */
sc_window_draw_weapon(c->window, *wp);
/* We deferred */
return(SC_WEAPON_TRACK_NEED_RECURSE);
}
} else if(stepres == SC_TRAJ_SIZZLE) {
/* Weapon sizzled */
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_SIZZLE);
} /* Any special orders? */
/* This code checks if we're at the apex of the weapon's flight curve.
If we are and this is an APEX phoenix weapon, it calls sc_phoenix()
which checks which phoenix function to run on the weapon. For
reasons of timing it is important to interrupt sc_weapon_track. */
if(goingup && sc_traj_get_velocity_y(traj) <= 0 && SC_PHOENIX_IS_AT_APEX((*wp)->weaponinfo)) {
switch(sc_phoenix(SC_PHOENIX_AT_APEX, c, wp, e)) {
/* In this case the weapon split successfully */
case SC_PHOENIX_SIZZLE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_SIZZLE);
/* In this case, we detonated in mid-air. Strange... */
case SC_PHOENIX_DETONATE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_DETONATE);
/* In this case something is probably very wrong, since phoenix failed */
case SC_PHOENIX_FAILURE:
/* In this case, nothing happened */
case SC_PHOENIX_NO_ACTION:
default:
/* do nothing */;
}
}
/* Check for RAND phoenix weapons and if so check whether now is the time to run them */
/* TEMP: the probability should be selectable or perhaps scaled by the expected arc length? */
if(SC_PHOENIX_IS_AT_RAND((*wp)->weaponinfo) && game_drand() < SC_PHOENIX_PROB_AT_RAND) {
switch(sc_phoenix(SC_PHOENIX_AT_RAND, c, wp, e)) {
/* In this case the weapon was successfully modified by sphoenix */
case SC_PHOENIX_SIZZLE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_SIZZLE);
/* In this case, we detonated */
case SC_PHOENIX_DETONATE:
sc_weapon_landfall(c, *wp);
sc_weapon_free(wp);
return(SC_WEAPON_TRACK_DETONATE);
/* Failure in the sphoenix code */
case SC_PHOENIX_FAILURE:
/* No action to perform; we hope this case doesn't arise */
case SC_PHOENIX_NO_ACTION:
default:
/* do nothing */;
}
}
/* If we're still running, then the weapon is still live. Go ahead
and re-draw it at its new position. */
sc_window_draw_weapon(c->window, *wp);
/* We were tracking a weapon; we'll need to recurse. */
return(SC_WEAPON_TRACK_NEED_RECURSE);
}
static inline sc_weapon_track_result _sc_weapon_track_chain(sc_config *c, sc_weapon **wp, sc_explosion **e) {
/* sc_weapon_track_chain
Track a weapon chain for a single player. This processes all
weapons in the chain; it will return either:
NO_ACTION, indicating that tracking has completed
NEED_RECURSE, indicating that nothing of particular interest
happened but tracking is not completed (we need to call
this function again),
DETONATE, indicating that at least one detonation occurred.
Tracking will resume once the detonation is processed.
All weapons in the weapon chain wp will be processed, even if some
weapons at the head of the list explode, and any explosions are
added to the explosions list e. */
sc_weapon_track_result result;
assert(c != NULL && wp != NULL && e != NULL);
result = SC_WEAPON_TRACK_NO_ACTION;
while(*wp != NULL) {
switch(_sc_weapon_track(c, wp, e)) {
case SC_WEAPON_TRACK_DETONATE:
result = SC_WEAPON_TRACK_DETONATE;
case SC_WEAPON_TRACK_SIZZLE:
/* In these cases, sc_weapon_track has already removed the
current weapon from the queue, so wp is already updated
to point to the next weapon. */
break;
case SC_WEAPON_TRACK_NEED_RECURSE:
if(result == SC_WEAPON_TRACK_NO_ACTION) {
result = SC_WEAPON_TRACK_NEED_RECURSE;
} /* Only if nothing occurred yet */
case SC_WEAPON_TRACK_NO_ACTION:
/* Advance to the next weapon in the queue. */
wp = &((*wp)->chain);
}
}
return(result);
}
sc_weapon_track_result sc_weapon_track_all(sc_config *c, sc_explosion **e) {
/* sc_weapon_track_all
Track the weapons of every player, in sync. This function will always
process weapons for all players; if at least one player created a new
explosion, it will return DETONATE. If at least one player still needs
tracking assistance, then it will return NEED_RECURSE. This function
only returns NO_ACTION if ALL players are done tracking. */
/* Note: we process all weapons in the chain, even after seeing a
detonation. This is to prevent other weapons from ``lagging''
when one weapon (i.e. chaos) starts a long chain of detonates.
In other words, every weapon gets its fair timeslice even if
there are a lot of explosions going on. */
sc_weapon_track_result result;
int i;
assert(c != NULL && e != NULL);
result = SC_WEAPON_TRACK_NO_ACTION;
for(i = 0; i < c->numplayers; ++i) {
switch(_sc_weapon_track_chain(c, &(c->players[i]->weapons), e)) {
case SC_WEAPON_TRACK_DETONATE:
result = SC_WEAPON_TRACK_DETONATE;
break;
case SC_WEAPON_TRACK_NEED_RECURSE:
if(result == SC_WEAPON_TRACK_NO_ACTION) {
result = SC_WEAPON_TRACK_NEED_RECURSE;
} /* Only if nothing occurred yet */
break;
default:
/* do nothing */;
}
}
return(result);
}
|