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 "PreBattle.h"
14 #include "Globals.h"
15 #include "Group.h"
16 #include "Menu_Base.h"
17 #include "Stuff.h"
18 #include "RTS.h"
19 #include "Inlines.h"
20 #include "TerrainTile.h"
21 #include "Random.h"
22 
23 #include <string>
24 #include <algorithm>
25 
26 using std::string;
27 using std::find;
28 
29 namespace PreBattle {
30 //global to this namespace
31 PBState pbState;
32 
PreBattle_State()33 PreBattle_State::PreBattle_State() {
34 	radarDragging = false;
35 	viewSide = -1;
36 
37 	SeedRandom(globalSettings.randomSeed);
38 
39 	globalSettings.rememberFleets.clear();
40 	for (int i = 0; i != sides.size(); ++i)
41 		globalSettings.rememberFleets.push_back(sides[i].name);
42 	while (globalSettings.rememberFleets.size() < maxPlayers)
43 		globalSettings.rememberFleets.push_back("");
44 
45 	SetStartingRects();
46 
47 	SetupTerrain();
48 
49 	//FIXME
50 	for (int i = 0; i != sides.size(); ++i)
51 		sides[i].myFlag = i;
52 
53 	for (int i = 0; i != sides.size(); ++i) {
54 		for (int j = 0; j != sides[i].groups.size(); ++j)
55 			sides[i].groups[j].SetLaunchWait();
56 	}
57 
58 	for (int i = 0; i != sides.size(); ++i) {
59 		for (int j = 0; j != sides[i].groups.size(); ++j) {
60 			sides[i].groups[j].ToggleDrawBound();
61 			sides[i].groups[j].ToggleDrawNumber();
62 		}
63 	}
64 
65 	int totalGroups = 0;
66 
67 	for (int i = 0; i != sides.size(); ++i)
68 		totalGroups += sides[i].groups.size();
69 
70 	int groupsPerFrame = totalGroups / staggerFrames;
71 
72 	//ensure scripts all start within the first 10 frames
73 	//also ensure that groupsPerFrame is not 0 (i.e. if there are less than staggerFrames groups)
74 	while (staggerFrames * groupsPerFrame < totalGroups)
75 		++groupsPerFrame;
76 
77 	int currentOpenFrame = 0;
78 
79 	vector<CoordsInt> doneGroups;
80 
81 	while (doneGroups.size() != totalGroups) {
82 		int ranSide = Random() % sides.size();
83 		int ranGroup = Random() % sides[ranSide].groups.size();
84 
85 		CoordsInt thisGroup = {ranSide, ranGroup};
86 
87 		if (find(doneGroups.begin(), doneGroups.end(), thisGroup) != doneGroups.end())
88 			continue;
89 		else {
90 			sides[ranSide].groups[ranGroup].InitAI(currentOpenFrame);
91 			doneGroups.push_back(thisGroup);
92 		}
93 
94 		if (doneGroups.size() % groupsPerFrame == 0)
95 			++currentOpenFrame;
96 	}
97 
98 	topRightBoxRect.x = globalSettings.screenWidth - 210;
99 	topRightBoxRect.y = 10;
100 	topRightBoxRect.w = 200;
101 	topRightBoxRect.h = 200;
102 
103 	radarRect.x = topRightBoxRect.x + smallBorderSize;
104 	radarRect.y = topRightBoxRect.y + smallBorderSize;
105 	radarRect.w = topRightBoxRect.w - (smallBorderSize << 1);
106 	radarRect.h = topRightBoxRect.h - (smallBorderSize << 1);
107 
108 	radarSurface = SDL_CreateRGBSurface(SDL_HWSURFACE, radarRect.w, radarRect.h, screenBPP,  0, 0, 0, 0);
109 
110 	SetStartingPositions();
111 
112 	SDL_Rect& myStartRect = sides[0].startingRect;
113 
114 	viewx = myStartRect.x + (myStartRect.w >> 1);
115 	viewy = myStartRect.y + (myStartRect.h >> 1);
116 
117 	frameCounter = 0;
118 
119 	JSDL.LoadMusic(globalSettings.bdp + "music/corruptor.ogg");
120 	JSDL.PlayMusic();
121 }
122 
~PreBattle_State()123 PreBattle_State::~PreBattle_State() {
124 	for (int i = 0; i != sides.size(); ++i) {
125 		for (int j = 0; j != sides[i].groups.size(); ++j)
126 			sides[i].groups[j].ResetForBattle();
127 	}
128 
129 	//GST_PreBattle case to catch exceptions
130 	if (gsTo == GST_MainMenu || gsTo == GST_PreBattle)
131 		Unload();
132 }
133 
Main()134 void PreBattle_State::Main() {
135 	if (globalSettings.batch)
136 		gsTo = GST_Battle;
137 	RTS::ScrollAndDrag();
138 
139 	RTS::DrawWorld();
140 
141 	DrawAllWindows();
142 }
143 
MouseD(Uint8 button,Uint16 x,Uint16 y)144 void PreBattle_State::MouseD(Uint8 button, Uint16 x, Uint16 y) {
145 	RTS::RTSMouseD(button, x, y);
146 }
147 
MouseU(Uint8 button,Uint16 x,Uint16 y)148 void PreBattle_State::MouseU(Uint8 button, Uint16 x, Uint16 y) {
149 	RTS::RTSMouseU(button, x, y);
150 }
151 
MouseM(Uint8 state,Uint16 x,Uint16 y)152 void PreBattle_State::MouseM(Uint8 state, Uint16 x, Uint16 y) {
153 	RTS::RTSMouseM(state, x, y);
154 }
155 
Keyboard(SDL_keysym & keysym)156 void PreBattle_State::Keyboard(SDL_keysym& keysym) {
157 	RTS::RTSKeyboard(keysym);
158 
159 	if (keysym.sym == SDLK_s)
160 		gsTo = GST_Battle;
161 }
162 
SetStartingRects()163 void SetStartingRects() {
164 	worldWidth = startingRectDim * 6;
165 	worldHeight = startingRectDim * 6;
166 
167 	sides[0].startingRect.x = startingRectDim;
168 	if (sides.size() < 3)
169 		sides[0].startingRect.y = static_cast<float>(worldHeight * 0.5) - (startingRectDim / 2);
170 	else
171 		sides[0].startingRect.y = startingRectDim;
172 
173 	if (sides.size() > 1) {
174 		sides[1].startingRect.x = worldWidth - startingRectDim * 2;
175 		if (sides.size() < 3)
176 			sides[1].startingRect.y = static_cast<float>(worldHeight * 0.5) - (startingRectDim / 2);
177 		else
178 			sides[1].startingRect.y = startingRectDim;
179 	}
180 
181 	if (sides.size() == 3) {
182 		sides[2].startingRect.x = static_cast<float>(worldWidth * 0.5) - (startingRectDim / 2);
183 		sides[2].startingRect.y = worldHeight - startingRectDim * 2;
184 	}
185 	else if (sides.size() == 4) {
186 		sides[2].startingRect.x = startingRectDim;
187 		sides[2].startingRect.y = worldHeight - startingRectDim * 2;
188 
189 		sides[3].startingRect.x = worldWidth - startingRectDim * 2;
190 		sides[3].startingRect.y = worldHeight - startingRectDim * 2;
191 	}
192 }
193 
SetStartingPositions()194 void SetStartingPositions() {
195 	//start off by placing everyone outside world so we don't get false collisions with as yet unplaced groups
196 	for (int i = 0; i != sides.size(); ++i) {
197 		for (int j = 0; j != sides[i].groups.size(); ++j)
198   			sides[i].groups[j].SetPos(worldWidth, worldHeight);
199 	}
200 
201 	for (int i = 0; i != sides.size(); ++i) {
202  		vector<bool> placedGroups(sides[i].groups.size(), false);
203 
204 		for (int j = 0; j != sides[i].groups.size(); ++j) {
205 	   	if (sides[i].groups[j].GoToStartCoords())
206          	placedGroups[j] = true;
207     	}
208 
209     	for (int j = 0; j != placedGroups.size(); ++j) {
210       	if (placedGroups[j] == false) {
211 				if (pbState == PBS_Position)
212 					FitGroupsInRemainingSpace(placedGroups);
213 				else {
214 					string errorMsg = sides[i].name + " have illegally positioned units";
215 					throw runtime_error(errorMsg.c_str());
216 				}
217 			}
218 		}
219 	}
220 }
221 
FitGroupsInRemainingSpace(vector<bool> placedGroups,bool ignoreOverlap)222 void FitGroupsInRemainingSpace(vector<bool> placedGroups, bool ignoreOverlap) {
223 	/*
224 	starting at offset (100, 100) try to place new groups
225 	wherever they fit. After each placement check for overlap
226 	or outside rect. if so, re-place. If we end up at
227 	bottom of rect still with leftover groups then just
228 	place all remaining evenly spaced from top left
229 	of rectangle, tough luck if they overlap
230 	*/
231 
232 	int tempWidth, tempHeight;
233 	int border = 100;
234 
235 	int x = sides[0].startingRect.x + border;
236 	int y = sides[0].startingRect.y + border;
237 
238 	for (int i = 0; i != placedGroups.size(); ++i) {
239 		//deal with ships with parents first. They always return 1
240 		//to set pos, even if they/their parent is invalidly placed
241 		if (sides[0].groups[i].GetParentCaSh() != -1) {
242 			sides[0].groups[i].GoToStartCoords();
243 			continue;
244 		}
245 
246 		if (placedGroups[i] == true)
247 			continue;
248 
249 		if (x > sides[0].startingRect.x + sides[0].startingRect.w - HCSWidth - border) {
250 			y += 300;
251 			x = sides[0].startingRect.x + border;
252 		}
253 
254 		if (y > sides[0].startingRect.y + sides[0].startingRect.h - HCSHeight - border) {
255 			if (ignoreOverlap)
256 				throw runtime_error("Couldn't fit all the groups in the starting rect");
257 			else
258 				break;
259 		}
260 
261 		if (sides[0].groups[i].SetPos(x, y) || ignoreOverlap) {
262 			placedGroups[i] = true;
263 			sides[0].groups[i].ChangeStartCoords(x, y);
264 		} else
265 			--i;
266 
267 		if (i > -1)
268 			sides[0].groups[i].GetDimensions(tempWidth, tempHeight);
269 		else
270 			tempWidth = FrWidth;
271 
272 		x += tempWidth + border;
273 	}
274 
275 	//any left over are placed ignoring any overlapping
276 	for (int i = 0; i != placedGroups.size(); ++i) {
277 		if (placedGroups[i] == false)
278 			FitGroupsInRemainingSpace(placedGroups, 1);
279 	}
280 }
281 
SetupTerrain()282 void SetupTerrain() {
283 	//load bitmaps into surfaces
284 	terrainPictures.push_back(JSDL.BitmapHDtoSurface(globalSettings.bdp + "graphics/spacetile.png", 0, 0, 4, 4, lightBlue));
285 	terrainPictures.push_back(JSDL.BitmapHDtoSurface(globalSettings.bdp + "graphics/spacetile2.png", 0, 0, 4, 4, lightBlue));
286 
287 	int total_tiles = (worldWidth/100)*(worldHeight/100);
288 	int num_areas = std::sqrt(static_cast<float>(total_tiles));
289 	int areas_per_side = std::sqrt(static_cast<float>(num_areas));
290 	int area_dim = worldWidth / areas_per_side;
291 	//not num_areas, because num_areas may well not have an exact square root so we need slightly more terrain areas to cover
292 	//everywhere
293 	int vector_size = (areas_per_side + 1) * (areas_per_side + 1);
294 	terrainTree.resize(vector_size);
295 
296 	int x = 0;
297 	int y = 0;
298 
299 	for (int i = 0; i != terrainTree.size(); ++i) {
300 		terrainTree[i].rect.x = x;
301 		terrainTree[i].rect.y = y;
302 		terrainTree[i].rect.w = area_dim;
303 		terrainTree[i].rect.h = area_dim;
304 
305 		terrainTree[i].tiles.reserve(vector_size);
306 
307 		for (int j = 0; j != vector_size; ++j) {
308 			SDL_Rect tile_rect;
309 			tile_rect.x = Random() % area_dim + x;
310 			tile_rect.y = Random() % area_dim + y;
311 			tile_rect.w = 4;
312 			tile_rect.h = 4;
313 			terrainTree[i].tiles.push_back(TerrainTile(terrainPictures[Random() % 2], tile_rect));
314 		}
315 
316 		x += area_dim;
317 		if (x > worldWidth) {
318 			x = 0;
319 			y += area_dim;
320 		}
321 	}
322 }
323 
324 
Unload()325 void Unload() {
326 	UnloadGraphics();
327 	sides.clear();
328 	KillAllWindows();
329 }
330 
UnloadGraphics()331 void UnloadGraphics() {
332 	for (int i = 0; i != terrainPictures.size(); ++i)
333 		SDL_FreeSurface(terrainPictures[i]);
334 
335 	terrainPictures.clear();
336 	terrainTree.clear();
337 
338 	SDL_FreeSurface(radarSurface);
339 	radarSurface = 0;
340 }
341 
342 } //end namespace
343 
344 
345