1 /*
2  * atanks - obliterate each other with oversize weapons
3  * Copyright (C) 2003  Thomas Hudson
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License
7  * as published by the Free Software Foundation; either version 2
8  * of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
18  * */
19 
20 #include <time.h>
21 #include <cassert>
22 #include "player.h"
23 #include "globaldata.h"
24 #include "files.h"
25 #include "tank.h"
26 #include "sound.h"
27 #include "debris_pool.h"
28 
29 
GLOBALDATA()30 GLOBALDATA::GLOBALDATA()
31 {
32 	// memset initialization, because Visual C++ 2013 can't do lists, yet.
33 	memset(order,       0, sizeof(TANK*)   * MAXPLAYERS);
34 	memset(tank_status, 0, sizeof(char)    * 128);
35 	memset(heads,       0, sizeof(vobj_t*) * CLASS_COUNT);
36 	memset(tails,       0, sizeof(vobj_t*) * CLASS_COUNT);
37 }
38 
39 
~GLOBALDATA()40 GLOBALDATA::~GLOBALDATA()
41 {
42 	this->destroy();
43 }
44 
45 /// @brief goes through the columns from @a left to @a right and sets slide type according to @a do_lock
addLandSlide(int32_t left,int32_t right,bool do_lock)46 void GLOBALDATA::addLandSlide(int32_t left, int32_t right, bool do_lock)
47 {
48 	// Opt out soon if no landslide is to be done
49 	if ( (SLIDE_NONE      == env.landSlideType)
50 	  || (SLIDE_TANK_ONLY == env.landSlideType) )
51 		return;
52 
53 	int32_t minX = std::min(left, right);
54 	int32_t maxX = std::max(left, right);
55 
56 	if (minX < 1)
57 		minX = 1;
58 	if (minX > (env.screenWidth - 1) )
59 		minX =  env.screenWidth - 1;
60 	if (maxX < 1)
61 		maxX = 1;
62 	if (maxX > (env.screenWidth - 1) )
63 		maxX =  env.screenWidth - 1;
64 
65 	if (do_lock)
66 		memset(&done[minX], 3, sizeof(char) * (maxX - minX + 1) );
67 	else
68 		memset(&done[minX], 2, sizeof(char) * (maxX - minX + 1) );
69 }
70 
71 
addObject(vobj_t * object)72 void GLOBALDATA::addObject (vobj_t *object)
73 {
74 	if (nullptr == object)
75 		return;
76 
77 	eClasses class_ = object->getClass();
78 
79 	objLocks[class_].lock();
80 
81 	/// --- case 1: first of its kind ---
82 	if (nullptr == tails[class_]) {
83 		heads[class_] = object;
84 		tails[class_] = object;
85 	}
86 
87 	/// --- case 2: normal addition ---
88 	else {
89 		tails[class_]->next = object;
90 		object->prev = tails[class_];
91 		tails[class_] = object;
92 	}
93 
94 	objLocks[class_].unlock();
95 }
96 
97 
98 // Combine both make_update and make_bgupdate with safety checks for
99 // the dimensions. This reduces code duplication.
addUpdate(int32_t x,int32_t y,int32_t w,int32_t h,BOX * target,int32_t & target_count)100 void GLOBALDATA::addUpdate(int32_t x, int32_t y, int32_t w, int32_t h,
101                            BOX* target, int32_t &target_count)
102 {
103 	assert (target && "ERROR: addUpdate called with nullptr target!");
104 
105 	bool combined = false;
106 
107 	assert ( (w > 0) && (h > 0) ); // No zero/negative updates, please!
108 
109 	int32_t left   = std::max(x - 1, 0);
110 	int32_t top    = std::max(y - 1, 0);
111 	int32_t right  = std::min(x + w + 1, env.screenWidth);
112 	int32_t bottom = std::min(y + h + 1, env.screenHeight);
113 
114 	// If the update is outside the screen, it is not needed:
115 	if ( (bottom <= 0) /* most common case */
116 	  || (left   >= env.screenWidth)
117 	  || (right  <= 0)
118 	  || (top    >= env.screenHeight) )
119 		return;
120 
121 	assert( (left < right ) );
122 	assert( (top  < bottom) );
123 
124 	if ( combineUpdates && target_count
125 	  && (target_count < env.max_screen_updates)) {
126 		// Re-purpose BOX::w as x2 and BOX::h as y2:
127 		BOX prev(target[target_count - 1].x,
128 		         target[target_count - 1].y,
129 		         target[target_count - 1].x + target[target_count - 1].w,
130 		         target[target_count - 1].y + target[target_count - 1].h);
131 		BOX next(left, top, right, bottom);
132 
133 		if ( (next.w > (prev.x - 3))
134 		  && (prev.w > (next.x - 3))
135 		  && (next.h > (prev.y - 3))
136 		  && (prev.h > (next.y - 3)) ) {
137 			next.set(next.x < prev.x ? next.x : prev.x,
138 			         next.y < prev.y ? next.y : prev.y,
139 			         next.w > prev.w ? next.w : prev.w,
140 			         next.h > prev.h ? next.h : prev.h);
141 			// recalculate x2/y2 back into w/h
142 			target[target_count - 1].set(next.x, next.y,
143 			                             next.w - next.x,
144 			                             next.h - next.y);
145 
146 			// Make sure the target update is sane:
147 			assert( (target[target_count - 1].w > 0)
148 			     && (target[target_count - 1].h > 0) );
149 
150 			combined = true;
151 		}
152 	}
153 
154 	if (!combined)
155 		target[target_count++].set(left, top, right - left, bottom - top);
156 
157 	if (!stopwindow && (target_count <= env.max_screen_updates))
158 		env.window_update(left, top, right - left, bottom - top);
159 }
160 
161 
162 // return true if any living tank is in the given box.
163 // left/right and top/bottom are determined automatically.
areTanksInBox(int32_t x1,int32_t y1,int32_t x2,int32_t y2)164 bool GLOBALDATA::areTanksInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
165 {
166 	TANK* lt = static_cast<TANK*>(heads[CLASS_TANK]);
167 
168 	while (lt) {
169 		// Tank found, is it in the box?
170 		if ( (!lt->destroy) && lt->isInBox(x1, y1, x2, y2))
171 			return true;
172 		lt->getNext(&lt);
173 	}
174 
175 	return false;
176 }
177 
178 
179 
180 // This function checks to see if one full second has passed since the
181 // last time the function was called.
182 // The function returns true if time has passed. The function
183 // returns false if time hasn't passed or it was unable to tell
184 // how much time has passed.
check_time_changed()185 bool GLOBALDATA::check_time_changed()
186 {
187 	volatile
188 	static time_t last_second    = 0;
189 	static time_t current_second = 0;
190 
191 	time(&current_second);
192 
193 	if ( current_second == last_second )
194 		return false;
195 
196 	// time has changed
197 	last_second = current_second;
198 
199 	return true;
200 }
201 
202 
203 /// @brief remove and delete *all* objects stored.
clear_objects()204 void GLOBALDATA::clear_objects()
205 {
206 	int32_t class_ = 0;
207 
208 	while (class_ < CLASS_COUNT) {
209 		while (tails[class_])
210 			delete tails[class_];
211 		++class_;
212 	}
213 }
214 
215 
216 // Call before calling allegro_exit()!
destroy()217 void GLOBALDATA::destroy()
218 {
219 	clear_objects();
220 
221 	if (debris_pool) {
222 		delete debris_pool;
223 		debris_pool = nullptr;
224 	}
225 
226 	if (canvas)  destroy_bitmap(canvas);     canvas       = nullptr;
227 	if (terrain) destroy_bitmap(terrain);    terrain      = nullptr;
228 	if (done)         delete [] done;        done         = nullptr;
229 	if (fp)           delete [] fp;          fp           = nullptr;
230 	if (surface)      delete [] surface;     surface      = nullptr;
231 	if (dropTo)       delete [] dropTo;      dropTo       = nullptr;
232 	if (velocity)     delete [] velocity;    velocity     = nullptr;
233 	if (dropIncr)     delete [] dropIncr;    dropIncr     = nullptr;
234 	if (updates)      delete [] updates;     updates      = nullptr;
235 	if (lastUpdates)  delete [] lastUpdates; lastUpdates  = nullptr;
236 }
237 
238 
do_updates()239 void GLOBALDATA::do_updates ()
240 {
241 	bool isBgUpdNeeded = lastUpdatesCount > 0;
242 
243 	acquire_bitmap(screen);
244 	for (int32_t i = 0; i < updateCount; ++i) {
245 		blit( canvas, screen,
246 				updates[i].x, updates[i].y, updates[i].x, updates[i].y,
247 				updates[i].w, updates[i].h);
248 
249 		if (isBgUpdNeeded)
250 			make_bgupdate( updates[i].x, updates[i].y,
251 							updates[i].w, updates[i].h);
252 	}
253 	release_bitmap(screen);
254 	if (!isBgUpdNeeded) {
255 		lastUpdatesCount = updateCount;
256 		memcpy (lastUpdates, updates, sizeof (BOX) * updateCount);
257 	}
258 	updateCount = 0;
259 }
260 
261 
262 // Do what has to be done after the game starts
first_init()263 void GLOBALDATA::first_init()
264 {
265 	// get memory for updates
266 	try {
267 		updates = new BOX[env.max_screen_updates];
268 	} catch (std::bad_alloc &e) {
269 		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
270 		     << "Failed to allocate memory for updates ["
271 		     << e.what() << "]" << endl;
272 		exit(1);
273 	}
274 
275 	// get memory for lastUpdates
276 	try {
277 		lastUpdates = new BOX[env.max_screen_updates];
278 	} catch (std::bad_alloc &e) {
279 		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
280 		     << "Failed to allocate memory for lastUpdates ["
281 		     << e.what() << "]" << endl;
282 		exit(1);
283 	}
284 
285 	canvas = create_bitmap (env.screenWidth, env.screenHeight);
286 	if (!canvas) {
287 		cout << "Failed to create canvas bitmap: " << allegro_error << endl;
288 		exit(1);
289 	}
290 
291 	terrain = create_bitmap (env.screenWidth, env.screenHeight);
292 	if (!terrain) {
293 		cout << "Failed to create terrain bitmap: " << allegro_error << endl;
294 		exit(1);
295 	}
296 
297 
298 	// get memory for the debris pool
299 	try {
300 		debris_pool = new sDebrisPool(env.max_screen_updates);
301 	} catch (std::bad_alloc &e) {
302 		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
303 		     << "Failed to allocate memory for debris_pool ["
304 		     << e.what() << "]" << endl;
305 		exit(1);
306 	}
307 
308 
309 	try {
310 		done     = new int8_t[env.screenWidth]{0};
311 		fp       = new int32_t[env.screenWidth]{0};
312 		surface  = new ai32_t[env.screenWidth]{ { 0 } };
313 		dropTo   = new int32_t[env.screenWidth]{0};
314 		velocity = new double[env.screenWidth]{0};
315 		dropIncr = new double[env.screenWidth]{0};
316 	} catch (std::bad_alloc &e) {
317 		cerr << "globaldata.cpp:" << __LINE__ << ":first_init() : "
318 		     << "Failed to allocate memory for base data arrays ["
319 		     << e.what() << "]" << endl;
320 		exit(1);
321 	}
322 
323 	initialise ();
324 }
325 
326 
327 /** @brief delegate freeing of a debris item to the debris pool.
328   *
329   * This delegating function, instead of making the debris pool public,
330   * exists as a point where locking, if it becomes necessary, can be
331   * added without having to rewrite a lot of code.
332 **/
free_debris_item(item_t * item)333 void GLOBALDATA::free_debris_item(item_t* item)
334 {
335 	debris_pool->free_item(item);
336 }
337 
338 
339 
get_avg_bgcolor(int32_t x1,int32_t y1,int32_t x2,int32_t y2,double xv,double yv)340 int32_t GLOBALDATA::get_avg_bgcolor(int32_t x1, int32_t y1,
341                                     int32_t x2, int32_t y2,
342                                     double xv, double yv)
343 {
344 	// Movement
345 	int32_t mvx      = ROUND(10. * xv); // eliminate slow movement
346 	int32_t mvy      = ROUND(10. * yv); // eliminate slow movement
347 	bool    mv_left  = mvx < 0;
348 	bool    mv_right = mvx > 0;
349 	bool    mv_up    = mvy < 0;
350 	bool    mv_down  = mvy > 0;
351 
352 	// Boundaries
353 	int32_t min_x = 1;
354 	int32_t max_x = env.screenWidth - 2;
355 	int32_t min_y = env.isBoxed ? MENUHEIGHT + 1 : MENUHEIGHT;
356 	int32_t max_y = env.screenHeight - 2;
357 
358 	// Coordinates
359 	int32_t left   = std::max(std::min(x1, x2), min_x);
360 	int32_t right  = std::min(std::max(x1, x2), max_x);
361 	int32_t centre = (x1 + x2) / 2;
362 	int32_t top    = std::max(std::min(y1, y2), min_y);
363 	int32_t bottom = std::min(std::max(y1, y2), max_y);
364 	int32_t middle = (y1 + y2) / 2;
365 
366 
367 	// Colors:
368 	int32_t col_tl, col_tc, col_tr; // top row
369 	int32_t col_ml, col_mc, col_mr; // middle row
370 	int32_t col_bl, col_bc, col_br; // bottom row
371 	int32_t r = 0, g = 0, b = 0;
372 
373 
374 	// Get Sky or Terrain colour, whatever fits:
375 	/*---------------------
376 	  --- Left side ---
377 	  ---------------------*/
378 	if ( PINK == (col_tl = getpixel(terrain, left, top)) )
379 		col_tl = getpixel(env.sky, left, top);
380 	if ( PINK == (col_ml = getpixel(terrain, left, middle)) )
381 		col_ml = getpixel(env.sky, left, middle);
382 	if ( PINK == (col_bl = getpixel(terrain, left, bottom)) )
383 		col_bl = getpixel(env.sky, left, bottom);
384 
385 	/*---------------------
386 	  --- The Center ---
387 	---------------------*/
388 	if ( PINK == (col_tc = getpixel(terrain, centre, top)) )
389 		col_tc = getpixel(env.sky, centre, top);
390 	if ( PINK == (col_mc = getpixel(terrain, centre, middle)) )
391 		col_mc = getpixel(env.sky, centre, middle);
392 	if ( PINK == (col_bc = getpixel(terrain, centre, bottom)) )
393 		col_bc = getpixel(env.sky, centre, bottom);
394 
395 	/*----------------------
396 	  --- Right side ---
397 	----------------------*/
398 	if ( PINK == (col_tr = getpixel(terrain, right, top)) )
399 		col_tr = getpixel(env.sky, right, top);
400 	if ( PINK == (col_mr = getpixel(terrain, right, middle)) )
401 		col_mr = getpixel(env.sky, right, middle);
402 	if ( PINK == (col_br = getpixel(terrain, right, bottom)) )
403 		col_br = getpixel(env.sky, right, bottom);
404 
405 
406 	// Fetch the rgb parts, according to movement:
407 
408 	/* --- X-Movement --- */
409 	if (mv_left) {
410 		// Movement to the left, weight left side colour twice
411 		r += (GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl)) * 2;
412 		g += (GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl)) * 2;
413 		b += (GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl)) * 2;
414 		// The others are counted once
415 		r += GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)
416 		   + GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br);
417 		g += GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)
418 		   + GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br);
419 		b += GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)
420 		   + GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br);
421 	} else if (mv_right) {
422 		// Movement to the right, weight right side colour twice
423 		r += (GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br)) * 2;
424 		g += (GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br)) * 2;
425 		b += (GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br)) * 2;
426 		// The others are counted once
427 		r += GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)
428 		   + GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl);
429 		g += GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)
430 		   + GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl);
431 		b += GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)
432 		   + GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl);
433 	} else {
434 		// No x-movement, weight centre colour twice
435 		r += (GET_R(col_tc) + GET_R(col_mc) + GET_R(col_bc)) * 2;
436 		g += (GET_G(col_tc) + GET_G(col_mc) + GET_G(col_bc)) * 2;
437 		b += (GET_B(col_tc) + GET_B(col_mc) + GET_B(col_bc)) * 2;
438 		// The others are counted once
439 		r += GET_R(col_tl) + GET_R(col_ml) + GET_R(col_bl)
440 		   + GET_R(col_tr) + GET_R(col_mr) + GET_R(col_br);
441 		g += GET_G(col_tl) + GET_G(col_ml) + GET_G(col_bl)
442 		   + GET_G(col_tr) + GET_G(col_mr) + GET_G(col_br);
443 		b += GET_B(col_tl) + GET_B(col_ml) + GET_B(col_bl)
444 		   + GET_B(col_tr) + GET_B(col_mr) + GET_B(col_br);
445 	}
446 
447 	/* --- Y-Movement --- */
448 	if (mv_up) {
449 		// Movement upwards, weight top side colour twice
450 		r += (GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr)) * 2;
451 		g += (GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr)) * 2;
452 		b += (GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr)) * 2;
453 		// The others are counted once
454 		r += GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)
455 		   + GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br);
456 		g += GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)
457 		   + GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br);
458 		b += GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)
459 		   + GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br);
460 	} else if (mv_down) {
461 		// Movement downwards, weight bottom side colour twice
462 		r += (GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br)) * 2;
463 		g += (GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br)) * 2;
464 		b += (GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br)) * 2;
465 		// The others are counted once
466 		r += GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)
467 		   + GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr);
468 		g += GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)
469 		   + GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr);
470 		b += GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)
471 		   + GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr);
472 	} else {
473 		// No y-movement, weight middle colour twice
474 		r += (GET_R(col_ml) + GET_R(col_mc) + GET_R(col_mr)) * 2;
475 		g += (GET_G(col_ml) + GET_G(col_mc) + GET_G(col_mr)) * 2;
476 		b += (GET_B(col_ml) + GET_B(col_mc) + GET_B(col_mr)) * 2;
477 		// The others are counted once
478 		r += GET_R(col_tl) + GET_R(col_tc) + GET_R(col_tr)
479 		   + GET_R(col_bl) + GET_R(col_bc) + GET_R(col_br);
480 		g += GET_G(col_tl) + GET_G(col_tc) + GET_G(col_tr)
481 		   + GET_G(col_bl) + GET_G(col_bc) + GET_G(col_br);
482 		b += GET_B(col_tl) + GET_B(col_tc) + GET_B(col_tr)
483 		   + GET_B(col_bl) + GET_B(col_bc) + GET_B(col_br);
484 	}
485 
486 
487 	/* I know this looks weird, but what we now have is some kind of summed
488 	 * matrix, which is always the same:
489 	 * Let's assume that xv and yv are both 0.0, so no movement is happening.
490 	 * The result is: (In counted times)
491 	 * 2|3|2  ( =  7)
492 	 * -+-+-
493 	 * 3|4|3  ( = 10)
494 	 * -+-+-
495 	 * 2|3|2  ( =  7)
496 	 *          = 24
497 	 * And it is always 24, no matter which movement combination you try
498 	 */
499 
500 	r /= 24;
501 	g /= 24;
502 	b /= 24;
503 
504 	return makecol(r > 0xff ? 0xff : r,
505 	               g > 0xff ? 0xff : g,
506 	               b > 0xff ? 0xff : b);
507 }
508 
509 
510 // Locks global->command for reading, reads value, then unlocks the variable
511 // and returns the value.
get_command()512 int32_t GLOBALDATA::get_command()
513 {
514 	cmdLock.lock();
515 	int32_t c = command;
516  	cmdLock.unlock();
517 	return c;
518 }
519 
520 
get_curr_tank()521 TANK* GLOBALDATA::get_curr_tank()
522 {
523 	return currTank;
524 }
525 
526 
527 /** @brief delegate getting a debris item to the debris pool.
528   *
529   * This delegating function, instead of making the debris pool public,
530   * exists as a point where locking, if it becomes necessary, can be
531   * added without having to rewrite a lot of code.
532 **/
get_debris_item(int32_t radius)533 sDebrisItem* GLOBALDATA::get_debris_item(int32_t radius)
534 {
535 	return debris_pool->get_item(radius);
536 }
537 
538 
get_next_tank(bool * wrapped_around)539 TANK* GLOBALDATA::get_next_tank(bool *wrapped_around)
540 {
541 	bool    found    = false;
542 	int32_t index    = tankindex + 1;
543 	int32_t oldindex = tankindex;
544 	int32_t wrapped  = 0;
545 
546 	while (!found && (wrapped < 2)) {
547 		if (index >= MAXPLAYERS) {
548 			index = 0;
549 			*wrapped_around = true;
550 			wrapped++;
551 		}
552 
553 		if ( order[index]
554 		  && (index != oldindex)
555 		  && !order[index]->destroy)
556 			found = true;
557 		else
558 			++index;
559 	}
560 
561 	tankindex = index;
562 
563 	// If this tank is valid, the currently selected weapon must be checked
564 	// first and changed if depleted
565 	TANK* next_tank = order[index];
566 	if (next_tank && next_tank->player)
567 		next_tank->check_weapon();
568 
569 	// Whatever happened, the status bar needs an update:
570 	if (oldindex != index)
571 		updateMenu = true;
572 
573 	return next_tank;
574 }
575 
576 
initialise()577 void GLOBALDATA::initialise ()
578 {
579 	clear_objects();
580 	numTanks = 0;
581 	clear_to_color (canvas, WHITE);
582 	clear_to_color (terrain, PINK);
583 
584 	for (int32_t i = 0; i < env.screenWidth; ++i) {
585 		done[i]    = 0;
586 		dropTo[i]  = env.screenHeight - 1;
587 		fp[i]      = 0;
588 	}
589 }
590 
591 
592 // return true if the dirt reaches into the given box.
593 // left/right and top/bottom are determined automatically.
isDirtInBox(int32_t x1,int32_t y1,int32_t x2,int32_t y2)594 bool GLOBALDATA::isDirtInBox(int32_t x1, int32_t y1, int32_t x2, int32_t y2)
595 {
596 	int32_t top = std::max(std::min(y1, y2),
597 	                       env.isBoxed ? MENUHEIGHT + 1 : MENUHEIGHT);
598 	// Exit early if the box is below the playing area
599 	if (top >= env.screenHeight)
600 		return false;
601 
602 	int32_t bottom = std::min(std::max(y1, y2), env.screenHeight - 2);
603 	// Exit early if the box is over the playing area
604 	if (bottom <= MENUHEIGHT)
605 		return false;
606 
607 	int32_t left   = std::max(std::min(x1, x2), 1);
608 	int32_t right  = std::min(std::max(x1, x2), env.screenWidth - 2);
609 
610 	// If the box is outside the playing area, this loop won't do anything
611 	for (int32_t x = left; x <= right; ++x) {
612 		if (surface[x].load(ATOMIC_READ) <= bottom)
613 			return true;
614 	}
615 
616 	return false;
617 }
618 
619 
620 /// @return true if the close button was pressed
isCloseBtnPressed()621 bool GLOBALDATA::isCloseBtnPressed()
622 {
623 	cbpLock.lock();
624 	bool result = close_button_pressed;
625 	cbpLock.unlock();
626 
627 	return result;
628 }
629 
630 
631 /** @brief load global data from a file
632   * This method is still present to provide backwards
633   * compatibility with configurations that were saved
634   * before the values were moved to ENVIRONMENT
635 **/
load_from_file(FILE * file)636 void GLOBALDATA::load_from_file (FILE* file)
637 {
638 	char  line[ MAX_CONFIG_LINE + 1] = { 0 };
639 	char  field[MAX_CONFIG_LINE + 1] = { 0 };
640 	char  value[MAX_CONFIG_LINE + 1] = { 0 };
641 	char* result                     = nullptr;
642 
643 	setlocale(LC_NUMERIC, "C");
644 
645 	// read until we hit line "*GLOBAL*" or "***" or EOF
646 	do {
647 		result = fgets(line, MAX_CONFIG_LINE, file);
648 		if ( !result
649 		  || !strncmp(line, "***", 3) )
650 			// eof OR end of record
651 			return;
652 	} while ( strncmp(line, "*GLOBAL*", 8) );
653 
654 	bool  done = false;
655 
656 	while (result && !done) {
657 		// read a line
658 		memset(line, '\0', MAX_CONFIG_LINE);
659 		if ( ( result = fgets(line, MAX_CONFIG_LINE, file) ) ) {
660 
661 			// if we hit end of the record, stop
662 			if (! strncmp(line, "***", 3) )
663 				return;
664 
665 			// strip newline character
666 			int32_t line_length = strlen(line);
667 			while ( line[line_length - 1] == '\n') {
668 				line[line_length - 1] = '\0';
669 				line_length--;
670 			}
671 
672 			// find equal sign
673 			int32_t equal_position = 1;
674 			while ( ( equal_position < line_length )
675 				 && ( line[equal_position] != '='  ) )
676 				equal_position++;
677 
678 			// make sure the equal sign position is valid
679 			if (line[equal_position] != '=')
680 				continue; // Go to next line
681 
682 			// seperate field from value
683 			memset(field, '\0', MAX_CONFIG_LINE);
684 			memset(value, '\0', MAX_CONFIG_LINE);
685 			strncpy(field, line, equal_position);
686 			strncpy(value, &( line[equal_position + 1] ), MAX_CONFIG_LINE);
687 
688 
689 			// Values that were moved to ENVIRONMENT:
690 			// They are loaded, for compatibility, but the next
691 			// save will put them into the correct section anyway.
692 			// So these can eventually be removed.
693 			if (!strcasecmp(field, "acceleratedai")) {
694 				sscanf(value, "%d", &env.skipComputerPlay);
695 				if (env.skipComputerPlay > SKIP_HUMANS_DEAD)
696 					env.skipComputerPlay = SKIP_HUMANS_DEAD;
697 			} else if (!strcasecmp(field, "checkupdates")) {
698 				int32_t val = 0;
699 				sscanf(value, "%d", &val);
700 				env.check_for_updates = val > 0 ? true : false;
701 			} else if (!strcasecmp(field, "colourtheme") ) {
702 				sscanf(value, "%d", &env.colourTheme);
703 				if (env.colourTheme < CT_REGULAR) env.colourTheme = CT_REGULAR;
704 				if (env.colourTheme > CT_CRISPY)  env.colourTheme = CT_CRISPY;
705 			} else if (!strcasecmp(field, "debrislevel") )
706 				sscanf(value, "%d", &env.debris_level);
707 			else if (!strcasecmp(field, "detailedland")) {
708 				int32_t val = 0;
709 				sscanf(value, "%d", &val);
710 				env.detailedLandscape = val > 0 ? true : false;
711 			} else if (!strcasecmp(field, "detailedsky")) {
712 				int32_t val = 0;
713 				sscanf(value, "%d", &val);
714 				env.detailedSky = val > 0 ? true : false;
715 			} else if (!strcasecmp(field, "dither")) {
716 				int32_t val = 0;
717 				sscanf(value, "%d", &val);
718 				env.ditherGradients = val > 0 ? true : false;
719 			} else if (!strcasecmp(field, "dividemoney") ) {
720 				int32_t val = 0;
721 				sscanf(value, "%d", &val);
722 				env.divide_money = val > 0 ? true : false;
723 			} else if (!strcasecmp(field, "enablesound")) {
724 				int32_t val = 0;
725 				sscanf(value, "%d", &val);
726 				env.sound_enabled = val > 0 ? true : false;
727 			} else if (!strcasecmp(field, "frames") ) {
728 				int32_t new_fps = 0;
729 				sscanf(value, "%d", &new_fps);
730 				env.set_fps(new_fps);
731 			} else if (!strcasecmp(field, "fullscreen"))
732 				sscanf(value, "%d", &env.full_screen);
733 			else if (!strcasecmp(field, "interest"))
734 				sscanf(value, "%lf", &env.interest);
735 			else if (!strcasecmp(field, "language") ) {
736 				uint32_t stored_lang = 0;
737 				sscanf(value, "%u", &stored_lang);
738 				env.language = static_cast<eLanguages>(stored_lang);
739 			} else if (!strcasecmp(field, "listenport"))
740 				sscanf(value, "%d", &env.network_port);
741 			else if (!strcasecmp(field, "maxfiretime") )
742 				sscanf(value, "%d", &env.maxFireTime);
743 			else if (!strcasecmp(field, "networking")) {
744 				int32_t val = 0;
745 				sscanf(value, "%d", &val);
746 				env.network_enabled = val > 0 ? true : false;
747 			} else if (!strcasecmp(field, "numpermanentplayers"))
748 				sscanf(value, "%d", &env.numPermanentPlayers);
749 			else if (!strcasecmp(field, "OSMOUSE")) {
750 				int32_t val = 0;
751 				sscanf(value, "%d", &val);
752 				env.osMouse = val > 0 ? true : false;
753 			} else if (!strcasecmp(field, "playmusic")) {
754 				int32_t val = 0;
755 				sscanf(value, "%d", &val);
756 				env.play_music = val > 0 ? true : false;
757 			} else if (!strcasecmp(field, "rounds") )
758 				sscanf(value, "%u", &env.rounds);
759 			else if (!strcasecmp(field, "screenwidth")
760 				  && !env.temp_screenWidth) {
761 				sscanf(value, "%d", &env.screenWidth);
762 				env.halfWidth = env.screenWidth / 2;
763 				env.temp_screenWidth = env.screenWidth;
764 			} else if (!strcasecmp(field, "screenheight")
765 				  && !env.temp_screenHeight) {
766 				sscanf(value, "%d", &env.screenHeight);
767 				env.halfHeight = env.screenHeight / 2;
768 				env.temp_screenHeight = env.screenHeight;
769 			}
770 			else if (!strcasecmp(field, "scorehitunit"))
771 				sscanf(value, "%d", &env.scoreHitUnit);
772 			else if (!strcasecmp(field, "scoreselfhit"))
773 				sscanf(value, "%d", &env.scoreSelfHit);
774 			else if (!strcasecmp(field, "scoreroundwinbonus"))
775 				sscanf(value, "%d", &env.scoreRoundWinBonus);
776 			else if (!strcasecmp(field, "scoreteamhit"))
777 				sscanf(value, "%d", &env.scoreTeamHit);
778 			else if (!strcasecmp(field, "scoreunitdestroybonus"))
779 				sscanf(value, "%d", &env.scoreUnitDestroyBonus);
780 			else if (!strcasecmp(field, "scoreunitselfdestroy"))
781 				sscanf(value, "%d", &env.scoreUnitSelfDestroy);
782 			else if (!strcasecmp(field, "sellpercent"))
783 				sscanf(value, "%lf", &env.sellpercent);
784 			else if (!strcasecmp(field, "sounddriver"))
785 				sscanf(value, "%d", &env.sound_driver);
786 			else if (!strcasecmp(field, "startmoney"))
787 				sscanf(value, "%d", &env.startmoney);
788 			else if (!strcasecmp(field, "turntype"))
789 				sscanf(value, "%d", &env.turntype);
790 			else if (!strcasecmp(field, "violentdeath") )
791 				sscanf(value, "%d", &env.violent_death);
792 		}     // end of read a line properly
793 	}     // end of while not done
794 }
795 
796 
lockClass(eClasses class_)797 void GLOBALDATA::lockClass(eClasses class_)
798 {
799 	objLocks[class_].lock();
800 }
801 
802 
lockLand()803 void GLOBALDATA::lockLand()
804 {
805 	landLock.lock();
806 }
807 
808 
make_bgupdate(int32_t x,int32_t y,int32_t w,int32_t h)809 void GLOBALDATA::make_bgupdate (int32_t x, int32_t y, int32_t w, int32_t h)
810 {
811 	if (lastUpdatesCount >= env.max_screen_updates) {
812 		make_fullUpdate();
813 		return;
814 	}
815 
816 	assert( (w > 0) && (h > 0) );
817 
818 	if ( (w > 0) && (h > 0) )
819 		addUpdate(x, y, w, h, lastUpdates, lastUpdatesCount);
820 }
821 
822 
make_fullUpdate()823 void GLOBALDATA::make_fullUpdate()
824 {
825 	// Replace Updates with a full screen update:
826 	combineUpdates   = false;
827 	updateCount      = 0;
828 	lastUpdatesCount = 0;
829 
830 	// They are split into 2 x 2 updates:
831 	for (int32_t x = 0; x < 2; ++x) {
832 		make_update(  env.halfWidth * x, 0,
833 		              env.halfWidth,     env.halfHeight);
834 		make_bgupdate(env.halfWidth * x, 0,
835 		              env.halfWidth,     env.halfHeight);
836 		make_update(  env.halfWidth * x, env.halfHeight,
837 		              env.halfWidth,     env.halfHeight);
838 		make_bgupdate(env.halfWidth * x, env.halfHeight,
839 		              env.halfWidth,     env.halfHeight);
840 	}
841 
842 	combineUpdates   = true;
843 }
844 
845 
make_update(int32_t x,int32_t y,int32_t w,int32_t h)846 void GLOBALDATA::make_update (int32_t x, int32_t y, int32_t w, int32_t h)
847 {
848 	if (updateCount >= env.max_screen_updates) {
849 		make_fullUpdate();
850 		return;
851 	}
852 
853 	// These asserts should catch screwed updates that make no sense
854 	assert( (h <= env.screenHeight) && (w <= env.screenWidth) );
855 	assert( (w > 0) && (h > 0) );
856 
857 	if ( (h > 0) && (w > 0) )
858 		addUpdate(x, y, w, h, updates, updateCount);
859 }
860 
861 
newRound()862 void GLOBALDATA::newRound()
863 {
864 	if ( (currentround > 0) && (currentround-- < env.nextCampaignRound) )
865 		env.nextCampaignRound -= env.campaign_rounds;
866 
867 	tankindex          = 0;
868 	naturals_activated = 0;
869 	combineUpdates     = true;
870 
871 	// clean all but texts and tanks
872 	int32_t class_ = 0;
873 	while (class_ < CLASS_COUNT) {
874 		if ( (CLASS_FLOATTEXT != class_) && (CLASS_TANK != class_) ) {
875 			while (tails[class_])
876 				delete tails[class_];
877 		}
878 		++class_;
879 	}
880 
881 
882 	// Re-init land slide
883 	for (int32_t i = 0; i < env.screenWidth; ++i) {
884 		done[i]    = 2; // Check at once
885 		dropTo[i]  = env.screenHeight - 1;
886 		fp[i]      = 0;
887 	}
888 
889 	// Init order array
890 	for (int32_t i = 0; i < MAXPLAYERS; ++i)
891 		order[i] = nullptr;
892 }
893 
894 
895 /// @brief Tell global that the close button was pressed
pressCloseButton()896 void GLOBALDATA::pressCloseButton()
897 {
898 	cbpLock.lock();
899 	close_button_pressed = true;
900 	cbpLock.unlock();
901 	set_command(GLOBAL_COMMAND_QUIT);
902 }
903 
904 
removeObject(vobj_t * object)905 void GLOBALDATA::removeObject (vobj_t *object)
906 {
907 	if (nullptr == object)
908 		return;
909 
910 	eClasses class_ = object->getClass();
911 
912 	/// --- 1: Is the list empty? ---
913 	if (nullptr == heads[class_])
914 		return;
915 
916 	objLocks[class_].lock();
917 
918 	/// --- 2: If the object is head, set it anew:
919 	if (object == heads[class_])
920 		heads[class_] = object->next;
921 
922 	/// --- 4: If the object is tail, set it anew:
923 	if (object == tails[class_])
924 		tails[class_] = object->prev;
925 
926 	/// --- 5: Take it out of the list:
927 	if (object->prev)
928 		object->prev->next = object->next;
929 	if (object->next)
930 		object->next->prev = object->prev;
931 	object->prev = nullptr;
932 	object->next = nullptr;
933 
934 	objLocks[class_].unlock();
935 }
936 
937 
removeTank(TANK * tank)938 void GLOBALDATA::removeTank(TANK* tank)
939 {
940 	if (nullptr == tank)
941 		return;
942 
943 	for (int32_t i = 0 ; i < MAXPLAYERS ; ++i) {
944 		if (tank == order[i])
945 			order[i] = nullptr;
946 	}
947 }
948 
949 
replace_canvas()950 void GLOBALDATA::replace_canvas ()
951 {
952 
953 	for (int32_t i = 0; i < lastUpdatesCount; ++i) {
954 		if ((lastUpdates[i].y + lastUpdates[i].h) > MENUHEIGHT) {
955 			blit (env.sky, canvas, lastUpdates[i].x, lastUpdates[i].y - MENUHEIGHT,
956 			                       lastUpdates[i].x, lastUpdates[i].y,
957 			                       lastUpdates[i].w, lastUpdates[i].h);
958 			masked_blit (terrain, canvas, lastUpdates[i].x, lastUpdates[i].y,
959 			                              lastUpdates[i].x, lastUpdates[i].y,
960 			                              lastUpdates[i].w, lastUpdates[i].h);
961 		} // End of having an update below the top bar
962 	}
963 
964 	int32_t l = 0;
965 	int32_t r = env.screenWidth - 1;
966 	int32_t t = MENUHEIGHT;
967 	int32_t b = env.screenHeight - 1;
968 
969 	vline(canvas, l,     t, b, env.wallColour); // Left edge
970 	vline(canvas, l + 1, t, b, env.wallColour); // Left edge
971 	vline(canvas, r,     t, b, env.wallColour); // right edge
972 	vline(canvas, r - 1, t, b, env.wallColour); // right edge
973 	hline(canvas, l,     b, r, env.wallColour); // bottom edge
974 	if (env.isBoxed)
975 		hline(canvas, l, t, r, env.wallColour); // top edge
976 
977 	lastUpdatesCount = 0;
978 }
979 
980 
981 // Set a new command, lock guarded
set_command(int32_t cmd)982 void GLOBALDATA::set_command(int32_t cmd)
983 {
984 	cmdLock.lock();
985 	command = cmd;
986 	cmdLock.unlock();
987 }
988 
989 
set_curr_tank(TANK * tank_)990 void GLOBALDATA::set_curr_tank(TANK* tank_)
991 {
992 	if (tank_ != currTank) {
993 		if (currTank)
994 			currTank->deactivate();
995 		currTank = tank_;
996 		if (currTank)
997 			currTank->activate();
998 	}
999 }
1000 
1001 
1002 /** @brief go through the land and slide what is to be slid and is not locked
1003   * Slide land basic control is done using the 'done[]' array.
1004   * done[x] == 0 : Nothing to do. All values assumed to be correct.
1005   * done[x] == 1 : This column is currently in sliding.
1006   * done[x] == 2 : This column is about to be slid, but the base values aren't set.
1007   * done[x] == 3 : This column is about to be slid but locked. (Explosion not done)
1008 **/
slideLand()1009 void GLOBALDATA::slideLand()
1010 {
1011 	// Opt out soon if no landslide is to be done
1012 	if ( (SLIDE_NONE      == env.landSlideType)
1013 	  || (SLIDE_TANK_ONLY == env.landSlideType)
1014 	  || ( (SLIDE_CARTOON == env.landSlideType)
1015 		&& (env.time_to_fall > 0) ) )
1016 		return;
1017 
1018 	for (int32_t col = 1; col < (env.screenWidth - 1); ++col) {
1019 
1020 		// Skip this column if it is done or locked
1021 		if (!done[col] || (3 == done[col]))
1022 			continue;
1023 
1024 		// Set base settings if this hasn't happen, yet
1025 		if (2 == done[col]) {
1026 			surface[col].store(0, ATOMIC_WRITE);
1027 			dropTo[col] = env.screenHeight - 1;
1028 			done[col]   = 1;
1029 
1030 			// Calc the top and bottom of the column to slide
1031 
1032 			// Find top-most non-PINK pixel
1033 			int32_t row = MENUHEIGHT + (env.isBoxed ? 1 : 0);
1034 
1035 			for ( ;(row < dropTo[col])
1036 				&& (PINK == getpixel(terrain, col, row));
1037 				++row) ;
1038 			surface[col].store(row, ATOMIC_WRITE); // This is the top pixel with all gaps
1039 
1040 			// Find bottom-most PINK pixel
1041 			int32_t top_row = row;
1042 			for (row = dropTo[col];
1043 				   (row > top_row)
1044 				&& (PINK != getpixel(terrain, col, row));
1045 				--row) ;
1046 			dropTo[col] = row;
1047 
1048 			// Find bottom-most unsupported pixel
1049 			for ( ;(row >= top_row)
1050 				&& (PINK == getpixel(terrain, col, row));
1051 				--row) ;
1052 
1053 			// Check whether there is anything to do or not
1054 			if ((row >= top_row) && (top_row < dropTo[col])) {
1055 				fp[col]       = row - top_row + 1;
1056 				velocity[col] = 0; // Not yet
1057 				done[col]     = 1; // Can be processed
1058 			}
1059 
1060 			// Otherwise this column is done
1061 			else {
1062 				if ( !skippingComputerPlay
1063 				  && (velocity[col] > .5)
1064 				  && (fp[col] > 1) )
1065 					play_natural_sound(DIRT_FRAGMENT, col, 64,
1066 							1000 - (fp[col] * 800 / env.screenHeight));
1067 				done[col] = 0; // Nothing to do
1068 				fp[col]   = 0;
1069 			}
1070 		} // End of preparations
1071 
1072 		// Do the slide if possible
1073 		if (1 == done[col]) {
1074 
1075 			// Only slide if no neighbours are locked
1076 			bool can_slide = true;
1077 			for (int32_t j = col - 1; can_slide && (j > 0) ; --j) {
1078 				if (3 == done[j])
1079 					can_slide = false;
1080 				else if (!done[j])
1081 					j = 0; // no further look needed.
1082 			}
1083 			for (int32_t j = col + 1; can_slide && (j < (env.screenWidth - 1)) ; ++j) {
1084 				if (3 == done[j])
1085 					can_slide = false;
1086 				else if (!done[j])
1087 					j = env.screenWidth; // no further look needed.
1088 			}
1089 
1090 			if (can_slide) {
1091 				// Do instant first, because only GRAVITY remains
1092 				// which is the case if cartoon wait time is over.
1093 				if ( (SLIDE_INSTANT == env.landSlideType) || skippingComputerPlay) {
1094 					int32_t surf = surface[col].load(ATOMIC_READ);
1095 					make_bgupdate (col, surf, 1, dropTo[col] - surf + 1);
1096 					make_update   (col, surf, 1, dropTo[col] - surf + 1);
1097 					blit (terrain, terrain, col, surf, col,
1098 					      dropTo[col] - fp[col] + 1, 1, fp[col]);
1099 					vline(terrain, col, surf, dropTo[col] - fp[col], PINK);
1100 					velocity[col] = fp[col]; // Or no sound would be played if done
1101 					done[col]     = 2; // Recheck
1102 				} else {
1103 					velocity[col] += env.gravity;
1104 					dropIncr[col] += velocity[col];
1105 
1106 					int32_t dropAdd = ROUND(dropIncr[col]);
1107 					int32_t max_top = MENUHEIGHT + (env.isBoxed ? 1 : 0);
1108 
1109 					if (dropAdd > 0) {
1110 
1111 						int32_t top_row = surface[col].load(ATOMIC_READ);
1112 
1113 						assert( (top_row >= 0)
1114 						     && (top_row < terrain->h)
1115 						     && "ERROR: top_row out of range!");
1116 
1117 						// If the top pixel is not PINK, and the source is not
1118 						// too high, increase dropAdd:
1119 						int32_t over_top = top_row - dropAdd;
1120 						while ( ( over_top <= max_top)
1121 						     && ( over_top >  0)
1122 						     && ( PINK != getpixel(terrain, col, over_top) ) ) {
1123 								++dropAdd;
1124 								--over_top;
1125 						}
1126 
1127 						if (dropAdd > (dropTo[col] - (top_row + fp[col])) ) {
1128 							dropAdd       = static_cast<int32_t>(dropTo[col]
1129 							                               - (top_row + fp[col])
1130 							                               + 1);
1131 							dropIncr[col] = dropAdd;
1132 							done[col]     = 2; // Recheck
1133 							over_top      = top_row - dropAdd;
1134 						}
1135 
1136 						int32_t slide_height = fp[col] + dropAdd;
1137 
1138 						assert( (over_top >= 0)
1139 						     && (over_top < terrain->h)
1140 						     && "ERROR: top_row - dropAdd out of range!");
1141 						assert( (slide_height > 0)
1142 						     && (slide_height <= terrain->h)
1143 						     && "ERROR: slide_height out of range!");
1144 						assert( (  (over_top + slide_height) <= terrain->h)
1145 						     && "ERROR: over_top + slide_height is out of range!");
1146 						assert( (  (top_row + slide_height) <= terrain->h)
1147 						     && "ERROR: over_top + slide_height is out of range!");
1148 
1149 						blit (terrain, terrain,
1150 								col, over_top,
1151 								col, top_row, 1,
1152 								slide_height);
1153 						make_bgupdate(col, over_top, 1,
1154 						              slide_height + dropAdd + 1);
1155 						make_update  (col, over_top, 1,
1156 						              slide_height + dropAdd + 1);
1157 						// If the top row reaches to the ceiling, there might
1158 						// not be a PINK pixel to blit. In that case, one has
1159 						// to be painted "by hand", or the slide will produce
1160 						// nice long columns. (Happens with dirt balls when
1161 						// "fixed" under the menubar.
1162 						if (over_top <= max_top) {
1163 							putpixel(terrain, col, max_top,     PINK);
1164 							putpixel(terrain, col, max_top + 1, PINK);
1165 						}
1166 
1167 						surface[col].fetch_add(dropAdd);
1168 						dropIncr[col] -= dropAdd;
1169 					}
1170 				}
1171 			}
1172 		} // End of actual slide
1173 	} // End of looping columns
1174 }
1175 
1176 
unlockClass(eClasses class_)1177 void GLOBALDATA::unlockClass(eClasses class_)
1178 {
1179 	objLocks[class_].unlock();
1180 }
1181 
1182 
unlockLand()1183 void GLOBALDATA::unlockLand()
1184 {
1185 	landLock.unlock();
1186 }
1187 
1188 
1189 /// @brief goes through the columns from @a left to @a right and unlocks what is locked.
unlockLandSlide(int32_t left,int32_t right)1190 void GLOBALDATA::unlockLandSlide(int32_t left, int32_t right)
1191 {
1192 	// Opt out soon if no landslide is to be done
1193 	if ( (SLIDE_NONE      == env.landSlideType)
1194 	  || (SLIDE_TANK_ONLY == env.landSlideType) )
1195 		return;
1196 
1197 	int32_t minX = std::min(left, right);
1198 	int32_t maxX = std::max(left, right);
1199 
1200 	if (minX < 1)
1201 		minX = 1;
1202 	if (maxX > (env.screenWidth - 1) )
1203 		maxX =  env.screenWidth - 1;
1204 
1205 	for (int32_t col = minX; col <= maxX; ++col) {
1206 		if ((done[col] > 2) || !done[col])
1207 			done[col] = 2;
1208 	}
1209 }
1210 
1211 #ifndef USE_MUTEX_INSTEAD_OF_SPINLOCK
1212 
1213 /// === Spin Lock Implementations ===
1214 
1215 /// @brief Default ctor
CSpinLock()1216 CSpinLock::CSpinLock() :
1217 	is_destroyed(ATOMIC_VAR_INIT(false))
1218 {
1219 	lock_flag.clear(); // Done this way, because VC++ can't do it normally.
1220 	owner_id = std::thread::id();
1221 }
1222 
1223 
1224 /// @brief destructor - mark as destroyed, lock and go
~CSpinLock()1225 CSpinLock::~CSpinLock()
1226 {
1227 	std::thread::id this_id = std::this_thread::get_id();
1228 	bool need_lock = (owner_id != this_id);
1229 
1230 	if (need_lock)
1231 		lock();
1232 	is_destroyed.store(true);
1233 	if (need_lock)
1234 		unlock();
1235 }
1236 
1237 
1238 /// @brief return true if this thread has an active lock
hasLock()1239 bool CSpinLock::hasLock()
1240 {
1241 	// This works, because unlock() sets the owner_id to -1.
1242 	return (std::this_thread::get_id() == owner_id);
1243 }
1244 
1245 
1246 /** @brief Get a lock
1247   * Warning: No recursive locking possible! Only lock once!
1248 **/
lock()1249 void CSpinLock::lock()
1250 {
1251 	std::thread::id this_id = std::this_thread::get_id();
1252 	assert( (owner_id != this_id) && "ERROR: Lock already owned!");
1253 
1254 	if (false == is_destroyed.load(ATOMIC_READ)) {
1255 		while (lock_flag.test_and_set()) {
1256 			std::this_thread::yield();
1257 		}
1258 		owner_id = this_id;
1259 	}
1260 }
1261 
1262 /// @brief unlock if this thread owns the lock. Otherwise do nothing.
unlock()1263 void CSpinLock::unlock()
1264 {
1265 	std::thread::id this_id = std::this_thread::get_id();
1266 	assert( (owner_id == this_id) && "ERROR: Lock *NOT* owned!");
1267 
1268 	if (owner_id == this_id) {
1269 		owner_id = std::thread::id();
1270 		lock_flag.clear(std::memory_order_release);
1271 	}
1272 }
1273 
1274 #endif // USE_MUTEX_INSTEAD_OF_SPINLOCK
1275 
1276