static COUNT initialize_turret_missile (PELEMENT ShipPtr, HELEMENT MissileArray[]) { #define MISSILE_HITS 2 #define MISSILE_DAMAGE 3 #define MISSILE_OFFSET 1 ELEMENTPTR TurretPtr; STARSHIPPTR StarShipPtr; MISSILE_BLOCK MissileBlock; GetElementStarShip (ShipPtr, &StarShipPtr); MissileBlock.cx = ShipPtr->next.location.x; MissileBlock.cy = ShipPtr->next.location.y; MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon; LockElement (GetSuccElement (ShipPtr), &TurretPtr); if (TurretPtr->turn_wait == 0 && (StarShipPtr->cur_status_flags & SPECIAL) && (StarShipPtr->cur_status_flags & (LEFT | RIGHT))) { if (StarShipPtr->cur_status_flags & RIGHT) ++TurretPtr->thrust_wait; else --TurretPtr->thrust_wait; TurretPtr->turn_wait = TURRET_WAIT + 1; } MissileBlock.face = MissileBlock.index = NORMALIZE_FACING (StarShipPtr->ShipFacing + TurretPtr->thrust_wait); UnlockElement (GetSuccElement (ShipPtr)); MissileBlock.sender = (ShipPtr->state_flags & (GOOD_GUY | BAD_GUY)) | IGNORE_SIMILAR; MissileBlock.pixoffs = TURRET_OFFSET; MissileBlock.speed = MISSILE_SPEED; MissileBlock.hit_points = MISSILE_HITS; MissileBlock.damage = MISSILE_DAMAGE; MissileBlock.life = MISSILE_LIFE; MissileBlock.preprocess_func = NULL_PTR; MissileBlock.blast_offs = MISSILE_OFFSET; MissileArray[0] = initialize_missile (&MissileBlock); if (MissileArray[0]) { ELEMENTPTR HowitzerPtr; LockElement (MissileArray[0], &HowitzerPtr); HowitzerPtr->collision_func = howitzer_collision; UnlockElement (MissileArray[0]); } return (1); }
void crc_processDispQueue(crc_State *state) { HELEMENT element; HELEMENT nextElement; #ifdef DUMP_CRC_OPS size_t i = 0; log_add(log_Debug, "START crc_processDispQueue()."); #endif for (element = GetHeadElement(); element != 0; element = nextElement) { ELEMENTPTR elementPtr; #ifdef DUMP_CRC_OPS log_add(log_Debug, "===== disp_q[%d]:", i); #endif LockElement(element, &elementPtr); crc_processELEMENT(state, element); nextElement = GetSuccElement(elementPtr); UnlockElement(element); #ifdef DUMP_CRC_OPS i++; #endif } #ifdef DUMP_CRC_OPS log_add(log_Debug, "END crc_processDispQueue()."); #endif }
// playerNr should be 0 or 1 STARSHIP* findPlayerShip (SIZE playerNr) { HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement; hElement = hNextElement) { ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if ((ElementPtr->state_flags & PLAYER_SHIP) && ElementPtr->playerNr == playerNr) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); UnlockElement (hElement); return StarShipPtr; } UnlockElement (hElement); } return NULL; }
void DrawScannedObjects (BOOLEAN Reversed) { HELEMENT hElement, hNextElement; for (hElement = Reversed ? GetTailElement () : GetHeadElement (); hElement; hElement = hNextElement) { ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); hNextElement = Reversed ? GetPredElement (ElementPtr) : GetSuccElement (ElementPtr); if (ElementPtr->state_flags & APPEARING) { STAMP s; s.origin = ElementPtr->current.location; s.frame = ElementPtr->next.image.frame; DrawStamp (&s); } UnlockElement (hElement); } }
BOOLEAN TimeSpaceMatterConflict (ELEMENT *ElementPtr) { HELEMENT hTestElement, hSuccElement; INTERSECT_CONTROL ElementControl; ElementControl.IntersectStamp.origin.x = WORLD_TO_DISPLAY (ElementPtr->current.location.x); ElementControl.IntersectStamp.origin.y = WORLD_TO_DISPLAY (ElementPtr->current.location.y); ElementControl.IntersectStamp.frame = SetEquFrameIndex (ElementPtr->current.image.farray[0], ElementPtr->current.image.frame); ElementControl.EndPoint = ElementControl.IntersectStamp.origin; for (hTestElement = GetHeadElement (); hTestElement != 0; hTestElement = hSuccElement) { ELEMENT *TestElementPtr; LockElement (hTestElement, &TestElementPtr); hSuccElement = GetSuccElement (TestElementPtr); if (TestElementPtr != ElementPtr && (CollidingElement (TestElementPtr) /* ship in transition */ || (TestElementPtr->state_flags & PLAYER_SHIP))) { INTERSECT_CONTROL TestElementControl; TestElementControl.IntersectStamp.origin.x = WORLD_TO_DISPLAY (TestElementPtr->current.location.x); TestElementControl.IntersectStamp.origin.y = WORLD_TO_DISPLAY (TestElementPtr->current.location.y); TestElementControl.IntersectStamp.frame = SetEquFrameIndex (TestElementPtr->current.image.farray[0], TestElementPtr->current.image.frame); TestElementControl.EndPoint = TestElementControl.IntersectStamp.origin; if (DrawablesIntersect (&ElementControl, &TestElementControl, MAX_TIME_VALUE)) { UnlockElement (hTestElement); break; } } UnlockElement (hTestElement); } return (hTestElement != 0 ? TRUE : FALSE); }
BOOLEAN OpponentAlive (STARSHIP *TestStarShipPtr) { HELEMENT hElement, hSuccElement; for (hElement = GetHeadElement (); hElement; hElement = hSuccElement) { ELEMENT *ElementPtr; STARSHIP *StarShipPtr; LockElement (hElement, &ElementPtr); hSuccElement = GetSuccElement (ElementPtr); GetElementStarShip (ElementPtr, &StarShipPtr); UnlockElement (hElement); if (StarShipPtr && StarShipPtr != TestStarShipPtr && StarShipPtr->RaceDescPtr->ship_info.crew_level == 0) return FALSE; } return TRUE; }
static void checkOtherShipLifeSpan (ELEMENT *deadShip) { STARSHIP *deadStarShip; GetElementStarShip (deadShip, &deadStarShip); if (winnerStarShip != NULL && deadStarShip != winnerStarShip && winnerStarShip->RaceDescPtr->ship_info.crew_level == 0) { // The opponent ship also died but won anyway (e.g. Glory device) // We need to keep the opponent ship alive longer so that the // winning player picks last. setMinStarShipLifeSpan (winnerStarShip, deadShip->life_span + 1); } else if (winnerStarShip == NULL) { // Both died at the same time, or the loser has already expired HELEMENT hElement, hNextElement; // Find the other dead ship(s) and keep them alive for at least as // long as this ship. for (hElement = GetHeadElement (); hElement; hElement = hNextElement) { ELEMENT *element; STARSHIP *starShip; LockElement (hElement, &element); hNextElement = GetSuccElement (element); GetElementStarShip (element, &starShip); if (starShip != NULL && element != deadShip && starShip->RaceDescPtr->ship_info.crew_level == 0) { // This is another dead ship setMinShipLifeSpan (element, deadShip->life_span); } UnlockElement (hElement); } } }
// Count the crew elements in the display list. static COUNT CountCrewElements (void) { COUNT result; HELEMENT hElement, hNextElement; result = 0; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if (ElementPtr->state_flags & CREW_OBJECT) ++result; UnlockElement (hElement); } return result; }
static void confusion_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { if (ElementPtr1->state_flags & PLAYER_SHIP) { HELEMENT hConfusionElement, hNextElement; ELEMENT *ConfusionPtr; STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr0, &StarShipPtr); for (hConfusionElement = GetHeadElement (); hConfusionElement; hConfusionElement = hNextElement) { LockElement (hConfusionElement, &ConfusionPtr); if (elementsOfSamePlayer (ConfusionPtr, ElementPtr0) && ConfusionPtr->current.image.farray == StarShipPtr->RaceDescPtr->ship_data.special && (ConfusionPtr->state_flags & NONSOLID)) { UnlockElement (hConfusionElement); break; } hNextElement = GetSuccElement (ConfusionPtr); UnlockElement (hConfusionElement); } if (hConfusionElement || (hConfusionElement = AllocElement ())) { LockElement (hConfusionElement, &ConfusionPtr); if (ConfusionPtr->state_flags == 0) /* not allocated before */ { InsertElement (hConfusionElement, GetHeadElement ()); ConfusionPtr->current = ElementPtr0->next; ConfusionPtr->current.image.frame = SetAbsFrameIndex ( ConfusionPtr->current.image.frame, 8 ); ConfusionPtr->next = ConfusionPtr->current; ConfusionPtr->playerNr = ElementPtr0->playerNr; ConfusionPtr->state_flags = FINITE_LIFE | NONSOLID | CHANGING; ConfusionPtr->preprocess_func = confuse_preprocess; SetPrimType ( &(GLOBAL (DisplayArray))[ConfusionPtr->PrimIndex], NO_PRIM ); SetElementStarShip (ConfusionPtr, StarShipPtr); GetElementStarShip (ElementPtr1, &StarShipPtr); ConfusionPtr->hTarget = StarShipPtr->hShip; } ConfusionPtr->life_span = 400; ConfusionPtr->turn_wait = (BYTE)(1 << ((BYTE)TFB_Random () & 1)); /* LEFT or RIGHT */ UnlockElement (hConfusionElement); } ElementPtr0->hit_points = 0; ElementPtr0->life_span = 0; ElementPtr0->state_flags |= DISAPPEARING | COLLISION | NONSOLID; } (void) pPt0; /* Satisfying compiler (unused parameter) */ (void) pPt1; /* Satisfying compiler (unused parameter) */ }
static void spawn_crew (ELEMENT *ElementPtr) { if (ElementPtr->state_flags & PLAYER_SHIP) { HELEMENT hCrew; hCrew = AllocElement (); if (hCrew != 0) { ELEMENT *CrewPtr; LockElement (hCrew, &CrewPtr); CrewPtr->next.location = ElementPtr->next.location; CrewPtr->playerNr = ElementPtr->playerNr; CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE; CrewPtr->life_span = 0; CrewPtr->death_func = spawn_crew; CrewPtr->pParent = ElementPtr->pParent; CrewPtr->hTarget = 0; UnlockElement (hCrew); PutElement (hCrew); } } else { HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ObjPtr; STARSHIP *EnemyStarShipPtr; LockElement (hElement, &ObjPtr); GetElementStarShip (ObjPtr, &EnemyStarShipPtr); hNextElement = GetSuccElement (ObjPtr); if ((ObjPtr->state_flags & PLAYER_SHIP) && !elementsOfSamePlayer (ObjPtr, ElementPtr) && ObjPtr->crew_level > 1) { SIZE dx, dy; DWORD d_squared; dx = ObjPtr->next.location.x - ElementPtr->next.location.x; if (dx < 0) dx = -dx; dy = ObjPtr->next.location.y - ElementPtr->next.location.y; if (dy < 0) dy = -dy; dx = WORLD_TO_DISPLAY (dx); dy = WORLD_TO_DISPLAY (dy); if (dx <= ABANDONER_RANGE && dy <= ABANDONER_RANGE && (d_squared = (DWORD)((UWORD)dx * (UWORD)dx) + (DWORD)((UWORD)dy * (UWORD)dy)) <= (DWORD)((UWORD)ABANDONER_RANGE * (UWORD)ABANDONER_RANGE)) { COUNT crew_loss; if (EnemyStarShipPtr && EnemyStarShipPtr->SpeciesID == SYREEN_ID) { crew_loss = ((LIMITED_MAX_ABANDONERS * (ABANDONER_RANGE - square_root (d_squared))) / ABANDONER_RANGE) + 1; } else { crew_loss = ((MAX_ABANDONERS * (ABANDONER_RANGE - square_root (d_squared))) / ABANDONER_RANGE) + 1; } if (crew_loss >= ObjPtr->crew_level) crew_loss = ObjPtr->crew_level - 1; AbandonShip (ObjPtr, ElementPtr, crew_loss); } } UnlockElement (hElement); } } }
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); } } }
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); }
void UninitShips (void) { BYTE crew_retrieved; SIZE i; HELEMENT hElement, hNextElement; STARSHIPPTR SPtr[NUM_PLAYERS]; StopSound (); UninitSpace (); for (i = 0; i < NUM_PLAYERS; ++i) SPtr[i] = 0; // Count the crew floating in space. crew_retrieved = 0; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENTPTR ElementPtr; LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if (ElementPtr->state_flags & CREW_OBJECT) ++crew_retrieved; UnlockElement (hElement); } for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENTPTR ElementPtr; extern void new_ship (PELEMENT ElementPtr); LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if ((ElementPtr->state_flags & PLAYER_SHIP) || ElementPtr->death_func == new_ship) { STARSHIPPTR StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); // There should only be one ship left in battle. // He gets the crew still floating in space. if (StarShipPtr->RaceDescPtr->ship_info.crew_level) { if (crew_retrieved >= StarShipPtr->RaceDescPtr->ship_info.max_crew - StarShipPtr->RaceDescPtr->ship_info.crew_level) StarShipPtr->RaceDescPtr->ship_info.crew_level = StarShipPtr->RaceDescPtr->ship_info.max_crew; else StarShipPtr->RaceDescPtr->ship_info.crew_level += crew_retrieved; } if (StarShipPtr->RaceDescPtr->uninit_func != NULL) (*StarShipPtr->RaceDescPtr->uninit_func) ( StarShipPtr->RaceDescPtr); StarShipPtr->ShipFacing = StarShipPtr->RaceDescPtr->ship_info.var2; StarShipPtr->special_counter = StarShipPtr->RaceDescPtr->ship_info.crew_level; SPtr[WHICH_SIDE (ElementPtr->state_flags)] = StarShipPtr; free_ship (StarShipPtr, TRUE); } UnlockElement (hElement); } GLOBAL (CurrentActivity) &= ~IN_BATTLE; if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) { } else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER && !(GLOBAL (CurrentActivity) & CHECK_ABORT)) { for (i = NUM_PLAYERS - 1; i >= 0; --i) { if (SPtr[i]) GetEncounterStarShip (SPtr[i], i); } } if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER) { ReinitQueue (&race_q[0]); ReinitQueue (&race_q[1]); if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) FreeHyperspace (); } }
void ship_death (ELEMENT *ShipPtr) { STARSHIP *StarShipPtr; STARSHIP *VictoriousStarShipPtr; HELEMENT hElement, hNextElement; ELEMENT *ElementPtr; StopDitty (); StopMusic (); GetElementStarShip (ShipPtr, &StarShipPtr); // JMS: Make sure the Foon-foon's spinning blade sound ends when the ship dies. if (StarShipPtr->SpeciesID == FOONFOON_ID) { COUNT i; for (i = FIRST_SFX_CHANNEL; i <= LAST_SFX_CHANNEL; ++i) { ELEMENT *posobj; if (!ChannelPlaying(i)) continue; posobj = GetPositionalObject (i); if (posobj) StopSource (i); } } if (ShipPtr->mass_points <= MAX_SHIP_MASS) { // Not running away and not reincarnating (Pkunk) // When a ship tries to run away, it is (dis)counted in DoRunAway(), // so when it dies while running away, we will not count it again assert (StarShipPtr->playerNr >= 0); battle_counter[StarShipPtr->playerNr]--; } VictoriousStarShipPtr = NULL; for (hElement = GetHeadElement (); hElement; hElement = hNextElement) { LockElement (hElement, &ElementPtr); if ((ElementPtr->state_flags & PLAYER_SHIP) && ElementPtr != ShipPtr /* and not running away */ && ElementPtr->mass_points <= MAX_SHIP_MASS) { GetElementStarShip (ElementPtr, &VictoriousStarShipPtr); if (VictoriousStarShipPtr->RaceDescPtr->ship_info.crew_level == 0) VictoriousStarShipPtr = NULL; UnlockElement (hElement); break; } hNextElement = GetSuccElement (ElementPtr); UnlockElement (hElement); } StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY; DeltaEnergy (ShipPtr, -(SIZE)StarShipPtr->RaceDescPtr->ship_info.energy_level); ShipPtr->life_span = NUM_EXPLOSION_FRAMES * 3; ShipPtr->state_flags &= ~DISAPPEARING; ShipPtr->state_flags |= FINITE_LIFE | NONSOLID; ShipPtr->postprocess_func = PostProcessStatus; ShipPtr->death_func = cleanup_dead_ship; ShipPtr->hTarget = 0; ZeroVelocityComponents (&ShipPtr->velocity); if (ShipPtr->crew_level) /* only happens for shofixti self-destruct */ { PlaySound (SetAbsSoundIndex ( StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1); DeltaCrew (ShipPtr, -(SIZE)ShipPtr->crew_level); if (VictoriousStarShipPtr == NULL) { // No ships left alive after a Shofixti Glory device, // thus Shofixti wins VictoriousStarShipPtr = StarShipPtr; } } else { ShipPtr->preprocess_func = explosion_preprocess; PlaySound (SetAbsSoundIndex (GameSounds, SHIP_EXPLODES), CalcSoundPosition (ShipPtr), ShipPtr, GAME_SOUND_PRIORITY + 1); } if (VictoriousStarShipPtr != NULL) VictoriousStarShipPtr->cur_status_flags |= PLAY_VICTORY_DITTY; // The winner is set once per battle. If both ships die, this function is // called twice, once for each ship. We need to preserve the winner // determined on the first call. if (winnerStarShip == NULL) winnerStarShip = VictoriousStarShipPtr; if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE) MeleeShipDeath (StarShipPtr); }
void cleanup_dead_ship (ELEMENT *DeadShipPtr) { STARSHIP *DeadStarShipPtr; ProcessSound ((SOUND)~0, NULL); GetElementStarShip (DeadShipPtr, &DeadStarShipPtr); { // Ship explosion has finished, or ship has just warped out // if DeadStarShipPtr->crew_level != 0 BOOLEAN MusicStarted; HELEMENT hElement, hSuccElement; /* Record crew left after the battle */ DeadStarShipPtr->crew_level = DeadStarShipPtr->RaceDescPtr->ship_info.crew_level; MusicStarted = FALSE; for (hElement = GetHeadElement (); hElement; hElement = hSuccElement) { ELEMENT *ElementPtr; STARSHIP *StarShipPtr; LockElement (hElement, &ElementPtr); hSuccElement = GetSuccElement (ElementPtr); GetElementStarShip (ElementPtr, &StarShipPtr); // Get the STARSHIP that this ELEMENT belongs to. if (StarShipPtr == DeadStarShipPtr) { // This element belongs to the dead ship; it may be the // ship's own element. SetElementStarShip (ElementPtr, 0); if (!(ElementPtr->state_flags & CREW_OBJECT) || ElementPtr->preprocess_func != crew_preprocess) { // Set the element up for deletion. SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); ElementPtr->life_span = 0; ElementPtr->state_flags = NONSOLID | DISAPPEARING | FINITE_LIFE; ElementPtr->preprocess_func = 0; ElementPtr->postprocess_func = 0; ElementPtr->death_func = 0; ElementPtr->collision_func = 0; } } if (StarShipPtr && (StarShipPtr->cur_status_flags & PLAY_VICTORY_DITTY)) { // StarShipPtr points to the remaining ship. MusicStarted = TRUE; PlayDitty (StarShipPtr); StarShipPtr->cur_status_flags &= ~PLAY_VICTORY_DITTY; } UnlockElement (hElement); } DeadShipPtr->state_flags |= DeadShipPtr->turn_wait; #define MIN_DITTY_FRAME_COUNT ((ONE_SECOND * 3) / BATTLE_FRAME_RATE) // The ship will be "alive" for at least 2 more frames to make sure // the elements it owns (set up for deletion above) expire first. // Ditty does NOT play in the following circumstances: // * The winning ship dies before the loser finishes exploding // * At the moment the losing ship dies, the winner has started // the warp out sequence DeadShipPtr->life_span = MusicStarted ? MIN_DITTY_FRAME_COUNT : 1; if (DeadStarShipPtr == winnerStarShip) { // This ship died but won the battle. We need to keep it alive // longer than the dead opponent ship so that the winning player // picks last. DeadShipPtr->life_span = MIN_DITTY_FRAME_COUNT + 1; } DeadShipPtr->death_func = new_ship; DeadShipPtr->preprocess_func = preprocess_dead_ship; DeadShipPtr->state_flags &= ~DISAPPEARING; // XXX: this increment was originally done by another piece of code // just below this one. I am almost sure it is not needed, but it // keeps the original framecount. ++DeadShipPtr->life_span; SetElementStarShip (DeadShipPtr, DeadStarShipPtr); } }
// 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); } } }
void UninitShips (void) { COUNT crew_retrieved; int i; HELEMENT hElement, hNextElement; STARSHIP *SPtr[NUM_PLAYERS]; StopSound (); UninitSpace (); for (i = 0; i < NUM_PLAYERS; ++i) SPtr[i] = 0; // Count the crew floating in space. crew_retrieved = CountCrewElements(); for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ElementPtr; LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if ((ElementPtr->state_flags & PLAYER_SHIP) || ElementPtr->death_func == new_ship) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); // There should only be one ship left in battle. // He gets the crew still floating in space. if (StarShipPtr->RaceDescPtr->ship_info.crew_level) { if (crew_retrieved >= StarShipPtr->RaceDescPtr->ship_info.max_crew - StarShipPtr->RaceDescPtr->ship_info.crew_level) StarShipPtr->RaceDescPtr->ship_info.crew_level = StarShipPtr->RaceDescPtr->ship_info.max_crew; else StarShipPtr->RaceDescPtr->ship_info.crew_level += crew_retrieved; } /* Record crew left after battle */ StarShipPtr->crew_level = StarShipPtr->RaceDescPtr->ship_info.crew_level; SPtr[StarShipPtr->playerNr] = StarShipPtr; free_ship (StarShipPtr->RaceDescPtr, TRUE, TRUE); StarShipPtr->RaceDescPtr = 0; } UnlockElement (hElement); } GLOBAL (CurrentActivity) &= ~IN_BATTLE; if (LOBYTE (GLOBAL (CurrentActivity)) == IN_ENCOUNTER && !(GLOBAL (CurrentActivity) & CHECK_ABORT)) { // Encounter battle in full game. // Record the crew left in the last ship standing. The crew left // is first recorded into STARSHIP.crew_level just a few lines // above here. for (i = NUM_PLAYERS - 1; i >= 0; --i) { if (SPtr[i] && !FleetIsInfinite (i)) UpdateShipFragCrew (SPtr[i]); } } if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER) { // Remove any ships left from the race queue. for (i = 0; i < NUM_PLAYERS; i++) ReinitQueue (&race_q[i]); if (inHQSpace ()) FreeHyperspace (); } }
// 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); } } }
static void spawn_crew (ELEMENT *ElementPtr) { if (ElementPtr->state_flags & PLAYER_SHIP) { HELEMENT hCrew; hCrew = AllocElement (); if (hCrew != 0) { ELEMENT *CrewPtr; LockElement (hCrew, &CrewPtr); CrewPtr->next.location = ElementPtr->next.location; CrewPtr->playerNr = ElementPtr->playerNr; CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE; CrewPtr->life_span = 0; CrewPtr->death_func = spawn_crew; CrewPtr->pParent = ElementPtr->pParent; CrewPtr->hTarget = 0; UnlockElement (hCrew); PutElement (hCrew); } } else { HELEMENT hElement, hNextElement; for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ObjPtr; LockElement (hElement, &ObjPtr); hNextElement = GetSuccElement (ObjPtr); if ((ObjPtr->state_flags & PLAYER_SHIP) && !elementsOfSamePlayer (ObjPtr, ElementPtr) && ObjPtr->crew_level > 1) { SIZE dx, dy; DWORD d_squared; dx = ObjPtr->next.location.x - ElementPtr->next.location.x; if (dx < 0) dx = -dx; dy = ObjPtr->next.location.y - ElementPtr->next.location.y; if (dy < 0) dy = -dy; dx = WORLD_TO_DISPLAY (dx); dy = WORLD_TO_DISPLAY (dy); #define ABANDONER_RANGE (208 << RESOLUTION_FACTOR) // JMS_GFX /* originally SPACE_HEIGHT */ if (dx <= ABANDONER_RANGE && dy <= ABANDONER_RANGE && (d_squared = (DWORD)((UWORD)dx * (UWORD)dx) + (DWORD)((UWORD)dy * (UWORD)dy)) <= (DWORD)((UWORD)ABANDONER_RANGE * (UWORD)ABANDONER_RANGE)) { #define MAX_ABANDONERS 8 COUNT crew_loss; crew_loss = ((MAX_ABANDONERS * (ABANDONER_RANGE - square_root (d_squared))) / ABANDONER_RANGE) + 1; if (crew_loss >= ObjPtr->crew_level) crew_loss = ObjPtr->crew_level - 1; AbandonShip (ObjPtr, ElementPtr, crew_loss); } } UnlockElement (hElement); } } }
void UninitShips (void) { COUNT crew_retrieved; SIZE i; HELEMENT hElement, hNextElement; STARSHIP *SPtr[NUM_PLAYERS]; StopSound (); UninitSpace (); for (i = 0; i < NUM_PLAYERS; ++i) SPtr[i] = 0; // Count the crew floating in space. crew_retrieved = CountCrewElements(); for (hElement = GetHeadElement (); hElement != 0; hElement = hNextElement) { ELEMENT *ElementPtr; extern void new_ship (ELEMENT *ElementPtr); LockElement (hElement, &ElementPtr); hNextElement = GetSuccElement (ElementPtr); if ((ElementPtr->state_flags & PLAYER_SHIP) || ElementPtr->death_func == new_ship) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); // There should only be one ship left in battle. // He gets the crew still floating in space. if (StarShipPtr->RaceDescPtr->ship_info.crew_level) { if (crew_retrieved >= StarShipPtr->RaceDescPtr->ship_info.max_crew - StarShipPtr->RaceDescPtr->ship_info.crew_level) StarShipPtr->RaceDescPtr->ship_info.crew_level = StarShipPtr->RaceDescPtr->ship_info.max_crew; else StarShipPtr->RaceDescPtr->ship_info.crew_level += crew_retrieved; } if (StarShipPtr->RaceDescPtr->uninit_func != NULL) (*StarShipPtr->RaceDescPtr->uninit_func) ( StarShipPtr->RaceDescPtr); /* Record crew left after battle */ StarShipPtr->crew_level = StarShipPtr->RaceDescPtr->ship_info.crew_level; SPtr[WHICH_SIDE (ElementPtr->state_flags)] = StarShipPtr; free_ship (StarShipPtr->RaceDescPtr, TRUE, TRUE); StarShipPtr->RaceDescPtr = 0; } UnlockElement (hElement); } GLOBAL (CurrentActivity) &= ~IN_BATTLE; if (LOBYTE (GLOBAL (CurrentActivity)) == IN_LAST_BATTLE) { } else if (LOBYTE (GLOBAL (CurrentActivity)) <= IN_ENCOUNTER && !(GLOBAL (CurrentActivity) & CHECK_ABORT)) { // XXX: This has no purpose for SuperMelee // In full-game, the sole purpose of this is to record the crew // left in the last ship standing. The crew left is first recorded // into STARSHIP.crew_level just a few lines above here. for (i = NUM_PLAYERS - 1; i >= 0; --i) { if (SPtr[i]) GetEncounterStarShip (SPtr[i], i); } } if (LOBYTE (GLOBAL (CurrentActivity)) != IN_ENCOUNTER) { // Remove any ships left from the race queue. for (i = 0; i < NUM_PLAYERS; i++) ReinitQueue (&race_q[i]); if (LOBYTE (GLOBAL (CurrentActivity)) == IN_HYPERSPACE) FreeHyperspace (); } }