static void intercept_pkunk_death (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; ElementPtr->state_flags &= ~DISAPPEARING; ElementPtr->life_span = 1; GetElementStarShip (ElementPtr, &StarShipPtr); if (StarShipPtr->RaceDescPtr->ship_info.crew_level == 0) { ELEMENT *ShipPtr; LockElement (StarShipPtr->hShip, &ShipPtr); if (GRAVITY_MASS (ShipPtr->mass_points + 1)) { ElementPtr->state_flags |= DISAPPEARING; ElementPtr->life_span = 0; } else { ShipPtr->mass_points = MAX_SHIP_MASS + 1; StarShipPtr->RaceDescPtr->preprocess_func = ShipPtr->preprocess_func; StarShipPtr->RaceDescPtr->postprocess_func = ShipPtr->postprocess_func; StarShipPtr->RaceDescPtr->init_weapon_func = (COUNT (*) (ELEMENT *ElementPtr, HELEMENT Weapon[])) ShipPtr->death_func; ElementPtr->death_func = new_pkunk; } UnlockElement (StarShipPtr->hShip); } }
void do_damage (ELEMENT *ElementPtr, SIZE damage) { if (ElementPtr->state_flags & PLAYER_SHIP) { if (!DeltaCrew (ElementPtr, -damage)) { ElementPtr->life_span = 0; ElementPtr->state_flags |= NONSOLID; } } else if (!GRAVITY_MASS (ElementPtr->mass_points)) { if ((BYTE)damage < ElementPtr->hit_points) ElementPtr->hit_points -= (BYTE)damage; else { ElementPtr->hit_points = 0; ElementPtr->life_span = 0; ElementPtr->state_flags |= NONSOLID; } } }
static void cannon_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { weapon_collision (ElementPtr0, pPt0, ElementPtr1, pPt1); if ((ElementPtr1->state_flags & PLAYER_SHIP) && ElementPtr1->crew_level && !GRAVITY_MASS (ElementPtr1->mass_points + 1)) { COUNT angle; SIZE cur_delta_x, cur_delta_y; STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr1, &StarShipPtr); StarShipPtr->cur_status_flags &= ~(SHIP_AT_MAX_SPEED | SHIP_BEYOND_MAX_SPEED); angle = FACING_TO_ANGLE ( GetFrameIndex (ElementPtr0->next.image.frame) ); DeltaVelocityComponents (&ElementPtr1->velocity, COSINE (angle, RECOIL_VELOCITY), SINE (angle, RECOIL_VELOCITY)); GetCurrentVelocityComponents (&ElementPtr1->velocity, &cur_delta_x, &cur_delta_y); if ((long)cur_delta_x * (long)cur_delta_x + (long)cur_delta_y * (long)cur_delta_y > (long)MAX_RECOIL_VELOCITY * (long)MAX_RECOIL_VELOCITY) { angle = ARCTAN (cur_delta_x, cur_delta_y); SetVelocityComponents (&ElementPtr1->velocity, COSINE (angle, MAX_RECOIL_VELOCITY), SINE (angle, MAX_RECOIL_VELOCITY)); } } }
static void self_destruct (PELEMENT ElementPtr) { STARSHIPPTR StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if (ElementPtr->state_flags & PLAYER_SHIP) { HELEMENT hDestruct; hDestruct = AllocElement (); if (hDestruct) { ELEMENTPTR DestructPtr; LockElement (hDestruct, &DestructPtr); DestructPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE | (ElementPtr->state_flags & (GOOD_GUY | BAD_GUY)); DestructPtr->next.location = ElementPtr->next.location; DestructPtr->life_span = 0; DestructPtr->pParent = ElementPtr->pParent; DestructPtr->hTarget = 0; DestructPtr->death_func = self_destruct; UnlockElement (hDestruct); PutElement (hDestruct); } ElementPtr->state_flags |= NONSOLID; ElementPtr->life_span = 0; ElementPtr->preprocess_func = destruct_preprocess; } else { HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENTPTR ObjPtr; LockElement (hElement, &ObjPtr); hNextElement = GetSuccElement (ObjPtr); if (CollidingElement (ObjPtr) || ORZ_MARINE (ObjPtr)) { #define DESTRUCT_RANGE 180 SIZE delta_x, delta_y; DWORD dist; if ((delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x) < 0) delta_x = -delta_x; if ((delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y) < 0) delta_y = -delta_y; delta_x = WORLD_TO_DISPLAY (delta_x); delta_y = WORLD_TO_DISPLAY (delta_y); if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE && (dist = (DWORD)(delta_x * delta_x) + (DWORD)(delta_y * delta_y)) <= (DWORD)(DESTRUCT_RANGE * DESTRUCT_RANGE)) { #define MAX_DESTRUCTION (DESTRUCT_RANGE / 10) SIZE destruction; destruction = ((MAX_DESTRUCTION * (DESTRUCT_RANGE - square_root (dist))) / DESTRUCT_RANGE) + 1; if (ObjPtr->state_flags & PLAYER_SHIP) { if (!DeltaCrew (ObjPtr, -destruction)) ObjPtr->life_span = 0; } else if (!GRAVITY_MASS (ObjPtr->mass_points)) { if ((BYTE)destruction < ObjPtr->hit_points) ObjPtr->hit_points -= (BYTE)destruction; else { ObjPtr->hit_points = 0; ObjPtr->life_span = 0; } } } } UnlockElement (hElement); } } }
// When hit by Baul spray, gas clouds explodes transforming into a lethal shockwave. static void generate_shockwave (ELEMENT *ElementPtr, BYTE which_player) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); // Gas is still 'solid' when it's hit by the spray. Let's make a shockwave and kill the gas cloud. if (!(ElementPtr->state_flags & NONSOLID)) { HELEMENT hShockwave; hShockwave = AllocElement (); if (hShockwave) { ELEMENT *ShockwavePtr; STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); PutElement (hShockwave); LockElement (hShockwave, &ShockwavePtr); SetElementStarShip (ShockwavePtr, StarShipPtr); ShockwavePtr->hit_points = ShockwavePtr->mass_points = 0; ShockwavePtr->playerNr = which_player; ShockwavePtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID | IGNORE_SIMILAR; ShockwavePtr->life_span = SHOCKWAVE_FRAMES; SetPrimType (&(GLOBAL (DisplayArray))[ShockwavePtr->PrimIndex], STAMP_PRIM); ShockwavePtr->current.image.farray = StarShipPtr->RaceDescPtr->ship_data.special; ShockwavePtr->current.image.frame = SetAbsFrameIndex(StarShipPtr->RaceDescPtr->ship_data.special[0], LAST_GAS_INDEX); ShockwavePtr->next.image.frame = SetAbsFrameIndex(ElementPtr->current.image.frame, LAST_GAS_INDEX); ShockwavePtr->current.location = ElementPtr->current.location; ShockwavePtr->preprocess_func = shockwave_preprocess; ShockwavePtr->postprocess_func = NULL; ShockwavePtr->death_func = NULL; ZeroVelocityComponents (&ShockwavePtr->velocity); UnlockElement (hShockwave); } // Gas dies on the next turn. ElementPtr->state_flags |= NONSOLID; // Explosion sounds. ProcessSound (SetAbsSoundIndex (StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 2), ElementPtr); ProcessSound (SetAbsSoundIndex (GameSounds, TARGET_DAMAGED_FOR_6_PLUS_PT), ElementPtr); } { // This is called during PostProcessQueue(), close to or at the end, // for the temporary shockwave element to apply the damage. // The effects are not seen until the next frame. HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ObjPtr; LockElement (hElement, &ObjPtr); hNextElement = GetSuccElement (ObjPtr); if (IS_GAS (ObjPtr)) { SIZE delta_x, delta_y; DWORD dist; if ((delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x) < 0) delta_x = -delta_x; if ((delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y) < 0) delta_y = -delta_y; delta_x = WORLD_TO_DISPLAY (delta_x); delta_y = WORLD_TO_DISPLAY (delta_y); if (delta_x <= SHOCKWAVE_RANGE && delta_y <= SHOCKWAVE_RANGE && (dist = (DWORD)(delta_x * delta_x) + (DWORD)(delta_y * delta_y)) <= (DWORD)(SHOCKWAVE_RANGE * SHOCKWAVE_RANGE)) { SIZE destruction; destruction = ((MAX_DESTRUCTION * (SHOCKWAVE_RANGE - square_root (dist))) / SHOCKWAVE_RANGE) + 1; // The shockwave is delayed according to how far it is from the shockwave that set it off. ObjPtr->life_span = (10 / destruction); ObjPtr->death_func = generate_shockwave_2; ObjPtr->playerNr = which_player; } } else if (CollidingElement (ObjPtr) || ORZ_MARINE (ObjPtr)) { SIZE delta_x, delta_y; DWORD dist; if ((delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x) < 0) delta_x = -delta_x; if ((delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y) < 0) delta_y = -delta_y; delta_x = WORLD_TO_DISPLAY (delta_x); delta_y = WORLD_TO_DISPLAY (delta_y); if (delta_x <= SHOCKWAVE_RANGE && delta_y <= SHOCKWAVE_RANGE && (dist = (DWORD)(delta_x * delta_x) + (DWORD)(delta_y * delta_y)) <= (DWORD)(SHOCKWAVE_RANGE * SHOCKWAVE_RANGE)) { SIZE destruction; destruction = ((MAX_DESTRUCTION * (SHOCKWAVE_RANGE - square_root (dist))) / SHOCKWAVE_RANGE) + 1; if (ObjPtr->state_flags & PLAYER_SHIP && ObjPtr->playerNr != which_player) { STARSHIP *EnemyShipPtr; GetElementStarShip (ObjPtr, &EnemyShipPtr); // Deal damage to ships except shield-using Yehat & Utwig. if (!((EnemyShipPtr->SpeciesID == YEHAT_ID || EnemyShipPtr->SpeciesID == UTWIG_ID) && ObjPtr->life_span > NORMAL_LIFE)) { if (!DeltaCrew (ObjPtr, -destruction)) ObjPtr->life_span = 0; } // Charge Utwig shield. else if (EnemyShipPtr->SpeciesID == UTWIG_ID && ObjPtr->life_span > NORMAL_LIFE) ObjPtr->life_span += destruction; } else if (!GRAVITY_MASS (ObjPtr->mass_points) && ObjPtr->playerNr != which_player) { if ((BYTE)destruction < ObjPtr->hit_points) ObjPtr->hit_points -= (BYTE)destruction; else { ObjPtr->hit_points = 0; ObjPtr->life_span = 0; } } } } UnlockElement (hElement); } } }
// XXX: This function should be split into two static void self_destruct (ELEMENT *ElementPtr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if (ElementPtr->state_flags & PLAYER_SHIP) { HELEMENT hDestruct; // Spawn a temporary element, which dies in this same frame, in order // to defer the effects of the glory explosion. // It will be the last element (or one of the last) for which the // death_func will be called from PostProcessQueue() in this frame. hDestruct = AllocElement (); if (hDestruct) { ELEMENT *DestructPtr; LockElement (hDestruct, &DestructPtr); DestructPtr->playerNr = ElementPtr->playerNr; DestructPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE; DestructPtr->next.location = ElementPtr->next.location; DestructPtr->life_span = 0; DestructPtr->pParent = ElementPtr->pParent; DestructPtr->hTarget = 0; DestructPtr->death_func = self_destruct; UnlockElement (hDestruct); PutElement (hDestruct); } ElementPtr->state_flags |= NONSOLID; // The ship is now dead. It's death_func, i.e. ship_death(), will be // called the next frame. ElementPtr->life_span = 0; ElementPtr->preprocess_func = destruct_preprocess; } else { // This is called during PostProcessQueue(), close to or at the end, // for the temporary destruct element to apply the effects of glory // explosion. The effects are not seen until the next frame. HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ObjPtr; LockElement (hElement, &ObjPtr); hNextElement = GetSuccElement (ObjPtr); if (CollidingElement (ObjPtr) || ORZ_MARINE (ObjPtr)) { #define DESTRUCT_RANGE 180 SIZE delta_x, delta_y; DWORD dist; if ((delta_x = ObjPtr->next.location.x - ElementPtr->next.location.x) < 0) delta_x = -delta_x; if ((delta_y = ObjPtr->next.location.y - ElementPtr->next.location.y) < 0) delta_y = -delta_y; delta_x = WORLD_TO_DISPLAY (delta_x); delta_y = WORLD_TO_DISPLAY (delta_y); if (delta_x <= DESTRUCT_RANGE && delta_y <= DESTRUCT_RANGE && (dist = (DWORD)(delta_x * delta_x) + (DWORD)(delta_y * delta_y)) <= (DWORD)(DESTRUCT_RANGE * DESTRUCT_RANGE)) { #define MAX_DESTRUCTION (DESTRUCT_RANGE / 10) SIZE destruction; destruction = ((MAX_DESTRUCTION * (DESTRUCT_RANGE - square_root (dist))) / DESTRUCT_RANGE) + 1; if (ObjPtr->state_flags & PLAYER_SHIP) { if (!DeltaCrew (ObjPtr, -destruction)) ObjPtr->life_span = 0; } else if (!GRAVITY_MASS (ObjPtr->mass_points)) { if ((BYTE)destruction < ObjPtr->hit_points) ObjPtr->hit_points -= (BYTE)destruction; else { ObjPtr->hit_points = 0; ObjPtr->life_span = 0; } } } } UnlockElement (hElement); } } }
BOOLEAN CalculateGravity (ELEMENT *ElementPtr) { BOOLEAN retval, HasGravity; HELEMENT hTestElement, hSuccElement; retval = FALSE; HasGravity = (BOOLEAN)(CollidingElement (ElementPtr) && GRAVITY_MASS (ElementPtr->mass_points + 1)); for (hTestElement = GetHeadElement (); hTestElement != 0; hTestElement = hSuccElement) { BOOLEAN TestHasGravity; ELEMENT *TestElementPtr; LockElement (hTestElement, &TestElementPtr); if (TestElementPtr != ElementPtr && CollidingElement (TestElementPtr) && (TestHasGravity = GRAVITY_MASS (TestElementPtr->mass_points + 1)) != HasGravity) { COUNT abs_dx, abs_dy; SIZE dx, dy; if (!(ElementPtr->state_flags & PRE_PROCESS)) { dx = ElementPtr->current.location.x - TestElementPtr->current.location.x; dy = ElementPtr->current.location.y - TestElementPtr->current.location.y; } else { dx = ElementPtr->next.location.x - TestElementPtr->next.location.x; dy = ElementPtr->next.location.y - TestElementPtr->next.location.y; } #ifdef DEBUG_GRAVITY if (TestElementPtr->state_flags & PLAYER_SHIP) { log_add (log_Debug, "CalculateGravity:"); log_add (log_Debug, "\tdx = %d, dy = %d", dx, dy); } #endif /* DEBUG_GRAVITY */ dx = WRAP_DELTA_X (dx); dy = WRAP_DELTA_Y (dy); #ifdef DEBUG_GRAVITY if (TestElementPtr->state_flags & PLAYER_SHIP) log_add (log_Debug, "\twrap_dx = %d, wrap_dy = %d", dx, dy); #endif /* DEBUG_GRAVITY */ abs_dx = dx >= 0 ? dx : -dx; abs_dy = dy >= 0 ? dy : -dy; abs_dx = WORLD_TO_DISPLAY (abs_dx); abs_dy = WORLD_TO_DISPLAY (abs_dy); #ifdef DEBUG_GRAVITY if (TestElementPtr->state_flags & PLAYER_SHIP) log_add (log_Debug, "\tdisplay_dx = %d, display_dy = %d", abs_dx, abs_dy); #endif /* DEBUG_GRAVITY */ if (abs_dx <= GRAVITY_THRESHOLD && abs_dy <= GRAVITY_THRESHOLD) { DWORD dist_squared; dist_squared = (DWORD)(abs_dx * abs_dx) + (DWORD)(abs_dy * abs_dy); if (dist_squared <= (DWORD)(GRAVITY_THRESHOLD * GRAVITY_THRESHOLD)) { #ifdef NEVER COUNT magnitude; #define DIFUSE_GRAVITY RES_SCALE(175) // JMS_GFX: Because of the ifdef NEVER this is actually never run. Well, changed it for consistency dist_squared += (DWORD)abs_dx * (DIFUSE_GRAVITY << 1) + (DWORD)abs_dy * (DIFUSE_GRAVITY << 1) + ((DWORD)(DIFUSE_GRAVITY * DIFUSE_GRAVITY) << 1); if ((magnitude = (COUNT)((DWORD)(GRAVITY_THRESHOLD * GRAVITY_THRESHOLD) / dist_squared)) == 0) magnitude = 1; #define MAX_MAGNITUDE RES_SCALE(6) // JMS_GFX: Because of the ifdef NEVER this is actually never run. Well, changed it for consistency else if (magnitude > MAX_MAGNITUDE) magnitude = MAX_MAGNITUDE; log_add (log_Debug, "magnitude = %u", magnitude); #endif /* NEVER */ #ifdef DEBUG_GRAVITY if (TestElementPtr->state_flags & PLAYER_SHIP) log_add (log_Debug, "dist_squared = %lu", dist_squared); #endif /* DEBUG_GRAVITY */ if (TestHasGravity) { retval = TRUE; UnlockElement (hTestElement); break; } else { COUNT angle; angle = ARCTAN (dx, dy); DeltaVelocityComponents (&TestElementPtr->velocity, COSINE (angle, WORLD_TO_VELOCITY (RES_SCALE(1))), SINE (angle, WORLD_TO_VELOCITY (RES_SCALE(1)))); // JMS_GFX if (TestElementPtr->state_flags & PLAYER_SHIP) { STARSHIP *StarShipPtr; GetElementStarShip (TestElementPtr, &StarShipPtr); StarShipPtr->cur_status_flags &= ~SHIP_AT_MAX_SPEED; StarShipPtr->cur_status_flags |= SHIP_IN_GRAVITY_WELL; } } } } } hSuccElement = GetSuccElement (TestElementPtr); UnlockElement (hTestElement); } return (retval); }