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