1 ////////////////////////////////////////////////////////////////////////////////
2 //    Scorched3D (c) 2000-2011
3 //
4 //    This file is part of Scorched3D.
5 //
6 //    Scorched3D is free software; you can redistribute it and/or modify
7 //    it under the terms of the GNU General Public License as published by
8 //    the Free Software Foundation; either version 2 of the License, or
9 //    (at your option) any later version.
10 //
11 //    Scorched3D is distributed in the hope that it will be useful,
12 //    but WITHOUT ANY WARRANTY; without even the implied warranty of
13 //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 //    GNU General Public License for more details.
15 //
16 //    You should have received a copy of the GNU General Public License along
17 //    with this program; if not, write to the Free Software Foundation, Inc.,
18 //    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 ////////////////////////////////////////////////////////////////////////////////
20 
21 #include <target/TargetSpace.h>
22 #include <target/TargetLife.h>
23 #include <target/TargetShield.h>
24 #include <target/TargetState.h>
25 #include <weapons/Accessory.h>
26 #include <weapons/ShieldRound.h>
27 #include <weapons/ShieldSquare.h>
28 #include <engine/ScorchedContext.h>
29 #include <engine/Simulator.h>
30 #include <common/OptionsScorched.h>
31 #include <common/Logger.h>
32 
TargetSpace()33 TargetSpace::TargetSpace() :
34 	spaceX_(-128), spaceY_(-128),
35 	spaceW_(1024), spaceH_(1024),
36 	spaceSq_(4), context_(0)
37 {
38 	spaceWSq_ = (spaceW_ / spaceSq_);
39 	spaceHSq_ = (spaceH_ / spaceSq_);
40 	noSquares_ = spaceWSq_ * spaceHSq_;
41 	squares_ = new Square[noSquares_];
42 	for (int i=0; i<noSquares_; i++) squares_[i].squarenum = i;
43 }
44 
~TargetSpace()45 TargetSpace::~TargetSpace()
46 {
47 	delete [] squares_;
48 }
49 
clear()50 void TargetSpace::clear()
51 {
52 	for (int i=0; i<noSquares_; i++) squares_[i].targets.clear();
53 }
54 
updateTarget(Target * target)55 void TargetSpace::updateTarget(Target *target)
56 {
57 	if (!target->getAlive() ||
58 		target->getTargetState().getNoCollision())
59 	{
60 		removeTarget(target);
61 	}
62 	else
63 	{
64 		static std::vector<Square*> squares;
65 		squares.clear();
66 		getSquares(target, squares);
67 
68 		bool same = false;
69 		if (squares.size() == target->getLife().getSpaceContainment().squares.size())
70 		{
71 			same = true;
72 			for (unsigned int i=0; i<squares.size(); i++)
73 			{
74 				if (squares[i]->squarenum != target->getLife().getSpaceContainment().squares[i])
75 				{
76 					same = false;
77 					break;
78 				}
79 			}
80 		}
81 
82 		if (!same)
83 		{
84 			removeTarget(target);
85 			std::vector<Square*>::iterator itor;
86 			for (itor = squares.begin();
87 				itor != squares.end();
88 				++itor)
89 			{
90 				Square *square = *itor;
91 				target->getLife().getSpaceContainment().squares.push_back(square->squarenum);
92 				square->targets.insert(std::pair<unsigned int, Target*>(target->getPlayerId(), target));
93 			}
94 		}
95 	}
96 }
97 
getSquares(Target * target,std::vector<Square * > & squares)98 void TargetSpace::getSquares(Target *target, std::vector<Square*> &squares)
99 {
100 	// Set the bounding constaints
101 	int x = target->getLife().getTargetPosition()[0].asInt();
102 	int y = target->getLife().getTargetPosition()[1].asInt();
103 	int w = target->getLife().getAabbSize()[0].asInt();
104 	int h = target->getLife().getAabbSize()[1].asInt();
105 
106 	// Update bounding constraints for shield
107 	Accessory *shieldAcc = target->getShield().getCurrentShield();
108 	if (shieldAcc)
109 	{
110 		Shield *shield = (Shield *) shieldAcc->getAction();
111 		if (shield->getRound())
112 		{
113 			ShieldRound *round = (ShieldRound *) shield;
114 
115 			w = MAX(w, (round->getActualRadius() * 2).asInt());
116 			h = MAX(h, (round->getActualRadius() * 2).asInt());
117 		}
118 		else
119 		{
120 			ShieldSquare *square = (ShieldSquare *) shield;
121 
122 			w = MAX(w, (square->getSize()[0] * 2).asInt());
123 			h = MAX(h, (square->getSize()[1] * 2).asInt());
124 		}
125 	}
126 
127 	// Find the bounding box coords
128 	int halfW = w / 2;
129 	int halfH = h / 2;
130 	int minX = x - halfW - 1;
131 	int minY = y - halfH - 1;
132 	int maxX = x + halfW + 1;
133 	int maxY = y + halfH + 1;
134 
135 	// Find the square positions
136 	normalizeCoords(minX, minY);
137 	normalizeCoords(maxX, maxY);
138 
139 	// Find the square numbers
140 	for (int b=minY; b<=maxY; b++)
141 	{
142 		for (int a=minX; a<=maxX; a++)
143 		{
144 			int num = spaceHSq_ * b + a;
145 			DIALOG_ASSERT(num >= 0 && num < noSquares_);
146 
147 			Square *square = &squares_[num];
148 			squares.push_back(square);
149 		}
150 	}
151 }
152 
removeTarget(Target * target)153 void TargetSpace::removeTarget(Target *target)
154 {
155 	std::vector<int> &squares = target->getLife().getSpaceContainment().squares;
156 	while (!squares.empty())
157 	{
158 		int squareNum = squares.back();
159 		squares.pop_back();
160 
161 		Square *square = &squares_[squareNum];
162 		square->targets.erase(target->getPlayerId());
163 	}
164 }
165 
getCollision(FixedVector & position)166 Target *TargetSpace::getCollision(FixedVector &position)
167 {
168 	int x = position[0].asInt();
169 	int y = position[1].asInt();
170 
171 	// Find the square positions
172 	normalizeCoords(x, y);
173 
174 	int num = spaceHSq_ * y + x;
175 	DIALOG_ASSERT(num >= 0 && num < noSquares_);
176 
177 	Square *square = &squares_[num];
178 
179 	Target *result = 0;
180 	std::map<unsigned int, Target *> &potentialTargets = square->targets;
181 	std::map<unsigned int, Target *>::iterator itor;
182 	for (itor = potentialTargets.begin();
183 		itor != potentialTargets.end();
184 		++itor)
185 	{
186 		Target *target = (*itor).second;
187 		if (!target->getAlive())
188 		{
189 			Logger::log(S3D::formatStringBuffer("ERROR: Dead target %u:%s in space",
190 				target->getPlayerId(), target->getCStrName().c_str()));
191 			continue;
192 		}
193 
194 		Accessory *shieldAcc = target->getShield().getCurrentShield();
195 		if (shieldAcc)
196 		{
197 			Shield *shield = (Shield *) shieldAcc->getAction();
198 			FixedVector direction = position - target->getLife().getTargetPosition();
199 			if (shield->inShield(direction))
200 			{
201 				result = target;
202 				break;
203 			}
204 		}
205 
206 		if (target->getLife().collision(position))
207 		{
208 			result = target;
209 			break;
210 		}
211 	}
212 
213 	if (context_->getOptionsGame().getActionSyncCheck() &&
214 		context_->getOptionsGame().getActionCollisionSyncCheck())
215 	{
216 		std::string targets;
217 		if (result)
218 		{
219 			targets.append(S3D::formatStringBuffer("%u:%s ",
220 				result->getPlayerId(),
221 				result->getLife().getTargetPosition().asQuickString()));
222 		}
223 
224 		context_->getSimulator().addSyncCheck(
225 			S3D::formatStringBuffer("CollisionSet : %s %s",
226 				position.asQuickString(),
227 				targets.c_str()));
228 	}
229 
230 	return result;
231 }
232 
getCollisionSet(FixedVector & position,fixed radius,std::map<unsigned int,Target * > & collisionTargets,bool ignoreHeight)233 void TargetSpace::getCollisionSet(FixedVector &position, fixed radius,
234 	std::map<unsigned int, Target *> &collisionTargets,
235 	bool ignoreHeight)
236 {
237 	int x = position[0].asInt();
238 	int y = position[1].asInt();
239 	int w = radius.asInt();
240 	int h = radius.asInt();
241 
242 	// Find the bounding box coords
243 	int halfW = w; // Not need 1/2 as its the radius thats passed in
244 	int halfH = h;
245 	int minX = x - halfW - 1;
246 	int minY = y - halfH - 1;
247 	int maxX = x + halfW + 1;
248 	int maxY = y + halfH + 1;
249 
250 	// Find the square positions
251 	normalizeCoords(minX, minY);
252 	normalizeCoords(maxX, maxY);
253 
254 	// Find the square numbers
255 	std::map<unsigned int, Target *>::iterator itor;
256 	for (int b=minY; b<=maxY; b++)
257 	{
258 		for (int a=minX; a<=maxX; a++)
259 		{
260 			int num = spaceHSq_ * b + a;
261 			DIALOG_ASSERT(num >= 0 && num < noSquares_);
262 
263 			Square *square = &squares_[num];
264 
265 			std::map<unsigned int, Target *> &potentialTargets =
266 				square->targets;
267 			for (itor = potentialTargets.begin();
268 				itor != potentialTargets.end();
269 				++itor)
270 			{
271 				Target *target = (*itor).second;
272 				if (!target->getAlive())
273 				{
274 					Logger::log(S3D::formatStringBuffer("ERROR: Dead target %u:%s in space",
275 						target->getPlayerId(), target->getCStrName().c_str()));
276 					continue;
277 				}
278 
279 				FixedVector checkPos = position;
280 				if (ignoreHeight)
281 				{
282 					FixedVector centerPos = target->getLife().getCenterPosition();
283 					checkPos[2] = centerPos[2];
284 				}
285 				fixed distance = target->getLife().collisionDistance(checkPos);
286 				if (distance < radius)
287 				{
288 					collisionTargets[target->getPlayerId()] = target;
289 				}
290 			}
291 		}
292 	}
293 
294 	if (context_->getOptionsGame().getActionSyncCheck() &&
295 		context_->getOptionsGame().getActionCollisionSyncCheck())
296 	{
297 		std::string targets;
298 		std::map<unsigned int, Target *>::iterator itor;
299 		for (itor = collisionTargets.begin();
300 			itor != collisionTargets.end();
301 			++itor)
302 		{
303 			targets.append(S3D::formatStringBuffer("%u:%s ",
304 				itor->second->getPlayerId(),
305 				itor->second->getLife().getTargetPosition().asQuickString()));
306 		}
307 
308 		context_->getSimulator().addSyncCheck(
309 			S3D::formatStringBuffer("CollisionSet : %s %s \"%s\"",
310 				position.asQuickString(),
311 				radius.asQuickString(),
312 				targets.c_str()));
313 	}
314 }
315 
316 #ifndef S3D_SERVER
317 #include <GLEXT/GLState.h>
318 #include <client/ScorchedClient.h>
319 #include <landscapemap/LandscapeMaps.h>
320 
drawBox(Vector & position,Vector & size)321 static void drawBox(Vector &position, Vector &size)
322 {
323 	float wid = size[0];
324 	float hgt = size[1];
325 	float dep = size[2];
326 	glPushMatrix();
327 		glTranslatef(position[0], position[1], position[2]);
328 		glBegin(GL_LINE_LOOP);
329 			// Top
330 			glNormal3f(0,1,0);
331 			glVertex3f(-wid/2,hgt/2,dep/2);
332 			glVertex3f(wid/2,hgt/2,dep/2);
333 			glVertex3f(wid/2,hgt/2,-dep/2);
334 			glVertex3f(-wid/2,hgt/2,-dep/2);
335 		glEnd();
336 		glBegin(GL_LINE_LOOP);
337 			// Back
338 			glNormal3f(0,0,1);
339 			glVertex3f(-wid/2,hgt/2,dep/2);
340 			glVertex3f(-wid/2,-hgt/2,dep/2);
341 			glVertex3f(wid/2,-hgt/2,dep/2);
342 			glVertex3f(wid/2,hgt/2,dep/2);
343 		glEnd();
344 		glBegin(GL_LINE_LOOP);
345 			// Front
346 			glNormal3f(0,0,-1);
347 			glVertex3f(-wid/2,hgt/2,-dep/2);
348 			glVertex3f(wid/2,hgt/2,-dep/2);
349 			glVertex3f(wid/2,-hgt/2,-dep/2);
350 			glVertex3f(-wid/2,-hgt/2,-dep/2);
351 		glEnd();
352 		glBegin(GL_LINE_LOOP);
353 			// Left
354 			glNormal3f(1,0,0);
355 			glVertex3f(wid/2,hgt/2,-dep/2);
356 			glVertex3f(wid/2,hgt/2,dep/2);
357 			glVertex3f(wid/2,-hgt/2,dep/2);
358 			glVertex3f(wid/2,-hgt/2,-dep/2);
359 		glEnd();
360 		glBegin(GL_LINE_LOOP);
361 			// Right
362 			glNormal3f(-1,0,0);
363 			glVertex3f(-wid/2,hgt/2,dep/2);
364 			glVertex3f(-wid/2,hgt/2,-dep/2);
365 			glVertex3f(-wid/2,-hgt/2,-dep/2);
366 			glVertex3f(-wid/2,-hgt/2,dep/2);
367 		glEnd();
368 		glBegin(GL_LINE_LOOP);
369 			// Bottom
370 			glNormal3f(0,-1,0);
371 			glVertex3f(-wid/2,-hgt/2,dep/2);
372 			glVertex3f(-wid/2,-hgt/2,-dep/2);
373 			glVertex3f(wid/2,-hgt/2,-dep/2);
374 			glVertex3f(wid/2,-hgt/2,dep/2);
375 		glEnd();
376 	glPopMatrix();
377 }
378 
draw()379 void TargetSpace::draw()
380 {
381 	GLState glState(GLState::TEXTURE_OFF | GLState::LIGHTING_OFF);
382 
383 	for (int b=0; b<spaceHSq_; b++)
384 	{
385 		for (int a=0; a<spaceWSq_; a++)
386 		{
387 			int num = spaceHSq_ * b + a;
388 			DIALOG_ASSERT(num >= 0 && num < noSquares_);
389 
390 			Square *square = &squares_[num];
391 			if (square->targets.empty())
392 			{
393 				continue;
394 			}
395 
396 			Vector position(a * spaceSq_ + spaceX_ + spaceSq_ / 2,
397 				b * spaceSq_ + spaceY_ + spaceSq_ / 2, 0);
398 			position[2] =
399 				ScorchedClient::instance()->getLandscapeMaps().
400 					getGroundMaps().getHeight((int) position[0], (int) position[1]).asFloat() - 7.0f;
401 			Vector size(spaceSq_, spaceSq_, 20);
402 
403 			if (position[0] > ScorchedClient::instance()->getLandscapeMaps().
404 				getGroundMaps().getLandscapeWidth() ||
405 				position[1] > ScorchedClient::instance()->getLandscapeMaps().
406 				getGroundMaps().getLandscapeHeight())
407 			{
408 				continue;
409 			}
410 
411 			glColor3f(1.0f, 0.0f, 0.0f);
412 			drawBox(position, size);
413 
414 			glBegin(GL_LINES);
415 			std::map<unsigned int, Target *>::iterator itor;
416 			for (itor = square->targets.begin();
417 				itor != square->targets.end();
418 				++itor)
419 			{
420 				Target *target = (*itor).second;
421 				glVertex3fv(target->getLife().getFloatPosition());
422 				glVertex3f(position[0], position[1], position[2] + 10.0f);
423 			}
424 			glEnd();
425 		}
426 	}
427 }
428 #endif
429