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