1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "../ship.h"
20 #include "syreen.h"
21 #include "resinst.h"
22
23 #include "libs/mathlib.h"
24
25 // Core characteristics
26 #define SYREEN_MAX_CREW_SIZE MAX_CREW_SIZE
27 #define MAX_CREW 12
28 #define MAX_ENERGY 16
29 #define ENERGY_REGENERATION 1
30 #define ENERGY_WAIT 6
31 #define MAX_THRUST /* DISPLAY_TO_WORLD (8) */ 36
32 #define THRUST_INCREMENT /* DISPLAY_TO_WORLD (2) */ 9
33 #define THRUST_WAIT 1
34 #define TURN_WAIT 1
35 #define SHIP_MASS 2
36
37 // Particle Beam Stiletto
38 #define WEAPON_ENERGY_COST 1
39 #define WEAPON_WAIT 8
40 #define SYREEN_OFFSET 30
41 #define MISSILE_SPEED DISPLAY_TO_WORLD (30)
42 #define MISSILE_LIFE 10
43 #define MISSILE_HITS 1
44 #define MISSILE_DAMAGE 2
45 #define MISSILE_OFFSET 3
46
47 // Syreen song
48 #define SPECIAL_ENERGY_COST 5
49 #define SPECIAL_WAIT 20
50 #define ABANDONER_RANGE 208 /* originally SPACE_HEIGHT */
51 #define MAX_ABANDONERS 8
52
53 static RACE_DESC syreen_desc =
54 {
55 { /* SHIP_INFO */
56 FIRES_FORE,
57 13, /* Super Melee cost */
58 MAX_CREW, SYREEN_MAX_CREW_SIZE,
59 MAX_ENERGY, MAX_ENERGY,
60 SYREEN_RACE_STRINGS,
61 SYREEN_ICON_MASK_PMAP_ANIM,
62 SYREEN_MICON_MASK_PMAP_ANIM,
63 NULL, NULL, NULL
64 },
65 { /* FLEET_STUFF */
66 0, /* Initial sphere of influence radius */
67 { /* Known location (center of SoI) */
68 0, 0,
69 },
70 },
71 {
72 MAX_THRUST,
73 THRUST_INCREMENT,
74 ENERGY_REGENERATION,
75 WEAPON_ENERGY_COST,
76 SPECIAL_ENERGY_COST,
77 ENERGY_WAIT,
78 TURN_WAIT,
79 THRUST_WAIT,
80 WEAPON_WAIT,
81 SPECIAL_WAIT,
82 SHIP_MASS,
83 },
84 {
85 {
86 SYREEN_BIG_MASK_PMAP_ANIM,
87 SYREEN_MED_MASK_PMAP_ANIM,
88 SYREEN_SML_MASK_PMAP_ANIM,
89 },
90 {
91 DAGGER_BIG_MASK_PMAP_ANIM,
92 DAGGER_MED_MASK_PMAP_ANIM,
93 DAGGER_SML_MASK_PMAP_ANIM,
94 },
95 {
96 NULL_RESOURCE,
97 NULL_RESOURCE,
98 NULL_RESOURCE,
99 },
100 {
101 SYREEN_CAPTAIN_MASK_PMAP_ANIM,
102 NULL, NULL, NULL, NULL, NULL
103 },
104 SYREEN_VICTORY_SONG,
105 SYREEN_SHIP_SOUNDS,
106 { NULL, NULL, NULL },
107 { NULL, NULL, NULL },
108 { NULL, NULL, NULL },
109 NULL, NULL
110 },
111 {
112 0,
113 (MISSILE_SPEED * MISSILE_LIFE * 2 / 3),
114 NULL,
115 },
116 (UNINIT_FUNC *) NULL,
117 (PREPROCESS_FUNC *) NULL,
118 (POSTPROCESS_FUNC *) NULL,
119 (INIT_WEAPON_FUNC *) NULL,
120 0,
121 0, /* CodeRef */
122 };
123
124 static COUNT
initialize_dagger(ELEMENT * ShipPtr,HELEMENT DaggerArray[])125 initialize_dagger (ELEMENT *ShipPtr, HELEMENT DaggerArray[])
126 {
127 STARSHIP *StarShipPtr;
128 MISSILE_BLOCK MissileBlock;
129
130 GetElementStarShip (ShipPtr, &StarShipPtr);
131 MissileBlock.cx = ShipPtr->next.location.x;
132 MissileBlock.cy = ShipPtr->next.location.y;
133 MissileBlock.farray = StarShipPtr->RaceDescPtr->ship_data.weapon;
134 MissileBlock.face = MissileBlock.index = StarShipPtr->ShipFacing;
135 MissileBlock.sender = ShipPtr->playerNr;
136 MissileBlock.flags = IGNORE_SIMILAR;
137 MissileBlock.pixoffs = SYREEN_OFFSET;
138 MissileBlock.speed = MISSILE_SPEED;
139 MissileBlock.hit_points = MISSILE_HITS;
140 MissileBlock.damage = MISSILE_DAMAGE;
141 MissileBlock.life = MISSILE_LIFE;
142 MissileBlock.preprocess_func = NULL;
143 MissileBlock.blast_offs = MISSILE_OFFSET;
144 DaggerArray[0] = initialize_missile (&MissileBlock);
145
146 return (1);
147 }
148
149 static void
spawn_crew(ELEMENT * ElementPtr)150 spawn_crew (ELEMENT *ElementPtr)
151 {
152 if (ElementPtr->state_flags & PLAYER_SHIP)
153 {
154 HELEMENT hCrew;
155
156 hCrew = AllocElement ();
157 if (hCrew != 0)
158 {
159 ELEMENT *CrewPtr;
160
161 LockElement (hCrew, &CrewPtr);
162 CrewPtr->next.location = ElementPtr->next.location;
163 CrewPtr->playerNr = ElementPtr->playerNr;
164 CrewPtr->state_flags = APPEARING | NONSOLID | FINITE_LIFE;
165 CrewPtr->life_span = 0;
166 CrewPtr->death_func = spawn_crew;
167 CrewPtr->pParent = ElementPtr->pParent;
168 CrewPtr->hTarget = 0;
169 UnlockElement (hCrew);
170
171 PutElement (hCrew);
172 }
173 }
174 else
175 {
176 HELEMENT hElement, hNextElement;
177
178 for (hElement = GetHeadElement ();
179 hElement != 0; hElement = hNextElement)
180 {
181 ELEMENT *ObjPtr;
182
183 LockElement (hElement, &ObjPtr);
184 hNextElement = GetSuccElement (ObjPtr);
185
186 if ((ObjPtr->state_flags & PLAYER_SHIP)
187 && !elementsOfSamePlayer (ObjPtr, ElementPtr)
188 && ObjPtr->crew_level > 1)
189 {
190 SIZE dx, dy;
191 DWORD d_squared;
192
193 dx = ObjPtr->next.location.x - ElementPtr->next.location.x;
194 if (dx < 0)
195 dx = -dx;
196 dy = ObjPtr->next.location.y - ElementPtr->next.location.y;
197 if (dy < 0)
198 dy = -dy;
199
200 dx = WORLD_TO_DISPLAY (dx);
201 dy = WORLD_TO_DISPLAY (dy);
202 if (dx <= ABANDONER_RANGE && dy <= ABANDONER_RANGE
203 && (d_squared = (DWORD)((UWORD)dx * (UWORD)dx)
204 + (DWORD)((UWORD)dy * (UWORD)dy)) <=
205 (DWORD)((UWORD)ABANDONER_RANGE * (UWORD)ABANDONER_RANGE))
206 {
207 COUNT crew_loss;
208
209 crew_loss = ((MAX_ABANDONERS
210 * (ABANDONER_RANGE - square_root (d_squared)))
211 / ABANDONER_RANGE) + 1;
212 if (crew_loss >= ObjPtr->crew_level)
213 crew_loss = ObjPtr->crew_level - 1;
214
215 AbandonShip (ObjPtr, ElementPtr, crew_loss);
216 }
217 }
218
219 UnlockElement (hElement);
220 }
221 }
222 }
223
224 static void
syreen_intelligence(ELEMENT * ShipPtr,EVALUATE_DESC * ObjectsOfConcern,COUNT ConcernCounter)225 syreen_intelligence (ELEMENT *ShipPtr, EVALUATE_DESC *ObjectsOfConcern,
226 COUNT ConcernCounter)
227 {
228 EVALUATE_DESC *lpEvalDesc;
229
230 ship_intelligence (ShipPtr,
231 ObjectsOfConcern, ConcernCounter);
232
233 lpEvalDesc = &ObjectsOfConcern[ENEMY_SHIP_INDEX];
234 if (lpEvalDesc->ObjectPtr != NULL)
235 {
236 STARSHIP *StarShipPtr;
237 STARSHIP *EnemyStarShipPtr;
238
239 GetElementStarShip (ShipPtr, &StarShipPtr);
240 GetElementStarShip (lpEvalDesc->ObjectPtr, &EnemyStarShipPtr);
241 if (!(EnemyStarShipPtr->RaceDescPtr->ship_info.ship_flags & CREW_IMMUNE)
242 && StarShipPtr->special_counter == 0
243 && lpEvalDesc->ObjectPtr->crew_level > 1
244 && lpEvalDesc->which_turn <= 14)
245 StarShipPtr->ship_input_state |= SPECIAL;
246 else
247 StarShipPtr->ship_input_state &= ~SPECIAL;
248 }
249 }
250
251 static void
syreen_postprocess(ELEMENT * ElementPtr)252 syreen_postprocess (ELEMENT *ElementPtr)
253 {
254 STARSHIP *StarShipPtr;
255
256 GetElementStarShip (ElementPtr, &StarShipPtr);
257 if ((StarShipPtr->cur_status_flags & SPECIAL)
258 && StarShipPtr->special_counter == 0
259 && DeltaEnergy (ElementPtr, -SPECIAL_ENERGY_COST))
260 {
261 ProcessSound (SetAbsSoundIndex (
262 /* SYREEN_SONG */
263 StarShipPtr->RaceDescPtr->ship_data.ship_sounds, 1), ElementPtr);
264 spawn_crew (ElementPtr);
265
266 StarShipPtr->special_counter =
267 StarShipPtr->RaceDescPtr->characteristics.special_wait;
268 }
269 }
270
271 RACE_DESC*
init_syreen(void)272 init_syreen (void)
273 {
274 RACE_DESC *RaceDescPtr;
275
276 syreen_desc.postprocess_func = syreen_postprocess;
277 syreen_desc.init_weapon_func = initialize_dagger;
278 syreen_desc.cyborg_control.intelligence_func = syreen_intelligence;
279
280 RaceDescPtr = &syreen_desc;
281
282 return (RaceDescPtr);
283 }
284
285