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 
20 #include "ui/graphical/menu/dialogs/dialogtransfer.h"
21 
22 #include "ui/graphical/menu/dialogs/dialogok.h"
23 #include "ui/graphical/menu/widgets/label.h"
24 #include "ui/graphical/menu/widgets/pushbutton.h"
25 #include "ui/graphical/menu/widgets/image.h"
26 #include "ui/graphical/application.h"
27 #include "pcx.h"
28 #include "main.h"
29 #include "game/data/units/unit.h"
30 #include "game/data/units/building.h"
31 #include "game/data/units/vehicle.h"
32 #include "game/data/map/map.h"
33 #include "video.h"
34 #include "game/data/base/base.h"
35 
36 //------------------------------------------------------------------------------
cNewDialogTransfer(const cUnit & sourceUnit,const cUnit & destinationUnit)37 cNewDialogTransfer::cNewDialogTransfer (const cUnit& sourceUnit, const cUnit& destinationUnit) :
38 	cWindow (LoadPCX (GFXOD_DIALOG_TRANSFER), eWindowBackgrounds::Alpha),
39 	resourceType (getCommonResourceType (sourceUnit, destinationUnit))
40 {
41 	transferLabel = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (142, 48), getPosition() + cPosition (142 + 32, 48 + 15)), "", FONT_LATIN_BIG, eAlignmentType::CenterHorizontal));
42 
43 	arrowImage = addChild (std::make_unique<cImage> (getPosition() + cPosition (140, 77)));
44 
45 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (20, 105), getPosition() + cPosition (40 + 80, 105 + 10)), sourceUnit.data.name, FONT_LATIN_SMALL_WHITE, eAlignmentType::CenterHorizontal));
46 	addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (190, 105), getPosition() + cPosition (210 + 80, 105 + 10)), destinationUnit.data.name, FONT_LATIN_SMALL_WHITE, eAlignmentType::CenterHorizontal));
47 
48 	sourceUnitCargoLabel = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (18, 60), getPosition() + cPosition (18 + 20, 60 + 10)), "", FONT_LATIN_SMALL_WHITE, eAlignmentType::CenterHorizontal));
49 	destinationUnitCargoLabel = addChild (std::make_unique<cLabel> (cBox<cPosition> (getPosition() + cPosition (272, 60), getPosition() + cPosition (272 + 20, 60 + 10)), "", FONT_LATIN_SMALL_WHITE, eAlignmentType::CenterHorizontal));
50 
51 	auto sourceUnitImage = addChild (std::make_unique<cImage> (getPosition() + cPosition (39, 26)));
52 	auto destinationUnitImage = addChild (std::make_unique<cImage> (getPosition() + cPosition (208, 26)));
53 
54 	resourceBar = addChild (std::make_unique<cResourceBar> (cBox<cPosition> (getPosition() + cPosition (43, 159), getPosition() + cPosition (43 + 223, 159 + 16)), 0, 100, getResourceBarType (sourceUnit, destinationUnit), eOrientationType::Horizontal));
55 	signalConnectionManager.connect (resourceBar->valueChanged, std::bind (&cNewDialogTransfer::transferValueChanged, this));
56 	auto increaseButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (279, 159), ePushButtonType::ArrowRightSmall));
57 	signalConnectionManager.connect (increaseButton->clicked, [&]() { resourceBar->increase (1); });
58 	auto decreaseButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (17, 159), ePushButtonType::ArrowLeftSmall));
59 	signalConnectionManager.connect (decreaseButton->clicked, [&]() { resourceBar->decrease (1); });
60 
61 	auto doneButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (159, 200), ePushButtonType::Angular, lngPack.i18n ("Text~Others~Done"), FONT_LATIN_NORMAL));
62 	doneButton->addClickShortcut (cKeySequence (cKeyCombination (eKeyModifierType::None, SDLK_RETURN)));
63 	signalConnectionManager.connect (doneButton->clicked, [&]() { done(); });
64 
65 	auto cancelButton = addChild (std::make_unique<cPushButton> (getPosition() + cPosition (71, 200), ePushButtonType::Angular, lngPack.i18n ("Text~Others~Cancel"), FONT_LATIN_NORMAL));
66 	cancelButton->addClickShortcut (cKeySequence (cKeyCombination (eKeyModifierType::None, SDLK_ESCAPE)));
67 	signalConnectionManager.connect (cancelButton->clicked, [&]() { close(); });
68 
69 	initUnitImage (*sourceUnitImage, sourceUnit);
70 	initUnitImage (*destinationUnitImage, destinationUnit);
71 
72 	initCargo (sourceCargo, sourceMaxCargo, sourceUnit, destinationUnit);
73 	initCargo (destinationCargo, destinationMaxCargo, destinationUnit, sourceUnit);
74 
75 	resourceBar->setMinValue (0);
76 	resourceBar->setMaxValue (destinationMaxCargo);
77 
78 	auto maxTransferToDestination = std::min (destinationCargo + sourceCargo, destinationMaxCargo) - destinationCargo;
79 	auto maxTransferToSource = std::min (sourceCargo + destinationCargo, sourceMaxCargo) - sourceCargo;
80 
81 	resourceBar->setFixedMinValue (destinationCargo - maxTransferToSource);
82 	resourceBar->setFixedMaxValue (destinationCargo + maxTransferToDestination);
83 
84 	resourceBar->setValue (destinationCargo + maxTransferToDestination);
85 
86 	transferValueChanged();
87 
88 	signalConnectionManager.connect (sourceUnit.destroyed, std::bind (&cNewDialogTransfer::closeOnUnitDestruction, this));
89 	signalConnectionManager.connect (destinationUnit.destroyed, std::bind (&cNewDialogTransfer::closeOnUnitDestruction, this));
90 }
91 
92 //------------------------------------------------------------------------------
getCommonResourceType(const cUnit & sourceUnit,const cUnit & destinationUnit) const93 sUnitData::eStorageResType cNewDialogTransfer::getCommonResourceType (const cUnit& sourceUnit, const cUnit& destinationUnit) const
94 {
95 	sUnitData::eStorageResType commonResourceType = sUnitData::STORE_RES_NONE;
96 	const auto sourceResource = sourceUnit.data.storeResType;
97 	const auto destinationResource = destinationUnit.data.storeResType;
98 	if (sourceResource == destinationResource)
99 	{
100 		commonResourceType = destinationResource;
101 	}
102 	else
103 	{
104 		if (sourceUnit.isAVehicle() && destinationUnit.isABuilding()) commonResourceType = sourceResource;
105 		if (sourceUnit.isABuilding() && destinationUnit.isAVehicle()) commonResourceType = destinationResource;
106 	}
107 
108 	return commonResourceType;
109 }
110 
111 //------------------------------------------------------------------------------
getResourceBarType(const cUnit & sourceUnit,const cUnit & destinationUnit) const112 eResourceBarType cNewDialogTransfer::getResourceBarType (const cUnit& sourceUnit, const cUnit& destinationUnit) const
113 {
114 	switch (getCommonResourceType (sourceUnit, destinationUnit))
115 	{
116 		default:
117 		case sUnitData::STORE_RES_METAL:
118 			return eResourceBarType::MetalSlim;
119 		case sUnitData::STORE_RES_OIL:
120 			return eResourceBarType::OilSlim;
121 		case sUnitData::STORE_RES_GOLD:
122 			return eResourceBarType::GoldSlim;
123 	}
124 }
125 
126 //------------------------------------------------------------------------------
initUnitImage(cImage & image,const cUnit & unit)127 void cNewDialogTransfer::initUnitImage (cImage& image, const cUnit& unit)
128 {
129 	const int unitImageWidth = 64;
130 	const int unitImageHeight = 64;
131 
132 	const auto zoom = (float)unitImageWidth / (unit.data.isBig ? cStaticMap::tilePixelWidth * 2 : cStaticMap::tilePixelWidth);
133 
134 	AutoSurface unitImageSurface (SDL_CreateRGBSurface (0, unitImageWidth, unitImageHeight, Video.getColDepth(), 0, 0, 0, 0));
135 	SDL_FillRect (unitImageSurface.get(), nullptr, 0xFF00FF);
136 	SDL_SetColorKey (unitImageSurface.get(), SDL_TRUE, 0xFF00FF);
137 
138 	SDL_Rect dest = {0, 0, 0, 0};
139 
140 	if (unit.isABuilding())
141 	{
142 		const auto& building = static_cast<const cBuilding&> (unit);
143 		building.render (0, unitImageSurface.get(), dest, zoom, false, false);
144 	}
145 	else if (unit.isAVehicle())
146 	{
147 		const auto& vehicle = static_cast<const cVehicle&> (unit);
148 		vehicle.render (nullptr, 0, nullptr, unitImageSurface.get(), dest, zoom, false);
149 	}
150 
151 	image.setImage (unitImageSurface.get());
152 }
153 
154 //------------------------------------------------------------------------------
initCargo(int & cargo,int & maxCargo,const cUnit & unit1,const cUnit & unit2)155 void cNewDialogTransfer::initCargo (int& cargo, int& maxCargo, const cUnit& unit1, const cUnit& unit2)
156 {
157 	if (unit1.isABuilding())
158 	{
159 		const auto& building = static_cast<const cBuilding&> (unit1);
160 
161 		if (unit2.isAVehicle())
162 		{
163 			switch (unit2.data.storeResType)
164 			{
165 				default:
166 				case sUnitData::STORE_RES_METAL:
167 					maxCargo = building.SubBase->MaxMetal;
168 					cargo = building.SubBase->getMetal();
169 					break;
170 				case sUnitData::STORE_RES_OIL:
171 					maxCargo = building.SubBase->MaxOil;
172 					cargo = building.SubBase->getOil();
173 					break;
174 				case sUnitData::STORE_RES_GOLD:
175 					maxCargo = building.SubBase->MaxGold;
176 					cargo = building.SubBase->getGold();
177 					break;
178 			}
179 		}
180 		else if (unit2.isABuilding())
181 		{
182 			maxCargo = building.data.storageResMax;
183 			cargo = building.data.getStoredResources();
184 		}
185 	}
186 	else if (unit1.isAVehicle())
187 	{
188 		maxCargo = unit1.data.storageResMax;
189 		cargo = unit1.data.getStoredResources();
190 	}
191 }
192 
193 namespace
194 {
195 
196 // TODO: move function into a better file ?
FlipSurfaceHorizontally(SDL_Surface * surface)197 void FlipSurfaceHorizontally (SDL_Surface* surface)
198 {
199 	assert (surface);
200 	if (SDL_MUSTLOCK (surface)) SDL_LockSurface (surface);
201 
202 	// Assume surface format uses Uint32*
203 	// TODO: check surface format (or support more format).
204 	Uint32* p = static_cast<Uint32*> (surface->pixels);
205 
206 	for (int h = 0; h != surface->h; ++h)
207 		for (int w = 0; w != surface->w / 2; ++w)
208 			std::swap (p[h * surface->w + w], p[ (h + 1) * surface->w - w - 1]);
209 
210 	if (SDL_MUSTLOCK (surface)) SDL_UnlockSurface (surface);
211 }
212 }
213 
214 
215 //------------------------------------------------------------------------------
getTransferValue() const216 int cNewDialogTransfer::getTransferValue() const
217 {
218 	return resourceBar->getValue() - destinationCargo;;
219 }
220 
221 //------------------------------------------------------------------------------
getResourceType() const222 sUnitData::eStorageResType cNewDialogTransfer::getResourceType() const
223 {
224 	return resourceType;
225 }
226 
227 //------------------------------------------------------------------------------
transferValueChanged()228 void cNewDialogTransfer::transferValueChanged()
229 {
230 	const auto transferValue = getTransferValue();
231 	transferLabel->setText (iToStr (std::abs (transferValue)));
232 
233 	sourceUnitCargoLabel->setText (iToStr (sourceCargo - transferValue));
234 	destinationUnitCargoLabel->setText (iToStr (destinationCargo + transferValue));
235 
236 	if (transferValue >= 0) arrowImage->hide();
237 	else
238 	{
239 		arrowImage->show();
240 		// Set right to left arrow image.
241 		// little hack: flip part of the image that represent the arrow
242 		const unsigned int w = 40;
243 		const unsigned int h = 20;
244 		AutoSurface arrowSurface (SDL_CreateRGBSurface (0, w, h, Video.getColDepth(), 0, 0, 0, 0));
245 		const Sint16 x = arrowImage->getPosition().x() - getPosition().x();     // 140
246 		const Sint16 y = arrowImage->getPosition().y() - getPosition().y();     //  77
247 		SDL_Rect src = {x, y, w, h};
248 		SDL_BlitSurface (getSurface(), &src, arrowSurface.get(), nullptr);
249 		FlipSurfaceHorizontally (arrowSurface.get());
250 
251 		arrowImage->setImage (arrowSurface.get());
252 	}
253 }
254 
255 //------------------------------------------------------------------------------
closeOnUnitDestruction()256 void cNewDialogTransfer::closeOnUnitDestruction()
257 {
258 	close();
259 	auto application = getActiveApplication();
260 	if (application)
261 	{
262 		application->show (std::make_shared<cDialogOk> (lngPack.i18n ("Text~Others~Unit_destroyed")));
263 	}
264 }
265