//Copyright Paul Reiche, Fred Ford. 1992-2002 /* * 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 "element.h" #include "init.h" #include "races.h" #include "ship.h" #include "status.h" #include "setup.h" #include "sounds.h" #include "weapon.h" #include "libs/mathlib.h" void spawn_planet (void) { HELEMENT hPlanetElement; hPlanetElement = AllocElement (); if (hPlanetElement) { ELEMENT *PlanetElementPtr; extern FRAME planet[]; LockElement (hPlanetElement, &PlanetElementPtr); PlanetElementPtr->playerNr = NEUTRAL_PLAYER_NUM; PlanetElementPtr->hit_points = 200; PlanetElementPtr->state_flags = APPEARING; PlanetElementPtr->life_span = NORMAL_LIFE + 1; SetPrimType (&DisplayArray[PlanetElementPtr->PrimIndex], STAMP_PRIM); PlanetElementPtr->current.image.farray = planet; PlanetElementPtr->current.image.frame = PlanetElementPtr->current.image.farray[0]; PlanetElementPtr->collision_func = collision; PlanetElementPtr->postprocess_func = (void (*) (struct element *ElementPtr))CalculateGravity; ZeroVelocityComponents (&PlanetElementPtr->velocity); do { PlanetElementPtr->current.location.x = WRAP_X (DISPLAY_ALIGN_X (TFB_Random ())); PlanetElementPtr->current.location.y = WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ())); } while (CalculateGravity (PlanetElementPtr) || TimeSpaceMatterConflict (PlanetElementPtr)); PlanetElementPtr->mass_points = PlanetElementPtr->hit_points; UnlockElement (hPlanetElement); PutElement (hPlanetElement); } } extern FRAME asteroid[]; static void spawn_rubble (ELEMENT *AsteroidElementPtr) { HELEMENT hRubbleElement; hRubbleElement = AllocElement (); if (hRubbleElement) { ELEMENT *RubbleElementPtr; PutElement (hRubbleElement); LockElement (hRubbleElement, &RubbleElementPtr); RubbleElementPtr->playerNr = AsteroidElementPtr->playerNr; RubbleElementPtr->state_flags = APPEARING | FINITE_LIFE | NONSOLID; RubbleElementPtr->life_span = 5; RubbleElementPtr->turn_wait = RubbleElementPtr->next_turn = 0; SetPrimType (&DisplayArray[RubbleElementPtr->PrimIndex], STAMP_PRIM); RubbleElementPtr->current.image.farray = asteroid; RubbleElementPtr->current.image.frame = SetAbsFrameIndex (asteroid[0], ANGLE_TO_FACING (FULL_CIRCLE)); RubbleElementPtr->current.location = AsteroidElementPtr->current.location; RubbleElementPtr->preprocess_func = animation_preprocess; RubbleElementPtr->death_func = spawn_asteroid; UnlockElement (hRubbleElement); } } static void asteroid_preprocess (ELEMENT *ElementPtr) { if (ElementPtr->turn_wait > 0) --ElementPtr->turn_wait; else { COUNT frame_index; frame_index = GetFrameIndex (ElementPtr->current.image.frame); if (ElementPtr->thrust_wait & (1 << 7)) --frame_index; else ++frame_index; ElementPtr->next.image.frame = SetAbsFrameIndex (ElementPtr->current.image.frame, NORMALIZE_FACING (frame_index)); ElementPtr->state_flags |= CHANGING; ElementPtr->turn_wait = (unsigned char)(ElementPtr->thrust_wait & ((1 << 7) - 1)); } } void spawn_asteroid (ELEMENT *ElementPtr) { HELEMENT hAsteroidElement; if ((hAsteroidElement = AllocElement ()) == 0) { if (ElementPtr != 0) { ElementPtr->state_flags &= ~DISAPPEARING; SetPrimType (&DisplayArray[ElementPtr->PrimIndex], NO_PRIM); ElementPtr->life_span = 1; } } else { ELEMENT *AsteroidElementPtr; COUNT val; LockElement (hAsteroidElement, &AsteroidElementPtr); AsteroidElementPtr->playerNr = NEUTRAL_PLAYER_NUM; AsteroidElementPtr->hit_points = 1; AsteroidElementPtr->mass_points = 3; AsteroidElementPtr->state_flags = APPEARING; AsteroidElementPtr->life_span = NORMAL_LIFE; SetPrimType (&DisplayArray[AsteroidElementPtr->PrimIndex], STAMP_PRIM); if ((val = (COUNT)TFB_Random ()) & (1 << 0)) { if (!(val & (1 << 1))) AsteroidElementPtr->current.location.x = 0; else AsteroidElementPtr->current.location.x = LOG_SPACE_WIDTH; AsteroidElementPtr->current.location.y = WRAP_Y (DISPLAY_ALIGN_Y (TFB_Random ())); } else { AsteroidElementPtr->current.location.x = WRAP_X (DISPLAY_ALIGN_X (TFB_Random ())); if (!(val & (1 << 1))) AsteroidElementPtr->current.location.y = 0; else AsteroidElementPtr->current.location.y = LOG_SPACE_HEIGHT; } { // Using these temporary variables because the execution order // of function arguments may vary per system, which may break // synchronisation on network games. SIZE magnitude = DISPLAY_TO_WORLD (((SIZE)TFB_Random () & 7) + 4); COUNT facing = (COUNT)TFB_Random (); SetVelocityVector (&AsteroidElementPtr->velocity, magnitude, facing); } AsteroidElementPtr->current.image.farray = asteroid; AsteroidElementPtr->current.image.frame = SetAbsFrameIndex (asteroid[0], NORMALIZE_FACING (TFB_Random ())); AsteroidElementPtr->turn_wait = AsteroidElementPtr->thrust_wait = (BYTE)TFB_Random () & (BYTE)((1 << 2) - 1); AsteroidElementPtr->thrust_wait |= (BYTE)TFB_Random () & (BYTE)(1 << 7); AsteroidElementPtr->preprocess_func = asteroid_preprocess; AsteroidElementPtr->death_func = spawn_rubble; AsteroidElementPtr->collision_func = collision; UnlockElement (hAsteroidElement); PutElement (hAsteroidElement); } } 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; } } } #define CREW_COLOR_LOW_INTENSITY \ BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02) #define CREW_COLOR_HIGH_INTENSITY \ BUILD_COLOR (MAKE_RGB15 (0x0A, 0x1E, 0x0A), 0x0A) void crew_preprocess (ELEMENT *ElementPtr) { HELEMENT hTarget; // Switch from dark to light or vice versa: Color oldColor = GetPrimColor (&DisplayArray[ElementPtr->PrimIndex]); Color newColor = sameColor (oldColor, CREW_COLOR_LOW_INTENSITY) ? CREW_COLOR_HIGH_INTENSITY : CREW_COLOR_LOW_INTENSITY; SetPrimColor (&DisplayArray[ElementPtr->PrimIndex], newColor); ElementPtr->state_flags |= CHANGING; hTarget = ElementPtr->hTarget; if (hTarget == 0) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr, &StarShipPtr); if (StarShipPtr && StarShipPtr->RaceDescPtr->ship_info.crew_level) ElementPtr->hTarget = StarShipPtr->hShip; else { COUNT facing; facing = 0; TrackShip (ElementPtr, &facing); } } if (hTarget) { #define CREW_DELTA SCALED_ONE SIZE delta; ELEMENT *ShipPtr; LockElement (hTarget, &ShipPtr); delta = ShipPtr->current.location.x - ElementPtr->current.location.x; delta = WRAP_DELTA_X (delta); if (delta > 0) ElementPtr->next.location.x += CREW_DELTA; else if (delta < 0) ElementPtr->next.location.x -= CREW_DELTA; delta = ShipPtr->current.location.y - ElementPtr->current.location.y; delta = WRAP_DELTA_Y (delta); if (delta > 0) ElementPtr->next.location.y += CREW_DELTA; else if (delta < 0) ElementPtr->next.location.y -= CREW_DELTA; UnlockElement (hTarget); } } void crew_collision (ELEMENT *ElementPtr0, POINT *pPt0, ELEMENT *ElementPtr1, POINT *pPt1) { if ((ElementPtr1->state_flags & PLAYER_SHIP) && ElementPtr1->life_span >= NORMAL_LIFE && ElementPtr0->hit_points > 0) { STARSHIP *StarShipPtr; GetElementStarShip (ElementPtr1, &StarShipPtr); if (!(StarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE)) { ProcessSound (SetAbsSoundIndex (GameSounds, GRAB_CREW), ElementPtr1); DeltaCrew (ElementPtr1, 1); } } ElementPtr0->hit_points = 0; ElementPtr0->life_span = 0; ElementPtr0->state_flags |= COLLISION | DISAPPEARING | NONSOLID; (void) pPt0; /* Satisfying compiler (unused parameter) */ (void) pPt1; /* Satisfying compiler (unused parameter) */ } void AbandonShip (ELEMENT *ShipPtr, ELEMENT *TargetPtr, COUNT crew_loss) { SIZE dx, dy; COUNT direction; RECT r; STARSHIP *StarShipPtr; HELEMENT hCrew; INTERSECT_CONTROL ShipIntersect; GetElementStarShip (ShipPtr, &StarShipPtr); if (StarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE) return; ShipIntersect = ShipPtr->IntersectControl; GetFrameRect (ShipIntersect.IntersectStamp.frame, &r); if ((direction = GetVelocityTravelAngle ( &ShipPtr->velocity)) == FULL_CIRCLE) dx = dy = 0; else { #define MORE_THAN_ENOUGH 100 direction += HALF_CIRCLE; dx = COSINE (direction, MORE_THAN_ENOUGH); dy = SINE (direction, MORE_THAN_ENOUGH); } while (crew_loss-- && (hCrew = AllocElement ())) { #define CREW_LIFE 300 ELEMENT *CrewPtr; DeltaCrew (ShipPtr, -1); PutElement (hCrew); LockElement (hCrew, &CrewPtr); CrewPtr->playerNr = NEUTRAL_PLAYER_NUM; CrewPtr->hit_points = 1; CrewPtr->state_flags = APPEARING | FINITE_LIFE | CREW_OBJECT; CrewPtr->life_span = CREW_LIFE; SetPrimType (&DisplayArray[CrewPtr->PrimIndex], POINT_PRIM); SetPrimColor (&DisplayArray[CrewPtr->PrimIndex], BUILD_COLOR (MAKE_RGB15 (0x00, 0x14, 0x00), 0x02)); CrewPtr->current.image.frame = DecFrameIndex (stars_in_space); CrewPtr->current.image.farray = &stars_in_space; CrewPtr->preprocess_func = crew_preprocess; CrewPtr->collision_func = crew_collision; SetElementStarShip (CrewPtr, StarShipPtr); GetElementStarShip (TargetPtr, &StarShipPtr); CrewPtr->hTarget = StarShipPtr->hShip; { SIZE w, h; INTERSECT_CONTROL CrewIntersect; ShipIntersect.IntersectStamp.origin = ShipPtr->IntersectControl.EndPoint; w = (SIZE)((COUNT)TFB_Random () % r.extent.width); h = (SIZE)((COUNT)TFB_Random () % r.extent.height); CrewIntersect.EndPoint = ShipIntersect.EndPoint; CrewIntersect.IntersectStamp.frame = DecFrameIndex (stars_in_space); if (dx == 0 && dy == 0) { CrewIntersect.EndPoint.x += w - (r.extent.width >> 1); CrewIntersect.EndPoint.y += h - (r.extent.height >> 1); CrewIntersect.IntersectStamp.origin = TargetPtr->IntersectControl.EndPoint; } else { if (dx == 0) CrewIntersect.EndPoint.x += w - (r.extent.width >> 1); else if (dx > 0) CrewIntersect.EndPoint.x += w; else CrewIntersect.EndPoint.x -= w; if (dy == 0) CrewIntersect.EndPoint.y += h - (r.extent.height >> 1); else if (dy > 0) CrewIntersect.EndPoint.y += h; else CrewIntersect.EndPoint.y -= h; CrewIntersect.IntersectStamp.origin.x = CrewIntersect.EndPoint.x + dx; CrewIntersect.IntersectStamp.origin.y = CrewIntersect.EndPoint.y + dy; } DrawablesIntersect (&CrewIntersect, &ShipIntersect, MAX_TIME_VALUE); CrewPtr->current.location.x = DISPLAY_TO_WORLD (CrewIntersect.EndPoint.x); CrewPtr->current.location.y = DISPLAY_TO_WORLD (CrewIntersect.EndPoint.y); } UnlockElement (hCrew); } }