1 #include "Interface_Utils.h"
2 #include "ContentManager.h"
3 #include "Directories.h"
4 #include "Faces.h"
5 #include "GameInstance.h"
6 #include "HImage.h"
7 #include "JAScreens.h"
8 #include "Line.h"
9 #include "MagazineModel.h"
10 #include "Overhead.h"
11 #include "Render_Dirty.h"
12 #include "Soldier_Macros.h"
13 #include "SysUtil.h"
14 #include "UILayout.h"
15 #include "Vehicles.h"
16 #include "Video.h"
17 #include "VObject.h"
18 #include "VSurface.h"
19 #include <stdexcept>
20 #include <string_theory/format>
21 #include <string_theory/string>
22 
23 
24 #define LIFE_BAR_SHADOW		FROMRGB(108, 12, 12)
25 #define LIFE_BAR			FROMRGB(200, 0, 0)
26 #define BANDAGE_BAR_SHADOW		FROMRGB(156, 60, 60)
27 #define BANDAGE_BAR			FROMRGB(222, 132, 132)
28 #define BLEEDING_BAR_SHADOW		FROMRGB(128, 128, 60)
29 #define BLEEDING_BAR			FROMRGB(240,  240, 20)
30 #define CURR_BREATH_BAR_SHADOW		FROMRGB(8, 12, 118) // the MAX max breatth, always at 100%
31 #define CURR_BREATH_BAR		FROMRGB(8, 12, 160)
32 #define CURR_MAX_BREATH		FROMRGB(0, 0, 0) // the current max breath, black
33 #define CURR_MAX_BREATH_SHADOW		FROMRGB(0, 0, 0)
34 #define MORALE_BAR_SHADOW		FROMRGB(8, 112, 12)
35 #define MORALE_BAR			FROMRGB(8, 180, 12)
36 #define BREATH_BAR_SHADOW		FROMRGB(60, 108, 108) // the lt blue current breath
37 #define BREATH_BAR			FROMRGB(113, 178, 218)
38 #define BREATH_BAR_SHAD_BACK		FROMRGB(1, 1, 1)
39 #define FACE_WIDTH			48
40 #define FACE_HEIGHT			43
41 
42 
43 // car portraits
44 enum
45 {
46 	ELDORADO_PORTRAIT = 0,
47 	HUMMER_PORTRAIT,
48 	ICE_CREAM_TRUCK_PORTRAIT,
49 	JEEP_PORTRAIT,
50 	NUMBER_CAR_PORTRAITS
51 };
52 
53 // the ids for the car portraits
54 static SGPVObject* giCarPortraits[NUMBER_CAR_PORTRAITS];
55 
56 static SGPVObject* guiBrownBackgroundForTeamPanel; // backgrounds for breath max background
57 
58 
59 // Load in the portraits for the car faces that will be use in mapscreen
LoadCarPortraitValues(void)60 void LoadCarPortraitValues(void)
61 {
62 	// the car portrait file names
63 	static char const* const pbCarPortraitFileNames[] =
64 	{
65 		INTERFACEDIR "/eldorado.sti",
66 		INTERFACEDIR "/hummer.sti",
67 		INTERFACEDIR "/ice cream truck.sti",
68 		INTERFACEDIR "/jeep.sti"
69 	};
70 
71 	if (giCarPortraits[0]) return;
72 	for (INT32 i = 0; i != NUMBER_CAR_PORTRAITS; ++i)
73 	{
74 		giCarPortraits[i] = AddVideoObjectFromFile(pbCarPortraitFileNames[i]);
75 	}
76 }
77 
78 
79 // get rid of the images we loaded for the mapscreen car portraits
UnLoadCarPortraits(void)80 void UnLoadCarPortraits(void)
81 {
82 	// car protraits loaded?
83 	if (!giCarPortraits[0]) return;
84 	for (INT32 i = 0; i != NUMBER_CAR_PORTRAITS; ++i)
85 	{
86 		DeleteVideoObject(giCarPortraits[i]);
87 		giCarPortraits[i] = 0;
88 	}
89 }
90 
91 
DrawBar(UINT32 const XPos,UINT32 const YPos,UINT32 const Height,UINT16 const Color,UINT16 const ShadowColor,UINT16 * const DestBuf)92 static void DrawBar(UINT32 const XPos, UINT32 const YPos, UINT32 const Height, UINT16 const Color, UINT16 const ShadowColor, UINT16* const DestBuf)
93 {
94 	LineDraw(TRUE, XPos + 0, YPos, XPos + 0, YPos - Height, ShadowColor, DestBuf);
95 	LineDraw(TRUE, XPos + 1, YPos, XPos + 1, YPos - Height, Color,       DestBuf);
96 	LineDraw(TRUE, XPos + 2, YPos, XPos + 2, YPos - Height, ShadowColor, DestBuf);
97 }
98 
99 
DrawLifeUIBar(SOLDIERTYPE const & s,UINT32 const XPos,UINT32 YPos,UINT32 const MaxHeight,UINT16 * const pDestBuf)100 static void DrawLifeUIBar(SOLDIERTYPE const& s, UINT32 const XPos, UINT32 YPos, UINT32 const MaxHeight, UINT16* const pDestBuf)
101 {
102 	UINT32 Height;
103 
104 	// FIRST DO MAX LIFE
105 	Height = MaxHeight * s.bLife / 100;
106 	DrawBar(XPos, YPos, Height, Get16BPPColor(LIFE_BAR), Get16BPPColor(LIFE_BAR_SHADOW), pDestBuf);
107 
108 	// NOW DO BANDAGE
109 	// Calculate bandage
110 	UINT32 Bandage = s.bLifeMax - s.bLife - s.bBleeding;
111 	if (Bandage != 0)
112 	{
113 		YPos   -= Height;
114 		Height  = MaxHeight * Bandage / 100;
115 		DrawBar(XPos, YPos, Height, Get16BPPColor(BANDAGE_BAR), Get16BPPColor(BANDAGE_BAR_SHADOW), pDestBuf);
116 	}
117 
118 	// NOW DO BLEEDING
119 	if (s.bBleeding != 0)
120 	{
121 		YPos   -= Height;
122 		Height  = MaxHeight * s.bBleeding / 100;
123 		DrawBar(XPos, YPos, Height, Get16BPPColor(BLEEDING_BAR), Get16BPPColor(BLEEDING_BAR_SHADOW), pDestBuf);
124 	}
125 }
126 
127 
DrawBreathUIBar(SOLDIERTYPE const & s,UINT32 const XPos,UINT32 const sYPos,UINT32 const MaxHeight,UINT16 * const pDestBuf)128 static void DrawBreathUIBar(SOLDIERTYPE const& s, UINT32 const XPos, UINT32 const sYPos, UINT32 const MaxHeight, UINT16* const pDestBuf)
129 {
130 	UINT32 Height;
131 
132 	if (s.bBreathMax <= 97)
133 	{
134 		Height = MaxHeight * (s.bBreathMax + 3) / 100;
135 		// the old background colors for breath max diff
136 		DrawBar(XPos, sYPos, Height, Get16BPPColor(BREATH_BAR_SHAD_BACK), Get16BPPColor(BREATH_BAR_SHAD_BACK), pDestBuf);
137 	}
138 
139 	Height = MaxHeight * s.bBreathMax / 100;
140 	DrawBar(XPos, sYPos, Height, Get16BPPColor(CURR_MAX_BREATH), Get16BPPColor(CURR_MAX_BREATH_SHADOW), pDestBuf);
141 
142 	// NOW DO BREATH
143 	Height = MaxHeight * s.bBreath / 100;
144 	DrawBar(XPos, sYPos, Height, Get16BPPColor(CURR_BREATH_BAR), Get16BPPColor(CURR_BREATH_BAR_SHADOW), pDestBuf);
145 }
146 
147 
DrawMoraleUIBar(SOLDIERTYPE const & s,UINT32 const XPos,UINT32 const YPos,UINT32 const MaxHeight,UINT16 * const pDestBuf)148 static void DrawMoraleUIBar(SOLDIERTYPE const& s, UINT32 const XPos, UINT32 const YPos, UINT32 const MaxHeight, UINT16* const pDestBuf)
149 {
150 	UINT32 const Height = MaxHeight * s.bMorale / 100;
151 	DrawBar(XPos, YPos, Height, Get16BPPColor(MORALE_BAR), Get16BPPColor(MORALE_BAR_SHADOW), pDestBuf);
152 }
153 
154 
DrawSoldierUIBars(SOLDIERTYPE const & s,INT16 const sXPos,INT16 const sYPos,BOOLEAN const fErase,SGPVSurface * const uiBuffer)155 void DrawSoldierUIBars(SOLDIERTYPE const& s, INT16 const sXPos, INT16 const sYPos, BOOLEAN const fErase, SGPVSurface* const uiBuffer)
156 {
157 	const UINT32 BarWidth  =  3;
158 	const UINT32 BarHeight = 42;
159 	const UINT32 BreathOff =  6;
160 	const UINT32 MoraleOff = 12;
161 
162 	// Erase what was there
163 	if (fErase)
164 	{
165 		RestoreExternBackgroundRect(sXPos, sYPos - BarHeight, MoraleOff + BarWidth, BarHeight + 1);
166 	}
167 
168 	if (s.bLife == 0) return;
169 
170 	if (!(s.uiStatusFlags & SOLDIER_ROBOT))
171 	{
172 		// DO MAX BREATH
173 		// brown guy
174 		UINT16 Region;
175 		if (guiCurrentScreen != MAP_SCREEN &&
176 			GetSelectedMan() == &s &&
177 			gTacticalStatus.ubCurrentTeam == OUR_TEAM &&
178 			OK_INTERRUPT_MERC(&s))
179 		{
180 			Region = 1; // gold, the second entry in the .sti
181 		}
182 		else
183 		{
184 			Region = 0; // brown, first entry
185 		}
186 		BltVideoObject(uiBuffer, guiBrownBackgroundForTeamPanel, Region, sXPos + BreathOff, sYPos - BarHeight);
187 	}
188 
189 	SGPVSurface::Lock l(uiBuffer);
190 	SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
191 	UINT16* const pDestBuf = l.Buffer<UINT16>();
192 
193 	DrawLifeUIBar(s, sXPos, sYPos, BarHeight, pDestBuf);
194 	if (!(s.uiStatusFlags & SOLDIER_ROBOT))
195 	{
196 		DrawBreathUIBar(s, sXPos + BreathOff, sYPos, BarHeight, pDestBuf);
197 		if (!(s.uiStatusFlags & SOLDIER_VEHICLE))
198 		{
199 			DrawMoraleUIBar(s, sXPos + MoraleOff, sYPos, BarHeight, pDestBuf);
200 		}
201 	}
202 }
203 
204 
DrawItemUIBarEx(OBJECTTYPE const & o,const UINT8 ubStatus,const INT16 x,const INT16 y,INT16 max_h,const INT16 sColor1,const INT16 sColor2,SGPVSurface * const uiBuffer)205 void DrawItemUIBarEx(OBJECTTYPE const& o, const UINT8 ubStatus, const INT16 x, const INT16 y, INT16 max_h, const INT16 sColor1, const INT16 sColor2, SGPVSurface* const uiBuffer)
206 {
207 	INT16 value;
208 	// Adjust for ammo, other things
209 	const ItemModel * item = GCM->getItem(o.usItem);
210 	if (ubStatus >= DRAW_ITEM_STATUS_ATTACHMENT1)
211 	{
212 		value = o.bAttachStatus[ubStatus - DRAW_ITEM_STATUS_ATTACHMENT1];
213 	}
214 	else if (item->isKey())
215 	{
216 		value = 100;
217 	}
218 	else
219 	{
220 		if (ubStatus >= MAX_OBJECTS_PER_SLOT)
221 			throw std::runtime_error(ST::format("invalid ubStatus value: {}", ubStatus).to_std_string());
222 
223 		if (item->isAmmo())
224 		{
225 			value = 100 * o.ubShotsLeft[ubStatus] / (item->asAmmo()->capacity ? item->asAmmo()->capacity : 1);
226 			if (value > 100) value = 100;
227 		}
228 		else
229 		{
230 			value = o.bStatus[ubStatus];
231 		}
232 	}
233 
234 	{ SGPVSurface::Lock l(uiBuffer);
235 		SetClippingRegionAndImageWidth(l.Pitch(), 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
236 		UINT16* const pDestBuf = l.Buffer<UINT16>();
237 
238 		--max_h; // LineDraw() includes the end point
239 		const INT h = max_h * value / 100;
240 		LineDraw(TRUE, x,     y, x,     y - h, sColor1, pDestBuf);
241 		LineDraw(TRUE, x + 1, y, x + 1, y - h, sColor2, pDestBuf);
242 	}
243 
244 	if (uiBuffer == guiSAVEBUFFER)
245 	{
246 		RestoreExternBackgroundRect(x, y - max_h, 2, max_h + 1);
247 	}
248 	else
249 	{
250 		InvalidateRegion(x, y - max_h, x + 2, y + 1);
251 	}
252 }
253 
254 
RenderSoldierFace(SOLDIERTYPE const & s,INT16 const sFaceX,INT16 const sFaceY)255 void RenderSoldierFace(SOLDIERTYPE const& s, INT16 const sFaceX, INT16 const sFaceY)
256 {
257 	if (s.uiStatusFlags & SOLDIER_VEHICLE)
258 	{
259 		// just draw the vehicle
260 		const UINT8 vehicle_type = pVehicleList[s.bVehicleID].ubVehicleType;
261 		BltVideoObject(guiSAVEBUFFER, giCarPortraits[vehicle_type], 0, sFaceX, sFaceY);
262 		RestoreExternBackgroundRect(sFaceX, sFaceY, FACE_WIDTH, FACE_HEIGHT);
263 	}
264 	else if (s.face->uiFlags & FACE_INACTIVE_HANDLED_ELSEWHERE) // OK, check if this face actually went active
265 	{
266 		ExternRenderFace(guiSAVEBUFFER, *s.face, sFaceX, sFaceY);
267 	}
268 	else
269 	{
270 		SetAutoFaceActive(FRAME_BUFFER, guiSAVEBUFFER, *s.face, sFaceX, sFaceY);
271 		RenderAutoFace(*s.face);
272 	}
273 }
274 
275 
LoadInterfaceUtilsGraphics()276 void LoadInterfaceUtilsGraphics()
277 {
278 	guiBrownBackgroundForTeamPanel = AddVideoObjectFromFile(INTERFACEDIR "/bars.sti");
279 }
280 
281 
DeleteInterfaceUtilsGraphics()282 void DeleteInterfaceUtilsGraphics()
283 {
284 	DeleteVideoObject(guiBrownBackgroundForTeamPanel);
285 }
286