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 "RTS.h"
14 #include "Globals.h"
15 #include "Group.h"
16 #include "PreBattle.h"
17 #include "TerrainTile.h"
18 #include "Projectile.h"
19 
20 #include <vector>
21 #include <list>
22 
23 using std::vector;
24 using std::list;
25 
26 namespace RTS {
27 
RTS_State()28 RTS_State::RTS_State() {
29 	radarDragging = false;
30 	lastAITime = now;
31 	lastScrollTime = now;
32 }
33 
~RTS_State()34 RTS_State::~RTS_State() {
35 	projectiles.clear();
36 	worldUpdateInterval = standardInterval;
37 
38 	if (gsTo != GST_PreBattle && gsTo != GST_Score)
39 		PreBattle::Unload();
40 	else if (gsTo == GST_PreBattle)
41 		RestartPreBattle();
42 }
43 
Main()44 void RTS_State::Main() {
45 	ScrollAndDrag();
46 
47 	if (now - lastAITime > worldUpdateInterval && !(paused)) {
48 		RunGroupAI();
49 		RunMoveCommands();
50 		RunFireCommands();
51 		Upkeep();
52 
53 		lastAITime = now;
54 
55 		++frameCounter;
56 	}
57 
58 	if (skipDisplayFrame || globalSettings.batch)
59 		return;
60 
61 	DrawWorld();
62 	DrawAllWindows();
63 }
64 
MouseD(Uint8 button,Uint16 x,Uint16 y)65 void RTS_State::MouseD(Uint8 button, Uint16 x, Uint16 y) {
66 	RTSMouseD(button, x, y);
67 }
68 
MouseU(Uint8 button,Uint16 x,Uint16 y)69 void RTS_State::MouseU(Uint8 button, Uint16 x, Uint16 y) {
70 	RTSMouseU(button, x, y);
71 }
72 
MouseM(Uint8 state,Uint16 x,Uint16 y)73 void RTS_State::MouseM(Uint8 state, Uint16 x, Uint16 y) {
74 	RTSMouseM(state, x, y);
75 }
76 
Keyboard(SDL_keysym & keysym)77 void RTS_State::Keyboard(SDL_keysym& keysym) {
78 	RTSKeyboard(keysym);
79 }
80 
RTSMouseD(Uint8 button,Uint16 x,Uint16 y)81 void RTSMouseD(Uint8 button, Uint16 x, Uint16 y) {
82 	if (button == SDL_BUTTON_RIGHT)
83 		myWindows.push_back(GenWindow(x, y, RTS_BasePU, 0, 0, 0));
84 
85 	//radar before groups
86 	if (button == SDL_BUTTON_LEFT
87 	&& x > radarRect.x
88 	&& x < radarRect.x + radarRect.w
89 	&& y > radarRect.y
90 	&& y < radarRect.y + radarRect.h) {
91 		radarDragging = true;
92 		RTSMouseM(0, x, y);
93 	} else {
94 		for (int i = 0; i != sides.size(); ++i) {
95 			for (int j = 0; j != sides[i].groups.size(); ++j)
96 				sides[i].groups[j].MouseD(button, x, y);
97 		}
98 	}
99 }
100 
RTSMouseU(Uint8 button,Uint16 x,Uint16 y)101 void RTSMouseU(Uint8 button, Uint16 x, Uint16 y) {
102 	if (button == SDL_BUTTON_LEFT)
103 		radarDragging = false;
104 
105 	for (int i = 0; i != sides.size(); ++i) {
106 		for (int j = 0; j != sides[i].groups.size(); ++j)
107 			sides[i].groups[j].MouseU(button, x, y);
108 	}
109 }
110 
RTSMouseM(Uint8 state,Uint16 x,Uint16 y)111 void RTSMouseM(Uint8 state, Uint16 x, Uint16 y) {
112 	//radar
113 	if (radarDragging
114 		&& x > radarRect.x
115 		&& x < radarRect.x + radarRect.w
116 		&& y > radarRect.y
117 		&& y < radarRect.y + radarRect.h) {
118 			int tempCenterx = (x - radarRect.x) * worldWidth / radarRect.w;
119 			int tempCentery = (y - radarRect.y) * worldHeight / radarRect.h;
120 
121 			viewx = tempCenterx - (globalSettings.screenWidth >> 1);
122 			viewy = tempCentery - (globalSettings.screenHeight >> 1);
123 
124 			viewSide = -1;
125 	}
126 }
127 
RTSKeyboard(SDL_keysym & keysym)128 void RTSKeyboard(SDL_keysym& keysym) {
129 	switch(keysym.sym) {
130 	case SDLK_ESCAPE:
131 		if (gsCurrent == GST_PreBattle && PreBattle::pbState == PBS_Position)
132 			gsTo = GST_ForceSelect;
133 		else
134 			gsTo = GST_MainMenu;
135 		return;
136 		break;
137 
138 	case SDLK_F2:
139 		for (int i = 0; i != sides.size(); ++i) {
140 			for (int j = 0; j != sides[i].groups.size(); ++j)
141 				sides[i].groups[j].ToggleDrawNumber();
142 		}
143 		break;
144 
145 	case SDLK_F3:
146 		for (int i = 0; i != sides.size(); ++i) {
147 			for (int j = 0; j != sides[i].groups.size(); ++j)
148 				sides[i].groups[j].ToggleDrawBound();
149 		}
150 		break;
151 
152 	case SDLK_F11:
153 		myWindows.push_back(GenWindow(0, 0, RTS_RestartQ, 0, 0, 0));
154 		break;
155 
156 	case SDLK_SPACE: {
157 		static bool speedSlider = false;
158 		static int sliderWinID = 0;
159 		if (speedSlider) {
160 			try {
161 				Slider* pSlider = dynamic_cast<Slider*>(LocateWindow(sliderWinID));
162 				pSlider->WinMessage(WC_YouClose, 0, 0, sliderWinID, none_constant);
163 				speedSlider = false;
164 				break;
165 			} catch (runtime_error e) {
166 			}
167 		}
168 		//if we fell through due to a runtime error because window was already closed, create a new one
169 		myWindows.push_back(GenWindow(topRightBoxRect.x, topRightBoxRect.y + topRightBoxRect.h, RTS_SetGameSpeed, 0, 0, 0));
170 		sliderWinID = windowIDs;
171 		speedSlider = true;
172 		}
173 		break;
174 
175 	case SDLK_KP_PLUS:
176 		SetWorldUpdateInterval(worldUpdateInterval - 10);
177 		break;
178 
179 	case SDLK_KP_MINUS:
180 		SetWorldUpdateInterval(worldUpdateInterval + 10);
181 		break;
182 
183 	case SDLK_p:
184 		if (paused == false)
185 			paused = true;
186 		else if (worldUpdateInterval < maxWorldUpdateInterval)
187 			paused = false;
188 		break;
189 
190 	case SDLK_i:
191 		SetWorldUpdateInterval(standardInterval);
192 		break;
193 
194 	case SDLK_o:
195 		SetWorldUpdateInterval(0);
196 		break;
197 	}
198 }
199 
ScrollAndDrag()200 void ScrollAndDrag() {
201 	int state, x, y;
202 	state = SDL_GetMouseState(&x, &y);
203 
204 	//scrolling via mouse, also used to break out of follow view
205 	if (now - lastScrollTime > scrollInterval) {
206 		if (x > globalSettings.screenWidth - 8 || JSDL.keyboard[SDLK_RIGHT]) {
207 			viewx += screenMoveSpeed;
208 			viewSide = -1;
209 		}
210 		if (x < 8  || JSDL.keyboard[SDLK_LEFT]) {
211 			viewx -= screenMoveSpeed;
212 			viewSide = -1;
213 		}
214 		if (y > globalSettings.screenHeight - 8 || JSDL.keyboard[SDLK_DOWN]) {
215 			viewy += screenMoveSpeed;
216 			viewSide = -1;
217 		}
218 		if (y < 8 || JSDL.keyboard[SDLK_UP]) {
219 			viewy -= screenMoveSpeed;
220 			viewSide = -1;
221 		}
222 
223 		lastScrollTime = now;
224 	}
225 
226 	if (gsCurrent == GST_PreBattle) {
227 		for (int i = 0; i != sides.size(); ++i) {
228 			for (int j = 0; j != sides[i].groups.size(); ++j)
229 				sides[i].groups[j].Drag(state, x, y);
230 		}
231 	}
232 }
233 
RunGroupAI()234 void RunGroupAI() {
235 	for (int i  = 0; i != sides.size(); ++i) {
236 		for (int j = 0; j != sides[i].groups.size(); ++j)
237 			sides[i].groups[j].RunGroupAI();
238 	}
239 }
240 
RunMoveCommands()241 void RunMoveCommands() {
242 	for (int i  = 0; i != sides.size(); ++i) {
243 		for (int j = 0; j != sides[i].groups.size(); ++j)
244 			sides[i].groups[j].Move();
245 	}
246 
247 	for (list<Projectile>::iterator iter = projectiles.begin(); iter != projectiles.end();) {
248 		if (!iter->Move())
249 			iter = projectiles.erase(iter);
250 		else
251 			++iter;
252 	}
253 }
254 
RunFireCommands()255 void RunFireCommands() {
256 	for (int i  = 0; i != sides.size(); ++i) {
257 		for (int j = 0; j != sides[i].groups.size(); ++j)
258 			sides[i].groups[j].RunFireCommands();
259 	}
260 }
261 
Upkeep()262 void Upkeep() {
263 	for (int i  = 0; i != sides.size(); ++i) {
264 		for (int j = 0; j != sides[i].groups.size(); ++j)
265 			sides[i].groups[j].Upkeep();
266 	}
267 
268 	int sidesLeft = 0;
269 
270 	for (int i  = 0; i != sides.size(); ++i) {
271 		int sideTotalCS = sides[i].GetTotalCapShips();
272 		if (sideTotalCS)
273 			++sidesLeft;
274 	}
275 
276 	if (sidesLeft < 2)
277 		gsTo = GST_Score;
278 	else if (globalSettings.batch && frameCounter > globalSettings.maxFrames)
279 		gsTo = GST_Score;
280 }
281 
DrawWorld()282 void DrawWorld() {
283 	FollowViewCenter();
284 	CheckViewPos();
285 
286 	JSDL.BltFill(screenRect, 0);
287 
288 	DrawTerrain();
289 
290 	for (int i = 0; i != sides.size(); ++i) {
291 		for (int j = 0; j != sides[i].groups.size(); ++j)
292 			sides[i].groups[j].SetUSRect();
293 	}
294 
295 	for (int i = 0; i != sides.size(); ++i) {
296 		for (int j = 0; j != sides[i].groups.size(); ++j)
297 			sides[i].groups[j].DrawSelfBackBack();
298 	}
299 
300 	for (int i = 0; i != sides.size(); ++i) {
301 		for (int j = 0; j != sides[i].groups.size(); ++j)
302 			sides[i].groups[j].DrawSelfBack();
303 	}
304 
305 	for (int i = 0; i != sides.size(); ++i) {
306 		for (int j = 0; j != sides[i].groups.size(); ++j)
307 			sides[i].groups[j].DrawSelfMiddle();
308 	}
309 
310 	for (int i = 0; i != sides.size(); ++i) {
311 		for (int j = 0; j != sides[i].groups.size(); ++j)
312 			sides[i].groups[j].DrawSelfFront();
313 	}
314 
315 	JSDL.LockBack();
316 	for (list<Projectile>::iterator iter = projectiles.begin(); iter != projectiles.end(); ++iter)
317 		iter->DrawSelfPixels();
318 
319 	for (int i = 0; i != sides.size(); ++i) {
320 		for (int j = 0; j != sides[i].groups.size(); ++j)
321 			sides[i].groups[j].DrawSelfPixels();
322 	}
323 	JSDL.UnlockBack();
324 
325 	for (list<Projectile>::iterator iter = projectiles.begin(); iter != projectiles.end(); ++iter)
326 		iter->DrawSelfBitmap();
327 
328 	if (gsCurrent == GST_PreBattle) {
329 		for (int i = 0; i != sides.size(); ++i) {
330 			SDL_Rect tempRect;
331 			tempRect.x = sides[i].startingRect.x - viewx;
332 			tempRect.y = sides[i].startingRect.y - viewy;
333 			tempRect.w = sides[i].startingRect.w;
334 			tempRect.h = sides[i].startingRect.h;
335 			DrawRectBorder(tempRect, sides[i].color);
336 		}
337 	}
338 
339 	DrawRadar();
340 }
341 
DrawTerrain()342 void DrawTerrain() {
343 	for (int i = 0; i != terrainTree.size(); ++i) {
344 		if (viewx + globalSettings.screenWidth > terrainTree[i].rect.x
345 		&& viewx < terrainTree[i].rect.x + terrainTree[i].rect.w
346 		&& viewy + globalSettings.screenHeight > terrainTree[i].rect.y
347 		&& viewy < terrainTree[i].rect.y + terrainTree[i].rect.h)
348 			for (int j = 0; j != terrainTree[i].tiles.size(); ++j)
349 				terrainTree[i].tiles[j].DrawSelf();
350 	}
351 }
352 
DrawRadar()353 void DrawRadar() {
354 	JSDL.BltFill(topRightBoxRect, gold);
355 
356 	SDL_FillRect(radarSurface, 0, black);
357 
358 	//Uint16 strided lpitch
359 	static const Uint16 lPitch = radarSurface->pitch >> 1;
360 
361 	SDL_LockSurface(radarSurface);
362 
363 	Uint16* videoBuffer = reinterpret_cast<Uint16*>(radarSurface->pixels);
364 
365 	for (int i  = 0; i != sides.size(); ++i) {
366 		for (int j = 0; j != sides[i].groups.size(); ++j) {
367 			if (sides[i].groups[j].GetAlive()) {
368 				CoordsInt center = sides[i].groups[j].GetCenter();
369 
370 				int radarx = center.x * radarRect.w / worldWidth;
371 				int radary = center.y * radarRect.h / worldHeight;
372 
373 				videoBuffer[radary*lPitch + radarx] = sides[i].radarColor;
374 			}
375 		}
376 	}
377 
378 	int radarx = viewx * radarRect.w / worldWidth;
379 	int radary = viewy * radarRect.h / worldHeight;
380 
381 	int lineWidth = radarRect.w * globalSettings.screenWidth / worldWidth;
382 	int lineHeight = radarRect.h * globalSettings.screenHeight / worldHeight;
383 
384 	for (int x = radarx; x != radarx + lineWidth + 1; ++x)
385 		videoBuffer[radary*lPitch + x] = white;
386 
387 	for (int x = radarx; x != radarx + lineWidth + 1; ++x)
388 		videoBuffer[(radary + lineHeight)*lPitch + x] = white;
389 
390 	for (int y = radary; y != radary + lineHeight + 1; ++y)
391 		videoBuffer[y*lPitch + radarx] = white;
392 
393 	for (int y = radary; y != radary + lineHeight + 1; ++y)
394 		videoBuffer[y*lPitch + radarx + lineWidth] = white;
395 
396 	SDL_UnlockSurface(radarSurface);
397 
398 	JSDL.Blt(radarSurface, radarRect);
399 }
400 
SetWorldUpdateInterval(int newValue)401 void SetWorldUpdateInterval(int newValue) {
402 	worldUpdateInterval = newValue;
403 
404 	if (worldUpdateInterval < 0)
405 		worldUpdateInterval = 0;
406 
407 	if (worldUpdateInterval == maxWorldUpdateInterval)
408 		paused = true;
409 	else
410 		paused = false;
411 
412 	if (worldUpdateInterval > maxWorldUpdateInterval)
413 		worldUpdateInterval = maxWorldUpdateInterval;
414 
415 	if (worldUpdateInterval > standardInterval - 10 && worldUpdateInterval < standardInterval + 10)
416 		worldUpdateInterval = standardInterval;
417 }
418 
419 //////
420 
MoveComToString(const AICommands & theCommands)421 string MoveComToString(const AICommands& theCommands) {
422 	string ret;
423 	char output[32];
424 
425 	if (!theCommands.bInverse)
426 		ret = "Moving towards ";
427 	else
428 		ret = "Moving away from ";
429 
430 	switch (theCommands.moveCommand) {
431 	case MC_NoMove:
432 		ret = "Not moving";
433 		break;
434 
435 	case MC_MoveCompass:
436 		switch (theCommands.moveTarget.y) {
437 		case 0:
438 			ret += "North";
439 			break;
440 
441 		case 1:
442 			ret += "North-East";
443 			break;
444 
445 		case 2:
446 			ret += "East";
447 			break;
448 
449 		case 3:
450 			ret += "South-East";
451 			break;
452 
453 		case 4:
454 			ret += "South";
455 			break;
456 
457 		case 5:
458 			ret += "South-West";
459 			break;
460 
461 		case 6:
462 			ret += "West";
463 			break;
464 
465 		case 7:
466 			ret += "North-West";
467 			break;
468 		}
469 		break;
470 
471 	case MC_MoveGroup:
472 		//+1 so side 1/group is 1
473 		sprintf(output, "%s Group %d", sides[theCommands.moveTarget.x].name.c_str(), theCommands.moveTarget.y + 1);
474 
475 		ret += output;
476 		break;
477 
478 	case MC_Patrol:
479 		ret = "Patrolling ";
480 		sprintf(output, "%s Group %d", sides[theCommands.moveTarget.x].name.c_str(), theCommands.moveTarget.y + 1);
481 
482 		ret += output;
483 		break;
484 	}
485 
486 	return ret;
487 }
488 
FireComToString(const AICommands & theCommands)489 string FireComToString(const AICommands& theCommands) {
490 	string ret;
491 	char output[32];
492 
493 	ret = "Fire target: ";
494 
495 	if (theCommands.bFire) {
496 		//+1 so side 1/group is 1
497 		sprintf(output, "%s Group %d", sides[theCommands.fireTarget.x].name.c_str(), theCommands.fireTarget.y + 1);
498 		ret += output;
499 	} else
500 		ret += "No target";
501 
502 	return ret;
503 }
504 
CheckViewPos()505 void CheckViewPos() {
506 	if (viewx > worldWidth - globalSettings.screenWidth)
507 		viewx = worldWidth - globalSettings.screenWidth;
508 	if (viewx < 0)
509 		viewx = 0;
510 	if (viewy > worldHeight - globalSettings.screenHeight)
511 		viewy = worldHeight - globalSettings.screenHeight;
512 	if (viewy < 0)
513 		viewy = 0;
514 }
515 
RestartPreBattle()516 void RestartPreBattle() {
517 	projectiles.clear();
518 	PreBattle::UnloadGraphics();
519 
520 	for (int i = 0; i != sides.size(); ++i)
521 		sides[i].Reset();
522 }
523 
524 } //end namespace
525