1 /*
2    Copyright (C) 2004 by James Gregory
3    Part of the GalaxyHack project
4 
5    This program is free software; you can redistribute it and/or modify
6    it under the terms of the GNU General Public License.
7    This program is distributed in the hope that it will be useful,
8    but WITHOUT ANY WARRANTY.
9 
10    See the COPYING file for more details.
11 */
12 
13 #include "RTSUnit_Base.h"
14 #include "Globals.h"
15 #include "Group.h"
16 #include "Inlines.h"
17 #include "Projectile.h"
18 #include "Random.h"
19 
20 using std::find;
21 
AutoFireUnit(const string & iName,int iMySide,int iMyGroup)22 AutoFireUnit::AutoFireUnit(const string& iName, int iMySide, int iMyGroup):
23 RTSUnit_Base(iName, iMySide, iMyGroup) {}
24 
LoadWeapCoords(istream_iterator<char> & iter,istream_iterator<char> & fileEnd)25 void AutoFireUnit::LoadWeapCoords(istream_iterator<char>& iter, istream_iterator<char>& fileEnd) {
26 	smallPositions.clear();
27 
28 	iter = find(iter, fileEnd, ':');
29 	++iter;
30 	++iter;
31 
32 	CoordsInt tempCoords;
33 
34 	for (int i = 0; i != smallNumber; ++i) {
35 		tempCoords.x = IterToInt(iter, fileEnd);
36 		++iter;
37 		tempCoords.y = IterToInt(iter, fileEnd);
38 		++iter;
39 		smallPositions.push_back(tempCoords);
40 	}
41 
42 	for (int i = 0; i != smallNumber; ++i) {
43 		if (smallPositions[i].x < 0 || smallPositions[i].x >= width || smallPositions[i].y < 0 || smallPositions[i].y >= height) {
44 			string error = "Position for a small weapon is outside of the unit for " + name;
45 			throw runtime_error(error.c_str());
46 		}
47 
48 		if (myType == UT_CaShUnit) {
49 			for (int j = 0; j != smallNumber; ++j) {
50 				if (i == j)
51 					continue;
52 				if (smallPositions[i] == smallPositions[j]) {
53 					string error = "Capital ship " + name + " has more than one small weapon in the same position";
54 					throw runtime_error(error.c_str());
55 				}
56 			}
57 		}
58 	}
59 
60 	iter = find(iter, fileEnd, ':');
61 	++iter;
62 
63 	bigPosition.x = IterToInt(iter, fileEnd);
64 	++iter;
65 	bigPosition.y = IterToInt(iter, fileEnd);
66 
67 	if (bigPosition.x < 0 || bigPosition.x >= width || bigPosition.y < 0 || bigPosition.y >= height) {
68 		string error = "Position for big weapon is outside of the unit for " + name;
69 		throw runtime_error(error.c_str());
70 	}
71 }
72 
InitSmall()73 void AutoFireUnit::InitSmall() {
74 	smallTargets.resize(smallNumber);
75 	smallTimer.resize(smallNumber);
76 	smallAiming.resize(smallNumber);
77 
78 	//smallStage must be initialized with ready
79 	smallStage.resize(smallNumber, w_Ready);
80 }
81 
SetupSmallForFiring(int nSmall,vector<CoordsInt> & inRange)82 void AutoFireUnit::SetupSmallForFiring(int nSmall, vector<CoordsInt>& inRange) {
83 	smallStage[nSmall] = w_Aiming;
84 
85 	int ran = Random() % inRange.size();
86 
87 	//x is side, y is group
88 	smallTargets[nSmall].x = inRange[ran].x;
89 	smallTargets[nSmall].y = inRange[ran].y;
90 
91 	smallTimer[nSmall] = frameCounter;
92 	smallAiming[nSmall] = Random() % maxAiming;
93 }
94 
95 //we ignore theCommands
FireSmall(AICommands & theCommands)96 void AutoFireUnit::FireSmall(AICommands& theCommands) {
97 	//loop through this unit's weapons
98 	for (int i = 0; i != smallNumber; ++i) {
99 		//if aiming time is up...
100 		if (smallStage[i] == w_Firing) {
101 			smallStage[i] = w_Reloading;
102 			smallTimer[i] = frameCounter;
103 
104 			float startx;
105 			if (bFlip)
106 				startx = myx + width - smallPositions[i].x;
107 			else
108 				startx = myx + smallPositions[i].x;
109 
110 			projectiles.push_back(Projectile(startx, myy + smallPositions[i].y, smallTargets[i], smallType, sides[mySide].laserColor));
111 		}
112 	}
113 }
114 
Upkeep()115 void AutoFireUnit::Upkeep() {
116 	RTSUnit_Base::Upkeep();
117 
118 	//check for small reloading
119 	for (int i = 0; i != smallNumber; ++i) {
120 		if (smallStage[i] == w_Reloading && frameCounter - smallTimer[i] > weaponLookup[smallType].reload)
121 			smallStage[i] = w_Ready;
122 	}
123 
124 	//check for small aiming (time decided when we choose
125 	//a target)
126 	for (int i = 0; i != smallNumber; ++i) {
127 		if (smallStage[i] == w_Aiming && frameCounter - smallTimer[i] > smallAiming[i])
128 			smallStage[i] = w_Firing;
129 	}
130 }
131 
SelectSmallTargets()132 void AutoFireUnit::SelectSmallTargets() {
133 	vector<CoordsInt> inRange;
134 	FindInRange(inRange, weaponLookup[smallType].range);
135 
136 	if (inRange.size() > 0) {
137 		//loop through this unit's weapons
138 		for (int i = 0; i != smallNumber; ++i) {
139 			//if the weapon is ready for new orders...
140 			if (smallStage[i] == w_Ready)
141 				SetupSmallForFiring(i, inRange);
142 		}
143 	}
144 }
145 
FindInRange(vector<CoordsInt> & inRange,int range)146 void AutoFireUnit::FindInRange(vector<CoordsInt>& inRange, int range) {
147 	for (int i = 0; i != sides.size(); ++i) {
148 		//only enemy sides
149 		if (sides[i].myFlag == sides[mySide].myFlag)
150 			continue;
151 
152 		for (int j = 0; j != sides[i].groups.size(); ++j) {
153 			if (!sides[i].groups[j].GetAlive())
154 				continue;
155 
156 			CoordsInt d = sides[mySide].groups[myGroup].GetDxDyClose(i, j);
157 
158 			//comparison done on squared distances so
159 			//we don't have to do square roots
160 			//because they get squared it doesn't matter if these
161 			//answers are negative or positive
162 			if (range * range >= d.x*d.x + d.y*d.y) {
163 				CoordsInt tempCoords = {i, j};
164 				inRange.push_back(tempCoords);
165 			}
166 		}
167 	}
168 }
169 
GetWeakSpot() const170 CoordsInt AutoFireUnit::GetWeakSpot() const {
171 	CoordsInt ret;
172     //relative to centre of unit
173 	ret.x = Random() % (width >> 1)  - (width >> 2);
174 	ret.y = Random() % (height >> 1) - (height >> 2);
175 	return ret;
176 }
177 
GetWeapCoords(vector<CoordsInt> & giveSmall,CoordsInt & giveBig) const178 void AutoFireUnit::GetWeapCoords(vector<CoordsInt>& giveSmall, CoordsInt& giveBig) const {
179 	giveSmall = smallPositions;
180 	giveBig = bigPosition;
181 }
182 
183 ///
184 
CapitalShip(int iMySide,int iMyGroup,const string & iName,CapShipType iCSType)185 CapitalShip::CapitalShip(int iMySide, int iMyGroup, const string& iName, CapShipType iCSType):
186 AutoFireUnit(iName, iMySide, iMyGroup) {
187 	//load if found either in struct or on hard drive
188 	if (sides[mySide].sdStruct.unitData.find(name) != sides[mySide].sdStruct.unitData.end()
189 	|| DoesFileExist(GetFleetDir(mySide) + name + ".dat"))
190 		LoadData();
191 
192 	else {
193 		myType = UT_CaShUnit;
194 		myCSType = iCSType;
195 		DefaultTypeDepStats();
196 		LoadPicture();
197 	}
198 }
199 
Upkeep()200 void CapitalShip::Upkeep() {
201 	AutoFireUnit::Upkeep();
202 
203 	if (armourCurrent < 1) {
204 		alive = false;
205 		explodeTimer = csExplodeFrames * framesPerAnimFrame;
206 	}
207 }
208 
Explode()209 void CapitalShip::Explode() {
210 	if (sides[mySide].groups[myGroup].GetOnScreen()) {
211 		if (explodeTimer > framesPerAnimFrame * 8) {
212 			switch (myCSType) {
213 			case CST_Heavy:
214 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE1], USRect);
215 				break;
216 			case CST_Medium:
217 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE1], USRect);
218 				break;
219 			case CST_Light:
220 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE1], USRect);
221 				break;
222 			}
223 		} else if (explodeTimer > framesPerAnimFrame * 7) {
224 			switch (myCSType) {
225 			case CST_Heavy:
226 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE2], USRect);
227 				break;
228 			case CST_Medium:
229 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE2], USRect);
230 				break;
231 			case CST_Light:
232 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE2], USRect);
233 				break;
234 			}
235 		} else if (explodeTimer > framesPerAnimFrame * 6) {
236 			switch (myCSType) {
237 			case CST_Heavy:
238 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE3], USRect);
239 				break;
240 			case CST_Medium:
241 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE3], USRect);
242 				break;
243 			case CST_Light:
244 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE3], USRect);
245 				break;
246 			}
247 		} else if (explodeTimer > framesPerAnimFrame * 5) {
248 			switch (myCSType) {
249 			case CST_Heavy:
250 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE4], USRect);
251 				break;
252 			case CST_Medium:
253 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE4], USRect);
254 				break;
255 			case CST_Light:
256 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE4], USRect);
257 				break;
258 			}
259 		} else if (explodeTimer > framesPerAnimFrame * 4) {
260 			switch (myCSType) {
261 			case CST_Heavy:
262 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE5], USRect);
263 				break;
264 			case CST_Medium:
265 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE5], USRect);
266 				break;
267 			case CST_Light:
268 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE5], USRect);
269 				break;
270 			}
271 		} else if (explodeTimer > framesPerAnimFrame * 3) {
272 			switch (myCSType) {
273 			case CST_Heavy:
274 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE6], USRect);
275 				break;
276 			case CST_Medium:
277 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE6], USRect);
278 				break;
279 			case CST_Light:
280 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE6], USRect);
281 				break;
282 			}
283 		} else if (explodeTimer > framesPerAnimFrame * 2) {
284 			switch (myCSType) {
285 			case CST_Heavy:
286 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE7], USRect);
287 				break;
288 			case CST_Medium:
289 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE7], USRect);
290 				break;
291 			case CST_Light:
292 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE7], USRect);
293 				break;
294 			}
295 		} else if (explodeTimer > framesPerAnimFrame) {
296 			switch (myCSType) {
297 			case CST_Heavy:
298 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE8], USRect);
299 				break;
300 			case CST_Medium:
301 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE8], USRect);
302 				break;
303 			case CST_Light:
304 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE8], USRect);
305 				break;
306 			}
307 		} else {
308 			switch (myCSType) {
309 			case CST_Heavy:
310 				JSDL.Blt(genPictures[GENPIC_HCSEXPLODE9], USRect);
311 				break;
312 			case CST_Medium:
313 				JSDL.Blt(genPictures[GENPIC_MCSEXPLODE9], USRect);
314 				break;
315 			case CST_Light:
316 				JSDL.Blt(genPictures[GENPIC_LCSEXPLODE9], USRect);
317 				break;
318 			}
319 		}
320 	}
321 }
322 
ChangeCSType(CapShipType newType)323 void CapitalShip::ChangeCSType(CapShipType newType) {
324 	myCSType = newType;
325 
326 	//includes 3 character size extension
327 	string oldPicName = picName;
328 	string newPicName = picName.substr(0, picName.size() - 3);
329 	DefaultTypeDepStats();
330 	//just got overwritten by defaulting stats
331 	picName = oldPicName;
332 	ChangeUnitPic(newPicName);
333 }
334 
DefaultTypeDepStats()335 void CapitalShip::DefaultTypeDepStats() {
336 	engineName = globalSettings.defaultCSEngine;
337 
338 	bigPosition.x = 0;
339 	bigPosition.y = 0;
340 
341 	picName = globalSettings.defaultCSPic;
342 
343 	switch (myCSType) {
344 	case CST_Heavy:
345 		width = HCSWidth;
346 		height = HCSHeight;
347 		capacity = HCSCap;
348 		armourName = globalSettings.defaultHCSArmour;
349 		break;
350 
351 	case CST_Medium:
352 		width = MCSWidth;
353 		height = MCSHeight;
354 		capacity = MCSCap;
355 		armourName = globalSettings.defaultMCSArmour;
356 		break;
357 
358 	case CST_Light:
359 		width = LCSWidth;
360 		height = LCSHeight;
361 		capacity = LCSCap;
362 		armourName = globalSettings.defaultLCSArmour;
363 		break;
364 	}
365 
366 	RTSUnit_Base::DefaultTypeDepStats();
367 }
368 
GetFrCapacity() const369 int CapitalShip::GetFrCapacity() const {
370 	switch (myCSType) {
371 	case CST_Heavy:
372 		return HCSFrCap;
373 		break;
374 
375 	case CST_Medium:
376 		return MCSFrCap;
377 		break;
378 
379 	case CST_Light:
380 		return LCSFrCap;
381 		break;
382 	}
383 }
384 
385 //these are default coords for new cap ships, they are overwritten by those found in unit files
SetSmallNumber()386 void CapitalShip::SetSmallNumber() {
387 	smallPositions.clear();
388 
389 	if (smallType == WT_None)
390 		smallNumber = 0;
391 	else {
392 		CoordsInt tempCoords;
393 
394 		switch (myCSType) {
395 		case CST_Heavy:
396 			smallNumber	= HCSCap;
397 
398 			tempCoords.x = 70;
399 			tempCoords.y = 26;
400 			smallPositions.push_back(tempCoords);
401 			tempCoords.x = 200;
402 			smallPositions.push_back(tempCoords);
403 			tempCoords.x = 330;
404 			smallPositions.push_back(tempCoords);
405 			tempCoords.x = 450;
406 			smallPositions.push_back(tempCoords);
407 
408 			tempCoords.x = 70;
409 			tempCoords.y = 82;
410 			smallPositions.push_back(tempCoords);
411 			tempCoords.x = 180;
412 			smallPositions.push_back(tempCoords);
413 			tempCoords.x = 300;
414 			smallPositions.push_back(tempCoords);
415 			tempCoords.x = 450;
416 			smallPositions.push_back(tempCoords);
417 
418 			tempCoords.x = 450;
419 			tempCoords.y = HCSHeight / 2;
420 			smallPositions.push_back(tempCoords);
421 			break;
422 
423 		case CST_Medium:
424 			smallNumber = MCSCap;
425 
426 			tempCoords.x = 70;
427 			tempCoords.y = 26;
428 			smallPositions.push_back(tempCoords);
429 			tempCoords.x = 180;
430 			smallPositions.push_back(tempCoords);
431 			tempCoords.x = 300;
432 			tempCoords.y = 30;
433 			smallPositions.push_back(tempCoords);
434 
435 			tempCoords.x = 70;
436 			tempCoords.y = 82;
437 			smallPositions.push_back(tempCoords);
438 			tempCoords.x = 180;
439 			smallPositions.push_back(tempCoords);
440 			tempCoords.x = 300;
441 			tempCoords.y = 76;
442 			smallPositions.push_back(tempCoords);
443 			break;
444 
445 		case CST_Light:
446 			smallNumber = LCSCap;
447 
448 			tempCoords.x = 50;
449 			tempCoords.y = LCSHeight / 2;
450 			smallPositions.push_back(tempCoords);
451 			tempCoords.x = 150;
452 			smallPositions.push_back(tempCoords);
453 			tempCoords.x = 250;
454 			smallPositions.push_back(tempCoords);
455 			break;
456 		}
457 	}
458 }
459 
Frigate(int iMySide,int iMyGroup,const string & iName)460 Frigate::Frigate(int iMySide, int iMyGroup, const string& iName):
461 AutoFireUnit(iName, iMySide, iMyGroup) {
462 	//load if found either in struct or on hard drive
463 	if (sides[mySide].sdStruct.unitData.find(name) != sides[mySide].sdStruct.unitData.end()
464 	        || DoesFileExist(GetFleetDir(mySide) + name + ".dat"))
465 		LoadData();
466 
467 	else {
468 		myType = UT_FrUnit;
469 		DefaultTypeDepStats();
470 		LoadPicture();
471 	}
472 }
473 
DefaultTypeDepStats()474 void Frigate::DefaultTypeDepStats() {
475 	width = FrWidth;
476 	height = FrHeight;
477 	picName = globalSettings.defaultFrPic;
478 	engineName = globalSettings.defaultFrEngine;
479 	armourName = globalSettings.defaultFrArmour;
480 
481 	bigPosition.x = 75;
482 	bigPosition.y = 25;
483 
484 	RTSUnit_Base::DefaultTypeDepStats();
485 }
486 
Upkeep()487 void Frigate::Upkeep() {
488 	AutoFireUnit::Upkeep();
489 
490 	if (armourCurrent < 1) {
491 		alive = false;
492 		explodeTimer = frExplodeFrames * framesPerAnimFrame;
493 	}
494 }
495 
Explode()496 void Frigate::Explode() {
497 	if (sides[mySide].groups[myGroup].GetOnScreen()) {
498 		if (explodeTimer > framesPerAnimFrame * 6)
499 			JSDL.Blt(genPictures[GENPIC_FREXPLODE1], USRect);
500 		else if (explodeTimer > framesPerAnimFrame * 5)
501 			JSDL.Blt(genPictures[GENPIC_FREXPLODE2], USRect);
502 		else if (explodeTimer > framesPerAnimFrame * 4)
503 			JSDL.Blt(genPictures[GENPIC_FREXPLODE3], USRect);
504 		else if (explodeTimer > framesPerAnimFrame * 3)
505 			JSDL.Blt(genPictures[GENPIC_FREXPLODE4], USRect);
506 		else if (explodeTimer > framesPerAnimFrame * 2)
507 			JSDL.Blt(genPictures[GENPIC_FREXPLODE5], USRect);
508 		else if (explodeTimer > framesPerAnimFrame)
509 			JSDL.Blt(genPictures[GENPIC_FREXPLODE6], USRect);
510 		else
511 			JSDL.Blt(genPictures[GENPIC_FREXPLODE7], USRect);
512 	}
513 }
514 
SetSmallNumber()515 void Frigate::SetSmallNumber() {
516 	smallPositions.clear();
517 
518 	if (smallType == WT_None)
519 		smallNumber = 0;
520 	else {
521 		smallNumber = FrSmNumber;
522 		CoordsInt tempCoords;
523 
524 		tempCoords.x = 30;
525 		tempCoords.y = FrHeight / 2;
526 		smallPositions.push_back(tempCoords);
527 		tempCoords.x = 70;
528 		tempCoords.y = FrHeight / 2;
529 		smallPositions.push_back(tempCoords);
530 	}
531 }
532 
DrawSelfPixels()533 void Frigate::DrawSelfPixels() {
534 	if (weaponLookup[bigType].category == WCAT_Large && bigStage == w_Firing && alive) {
535 		CoordsInt tmp = sides[bigTarget.x].groups[bigTarget.y].GetUnitCenter(bigTargetUnit);
536 
537 		int startx;
538 		if (bFlip)
539 			startx = static_cast<int>(myx) + width - bigPosition.x;
540 		else
541 			startx = static_cast<int>(myx) + bigPosition.x;
542 
543 		int finx = tmp.x + targetWeakSpot.x;
544 		int finy = tmp.y + targetWeakSpot.y;
545 
546 		Projectile_Base::DrawBigLaser(startx, static_cast<int>(myy) + bigPosition.y, finx, finy, sides[mySide].laserColor);
547 	}
548 }
549 
550