1 #include "Font_Control.h"
2 #include "Soldier_Control.h"
3 #include "Overhead.h"
4 #include "Boxing.h"
5 #include "Render_Fun.h"
6 #include "Random.h"
7 #include "Timer_Control.h"
8 #include "WorldMan.h"
9 #include "Soldier_Profile.h"
10 #include "NPC.h"
11 #include "OppList.h"
12 #include "AI.h"
13 #include "Handle_UI.h"
14 #include "Points.h"
15 #include "Interface.h"
16 #include "Interface_Dialogue.h"
17 #include "TeamTurns.h"
18 #include "Music_Control.h"
19 #include "ContentMusic.h"
20 #include "History.h"
21 #include "Game_Clock.h"
22 #include "StrategicMap.h"
23 #include "Animation_Data.h"
24 #include "Items.h"
25 
26 INT16   gsBoxerGridNo[ NUM_BOXERS ] = { 11393, 11233, 11073 };
27 SOLDIERTYPE* gBoxer[NUM_BOXERS];
28 BOOLEAN gfBoxerFought[ NUM_BOXERS ] = { FALSE, FALSE, FALSE };
29 BOOLEAN gfLastBoxingMatchWonByPlayer = FALSE;
30 UINT8   gubBoxingMatchesWon = 0;
31 UINT8   gubBoxersRests = 0;
32 BOOLEAN gfBoxersResting = FALSE;
33 
34 
ExitBoxing(void)35 void ExitBoxing(void)
36 {
37 	// find boxers and turn them neutral again
38 
39 	// first time through loop, look for AI guy, then for PC guy.... for stupid
40 	// oppcnt/alert status reasons
41 	for (UINT8 ubPass = 0; ubPass < 2; ++ubPass)
42 	{
43 		// because boxer could die, loop through all soldier ptrs
44 		FOR_EACH_SOLDIER(s)
45 		{
46 			if (!(s->uiStatusFlags & SOLDIER_BOXER)) continue;
47 			if (GetRoom(s->sGridNo) != BOXING_RING)  continue;
48 
49 			if (s->uiStatusFlags & SOLDIER_PC)
50 			{
51 				if (ubPass == 0) continue; // pass 0, only handle AI
52 				// put guy under AI control temporarily
53 				s->uiStatusFlags |= SOLDIER_PCUNDERAICONTROL;
54 			}
55 			else
56 			{
57 				if (ubPass == 1) continue; // pass 1, only handle PCs
58 				// reset AI boxer to neutral
59 				SetSoldierNeutral(s);
60 				RecalculateOppCntsDueToBecomingNeutral(s);
61 			}
62 			CancelAIAction(s);
63 			s->bAlertStatus = STATUS_GREEN;
64 			s->bUnderFire   = 0;
65 
66 			// if necessary, revive boxer so he can leave ring
67 			if (s->bLife > 0 && (s->bLife < OKLIFE || s->bBreath < OKBREATH))
68 			{
69 				s->bLife = __max(OKLIFE * 2, s->bLife);
70 				if (s->bBreath < 100)
71 				{
72 					// deduct -ve BPs to grant some BPs back (properly)
73 					DeductPoints(s, 0, -100 * (100 - s->bBreath));
74 				}
75 				BeginSoldierGetup(s);
76 			}
77 		}
78 	}
79 
80 	DeleteTalkingMenu();
81 	EndAllAITurns();
82 
83 	if (CheckForEndOfCombatMode(FALSE))
84 	{
85 		EndTopMessage();
86 		SetMusicMode(MUSIC_TACTICAL_NOTHING);
87 		// Lock UI until we get out of the ring
88 		guiPendingOverrideEvent = LU_BEGINUILOCK;
89 	}
90 }
91 
92 
93 // in both these cases we're going to want the AI to take over and move the boxers
94 // out of the ring!
EndBoxingMatch(SOLDIERTYPE * pLoser)95 void EndBoxingMatch( SOLDIERTYPE * pLoser )
96 {
97 	if (pLoser->bTeam == OUR_TEAM )
98 	{
99 		SetBoxingState( LOST_ROUND );
100 	}
101 	else
102 	{
103 		SetBoxingState( WON_ROUND );
104 		gfLastBoxingMatchWonByPlayer = TRUE;
105 		gubBoxingMatchesWon++;
106 	}
107 	TriggerNPCRecord( DARREN, 22 );
108 }
109 
BoxingPlayerDisqualified(SOLDIERTYPE * pOffender,INT8 bReason)110 void BoxingPlayerDisqualified( SOLDIERTYPE * pOffender, INT8 bReason )
111 {
112 	if (bReason == BOXER_OUT_OF_RING || bReason == NON_BOXER_IN_RING)
113 	{
114 		EVENT_StopMerc(pOffender);
115 	}
116 	SetBoxingState( DISQUALIFIED );
117 	TriggerNPCRecord( DARREN, 21 );
118 	//ExitBoxing();
119 }
120 
TriggerEndOfBoxingRecord(SOLDIERTYPE * pSoldier)121 void TriggerEndOfBoxingRecord( SOLDIERTYPE * pSoldier )
122 {
123 	// unlock UI
124 	guiPendingOverrideEvent = LU_ENDUILOCK;
125 
126 	if ( pSoldier )
127 	{
128 		switch( gTacticalStatus.bBoxingState )
129 		{
130 			case WON_ROUND:
131 				AddHistoryToPlayersLog( HISTORY_WON_BOXING, pSoldier->ubProfile, GetWorldTotalMin(), gWorldSectorX, gWorldSectorY );
132 				TriggerNPCRecord( DARREN, 23 );
133 				break;
134 			case LOST_ROUND:
135 				// log as lost
136 				AddHistoryToPlayersLog( HISTORY_LOST_BOXING, pSoldier->ubProfile, GetWorldTotalMin(), gWorldSectorX, gWorldSectorY );
137 				TriggerNPCRecord( DARREN, 24 );
138 				break;
139 			case DISQUALIFIED:
140 				AddHistoryToPlayersLog( HISTORY_DISQUALIFIED_BOXING, pSoldier->ubProfile, GetWorldTotalMin(), gWorldSectorX, gWorldSectorY );
141 				break;
142 		}
143 	}
144 
145 	SetBoxingState( NOT_BOXING );
146 }
147 
CountPeopleInBoxingRing(void)148 UINT8 CountPeopleInBoxingRing( void )
149 {
150 	UINT8 ubTotalInRing = 0;
151 
152 	FOR_EACH_MERC(i)
153 	{
154 		if (GetRoom((*i)->sGridNo) == BOXING_RING)
155 		{
156 			++ubTotalInRing;
157 		}
158 	}
159 
160 	return( ubTotalInRing );
161 }
162 
163 
164 static void PickABoxer();
165 
166 
CountPeopleInBoxingRingAndDoActions(void)167 static void CountPeopleInBoxingRingAndDoActions(void)
168 {
169 	UINT8       ubTotalInRing = 0;
170 	UINT8       ubPlayersInRing = 0;
171 	SOLDIERTYPE *pInRing[2] = { NULL, NULL };
172 	SOLDIERTYPE *pNonBoxingPlayer = NULL;
173 
174 	FOR_EACH_MERC(i)
175 	{
176 		SOLDIERTYPE* const s = *i;
177 		if (GetRoom(s->sGridNo) != BOXING_RING) continue;
178 
179 		if (ubTotalInRing < 2) pInRing[ubTotalInRing] = s;
180 		++ubTotalInRing;
181 
182 		if (s->uiStatusFlags & SOLDIER_PC)
183 		{
184 			++ubPlayersInRing;
185 			if (!pNonBoxingPlayer && !(s->uiStatusFlags & SOLDIER_BOXER))
186 			{
187 				pNonBoxingPlayer = s;
188 			}
189 		}
190 	}
191 
192 	if ( ubPlayersInRing > 1 )
193 	{
194 		// boxing match just became invalid!
195 		if ( gTacticalStatus.bBoxingState <= PRE_BOXING )
196 		{
197 			BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING );
198 			// set to not in boxing or it won't be handled otherwise
199 			SetBoxingState( NOT_BOXING );
200 		}
201 		else
202 		{
203 			BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING );
204 		}
205 
206 		return;
207 	}
208 
209 
210 	if ( gTacticalStatus.bBoxingState == BOXING_WAITING_FOR_PLAYER )
211 	{
212 		if ( ubTotalInRing == 1 && ubPlayersInRing == 1 )
213 		{
214 			// time to go to pre-boxing
215 			SetBoxingState( PRE_BOXING );
216 			PickABoxer();
217 		}
218 	}
219 	else
220 	// if pre-boxing, check for two people (from different teams!) in the ring
221 	if ( gTacticalStatus.bBoxingState == PRE_BOXING )
222 	{
223 		if ( ubTotalInRing == 2 && ubPlayersInRing == 1 )
224 		{
225 			// ladieees and gennleman, we have a fight!
226 			for (UINT32 uiLoop = 0; uiLoop < 2; ++uiLoop)
227 			{
228 				if ( ! ( pInRing[ uiLoop ]->uiStatusFlags & SOLDIER_BOXER ) )
229 				{
230 					// set as boxer!
231 					pInRing[ uiLoop ]->uiStatusFlags |= SOLDIER_BOXER;
232 				}
233 			}
234 			// start match!
235 			SetBoxingState( BOXING );
236 			gfLastBoxingMatchWonByPlayer = FALSE;
237 
238 			// give the first turn to a randomly chosen boxer
239 			EnterCombatMode( pInRing[ Random( 2 ) ]->bTeam );
240 		}
241 	}
242 	/*
243 	else
244 	{
245 		// check to see if the player has more than one person in the ring
246 		if ( ubPlayersInRing > 1 )
247 		{
248 			// boxing match just became invalid!
249 			BoxingPlayerDisqualified( pNonBoxingPlayer, NON_BOXER_IN_RING );
250 			return;
251 		}
252 	}*/
253 }
254 
255 
CheckOnBoxers()256 bool CheckOnBoxers()
257 {
258 	// repick boxer IDs every time
259 	if (!gBoxer[0])
260 	{
261 		// get boxer soldier IDs!
262 		for (UINT32 i = 0; i != NUM_BOXERS; ++i)
263 		{
264 			SOLDIERTYPE* const s = WhoIsThere2(gsBoxerGridNo[i], 0);
265 			if (!s || FindObjClass(s, IC_WEAPON) != NO_SLOT) continue;
266 			// No weapon so this guy is a boxer
267 			gBoxer[i] = s;
268 		}
269 	}
270 
271 	return gBoxer[0] || gBoxer[1] || gBoxer[2];
272 }
273 
274 
BoxerExists()275 bool BoxerExists()
276 {
277 	FOR_EACH(GridNo const, i, gsBoxerGridNo)
278 	{
279 		if (WhoIsThere2(*i, 0)) return true;
280 	}
281 	return false;
282 }
283 
284 
PickABoxer()285 static void PickABoxer()
286 {
287 	for (UINT32 i = 0; i != NUM_BOXERS; ++i)
288 	{
289 		SOLDIERTYPE* const boxer = gBoxer[i];
290 		if (!boxer) continue;
291 
292 		if (gfBoxerFought[i])
293 		{
294 			// pathetic attempt to prevent multiple AI boxers
295 			boxer->uiStatusFlags &= ~SOLDIER_BOXER;
296 		}
297 		else if (boxer->bActive && boxer->bInSector && boxer->bLife >= OKLIFE)
298 		{
299 			// Pick this boxer
300 			boxer->uiStatusFlags |= SOLDIER_BOXER;
301 			SetSoldierNonNeutral(boxer);
302 			RecalculateOppCntsDueToNoLongerNeutral(boxer);
303 			CancelAIAction(boxer);
304 			RESETTIMECOUNTER(boxer->AICounter, 0);
305 			gfBoxerFought[i] = TRUE;
306 			// Improve stats based on the # of rests these guys have had
307 			boxer->bStrength  = __min(100, boxer->bStrength  + gubBoxersRests * 5);
308 			boxer->bDexterity = __min(100, boxer->bDexterity + gubBoxersRests * 5);
309 			boxer->bAgility   = __min(100, boxer->bAgility   + gubBoxersRests * 5);
310 			boxer->bLifeMax   = __min(100, boxer->bLifeMax   + gubBoxersRests * 5);
311 			// Give the 3rd boxer martial arts
312 			if (i == NUM_BOXERS - 1 && boxer->ubBodyType == REGMALE)
313 			{
314 				boxer->ubSkillTrait1 = MARTIALARTS;
315 			}
316 			break;
317 		}
318 	}
319 }
320 
321 
BoxerAvailable()322 bool BoxerAvailable()
323 {
324 	// No way around this, BoxerAvailable will have to go find boxer IDs if they aren't set.
325 	if (!CheckOnBoxers()) return false;
326 
327 	for (UINT8 i = 0; i != NUM_BOXERS; ++i)
328 	{
329 		if (gBoxer[i] && !gfBoxerFought[i]) return true;
330 	}
331 	return false;
332 }
333 
334 
335 // NOTE THIS IS NOW BROKEN BECAUSE NPC.C ASSUMES THAT BOXERSAVAILABLE < 3 IS A
336 // SEQUEL FIGHT.   Maybe we could check Kingpin's location instead!
BoxersAvailable(void)337 static UINT8 BoxersAvailable(void)
338 {
339 	UINT8 ubLoop;
340 	UINT8 ubCount = 0;
341 
342 	for( ubLoop = 0; ubLoop < NUM_BOXERS; ubLoop++ )
343 	{
344 		if (gBoxer[ubLoop] != NULL && !gfBoxerFought[ubLoop]) ubCount++;
345 	}
346 
347 	return( ubCount );
348 }
349 
AnotherFightPossible(void)350 BOOLEAN AnotherFightPossible( void )
351 {
352 	// Check that and a boxer is still available and
353 	// a player has at least OKLIFE + 5 life
354 
355 	// and at least one fight HAS occurred
356 	UINT8 ubAvailable;
357 
358 	ubAvailable = BoxersAvailable();
359 
360 	if ( ubAvailable == NUM_BOXERS || ubAvailable == 0 )
361 	{
362 		return( FALSE );
363 	}
364 
365 	CFOR_EACH_IN_TEAM(s, OUR_TEAM)
366 	{
367 		if (s->bInSector && s->bLife > OKLIFE + 5 && !s->bCollapsed)
368 		{
369 			return TRUE;
370 		}
371 	}
372 
373 	return( FALSE );
374 }
375 
376 
BoxingMovementCheck(SOLDIERTYPE * pSoldier)377 void BoxingMovementCheck( SOLDIERTYPE * pSoldier )
378 {
379 	if (GetRoom(pSoldier->sGridNo) == BOXING_RING)
380 	{
381 		// someone moving in/into the ring
382 		CountPeopleInBoxingRingAndDoActions();
383 	}
384 	else if ( ( gTacticalStatus.bBoxingState == BOXING ) && ( pSoldier->uiStatusFlags & SOLDIER_BOXER ) )
385 	{
386 		// boxer stepped out of the ring!
387 		BoxingPlayerDisqualified( pSoldier, BOXER_OUT_OF_RING );
388 		// add the history record here.
389 		AddHistoryToPlayersLog( HISTORY_DISQUALIFIED_BOXING, pSoldier->ubProfile, GetWorldTotalMin(), gWorldSectorX, gWorldSectorY );
390 		// make not a boxer any more
391 		pSoldier->uiStatusFlags &= ~(SOLDIER_BOXER);
392 		pSoldier->uiStatusFlags &= (~SOLDIER_PCUNDERAICONTROL);
393 	}
394 }
395 
SetBoxingState(INT8 bNewState)396 void SetBoxingState( INT8 bNewState )
397 {
398 	if ( gTacticalStatus.bBoxingState == NOT_BOXING )
399 	{
400 		if ( bNewState != NOT_BOXING )
401 		{
402 			// pause time
403 			PauseGame();
404 		}
405 
406 	}
407 	else
408 	{
409 		if ( bNewState == NOT_BOXING )
410 		{
411 			// unpause time
412 			UnPauseGame();
413 
414 			if ( BoxersAvailable() == NUM_BOXERS )
415 			{
416 				// set one boxer to be set as boxed so that the game will allow another
417 				// fight to occur
418 				gfBoxerFought[ 0 ] = TRUE;
419 			}
420 
421 		}
422 	}
423 	gTacticalStatus.bBoxingState = bNewState;
424 }
425 
426 
ClearAllBoxerFlags(void)427 void ClearAllBoxerFlags(void)
428 {
429 	FOR_EACH_MERC(i) (*i)->uiStatusFlags &= ~SOLDIER_BOXER;
430 }
431