1 /***************************************************************************
2  *      Mechanized Assault and Exploration Reloaded Projectfile            *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
18  ***************************************************************************/
19 #include <cmath>
20 
21 #include "game/data/units/building.h"
22 
23 #include "game/logic/client.h"
24 #include "game/logic/clientevents.h"
25 #include "utility/listhelpers.h"
26 #include "game/logic/fxeffects.h"
27 #include "main.h"
28 #include "netmessage.h"
29 #include "pcx.h"
30 #include "game/data/player/player.h"
31 #include "game/logic/server.h"
32 #include "game/logic/serverevents.h"
33 #include "settings.h"
34 #include "game/logic/upgradecalculator.h"
35 #include "game/data/units/vehicle.h"
36 #include "video.h"
37 #include "unifonts.h"
38 #include "game/data/report/savedreportsimple.h"
39 #include "game/data/report/special/savedreportresourcechanged.h"
40 #include "utility/random.h"
41 
42 #include "ui/sound/soundmanager.h"
43 #include "ui/sound/effects/soundeffectvoice.h"
44 
45 using namespace std;
46 
47 
48 //--------------------------------------------------------------------------
cBuildListItem()49 cBuildListItem::cBuildListItem()
50 {}
51 
52 //--------------------------------------------------------------------------
cBuildListItem(sID type_,int remainingMetal_)53 cBuildListItem::cBuildListItem (sID type_, int remainingMetal_) :
54 	type (type_),
55 	remainingMetal (remainingMetal_)
56 {}
57 
58 //--------------------------------------------------------------------------
cBuildListItem(const cBuildListItem & other)59 cBuildListItem::cBuildListItem (const cBuildListItem& other) :
60 	type (other.type),
61 	remainingMetal (other.remainingMetal)
62 {}
63 
64 //--------------------------------------------------------------------------
cBuildListItem(cBuildListItem && other)65 cBuildListItem::cBuildListItem (cBuildListItem&& other) :
66 	type (std::move (other.type)),
67 	remainingMetal (std::move (other.remainingMetal))
68 {}
69 
70 //--------------------------------------------------------------------------
operator =(const cBuildListItem & other)71 cBuildListItem& cBuildListItem::operator= (const cBuildListItem& other)
72 {
73 	type = other.type;
74 	remainingMetal = other.remainingMetal;
75 	return *this;
76 }
77 
78 //--------------------------------------------------------------------------
operator =(cBuildListItem && other)79 cBuildListItem& cBuildListItem::operator= (cBuildListItem && other)
80 {
81 	type = std::move (other.type);
82 	remainingMetal = std::move (other.remainingMetal);
83 	return *this;
84 }
85 
86 //--------------------------------------------------------------------------
getType() const87 const sID& cBuildListItem::getType() const
88 {
89 	return type;
90 }
91 
92 //--------------------------------------------------------------------------
setType(const sID & type_)93 void cBuildListItem::setType (const sID& type_)
94 {
95 	auto oldType = type;
96 	type = type_;
97 	if (type != oldType) typeChanged();
98 }
99 
100 //--------------------------------------------------------------------------
getRemainingMetal() const101 int cBuildListItem::getRemainingMetal() const
102 {
103 	return remainingMetal;
104 }
105 
106 //--------------------------------------------------------------------------
setRemainingMetal(int value)107 void cBuildListItem::setRemainingMetal (int value)
108 {
109 	std::swap (remainingMetal, value);
110 	if (value != remainingMetal) remainingMetalChanged();
111 }
112 
113 //--------------------------------------------------------------------------
114 // cBuilding Implementation
115 //--------------------------------------------------------------------------
116 
117 //--------------------------------------------------------------------------
cBuilding(const sUnitData * b,cPlayer * Owner,unsigned int ID)118 cBuilding::cBuilding (const sUnitData* b, cPlayer* Owner, unsigned int ID) :
119 	cUnit ((Owner != 0 && b != 0) ? Owner->getUnitDataCurrentVersion (b->ID) : 0,
120 		   Owner,
121 		   ID),
122 	isWorking (false)
123 {
124 	setSentryActive (data.canAttack != TERRAIN_NONE);
125 
126 	RubbleTyp = 0;
127 	RubbleValue = 0;
128 	researchArea = cResearch::kAttackResearch;
129 	uiData = b ? UnitsData.getBuildingUI (b->ID) : 0;
130 	points = 0;
131 
132 	if (Owner == nullptr || b == nullptr)
133 	{
134 		return;
135 	}
136 
137 	BaseN = false;
138 	BaseBN = false;
139 	BaseE = false;
140 	BaseBE = false;
141 	BaseS = false;
142 	BaseBS = false;
143 	BaseW = false;
144 	BaseBW = false;
145 	repeatBuild = false;
146 
147 	MaxMetalProd = 0;
148 	MaxGoldProd = 0;
149 	MaxOilProd = 0;
150 	data.setHitpoints (data.getHitpointsMax());
151 	data.setAmmo (data.getAmmoMax());
152 	SubBase = nullptr;
153 	buildSpeed = 0;
154 
155 	if (data.isBig)
156 	{
157 		DamageFXPointX  = random (64) + 32;
158 		DamageFXPointY  = random (64) + 32;
159 		DamageFXPointX2 = random (64) + 32;
160 		DamageFXPointY2 = random (64) + 32;
161 	}
162 	else
163 	{
164 		DamageFXPointX = random (64 - 24);
165 		DamageFXPointY = random (64 - 24);
166 	}
167 
168 	refreshData();
169 
170 	buildListChanged.connect ([&]() { statusChanged(); });
171 	buildListFirstItemDataChanged.connect ([&]() { statusChanged(); });
172 	researchAreaChanged.connect ([&]() { statusChanged(); });
173 	metalPerRoundChanged.connect([&]() { statusChanged(); });
174 	ownerChanged.connect ([&]() { registerOwnerEvents(); });
175 
176 	registerOwnerEvents();
177 }
178 
179 //--------------------------------------------------------------------------
~cBuilding()180 cBuilding::~cBuilding()
181 {
182 }
183 
184 //----------------------------------------------------
185 /** Returns a string with the current state */
186 //----------------------------------------------------
getStatusStr(const cPlayer * player) const187 string cBuilding::getStatusStr (const cPlayer* player) const
188 {
189 	if (isDisabled())
190 	{
191 		string sText;
192 		sText = lngPack.i18n ("Text~Comp~Disabled") + " (";
193 		sText += iToStr (getDisabledTurns()) + ")";
194 		return sText;
195 	}
196 	if (isUnitWorking() || (factoryHasJustFinishedBuilding() && isDisabled() == false))
197 	{
198 		// Factory:
199 		if (!data.canBuild.empty() && !buildList.empty() && getOwner() == player)
200 		{
201 			const cBuildListItem& buildListItem = buildList[0];
202 			const string& unitName = buildListItem.getType().getUnitDataOriginalVersion()->name;
203 			string sText;
204 
205 			if (buildListItem.getRemainingMetal() > 0)
206 			{
207 				int iRound;
208 
209 				iRound = (int) ceilf (buildListItem.getRemainingMetal() / (float)getMetalPerRound());
210 				sText = lngPack.i18n ("Text~Comp~Producing") + lngPack.i18n ("Text~Punctuation~Colon");
211 				sText += unitName + " (";
212 				sText += iToStr (iRound) + ")";
213 
214 				if (font->getTextWide (sText, FONT_LATIN_SMALL_WHITE) > 126)
215 				{
216 					sText = lngPack.i18n ("Text~Comp~Producing") + ":\n";
217 					sText += unitName + " (";
218 					sText += iToStr (iRound) + ")";
219 				}
220 
221 				return sText;
222 			}
223 			else //new unit is rdy + which kind of unit
224 			{
225 				sText = lngPack.i18n ("Text~Comp~Producing_Fin");
226 				sText += lngPack.i18n ("Text~Punctuation~Colon");
227 				sText += unitName;
228 
229 				if (font->getTextWide (sText) > 126)
230 				{
231 					sText = lngPack.i18n ("Text~Comp~Producing_Fin");
232 					sText += ":\n";
233 					sText += unitName;
234 				}
235 				return sText;
236 			}
237 		}
238 
239 		// Research Center
240 		if (data.canResearch && getOwner() == player)
241 		{
242 			string sText = lngPack.i18n ("Text~Comp~Working") + "\n";
243 			for (int area = 0; area < cResearch::kNrResearchAreas; area++)
244 			{
245 				if (getOwner()->getResearchCentersWorkingOnArea ((cResearch::ResearchArea)area) > 0)
246 				{
247 					switch (area)
248 					{
249 						case cResearch::kAttackResearch: sText += lngPack.i18n ("Text~Others~Attack"); break;
250 						case cResearch::kShotsResearch: sText += lngPack.i18n ("Text~Others~Shots_7"); break;
251 						case cResearch::kRangeResearch: sText += lngPack.i18n ("Text~Others~Range"); break;
252 						case cResearch::kArmorResearch: sText += lngPack.i18n ("Text~Others~Armor_7"); break;
253 						case cResearch::kHitpointsResearch: sText += lngPack.i18n ("Text~Others~Hitpoints_7"); break;
254 						case cResearch::kSpeedResearch: sText += lngPack.i18n ("Text~Others~Speed"); break;
255 						case cResearch::kScanResearch: sText += lngPack.i18n ("Text~Others~Scan"); break;
256 						case cResearch::kCostResearch: sText += lngPack.i18n ("Text~Others~Costs"); break;
257 					}
258 					sText += lngPack.i18n ("Text~Punctuation~Colon") + iToStr (getOwner()->getResearchState().getRemainingTurns (area, getOwner()->getResearchCentersWorkingOnArea ((cResearch::ResearchArea)area))) + "\n";
259 				}
260 			}
261 			return sText;
262 		}
263 
264 		// Goldraffinerie:
265 		if (data.convertsGold && getOwner() == player)
266 		{
267 			string sText;
268 			sText = lngPack.i18n ("Text~Comp~Working") + "\n";
269 			sText += lngPack.i18n ("Text~Title~Credits") + lngPack.i18n ("Text~Punctuation~Colon");
270 			sText += iToStr (getOwner()->getCredits());
271 			return sText;
272 		}
273 
274 		return lngPack.i18n ("Text~Comp~Working");
275 	}
276 
277 	if (isAttacking())
278 		return lngPack.i18n ("Text~Comp~AttackingStatusStr");
279 	else if (isBeeingAttacked())
280 		return lngPack.i18n ("Text~Comp~IsBeeingAttacked");
281 	else if (isSentryActive())
282 		return lngPack.i18n ("Text~Comp~Sentry");
283 	else if (isManualFireActive())
284 		return lngPack.i18n ("Text~Comp~ReactionFireOff");
285 
286 	return lngPack.i18n ("Text~Comp~Waits");
287 }
288 
289 
290 //--------------------------------------------------------------------------
makeReport(cSoundManager & soundManager) const291 void cBuilding::makeReport (cSoundManager& soundManager) const
292 {
293 	if (data.canResearch && isUnitWorking() && getOwner() && getOwner()->isCurrentTurnResearchAreaFinished (getResearchArea()))
294 	{
295 		soundManager.playSound (std::make_shared<cSoundEffectVoice> (eSoundEffectType::VoiceUnitStatus, VoiceData.VOIResearchComplete));
296 	}
297 }
298 
299 //--------------------------------------------------------------------------
300 /** Refreshs all data to the maximum values */
301 //--------------------------------------------------------------------------
refreshData()302 bool cBuilding::refreshData()
303 {
304 	// NOTE: according to MAX 1.04 units get their shots/movepoints back even if they are disabled
305 
306 	if (data.getShots() < data.getShotsMax())
307 	{
308 		data.setShots (std::min (this->data.getShotsMax(), this->data.getAmmo()));
309 		return true;
310 	}
311 	return false;
312 }
313 
render_rubble(SDL_Surface * surface,const SDL_Rect & dest,float zoomFactor,bool drawShadow) const314 void cBuilding::render_rubble (SDL_Surface* surface, const SDL_Rect& dest, float zoomFactor, bool drawShadow) const
315 {
316 	assert (!getOwner());
317 
318 	SDL_Rect src;
319 
320 	if (data.isBig)
321 	{
322 		if (!UnitsData.dirt_big) return;
323 		src.w = src.h = (int) (UnitsData.dirt_big_org->h * zoomFactor);
324 	}
325 	else
326 	{
327 		if (!UnitsData.dirt_small) return;
328 		src.w = src.h = (int) (UnitsData.dirt_small_org->h * zoomFactor);
329 	}
330 
331 	src.x = src.w * RubbleTyp;
332 	SDL_Rect tmp = dest;
333 	src.y = 0;
334 
335 	// draw the shadows
336 	if (drawShadow)
337 	{
338 		if (data.isBig)
339 		{
340 			CHECK_SCALING (*UnitsData.dirt_big_shw, *UnitsData.dirt_big_shw_org, zoomFactor);
341 			SDL_BlitSurface (UnitsData.dirt_big_shw.get(), &src, surface, &tmp);
342 		}
343 		else
344 		{
345 			CHECK_SCALING (*UnitsData.dirt_small_shw, *UnitsData.dirt_small_shw_org, zoomFactor);
346 			SDL_BlitSurface (UnitsData.dirt_small_shw.get(), &src, surface, &tmp);
347 		}
348 	}
349 
350 	// draw the building
351 	tmp = dest;
352 
353 	if (data.isBig)
354 	{
355 		CHECK_SCALING (*UnitsData.dirt_big, *UnitsData.dirt_big_org, zoomFactor);
356 		SDL_BlitSurface (UnitsData.dirt_big.get(), &src, surface, &tmp);
357 	}
358 	else
359 	{
360 		CHECK_SCALING (*UnitsData.dirt_small, *UnitsData.dirt_small_org, zoomFactor);
361 		SDL_BlitSurface (UnitsData.dirt_small.get(), &src, surface, &tmp);
362 	}
363 }
364 
render_beton(SDL_Surface * surface,const SDL_Rect & dest,float zoomFactor) const365 void cBuilding::render_beton (SDL_Surface* surface, const SDL_Rect& dest, float zoomFactor) const
366 {
367 	SDL_Rect tmp = dest;
368 	if (data.isBig)
369 	{
370 		CHECK_SCALING (*GraphicsData.gfx_big_beton, *GraphicsData.gfx_big_beton_org, zoomFactor);
371 
372 		if (alphaEffectValue && cSettings::getInstance().isAlphaEffects())
373 			SDL_SetSurfaceAlphaMod (GraphicsData.gfx_big_beton.get(), alphaEffectValue);
374 		else
375 			SDL_SetSurfaceAlphaMod (GraphicsData.gfx_big_beton.get(), 254);
376 
377 		SDL_BlitSurface (GraphicsData.gfx_big_beton.get(), nullptr, surface, &tmp);
378 	}
379 	else
380 	{
381 		CHECK_SCALING (*UnitsData.ptr_small_beton, *UnitsData.ptr_small_beton_org, zoomFactor);
382 		if (alphaEffectValue && cSettings::getInstance().isAlphaEffects())
383 			SDL_SetSurfaceAlphaMod (UnitsData.ptr_small_beton, alphaEffectValue);
384 		else
385 			SDL_SetSurfaceAlphaMod (UnitsData.ptr_small_beton, 254);
386 
387 		SDL_BlitSurface (UnitsData.ptr_small_beton, nullptr, surface, &tmp);
388 		SDL_SetSurfaceAlphaMod (UnitsData.ptr_small_beton, 254);
389 	}
390 }
391 
render_simple(SDL_Surface * surface,const SDL_Rect & dest,float zoomFactor,int animationTime,int alpha) const392 void cBuilding::render_simple (SDL_Surface* surface, const SDL_Rect& dest, float zoomFactor, int animationTime, int alpha) const
393 {
394 	int frameNr = dir;
395 	if (data.hasFrames && data.isAnimated && cSettings::getInstance().isAnimations() &&
396 		isDisabled() == false)
397 	{
398 		frameNr = (animationTime % data.hasFrames);
399 	}
400 
401 	render_simple (surface, dest, zoomFactor, data, *uiData, getOwner(), frameNr, alpha);
402 }
403 
render_simple(SDL_Surface * surface,const SDL_Rect & dest,float zoomFactor,const sUnitData & data,const sBuildingUIData & uiData,const cPlayer * owner,int frameNr,int alpha)404 /*static*/ void cBuilding::render_simple (SDL_Surface* surface, const SDL_Rect& dest, float zoomFactor, const sUnitData& data, const sBuildingUIData& uiData, const cPlayer* owner, int frameNr, int alpha)
405 {
406 	// read the size:
407 	SDL_Rect src;
408 	src.x = 0;
409 	src.y = 0;
410 	if (data.hasFrames)
411 	{
412 		src.w = Round (64.0f * zoomFactor);
413 		src.h = Round (64.0f * zoomFactor);
414 	}
415 	else
416 	{
417 		src.w = (int) (uiData.img_org->w * zoomFactor);
418 		src.h = (int) (uiData.img_org->h * zoomFactor);
419 	}
420 
421 	// blit the players color and building graphic
422 	if (data.hasPlayerColor && owner) SDL_BlitSurface (owner->getColor().getTexture(), nullptr, GraphicsData.gfx_tmp.get(), nullptr);
423 	else SDL_FillRect (GraphicsData.gfx_tmp.get(), nullptr, 0x00FF00FF);
424 
425 	if (data.hasFrames)
426 	{
427 		src.x = frameNr * Round (64.0f * zoomFactor);
428 
429 		CHECK_SCALING (*uiData.img, *uiData.img_org, zoomFactor);
430 		SDL_BlitSurface (uiData.img.get(), &src, GraphicsData.gfx_tmp.get(), nullptr);
431 
432 		src.x = 0;
433 	}
434 	else if (data.hasClanLogos)
435 	{
436 		CHECK_SCALING (*uiData.img, *uiData.img_org, zoomFactor);
437 		src.x = 0;
438 		src.y = 0;
439 		src.w = (int) (128 * zoomFactor);
440 		src.h = (int) (128 * zoomFactor);
441 		// select clan image
442 		if (owner && owner->getClan() != -1)
443 			src.x = (int) ((owner->getClan() + 1) * 128 * zoomFactor);
444 		SDL_BlitSurface (uiData.img.get(), &src, GraphicsData.gfx_tmp.get(), nullptr);
445 	}
446 	else
447 	{
448 		CHECK_SCALING (*uiData.img, *uiData.img_org, zoomFactor);
449 		SDL_BlitSurface (uiData.img.get(), nullptr, GraphicsData.gfx_tmp.get(), nullptr);
450 	}
451 
452 	// draw the building
453 	SDL_Rect tmp = dest;
454 
455 	src.x = 0;
456 	src.y = 0;
457 
458 	SDL_SetSurfaceAlphaMod (GraphicsData.gfx_tmp.get(), alpha);
459 	SDL_BlitSurface (GraphicsData.gfx_tmp.get(), &src, surface, &tmp);
460 }
461 
462 
render(unsigned long long animationTime,SDL_Surface * surface,const SDL_Rect & dest,float zoomFactor,bool drawShadow,bool drawConcrete) const463 void cBuilding::render (unsigned long long animationTime, SDL_Surface* surface, const SDL_Rect& dest, float zoomFactor, bool drawShadow, bool drawConcrete) const
464 {
465 	// Note: when changing something in this function,
466 	// make sure to update the caching rules!
467 
468 	// check, if it is dirt:
469 	if (!getOwner())
470 	{
471 		render_rubble (surface, dest, zoomFactor, drawShadow);
472 		return;
473 	}
474 
475 	// draw the concrete
476 	if (data.hasBetonUnderground && drawConcrete)
477 	{
478 		render_beton (surface, dest, zoomFactor);
479 	}
480 	// draw the connector slots:
481 	if ((this->SubBase && !alphaEffectValue) || data.isConnectorGraphic)
482 	{
483 		drawConnectors (surface, dest, zoomFactor, drawShadow);
484 		if (data.isConnectorGraphic) return;
485 	}
486 
487 	// draw the shadows
488 	if (drawShadow)
489 	{
490 		SDL_Rect tmp = dest;
491 		if (alphaEffectValue && cSettings::getInstance().isAlphaEffects())
492 			SDL_SetSurfaceAlphaMod (uiData->shw.get(), alphaEffectValue / 5);
493 		else
494 			SDL_SetSurfaceAlphaMod (uiData->shw.get(), 50);
495 
496 		CHECK_SCALING (*uiData->shw, *uiData->shw_org, zoomFactor);
497 		blittAlphaSurface (uiData->shw.get(), nullptr, surface, &tmp);
498 	}
499 
500 	render_simple (surface, dest, zoomFactor, animationTime, alphaEffectValue && cSettings::getInstance().isAlphaEffects() ? alphaEffectValue : 254);
501 }
502 
503 //--------------------------------------------------------------------------
updateNeighbours(const cMap & map)504 void cBuilding::updateNeighbours (const cMap& map)
505 {
506 	if (!data.isBig)
507 	{
508 		getOwner()->base.checkNeighbour (getPosition() + cPosition (0, -1), *this);
509 		getOwner()->base.checkNeighbour (getPosition() + cPosition (1, 0), *this);
510 		getOwner()->base.checkNeighbour (getPosition() + cPosition (0, 1), *this);
511 		getOwner()->base.checkNeighbour (getPosition() + cPosition (-1, 0), *this);
512 	}
513 	else
514 	{
515 		getOwner()->base.checkNeighbour (getPosition() + cPosition (0, -1), *this);
516 		getOwner()->base.checkNeighbour (getPosition() + cPosition (1, -1), *this);
517 		getOwner()->base.checkNeighbour (getPosition() + cPosition (2, 0), *this);
518 		getOwner()->base.checkNeighbour (getPosition() + cPosition (2, 1), *this);
519 		getOwner()->base.checkNeighbour (getPosition() + cPosition (0, 2), *this);
520 		getOwner()->base.checkNeighbour (getPosition() + cPosition (1, 2), *this);
521 		getOwner()->base.checkNeighbour (getPosition() + cPosition (-1, 0), *this);
522 		getOwner()->base.checkNeighbour (getPosition() + cPosition (-1, 1), *this);
523 	}
524 	CheckNeighbours (map);
525 }
526 
527 //--------------------------------------------------------------------------
528 /** Checks, if there are neighbours */
529 //--------------------------------------------------------------------------
CheckNeighbours(const cMap & map)530 void cBuilding::CheckNeighbours (const cMap& map)
531 {
532 #define CHECK_NEIGHBOUR(x, y, m) \
533 	if (map.isValidPosition (cPosition(x, y))) \
534 	{ \
535 		const cBuilding* b = map.getField(cPosition(x, y)).getTopBuilding(); \
536 		if (b && b->getOwner() == getOwner() && b->data.connectsToBase) \
537 		{m = true;}else{m = false;} \
538 	}
539 
540 	if (!data.isBig)
541 	{
542 		CHECK_NEIGHBOUR (getPosition().x()    , getPosition().y() - 1, BaseN)
543 		CHECK_NEIGHBOUR (getPosition().x() + 1, getPosition().y()    , BaseE)
544 		CHECK_NEIGHBOUR (getPosition().x()    , getPosition().y() + 1, BaseS)
545 		CHECK_NEIGHBOUR (getPosition().x() - 1, getPosition().y()    , BaseW)
546 	}
547 	else
548 	{
549 		CHECK_NEIGHBOUR (getPosition().x()    , getPosition().y() - 1, BaseN)
550 		CHECK_NEIGHBOUR (getPosition().x() + 1, getPosition().y() - 1, BaseBN)
551 		CHECK_NEIGHBOUR (getPosition().x() + 2, getPosition().y()    , BaseE)
552 		CHECK_NEIGHBOUR (getPosition().x() + 2, getPosition().y() + 1, BaseBE)
553 		CHECK_NEIGHBOUR (getPosition().x()    , getPosition().y() + 2, BaseS)
554 		CHECK_NEIGHBOUR (getPosition().x() + 1, getPosition().y() + 2, BaseBS)
555 		CHECK_NEIGHBOUR (getPosition().x() - 1, getPosition().y()    , BaseW)
556 		CHECK_NEIGHBOUR (getPosition().x() - 1, getPosition().y() + 1, BaseBW)
557 	}
558 }
559 
560 //--------------------------------------------------------------------------
561 /** Draws the connectors at the building: */
562 //--------------------------------------------------------------------------
drawConnectors(SDL_Surface * surface,SDL_Rect dest,float zoomFactor,bool drawShadow) const563 void cBuilding::drawConnectors (SDL_Surface* surface, SDL_Rect dest, float zoomFactor, bool drawShadow) const
564 {
565 	SDL_Rect src, temp;
566 
567 	CHECK_SCALING (*UnitsData.ptr_connector, *UnitsData.ptr_connector_org, zoomFactor);
568 	CHECK_SCALING (*UnitsData.ptr_connector_shw, *UnitsData.ptr_connector_shw_org, zoomFactor);
569 
570 	if (alphaEffectValue) SDL_SetSurfaceAlphaMod (UnitsData.ptr_connector, alphaEffectValue);
571 	else SDL_SetSurfaceAlphaMod (UnitsData.ptr_connector, 254);
572 
573 	src.y = 0;
574 	src.x = 0;
575 	src.h = src.w = UnitsData.ptr_connector->h;
576 
577 	if (!data.isBig)
578 	{
579 		if (BaseN &&  BaseE &&  BaseS &&  BaseW) src.x = 15;
580 		else if (BaseN &&  BaseE &&  BaseS && !BaseW) src.x = 13;
581 		else if (BaseN &&  BaseE && !BaseS &&  BaseW) src.x = 12;
582 		else if (BaseN &&  BaseE && !BaseS && !BaseW) src.x =  8;
583 		else if (BaseN && !BaseE &&  BaseS &&  BaseW) src.x = 11;
584 		else if (BaseN && !BaseE &&  BaseS && !BaseW) src.x =  5;
585 		else if (BaseN && !BaseE && !BaseS &&  BaseW) src.x =  7;
586 		else if (BaseN && !BaseE && !BaseS && !BaseW) src.x =  1;
587 		else if (!BaseN &&  BaseE &&  BaseS &&  BaseW) src.x = 14;
588 		else if (!BaseN &&  BaseE &&  BaseS && !BaseW) src.x =  9;
589 		else if (!BaseN &&  BaseE && !BaseS &&  BaseW) src.x =  6;
590 		else if (!BaseN &&  BaseE && !BaseS && !BaseW) src.x =  2;
591 		else if (!BaseN && !BaseE &&  BaseS &&  BaseW) src.x = 10;
592 		else if (!BaseN && !BaseE &&  BaseS && !BaseW) src.x =  3;
593 		else if (!BaseN && !BaseE && !BaseS &&  BaseW) src.x =  4;
594 		else if (!BaseN && !BaseE && !BaseS && !BaseW) src.x =  0;
595 		src.x *= src.h;
596 
597 		if (src.x != 0 || data.isConnectorGraphic)
598 		{
599 			// blit shadow
600 			temp = dest;
601 			if (drawShadow) blittAlphaSurface (UnitsData.ptr_connector_shw, &src, surface, &temp);
602 			// blit the image
603 			temp = dest;
604 			SDL_BlitSurface (UnitsData.ptr_connector, &src, surface, &temp);
605 		}
606 	}
607 	else
608 	{
609 		// make connector stubs of big buildings.
610 		// upper left field
611 		src.x = 0;
612 		if (BaseN &&  BaseW) src.x = 7;
613 		else if (BaseN && !BaseW) src.x = 1;
614 		else if (!BaseN &&  BaseW) src.x = 4;
615 		src.x *= src.h;
616 
617 		if (src.x != 0)
618 		{
619 			temp = dest;
620 			if (drawShadow) blittAlphaSurface (UnitsData.ptr_connector_shw, &src, surface, &temp);
621 			temp = dest;
622 			SDL_BlitSurface (UnitsData.ptr_connector, &src, surface, &temp);
623 		}
624 
625 		// upper right field
626 		src.x = 0;
627 		dest.x += Round (64.0f * zoomFactor);
628 		if (BaseBN &&  BaseE) src.x = 8;
629 		else if (BaseBN && !BaseE) src.x = 1;
630 		else if (!BaseBN &&  BaseE) src.x = 2;
631 		src.x *= src.h;
632 
633 		if (src.x != 0)
634 		{
635 			temp = dest;
636 			if (drawShadow) blittAlphaSurface (UnitsData.ptr_connector_shw, &src, surface, &temp);
637 			temp = dest;
638 			SDL_BlitSurface (UnitsData.ptr_connector, &src, surface, &temp);
639 		}
640 
641 		// lower right field
642 		src.x = 0;
643 		dest.y += Round (64.0f * zoomFactor);
644 		if (BaseBE && BaseBS) src.x = 9;
645 		else if (BaseBE && !BaseBS) src.x = 2;
646 		else if (!BaseBE && BaseBS) src.x = 3;
647 		src.x *= src.h;
648 
649 		if (src.x != 0)
650 		{
651 			temp = dest;
652 			if (drawShadow) blittAlphaSurface (UnitsData.ptr_connector_shw, &src, surface, &temp);
653 			temp = dest;
654 			SDL_BlitSurface (UnitsData.ptr_connector, &src, surface, &temp);
655 		}
656 
657 		// lower left field
658 		src.x = 0;
659 		dest.x -= Round (64.0f * zoomFactor);
660 		if (BaseS && BaseBW) src.x = 10;
661 		else if (BaseS && !BaseBW) src.x = 3;
662 		else if (!BaseS && BaseBW) src.x = 4;
663 		src.x *= src.h;
664 
665 		if (src.x != 0)
666 		{
667 			temp = dest;
668 			if (drawShadow) blittAlphaSurface (UnitsData.ptr_connector_shw, &src, surface, &temp);
669 			temp = dest;
670 			SDL_BlitSurface (UnitsData.ptr_connector, &src, surface, &temp);
671 		}
672 	}
673 }
674 
675 //--------------------------------------------------------------------------
676 /** starts the building for the server thread */
677 //--------------------------------------------------------------------------
ServerStartWork(cServer & server)678 void cBuilding::ServerStartWork (cServer& server)
679 {
680 	if (isUnitWorking())
681 	{
682 		sendDoStartWork (server, *this);
683 		return;
684 	}
685 
686 	//-- first check all requirements
687 
688 	if (isDisabled())
689 	{
690 		sendSavedReport (server, cSavedReportSimple (eSavedReportType::BuildingDisabled), getOwner());
691 		return;
692 	}
693 
694 	// needs human workers:
695 	if (data.needsHumans)
696 	{
697 		if (SubBase->HumanNeed + data.needsHumans > SubBase->HumanProd)
698 		{
699 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::TeamInsufficient), getOwner());
700 			return;
701 		}
702 	}
703 
704 	// needs gold:
705 	if (data.convertsGold)
706 	{
707 		if (data.convertsGold + SubBase->GoldNeed > SubBase->getGoldProd() + SubBase->getGold())
708 		{
709 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::GoldInsufficient), getOwner());
710 			return;
711 		}
712 	}
713 
714 	// needs raw material:
715 	if (data.needsMetal)
716 	{
717 		if (SubBase->MetalNeed + std::min (getMetalPerRound(), buildList[0].getRemainingMetal()) > SubBase->getMetalProd() + SubBase->getMetal())
718 		{
719 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::MetalInsufficient), getOwner());
720 			return;
721 		}
722 	}
723 
724 	// needs oil:
725 	if (data.needsOil)
726 	{
727 		// check if there is enough Oil for the generators
728 		// (current production + reserves)
729 		if (data.needsOil + SubBase->OilNeed > SubBase->getOil() + SubBase->getMaxOilProd())
730 		{
731 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::FuelInsufficient), getOwner());
732 			return;
733 		}
734 		else if (data.needsOil + SubBase->OilNeed > SubBase->getOil() + SubBase->getOilProd())
735 		{
736 			// increase oil production
737 			int missingOil = data.needsOil + SubBase->OilNeed - (SubBase->getOil() + SubBase->getOilProd());
738 
739 			int metal = SubBase->getMetalProd();
740 			int gold = SubBase->getGoldProd();
741 
742 			// temporay decrease metal and gold production
743 			SubBase->setMetalProd (0);
744 			SubBase->setGoldProd (0);
745 
746 			SubBase->changeOilProd (missingOil);
747 
748 			SubBase->setGoldProd (gold);
749 			SubBase->setMetalProd (metal);
750 
751 			sendSavedReport (server, cSavedReportResourceChanged (RES_OIL, missingOil, true), getOwner());
752 			if (SubBase->getMetalProd() < metal)
753 				sendSavedReport (server, cSavedReportResourceChanged (RES_METAL, metal - SubBase->getMetalProd(), false), getOwner());
754 			if (SubBase->getGoldProd() < gold)
755 				sendSavedReport (server, cSavedReportResourceChanged (RES_GOLD, gold - SubBase->getGoldProd(), false), getOwner());
756 		}
757 	}
758 
759 	// IsWorking is set to true before checking the energy production.
760 	// So if an energy generator has to be started,
761 	// it can use the fuel production of this building
762 	// (when this building is a mine).
763 	setWorking (true);
764 
765 	// set mine values. This has to be undone, if the energy is insufficient
766 	if (data.canMineMaxRes > 0)
767 	{
768 		int mineFree = data.canMineMaxRes;
769 
770 		SubBase->changeMetalProd (MaxMetalProd);
771 		mineFree -= MaxMetalProd;
772 
773 		SubBase->changeGoldProd (min (MaxGoldProd, mineFree));
774 		mineFree -= min (MaxGoldProd, mineFree);
775 
776 		SubBase->changeOilProd (min (MaxOilProd, mineFree));
777 	}
778 
779 	// Energy consumers:
780 	if (data.needsEnergy)
781 	{
782 		if (data.needsEnergy + SubBase->EnergyNeed > SubBase->EnergyProd)
783 		{
784 			// try to increase energy production
785 			if (!SubBase->increaseEnergyProd (server, data.needsEnergy + SubBase->EnergyNeed - SubBase->EnergyProd))
786 			{
787 				setWorking (false);
788 
789 				// reset mine values
790 				if (data.canMineMaxRes > 0)
791 				{
792 					int metal = SubBase->getMetalProd();
793 					int oil =  SubBase->getOilProd();
794 					int gold = SubBase->getGoldProd();
795 
796 					SubBase->setMetalProd (0);
797 					SubBase->setOilProd (0);
798 					SubBase->setGoldProd (0);
799 
800 					SubBase->setMetalProd (min (metal, SubBase->getMaxAllowedMetalProd()));
801 					SubBase->setGoldProd (min (gold, SubBase->getMaxAllowedGoldProd()));
802 					SubBase->setOilProd (min (oil, SubBase->getMaxAllowedOilProd()));
803 				}
804 
805 				sendSavedReport (server, cSavedReportSimple (eSavedReportType::EnergyInsufficient), getOwner());
806 				return;
807 			}
808 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::EnergyToLow), getOwner());
809 		}
810 	}
811 
812 	//-- everything is ready to start the building
813 
814 	SubBase->EnergyProd += data.produceEnergy;
815 	SubBase->EnergyNeed += data.needsEnergy;
816 
817 	SubBase->HumanNeed += data.needsHumans;
818 	SubBase->HumanProd += data.produceHumans;
819 
820 	SubBase->OilNeed += data.needsOil;
821 
822 	// raw material consumer:
823 	if (data.needsMetal)
824 		SubBase->MetalNeed += std::min (getMetalPerRound(), buildList[0].getRemainingMetal());
825 
826 	// gold consumer:
827 	SubBase->GoldNeed += data.convertsGold;
828 
829 	// research building
830 	if (data.canResearch)
831 	{
832 		getOwner()->startAResearch (researchArea);
833 	}
834 
835 	if (data.canScore)
836 	{
837 		sendNumEcos (server, *getOwner());
838 	}
839 
840 	sendSubbaseValues (server, *SubBase, *getOwner());
841 	sendDoStartWork (server, *this);
842 }
843 
844 //------------------------------------------------------------
845 /** starts the building in the client thread */
846 //------------------------------------------------------------
clientStartWork()847 void cBuilding::clientStartWork()
848 {
849 	if (isUnitWorking())
850 		return;
851 	setWorking (true);
852 	if (data.canResearch)
853 		getOwner()->startAResearch (researchArea);
854 }
855 
856 //--------------------------------------------------------------------------
857 /** Stops the building's working in the server thread */
858 //--------------------------------------------------------------------------
ServerStopWork(cServer & server,bool override)859 void cBuilding::ServerStopWork (cServer& server, bool override)
860 {
861 	if (!isUnitWorking())
862 	{
863 		sendDoStopWork (server, *this);
864 		return;
865 	}
866 
867 	// energy generators
868 	if (data.produceEnergy)
869 	{
870 		if (SubBase->EnergyNeed > SubBase->EnergyProd - data.produceEnergy && !override)
871 		{
872 			sendSavedReport (server, cSavedReportSimple (eSavedReportType::EnergyIsNeeded), getOwner());
873 			return;
874 		}
875 
876 		SubBase->EnergyProd -= data.produceEnergy;
877 		SubBase->OilNeed -= data.needsOil;
878 	}
879 
880 	setWorking (false);
881 
882 	// Energy consumers:
883 	if (data.needsEnergy)
884 		SubBase->EnergyNeed -= data.needsEnergy;
885 
886 	// raw material consumer:
887 	if (data.needsMetal)
888 		SubBase->MetalNeed -= std::min (getMetalPerRound(), buildList[0].getRemainingMetal());
889 
890 	// gold consumer
891 	if (data.convertsGold)
892 		SubBase->GoldNeed -= data.convertsGold;
893 
894 	// human consumer
895 	if (data.needsHumans)
896 		SubBase->HumanNeed -= data.needsHumans;
897 
898 	// Minen:
899 	if (data.canMineMaxRes > 0)
900 	{
901 		int metal = SubBase->getMetalProd();
902 		int oil =  SubBase->getOilProd();
903 		int gold = SubBase->getGoldProd();
904 
905 		SubBase->setMetalProd (0);
906 		SubBase->setOilProd (0);
907 		SubBase->setGoldProd (0);
908 
909 		SubBase->setMetalProd (min (metal, SubBase->getMaxAllowedMetalProd()));
910 		SubBase->setGoldProd (min (gold, SubBase->getMaxAllowedGoldProd()));
911 		SubBase->setOilProd (min (oil, SubBase->getMaxAllowedOilProd()));
912 	}
913 
914 	if (data.canResearch)
915 	{
916 		getOwner()->stopAResearch (researchArea);
917 	}
918 
919 	if (data.canScore)
920 	{
921 		sendNumEcos (server, *getOwner());
922 	}
923 
924 	sendSubbaseValues (server, *SubBase, *getOwner());
925 	sendDoStopWork (server, *this);
926 }
927 
928 //------------------------------------------------------------
929 /** stops the building in the client thread */
930 //------------------------------------------------------------
clientStopWork()931 void cBuilding::clientStopWork()
932 {
933 	if (!isUnitWorking())
934 		return;
935 	setWorking (false);
936 	if (data.canResearch)
937 		getOwner()->stopAResearch (researchArea);
938 }
939 
940 //------------------------------------------------------------
canTransferTo(const cPosition & position,const cMapField & overUnitField) const941 bool cBuilding::canTransferTo (const cPosition& position, const cMapField& overUnitField) const
942 {
943 	if (overUnitField.getVehicle())
944 	{
945 		const cVehicle* v = overUnitField.getVehicle();
946 
947 		if (v->getOwner() != this->getOwner())
948 			return false;
949 
950 		if (v->data.storeResType != data.storeResType)
951 			return false;
952 
953 		if (v->isUnitBuildingABuilding() || v->isUnitClearing())
954 			return false;
955 
956 		for (size_t i = 0; i != SubBase->buildings.size(); ++i)
957 		{
958 			const cBuilding* b = SubBase->buildings[i];
959 
960 			if (b->isNextTo (position)) return true;
961 		}
962 
963 		return false;
964 	}
965 	else if (overUnitField.getTopBuilding())
966 	{
967 		const cBuilding* b = overUnitField.getTopBuilding();
968 
969 		if (b == this)
970 			return false;
971 
972 		if (b->SubBase != SubBase)
973 			return false;
974 
975 		if (b->getOwner() != this->getOwner())
976 			return false;
977 
978 		if (data.storeResType != b->data.storeResType)
979 			return false;
980 
981 		return true;
982 	}
983 	return false;
984 }
985 
986 //--------------------------------------------------------------------------
canExitTo(const cPosition & position,const cMap & map,const sUnitData & vehicleData) const987 bool cBuilding::canExitTo (const cPosition& position, const cMap& map, const sUnitData& vehicleData) const
988 {
989 	if (!map.possiblePlaceVehicle (vehicleData, position, getOwner())) return false;
990 	if (!isNextTo (position)) return false;
991 
992 	return true;
993 }
994 
995 //--------------------------------------------------------------------------
canLoad(const cPosition & position,const cMap & map,bool checkPosition) const996 bool cBuilding::canLoad (const cPosition& position, const cMap& map, bool checkPosition) const
997 {
998 	if (map.isValidPosition (position) == false) return false;
999 
1000 	if (canLoad (map.getField (position).getPlane(), checkPosition)) return true;
1001 	else return canLoad (map.getField (position).getVehicle(), checkPosition);
1002 }
1003 
1004 //--------------------------------------------------------------------------
1005 /** returns, if the vehicle can be loaded from its position: */
1006 //--------------------------------------------------------------------------
canLoad(const cVehicle * Vehicle,bool checkPosition) const1007 bool cBuilding::canLoad (const cVehicle* Vehicle, bool checkPosition) const
1008 {
1009 	if (!Vehicle) return false;
1010 
1011 	if (Vehicle->isUnitLoaded()) return false;
1012 
1013 	if (data.getStoredUnits() == data.storageUnitsMax) return false;
1014 
1015 	if (checkPosition && !isNextTo (Vehicle->getPosition())) return false;
1016 
1017 	if (!Contains (data.storeUnitsTypes, Vehicle->data.isStorageType)) return false;
1018 
1019 	if (Vehicle->getClientMoveJob() && (Vehicle->isUnitMoving() || Vehicle->isAttacking() || Vehicle->MoveJobActive)) return false;
1020 
1021 	if (Vehicle->getOwner() != getOwner() || Vehicle->isUnitBuildingABuilding() || Vehicle->isUnitClearing()) return false;
1022 
1023 	if (Vehicle->isBeeingAttacked()) return false;
1024 
1025 	return true;
1026 }
1027 
1028 //--------------------------------------------------------------------------
1029 /** loads a vehicle: */
1030 //--------------------------------------------------------------------------
storeVehicle(cVehicle & vehicle,cMap & map)1031 void cBuilding::storeVehicle (cVehicle& vehicle, cMap& map)
1032 {
1033 	map.deleteVehicle (vehicle);
1034 	if (vehicle.isSentryActive())
1035 	{
1036 		vehicle.getOwner()->deleteSentry (vehicle);
1037 	}
1038 
1039 	vehicle.setLoaded (true);
1040 	vehicle.setIsBeeinAttacked (false);
1041 
1042 	storedUnits.push_back (&vehicle);
1043 	data.setStoredUnits (data.getStoredUnits() + 1);
1044 
1045 	getOwner()->doScan();
1046 }
1047 
1048 //-----------------------------------------------------------------------
1049 // Unloads a vehicle
exitVehicleTo(cVehicle & vehicle,const cPosition & position,cMap & map)1050 void cBuilding::exitVehicleTo (cVehicle& vehicle, const cPosition& position, cMap& map)
1051 {
1052 	Remove (storedUnits, &vehicle);
1053 
1054 	data.setStoredUnits (data.getStoredUnits() - 1);
1055 
1056 	map.addVehicle (vehicle, position);
1057 
1058 	vehicle.setPosition (position);
1059 
1060 	vehicle.setLoaded (false);
1061 
1062 	getOwner()->doScan();
1063 }
1064 
1065 //-------------------------------------------------------------------------------
1066 // Draws big symbols for the info menu:
1067 //-------------------------------------------------------------------------------
DrawSymbolBig(eSymbolsBig sym,int x,int y,int maxx,int value,int orgvalue,SDL_Surface * sf)1068 void cBuilding::DrawSymbolBig (eSymbolsBig sym, int x, int y, int maxx, int value, int orgvalue, SDL_Surface* sf)
1069 {
1070 	SDL_Rect src = {0, 0, 0, 0};
1071 
1072 	switch (sym)
1073 	{
1074 		case SBSpeed:
1075 			src.x = 0;
1076 			src.y = 109;
1077 			src.w = 11;
1078 			src.h = 12;
1079 			break;
1080 
1081 		case SBHits:
1082 			src.x = 11;
1083 			src.y = 109;
1084 			src.w = 7;
1085 			src.h = 11;
1086 			break;
1087 
1088 		case SBAmmo:
1089 			src.x = 18;
1090 			src.y = 109;
1091 			src.w = 9;
1092 			src.h = 14;
1093 			break;
1094 
1095 		case SBAttack:
1096 			src.x = 27;
1097 			src.y = 109;
1098 			src.w = 10;
1099 			src.h = 14;
1100 			break;
1101 
1102 		case SBShots:
1103 			src.x = 37;
1104 			src.y = 109;
1105 			src.w = 15;
1106 			src.h = 7;
1107 			break;
1108 
1109 		case SBRange:
1110 			src.x = 52;
1111 			src.y = 109;
1112 			src.w = 13;
1113 			src.h = 13;
1114 			break;
1115 
1116 		case SBArmor:
1117 			src.x = 65;
1118 			src.y = 109;
1119 			src.w = 11;
1120 			src.h = 14;
1121 			break;
1122 
1123 		case SBScan:
1124 			src.x = 76;
1125 			src.y = 109;
1126 			src.w = 13;
1127 			src.h = 13;
1128 			break;
1129 
1130 		case SBMetal:
1131 			src.x = 89;
1132 			src.y = 109;
1133 			src.w = 12;
1134 			src.h = 15;
1135 			break;
1136 
1137 		case SBOil:
1138 			src.x = 101;
1139 			src.y = 109;
1140 			src.w = 11;
1141 			src.h = 12;
1142 			break;
1143 
1144 		case SBGold:
1145 			src.x = 112;
1146 			src.y = 109;
1147 			src.w = 13;
1148 			src.h = 10;
1149 			break;
1150 
1151 		case SBEnergy:
1152 			src.x = 125;
1153 			src.y = 109;
1154 			src.w = 13;
1155 			src.h = 17;
1156 			break;
1157 
1158 		case SBHuman:
1159 			src.x = 138;
1160 			src.y = 109;
1161 			src.w = 12;
1162 			src.h = 16;
1163 			break;
1164 	}
1165 
1166 	maxx -= src.w;
1167 
1168 	if (orgvalue < value)
1169 	{
1170 		maxx -= src.w + 3;
1171 	}
1172 
1173 	int offx = src.w;
1174 
1175 	while (offx * value >= maxx)
1176 	{
1177 		offx--;
1178 
1179 		if (offx < 4)
1180 		{
1181 			value /= 2;
1182 			orgvalue /= 2;
1183 			offx = src.w;
1184 		}
1185 	}
1186 
1187 	SDL_Rect dest;
1188 	dest.x = x;
1189 	dest.y = y;
1190 
1191 	Uint32 color = SDL_MapRGB (sf->format, 0xFC, 0, 0);
1192 	for (int i = 0; i < value; i++)
1193 	{
1194 		if (i == orgvalue)
1195 		{
1196 			SDL_Rect mark;
1197 			dest.x += src.w + 3;
1198 			mark.x = dest.x - src.w / 2;
1199 			mark.y = dest.y;
1200 			mark.w = 1;
1201 			mark.h = src.h;
1202 			SDL_FillRect (sf, &mark, color);
1203 		}
1204 
1205 		SDL_BlitSurface (GraphicsData.gfx_hud_stuff.get(), &src, sf, &dest);
1206 
1207 		dest.x += offx;
1208 	}
1209 }
1210 
1211 //-------------------------------------------------------------------------------
1212 /** checks the resources that are available under the mining station */
1213 //--------------------------------------------------------------------------
1214 
CheckRessourceProd(const cServer & server)1215 void cBuilding::CheckRessourceProd (const cServer& server)
1216 {
1217 	auto position = getPosition();
1218 
1219 	MaxMetalProd = 0;
1220 	MaxGoldProd = 0;
1221 	MaxOilProd = 0;
1222 	const sResources* res = &server.Map->getResource (position);
1223 
1224 	switch (res->typ)
1225 	{
1226 		case RES_METAL: MaxMetalProd += res->value; break;
1227 		case RES_GOLD:  MaxGoldProd  += res->value; break;
1228 		case RES_OIL:   MaxOilProd   += res->value; break;
1229 	}
1230 
1231 	position.x()++;
1232 	res = &server.Map->getResource (position);
1233 	switch (res->typ)
1234 	{
1235 		case RES_METAL: MaxMetalProd += res->value; break;
1236 		case RES_GOLD:  MaxGoldProd  += res->value; break;
1237 		case RES_OIL:   MaxOilProd   += res->value; break;
1238 	}
1239 
1240 	position.y()++;
1241 	res = &server.Map->getResource (position);
1242 	switch (res->typ)
1243 	{
1244 		case RES_METAL: MaxMetalProd += res->value; break;
1245 		case RES_GOLD:  MaxGoldProd  += res->value; break;
1246 		case RES_OIL:   MaxOilProd   += res->value; break;
1247 	}
1248 
1249 	position.x()--;
1250 	res = &server.Map->getResource (position);
1251 	switch (res->typ)
1252 	{
1253 		case RES_METAL: MaxMetalProd += res->value; break;
1254 		case RES_GOLD:  MaxGoldProd  += res->value; break;
1255 		case RES_OIL:   MaxOilProd   += res->value; break;
1256 	}
1257 
1258 	MaxMetalProd = min (MaxMetalProd, data.canMineMaxRes);
1259 	MaxGoldProd  = min (MaxGoldProd,  data.canMineMaxRes);
1260 	MaxOilProd   = min (MaxOilProd,   data.canMineMaxRes);
1261 }
1262 
1263 //--------------------------------------------------------------------------
1264 /** calculates the costs and the duration of the 3 buildspeeds
1265  * for the vehicle with the given base costs
1266  * iRemainingMetal is only needed for recalculating costs of vehicles
1267  * in the Buildqueue and is set per default to -1 */
1268 //--------------------------------------------------------------------------
calcTurboBuild(std::array<int,3> & turboBuildRounds,std::array<int,3> & turboBuildCosts,int vehicleCosts,int remainingMetal) const1269 void cBuilding::calcTurboBuild (std::array<int, 3>& turboBuildRounds, std::array<int, 3>& turboBuildCosts, int vehicleCosts, int remainingMetal) const
1270 {
1271 	// first calc costs for a new Vehical
1272 
1273 	// 1x
1274 	turboBuildCosts[0] = vehicleCosts;
1275 
1276 	// 2x
1277 	int a = turboBuildCosts[0];
1278 	turboBuildCosts[1] = turboBuildCosts[0];
1279 
1280 	while (a >= 2 * data.needsMetal)
1281 	{
1282 		turboBuildCosts[1] += 2 * data.needsMetal;
1283 		a -= 2 * data.needsMetal;
1284 	}
1285 
1286 	// 4x
1287 	turboBuildCosts[2] = turboBuildCosts[1];
1288 	a = turboBuildCosts[1];
1289 
1290 	while (a >= 15)
1291 	{
1292 		turboBuildCosts[2] += (12 * data.needsMetal - min (a, 8 * data.needsMetal));
1293 		a -= 8 * data.needsMetal;
1294 	}
1295 
1296 	// now this is a litle bit tricky ...
1297 	// trying to calculate a plausible value,
1298 	// if we are changing the speed of an already started build-job
1299 	if (remainingMetal >= 0)
1300 	{
1301 		float WorkedRounds;
1302 
1303 		switch (buildSpeed)  // BuildSpeed here is the previous build speed
1304 		{
1305 			case 0:
1306 				WorkedRounds = (turboBuildCosts[0] - remainingMetal) / (1.f * data.needsMetal);
1307 				turboBuildCosts[0] -= (int) (1     *  1 * data.needsMetal * WorkedRounds);
1308 				turboBuildCosts[1] -= (int) (0.5f  *  4 * data.needsMetal * WorkedRounds);
1309 				turboBuildCosts[2] -= (int) (0.25f * 12 * data.needsMetal * WorkedRounds);
1310 				break;
1311 
1312 			case 1:
1313 				WorkedRounds = (turboBuildCosts[1] - remainingMetal) / (float) (4 * data.needsMetal);
1314 				turboBuildCosts[0] -= (int) (2    *  1 * data.needsMetal * WorkedRounds);
1315 				turboBuildCosts[1] -= (int) (1    *  4 * data.needsMetal * WorkedRounds);
1316 				turboBuildCosts[2] -= (int) (0.5f * 12 * data.needsMetal * WorkedRounds);
1317 				break;
1318 
1319 			case 2:
1320 				WorkedRounds = (turboBuildCosts[2] - remainingMetal) / (float) (12 * data.needsMetal);
1321 				turboBuildCosts[0] -= (int) (4 *  1 * data.needsMetal * WorkedRounds);
1322 				turboBuildCosts[1] -= (int) (2 *  4 * data.needsMetal * WorkedRounds);
1323 				turboBuildCosts[2] -= (int) (1 * 12 * data.needsMetal * WorkedRounds);
1324 				break;
1325 		}
1326 	}
1327 
1328 	// calc needed Rounds
1329 	turboBuildRounds[0] = (int) ceilf (turboBuildCosts[0] / (1.f * data.needsMetal));
1330 
1331 	if (data.maxBuildFactor > 1)
1332 	{
1333 		turboBuildRounds[1] = (int) ceilf (turboBuildCosts[1] / (4.f * data.needsMetal));
1334 		turboBuildRounds[2] = (int) ceilf (turboBuildCosts[2] / (12.f * data.needsMetal));
1335 	}
1336 	else
1337 	{
1338 		turboBuildRounds[1] = 0;
1339 		turboBuildRounds[2] = 0;
1340 	}
1341 }
1342 
1343 //--------------------------------------------------------------------------
isDetectedByPlayer(const cPlayer * player) const1344 bool cBuilding::isDetectedByPlayer (const cPlayer* player) const
1345 {
1346 	return Contains (detectedByPlayerList, player);
1347 }
1348 
1349 //--------------------------------------------------------------------------
setDetectedByPlayer(cServer & server,cPlayer * player,bool addToDetectedInThisTurnList)1350 void cBuilding::setDetectedByPlayer (cServer& server, cPlayer* player, bool addToDetectedInThisTurnList)
1351 {
1352 	if (!isDetectedByPlayer (player))
1353 		detectedByPlayerList.push_back (player);
1354 }
1355 
1356 //--------------------------------------------------------------------------
resetDetectedByPlayer(const cPlayer * player)1357 void cBuilding::resetDetectedByPlayer (const cPlayer* player)
1358 {
1359 	Remove (detectedByPlayerList, player);
1360 }
1361 
1362 //--------------------------------------------------------------------------
makeDetection(cServer & server)1363 void cBuilding::makeDetection (cServer& server)
1364 {
1365 	// check whether the building has been detected by others
1366 	if (data.isStealthOn == TERRAIN_NONE) return;
1367 
1368 	if (data.isStealthOn & AREA_EXP_MINE)
1369 	{
1370 		auto& playerList = server.playerList;
1371 		for (unsigned int i = 0; i < playerList.size(); i++)
1372 		{
1373 			auto& player = *playerList[i];
1374 			if (&player == getOwner()) continue;
1375 			if (player.hasMineDetection (getPosition()))
1376 			{
1377 				setDetectedByPlayer (server, &player);
1378 			}
1379 		}
1380 	}
1381 }
1382 
1383 //--------------------------------------------------------------------------
sBuildingUIData()1384 sBuildingUIData::sBuildingUIData()
1385 {}
1386 
1387 //--------------------------------------------------------------------------
sBuildingUIData(sBuildingUIData && other)1388 sBuildingUIData::sBuildingUIData (sBuildingUIData&& other) :
1389 	img (std::move (other.img)), img_org (std::move (other.img_org)),
1390 	shw (std::move (other.shw)), shw_org (std::move (other.shw_org)),
1391 	eff (std::move (other.eff)), eff_org (std::move (other.eff_org)),
1392 	video (std::move (other.video)),
1393 	info (std::move (other.info)),
1394 	Start (std::move (other.Start)),
1395 	Running (std::move (other.Running)),
1396 	Stop (std::move (other.Stop)),
1397 	Attack (std::move (other.Attack)),
1398 	Wait (std::move (other.Wait))
1399 {}
1400 
1401 //--------------------------------------------------------------------------
operator =(sBuildingUIData && other)1402 sBuildingUIData& sBuildingUIData::operator= (sBuildingUIData && other)
1403 {
1404 	img = std::move (other.img);
1405 	img_org = std::move (other.img_org);
1406 	shw = std::move (other.shw);
1407 	shw_org = std::move (other.shw_org);
1408 	eff = std::move (other.eff);
1409 	eff_org = std::move (other.eff_org);
1410 	video = std::move (other.video);
1411 	info = std::move (other.info);
1412 
1413 	Start = std::move (other.Start);
1414 	Running = std::move (other.Running);
1415 	Stop = std::move (other.Stop);
1416 	Attack = std::move (other.Attack);
1417 	Wait = std::move (other.Wait);
1418 	return *this;
1419 }
1420 
1421 //--------------------------------------------------------------------------
scaleSurfaces(float factor)1422 void sBuildingUIData::scaleSurfaces (float factor)
1423 {
1424 	scaleSurface (img_org.get(), img.get(), (int) (img_org->w * factor), (int) (img_org->h * factor));
1425 	scaleSurface (shw_org.get(), shw.get(), (int) (shw_org->w * factor), (int) (shw_org->h * factor));
1426 	if (eff_org) scaleSurface (eff_org.get(), eff.get(), (int) (eff_org->w * factor), (int) (eff_org->h * factor));
1427 }
1428 
1429 //-----------------------------------------------------------------------------
1430 //-----------------------------------------------------------------------------
1431 //-- methods, that already have been extracted as part of cUnit refactoring
1432 //-----------------------------------------------------------------------------
1433 //-----------------------------------------------------------------------------
1434 
1435 //-----------------------------------------------------------------------------
factoryHasJustFinishedBuilding() const1436 bool cBuilding::factoryHasJustFinishedBuilding() const
1437 {
1438 	return (!buildList.empty() && isUnitWorking() == false && buildList[0].getRemainingMetal() <= 0);
1439 }
1440 
1441 //-----------------------------------------------------------------------------
executeStopCommand(const cClient & client) const1442 void cBuilding::executeStopCommand (const cClient& client) const
1443 {
1444 	sendWantStopWork (client, *this);
1445 }
1446 
1447 //-----------------------------------------------------------------------------
executeUpdateBuildingCommmand(const cClient & client,bool updateAllOfSameType) const1448 void cBuilding::executeUpdateBuildingCommmand (const cClient& client, bool updateAllOfSameType) const
1449 {
1450 	sendUpgradeBuilding (client, *this, updateAllOfSameType);
1451 }
1452 
1453 //-----------------------------------------------------------------------------
buildingCanBeStarted() const1454 bool cBuilding::buildingCanBeStarted() const
1455 {
1456 	return (data.canWork && isUnitWorking() == false
1457 			&& (!buildList.empty() || data.canBuild.empty()));
1458 }
1459 
1460 //-----------------------------------------------------------------------------
buildingCanBeUpgraded() const1461 bool cBuilding::buildingCanBeUpgraded() const
1462 {
1463 	const sUnitData& upgraded = *getOwner()->getUnitDataCurrentVersion (data.ID);
1464 	return (data.getVersion() != upgraded.getVersion() && SubBase && SubBase->getMetal() >= 2);
1465 }
1466 
1467 //-----------------------------------------------------------------------------
isBuildListEmpty() const1468 bool cBuilding::isBuildListEmpty() const
1469 {
1470 	return buildList.empty();
1471 }
1472 
1473 //-----------------------------------------------------------------------------
getBuildListSize() const1474 size_t cBuilding::getBuildListSize() const
1475 {
1476 	return buildList.size();
1477 }
1478 
1479 //-----------------------------------------------------------------------------
getBuildListItem(size_t index) const1480 const cBuildListItem& cBuilding::getBuildListItem (size_t index) const
1481 {
1482 	return buildList[index];
1483 }
1484 
1485 //-----------------------------------------------------------------------------
getBuildListItem(size_t index)1486 cBuildListItem& cBuilding::getBuildListItem (size_t index)
1487 {
1488 	return buildList[index];
1489 }
1490 
1491 //-----------------------------------------------------------------------------
setBuildList(std::vector<cBuildListItem> buildList_)1492 void cBuilding::setBuildList (std::vector<cBuildListItem> buildList_)
1493 {
1494 	buildList = std::move (buildList_);
1495 
1496 	buildListFirstItemSignalConnectionManager.disconnectAll();
1497 	if (!buildList.empty())
1498 	{
1499 		buildListFirstItemSignalConnectionManager.connect (buildList[0].remainingMetalChanged, [this]() { buildListFirstItemDataChanged(); });
1500 		buildListFirstItemSignalConnectionManager.connect (buildList[0].typeChanged, [this]() { buildListFirstItemDataChanged(); });
1501 	}
1502 
1503 	buildListChanged();
1504 }
1505 
1506 //-----------------------------------------------------------------------------
addBuildListItem(cBuildListItem item)1507 void cBuilding::addBuildListItem (cBuildListItem item)
1508 {
1509 	buildList.push_back (std::move (item));
1510 
1511 	buildListFirstItemSignalConnectionManager.disconnectAll();
1512 	if (!buildList.empty())
1513 	{
1514 		buildListFirstItemSignalConnectionManager.connect (buildList[0].remainingMetalChanged, [this]() { buildListFirstItemDataChanged(); });
1515 		buildListFirstItemSignalConnectionManager.connect (buildList[0].typeChanged, [this]() { buildListFirstItemDataChanged(); });
1516 	}
1517 
1518 	buildListChanged();
1519 }
1520 
1521 //-----------------------------------------------------------------------------
removeBuildListItem(size_t index)1522 void cBuilding::removeBuildListItem (size_t index)
1523 {
1524 	buildList.erase (buildList.begin() + index);
1525 
1526 	buildListFirstItemSignalConnectionManager.disconnectAll();
1527 	if (!buildList.empty())
1528 	{
1529 		buildListFirstItemSignalConnectionManager.connect (buildList[0].remainingMetalChanged, [this]() { buildListFirstItemDataChanged(); });
1530 		buildListFirstItemSignalConnectionManager.connect (buildList[0].typeChanged, [this]() { buildListFirstItemDataChanged(); });
1531 	}
1532 
1533 	buildListChanged();
1534 }
1535 
1536 //-----------------------------------------------------------------------------
getBuildSpeed() const1537 int cBuilding::getBuildSpeed() const
1538 {
1539 	return buildSpeed;
1540 }
1541 
1542 //-----------------------------------------------------------------------------
getMetalPerRound() const1543 int cBuilding::getMetalPerRound() const
1544 {
1545 	return metalPerRound;
1546 }
1547 
1548 //-----------------------------------------------------------------------------
getRepeatBuild() const1549 bool cBuilding::getRepeatBuild() const
1550 {
1551 	return repeatBuild;
1552 }
1553 
1554 //-----------------------------------------------------------------------------
setWorking(bool value)1555 void cBuilding::setWorking (bool value)
1556 {
1557 	std::swap (isWorking, value);
1558 	if (value != isWorking) workingChanged();
1559 }
1560 
1561 //-----------------------------------------------------------------------------
setBuildSpeed(int value)1562 void cBuilding::setBuildSpeed(int value)
1563 {
1564 	std::swap(buildSpeed, value);
1565 	if(value != buildSpeed) buildSpeedChanged();
1566 }
1567 
1568 //-----------------------------------------------------------------------------
setMetalPerRound(int value)1569 void cBuilding::setMetalPerRound(int value)
1570 {
1571 	std::swap(metalPerRound, value);
1572 	if(value != metalPerRound) metalPerRoundChanged();
1573 }
1574 
1575 //-----------------------------------------------------------------------------
setRepeatBuild(bool value)1576 void cBuilding::setRepeatBuild(bool value)
1577 {
1578 	std::swap(repeatBuild, value);
1579 	if(value != repeatBuild) repeatBuildChanged();
1580 }
1581 
1582 //-----------------------------------------------------------------------------
setResearchArea(cResearch::ResearchArea area)1583 void cBuilding::setResearchArea (cResearch::ResearchArea area)
1584 {
1585 	std::swap (researchArea, area);
1586 	if (researchArea != area) researchAreaChanged();
1587 }
1588 
1589 //-----------------------------------------------------------------------------
getResearchArea() const1590 cResearch::ResearchArea cBuilding::getResearchArea() const
1591 {
1592 	return researchArea;
1593 }
1594 
1595 //-----------------------------------------------------------------------------
registerOwnerEvents()1596 void cBuilding::registerOwnerEvents()
1597 {
1598 	ownerSignalConnectionManager.disconnectAll();
1599 
1600 	if (getOwner() == nullptr) return;
1601 
1602 	if (data.convertsGold)
1603 	{
1604 		ownerSignalConnectionManager.connect (getOwner()->creditsChanged, [&]() { statusChanged(); });
1605 	}
1606 
1607 	if (data.canResearch)
1608 	{
1609 		ownerSignalConnectionManager.connect (getOwner()->researchCentersWorkingOnAreaChanged, [&] (cResearch::ResearchArea) { statusChanged(); });
1610 		ownerSignalConnectionManager.connect (getOwner()->getResearchState().neededResearchPointsChanged, [&] (cResearch::ResearchArea) { statusChanged(); });
1611 		ownerSignalConnectionManager.connect (getOwner()->getResearchState().currentResearchPointsChanged, [&] (cResearch::ResearchArea) { statusChanged(); });
1612 	}
1613 }
1614