1 #include "Directories.h"
2 #include "Font_Control.h"
3 #include "Lighting.h"
4 #include "Overhead.h"
5 #include "Soldier_Find.h"
6 #include "TileDef.h"
7 #include "VObject.h"
8 #include "Debug.h"
9 #include "Soldier_Control.h"
10 #include "Weapons.h"
11 #include "Handle_Items.h"
12 #include "WorldDef.h"
13 #include "Rotting_Corpses.h"
14 #include "Tile_Cache.h"
15 #include "Isometric_Utils.h"
16 #include "Animation_Control.h"
17 #include "Utilities.h"
18 #include "Game_Clock.h"
19 #include "Soldier_Create.h"
20 #include "RenderWorld.h"
21 #include "Soldier_Add.h"
22 #include "StrategicMap.h"
23 #include "LOS.h"
24 #include "Structure.h"
25 #include "Message.h"
26 #include "Sound_Control.h"
27 #include "PathAI.h"
28 #include "Random.h"
29 #include "Dialogue_Control.h"
30 #include "Items.h"
31 #include "Smell.h"
32 #include "World_Items.h"
33 #include "Explosion_Control.h"
34 #include "GameSettings.h"
35 #include "Interface_Items.h"
36 #include "Soldier_Profile.h"
37 #include "Soldier_Macros.h"
38 #include "Keys.h"
39 #include "Render_Fun.h"
40 #include "Strategic.h"
41 #include "QArray.h"
42 #include "Interface.h"
43 #include "MemMan.h"
44 #include "WorldMan.h"
45 
46 #include "FileMan.h"
47 #include "ContentManager.h"
48 #include "GameInstance.h"
49 #include "policy/GamePolicy.h"
50 
51 #define CORPSE_WARNING_MAX			5
52 #define CORPSE_WARNING_DIST			5
53 
54 #define CORPSE_INDEX_OFFSET			10000
55 
56 //#define DELAY_UNTIL_ROTTING			( 1 * NUM_SEC_IN_DAY )
57 #define DELAY_UNTIL_ROTTING			( 1 * NUM_SEC_IN_DAY / 60 )
58 #define DELAY_UNTIL_DONE_ROTTING		( 3 * NUM_SEC_IN_DAY / 60 )
59 
60 #define MAX_NUM_CROWS				6
61 
62 
63 // When adding a corpse, add struct data...
64 static const char* const zCorpseFilenames[NUM_CORPSES] =
65 {
66 	"",
67 	ANIMSDIR "/corpses/s_d_jfk.sti",
68 	ANIMSDIR "/corpses/s_d_bck.sti",
69 	ANIMSDIR "/corpses/s_d_fwd.sti",
70 	ANIMSDIR "/corpses/s_d_dhd.sti",
71 	ANIMSDIR "/corpses/s_d_prn.sti",
72 	ANIMSDIR "/corpses/s_d_wtr.sti",
73 	ANIMSDIR "/corpses/s_d_fall.sti",
74 	ANIMSDIR "/corpses/s_d_fallf.sti",
75 
76 	ANIMSDIR "/corpses/m_d_jfk.sti",
77 	ANIMSDIR "/corpses/m_d_bck.sti",
78 	ANIMSDIR "/corpses/m_d_fwd.sti",
79 	ANIMSDIR "/corpses/m_d_dhd.sti",
80 	ANIMSDIR "/corpses/m_d_prn.sti",
81 	ANIMSDIR "/corpses/s_d_wtr.sti",
82 	ANIMSDIR "/corpses/m_d_fall.sti",
83 	ANIMSDIR "/corpses/m_d_fallf.sti",
84 
85 	ANIMSDIR "/corpses/f_d_jfk.sti",
86 	ANIMSDIR "/corpses/f_d_bck.sti",
87 	ANIMSDIR "/corpses/f_d_fwd.sti",
88 	ANIMSDIR "/corpses/f_d_dhd.sti",
89 	ANIMSDIR "/corpses/f_d_prn.sti",
90 	ANIMSDIR "/corpses/s_d_wtr.sti",
91 	ANIMSDIR "/corpses/f_d_fall.sti",
92 	ANIMSDIR "/corpses/f_d_fallf.sti",
93 
94 	// Civs....
95 	ANIMSDIR "/corpses/m_dead1.sti",
96 	ANIMSDIR "/corpses/k_dead2.sti",
97 	ANIMSDIR "/corpses/h_dead2.sti",
98 	ANIMSDIR "/corpses/ft_dead1.sti",
99 	ANIMSDIR "/corpses/s_dead1.sti",
100 	ANIMSDIR "/corpses/w_dead1.sti",
101 	ANIMSDIR "/corpses/cp_dead1.sti",
102 	ANIMSDIR "/corpses/m_dead2.sti",
103 	ANIMSDIR "/corpses/k_dead1.sti",
104 	ANIMSDIR "/corpses/h_dead1.sti",
105 
106 	ANIMSDIR "/corpses/ft_dead2.sti",
107 	ANIMSDIR "/corpses/s_dead2.sti",
108 	ANIMSDIR "/corpses/w_dead2.sti",
109 	ANIMSDIR "/corpses/cp_dead2.sti",
110 	ANIMSDIR "/corpses/ct_dead.sti",
111 	ANIMSDIR "/corpses/cw_dead1.sti",
112 	ANIMSDIR "/corpses/mn_dead2.sti",
113 	ANIMSDIR "/corpses/i_dead1.sti",
114 	ANIMSDIR "/corpses/l_dead1.sti",
115 
116 	ANIMSDIR "/corpses/p_decomp2.sti",
117 	ANIMSDIR "/corpses/tk_wrek.sti",
118 	ANIMSDIR "/corpses/tk2_wrek.sti",
119 	ANIMSDIR "/corpses/hm_wrek.sti",
120 	ANIMSDIR "/corpses/ic_wrek.sti",
121 	ANIMSDIR "/corpses/qn_dead.sti",
122 	ANIMSDIR "/corpses/j_dead.sti",
123 	ANIMSDIR "/corpses/s_burnt.sti",
124 	ANIMSDIR "/corpses/s_expld.sti",
125 };
126 
127 
128 // When adding a corpse, add struct data...
129 static const char* const zNoBloodCorpseFilenames[NUM_CORPSES] =
130 {
131 	"",
132 	ANIMSDIR "/corpses/m_d_jfk_nb.sti",
133 	ANIMSDIR "/corpses/s_d_bck_nb.sti",
134 	ANIMSDIR "/corpses/s_d_fwd_nb.sti",
135 	ANIMSDIR "/corpses/s_d_dhd_nb.sti",
136 	ANIMSDIR "/corpses/s_d_prn_nb.sti",
137 	ANIMSDIR "/corpses/s_d_wtr.sti",
138 	ANIMSDIR "/corpses/s_d_fall_nb.sti",
139 	ANIMSDIR "/corpses/s_d_fallf_nb.sti",
140 
141 	ANIMSDIR "/corpses/m_d_jfk_nb.sti",
142 	ANIMSDIR "/corpses/m_d_bck_nb.sti",
143 	ANIMSDIR "/corpses/m_d_fwd_nb.sti",
144 	ANIMSDIR "/corpses/m_d_dhd_nb.sti",
145 	ANIMSDIR "/corpses/m_d_prn_nb.sti",
146 	ANIMSDIR "/corpses/s_d_wtr.sti",
147 	ANIMSDIR "/corpses/m_d_fall_nb.sti",
148 	ANIMSDIR "/corpses/m_d_fallf_nb.sti",
149 
150 	ANIMSDIR "/corpses/f_d_jfk_nb.sti",
151 	ANIMSDIR "/corpses/f_d_bck_nb.sti",
152 	ANIMSDIR "/corpses/f_d_fwd_nb.sti",
153 	ANIMSDIR "/corpses/f_d_dhd_nb.sti",
154 	ANIMSDIR "/corpses/f_d_prn_nb.sti",
155 	ANIMSDIR "/corpses/s_d_wtr.sti",
156 	ANIMSDIR "/corpses/f_d_fall_nb.sti",
157 	ANIMSDIR "/corpses/f_d_fallf_nb.sti",
158 
159 	// Civs....
160 	ANIMSDIR "/corpses/m_dead1_nb.sti",
161 	ANIMSDIR "/corpses/k_dead2_nb.sti",
162 	ANIMSDIR "/corpses/h_dead2_nb.sti",
163 	ANIMSDIR "/corpses/ft_dead1_nb.sti",
164 	ANIMSDIR "/corpses/s_dead1_nb.sti",
165 	ANIMSDIR "/corpses/w_dead1_nb.sti",
166 	ANIMSDIR "/corpses/cp_dead1_nb.sti",
167 	ANIMSDIR "/corpses/m_dead2_nb.sti",
168 	ANIMSDIR "/corpses/k_dead1_nb.sti",
169 	ANIMSDIR "/corpses/h_dead1_nb.sti",
170 
171 	ANIMSDIR "/corpses/ft_dead2_nb.sti",
172 	ANIMSDIR "/corpses/s_dead2_nb.sti",
173 	ANIMSDIR "/corpses/w_dead2_nb.sti",
174 	// The following line used to reference the non-existing animation
175 	// cp_dead2_nb.sti.
176 	ANIMSDIR "/corpses/cp_dead2.sti",
177 	ANIMSDIR "/corpses/ct_dead.sti",
178 	ANIMSDIR "/corpses/cw_dead1.sti",
179 	ANIMSDIR "/corpses/mn_dead2.sti",
180 	ANIMSDIR "/corpses/i_dead1.sti",
181 	ANIMSDIR "/corpses/l_dead1.sti",
182 	ANIMSDIR "/corpses/p_decomp2.sti",
183 
184 	ANIMSDIR "/corpses/tk_wrek.sti",
185 	ANIMSDIR "/corpses/tk2_wrek.sti",
186 	ANIMSDIR "/corpses/hm_wrek.sti",
187 	ANIMSDIR "/corpses/ic_wrek.sti",
188 	ANIMSDIR "/corpses/qn_dead.sti",
189 	ANIMSDIR "/corpses/j_dead.sti",
190 	ANIMSDIR "/corpses/s_burnt.sti",
191 	ANIMSDIR "/corpses/s_expld.sti",
192 };
193 
194 UINT8 gb4DirectionsFrom8[8] =
195 {
196 	0, // NORTH
197 	0, // NE
198 	0, // E
199 	0, // SE
200 	1, // S
201 	0, // SW,
202 	2, // W,
203 	0 // NW
204 };
205 
206 
207 static const UINT8 gb2DirectionsFrom8[8] =
208 {
209 	0, // NORTH
210 	7, // NE
211 	7, // E
212 	7, // SE
213 	0, // S
214 	7, // SW,
215 	7, // W,
216 	7 // NW
217 };
218 
219 
220 static const BOOLEAN gbCorpseValidForDecapitation[NUM_CORPSES] =
221 {
222 	0,
223 	0,
224 	1,
225 	1,
226 	1,
227 	1,
228 	1,
229 	1,
230 	1,
231 
232 	0,
233 	1,
234 	1,
235 	1,
236 	1,
237 	1,
238 	1,
239 	1,
240 
241 	0,
242 	1,
243 	1,
244 	1,
245 	1,
246 	1,
247 	1,
248 	1,
249 
250 	// Civs....
251 	1,
252 	1,
253 	1,
254 	1,
255 	1,
256 	1,
257 	1,
258 	1,
259 	1,
260 	1,
261 
262 	1,
263 	1,
264 	1,
265 	1,
266 	0,
267 	0,
268 	0,
269 	0,
270 	0,
271 	1,
272 
273 	0,
274 	0,
275 	0,
276 	0,
277 	0,
278 	0,
279 	0,
280 	0,
281 };
282 
283 
284 static const INT8 gDecapitatedCorpse[NUM_CORPSES] =
285 {
286 	0,
287 	SMERC_JFK,
288 	SMERC_JFK,
289 	SMERC_JFK,
290 	SMERC_JFK,
291 	SMERC_JFK,
292 	SMERC_JFK,
293 	SMERC_JFK,
294 	SMERC_JFK,
295 
296 	MMERC_JFK,
297 	MMERC_JFK,
298 	MMERC_JFK,
299 	MMERC_JFK,
300 	MMERC_JFK,
301 	MMERC_JFK,
302 	MMERC_JFK,
303 	MMERC_JFK,
304 
305 	FMERC_JFK,
306 	FMERC_JFK,
307 	FMERC_JFK,
308 	FMERC_JFK,
309 	FMERC_JFK,
310 	FMERC_JFK,
311 	FMERC_JFK,
312 	FMERC_JFK,
313 
314 	// Civs....
315 	0,
316 	0,
317 	0,
318 	0,
319 	0,
320 	0,
321 	0,
322 	0,
323 	0,
324 	0,
325 
326 	0,
327 	0,
328 	0,
329 	0,
330 	0,
331 	0,
332 	0,
333 	0,
334 	0,
335 	0,
336 
337 	0,
338 	0,
339 	0,
340 	0,
341 	0,
342 	0,
343 	0,
344 	0,
345 };
346 
347 
348 
349 ROTTING_CORPSE gRottingCorpse[ MAX_ROTTING_CORPSES ];
350 INT32 giNumRottingCorpse = 0;
351 
352 
GetFreeRottingCorpse(void)353 static ROTTING_CORPSE* GetFreeRottingCorpse(void)
354 {
355 	for (ROTTING_CORPSE* c = gRottingCorpse; c != gRottingCorpse + giNumRottingCorpse; ++c)
356 	{
357 		if (!c->fActivated) return c;
358 	}
359 	if (giNumRottingCorpse < MAX_ROTTING_CORPSES)
360 	{
361 		return &gRottingCorpse[giNumRottingCorpse++];
362 	}
363 	return NULL;
364 }
365 
366 
GetCorpseStructIndex(const ROTTING_CORPSE_DEFINITION * pCorpseDef,BOOLEAN fForImage)367 UINT16 GetCorpseStructIndex(const ROTTING_CORPSE_DEFINITION* pCorpseDef, BOOLEAN fForImage)
368 {
369 	INT8 bDirection;
370 
371 	switch( pCorpseDef->ubType )
372 	{
373 		case QUEEN_MONSTER_DEAD:
374 		case BURNT_DEAD:
375 		case EXPLODE_DEAD:
376 
377 			bDirection = 0;
378 			break;
379 
380 		case ICECREAM_DEAD:
381 		case HUMMER_DEAD:
382 
383 			// OK , these have 2 directions....
384 			bDirection = gb2DirectionsFrom8[ pCorpseDef->bDirection ];
385 			if (fForImage) bDirection = OneCDirection(bDirection);
386 			break;
387 
388 		case SMERC_FALL:
389 		case SMERC_FALLF:
390 		case MMERC_FALL:
391 		case MMERC_FALLF:
392 		case FMERC_FALL:
393 		case FMERC_FALLF:
394 
395 			// OK , these have 4 directions....
396 			bDirection = gb4DirectionsFrom8[ pCorpseDef->bDirection ];
397 
398 			if ( fForImage )
399 			{
400 				bDirection = OneCDirection(bDirection);
401 			}
402 			break;
403 
404 		default:
405 
406 			// Uses 8
407 			bDirection =  pCorpseDef->bDirection;
408 			if (fForImage) bDirection = OneCDirection(bDirection);
409 			break;
410 	}
411 
412 	return( bDirection );
413 }
414 
415 
416 static void CreateCorpsePalette(ROTTING_CORPSE*);
417 
418 
AddRottingCorpse(ROTTING_CORPSE_DEFINITION * const pCorpseDef)419 ROTTING_CORPSE* AddRottingCorpse(ROTTING_CORPSE_DEFINITION* const pCorpseDef)
420 try
421 {
422 	if (pCorpseDef->sGridNo == NOWHERE)   return NULL;
423 	if (pCorpseDef->ubType  == NO_CORPSE) return NULL;
424 
425 	ROTTING_CORPSE* const c = GetFreeRottingCorpse();
426 	if (c == NULL) return NULL;
427 
428 	// Copy elements in
429 	c->def = *pCorpseDef;
430 
431 	// If we are a soecial type...
432 	AnimationFlags uiDirectionUseFlag;
433 	switch (pCorpseDef->ubType)
434 	{
435 		case SMERC_FALL:
436 		case SMERC_FALLF:
437 		case MMERC_FALL:
438 		case MMERC_FALLF:
439 		case FMERC_FALL:
440 		case FMERC_FALLF: uiDirectionUseFlag = ANITILE_USE_4DIRECTION_FOR_START_FRAME; break;
441 		default:          uiDirectionUseFlag = ANITILE_USE_DIRECTION_FOR_START_FRAME;  break;
442 	}
443 
444 	if (!(gTacticalStatus.uiFlags & LOADING_SAVED_GAME))
445 	{
446 		// OK, AS WE ADD, CHECK FOR TOD AND DECAY APPROPRIATELY
447 		if (GetWorldTotalMin() - c->def.uiTimeOfDeath > DELAY_UNTIL_ROTTING &&
448 			c->def.ubType < ROTTING_STAGE2 &&
449 			c->def.ubType <= FMERC_FALLF)
450 		{
451 			// Rott!
452 			c->def.ubType = ROTTING_STAGE2;
453 		}
454 
455 		// If time of death is a few days, now, don't add at all!
456 		if (GetWorldTotalMin() - c->def.uiTimeOfDeath > DELAY_UNTIL_DONE_ROTTING) return NULL;
457 	}
458 
459 	// Check if on roof or not...
460 	AnimationLevel const ubLevelID = (c->def.bLevel == 0 ? ANI_STRUCT_LEVEL : ANI_ONROOF_LEVEL);
461 
462 	ANITILE_PARAMS AniParams;
463 	AniParams = ANITILE_PARAMS{};
464 	AniParams.sGridNo        = c->def.sGridNo;
465 	AniParams.ubLevelID      = ubLevelID;
466 	AniParams.sDelay         = 150;
467 	AniParams.sStartFrame    = 0;
468 	AniParams.uiFlags        = ANITILE_PAUSED | ANITILE_OPTIMIZEFORSLOWMOVING | ANITILE_ANIMATE_Z | ANITILE_ERASEITEMFROMSAVEBUFFFER | uiDirectionUseFlag;
469 	AniParams.sX             = CenterX(c->def.sGridNo);
470 	AniParams.sY             = CenterY(c->def.sGridNo);
471 	AniParams.sZ             = c->def.sHeightAdjustment;
472 	AniParams.v.user.uiData3 = c->def.bDirection;
473 
474 	if (!gGameSettings.fOptions[TOPTION_BLOOD_N_GORE])
475 	{
476 		AniParams.zCachedFile = zNoBloodCorpseFilenames[c->def.ubType];
477 	}
478 	else
479 	{
480 		AniParams.zCachedFile = zCorpseFilenames[c->def.ubType];
481 	}
482 
483 	ANITILE* const ani = CreateAnimationTile(&AniParams);
484 	c->pAniTile = ani;
485 
486 	LEVELNODE*       const n    = ani->pLevelNode;
487 	const LEVELNODE* const land = gpWorldLevelData[c->def.sGridNo].pLandHead;
488 
489 	// Set flag and index values
490 	n->uiFlags             |= LEVELNODE_ROTTINGCORPSE;
491 	n->ubShadeLevel         = land->ubShadeLevel;
492 	n->ubSumLights          = land->ubSumLights;
493 	n->ubMaxLights          = land->ubMaxLights;
494 	n->ubNaturalShadeLevel  = land->ubNaturalShadeLevel;
495 
496 	// Get palette and create palettes and do substitutions
497 	CreateCorpsePalette(c);
498 
499 	c->fActivated = TRUE;
500 	ani->v.user.uiData = CORPSE2ID(c);
501 	c->def.ubAIWarningValue = CORPSE_WARNING_MAX;
502 
503 	SetRenderFlags(RENDER_FLAG_FULL);
504 
505 	if (c->def.usFlags & ROTTING_CORPSE_VEHICLE)
506 	{
507 		ani->uiFlags |= ANITILE_FORWARD | ANITILE_LOOPING;
508 		ani->uiFlags &= ~ANITILE_PAUSED;
509 	}
510 
511 	InvalidateWorldRedundency();
512 
513 	// OK, loop through gridnos for corpse and remove blood.....
514 
515 	// Get root filename... this removes path and extension
516 	// Used to find struct data for this corpse...
517 	ST::string zFilename(FileMan::getFileNameWithoutExt(AniParams.zCachedFile));
518 
519 	// Add structure data.....
520 	CheckForAndAddTileCacheStructInfo(n, c->def.sGridNo, ani->sCachedTileID, GetCorpseStructIndex(pCorpseDef, TRUE));
521 
522 	const STRUCTURE_FILE_REF* const pStructureFileRef = GetCachedTileStructureRefFromFilename(zFilename.c_str());
523 	if (pStructureFileRef != NULL)
524 	{
525 		const UINT16                  usStructIndex   = GetCorpseStructIndex(pCorpseDef, TRUE);
526 		const DB_STRUCTURE_REF* const pDBStructureRef = &pStructureFileRef->pDBStructureRef[usStructIndex];
527 		for (UINT8 ubLoop = 0; ubLoop < pDBStructureRef->pDBStructure->ubNumberOfTiles; ++ubLoop)
528 		{
529 			DB_STRUCTURE_TILE* const* const ppTile      = pDBStructureRef->ppTile;
530 			const INT16                     sTileGridNo = pCorpseDef->sGridNo + ppTile[ubLoop]->sPosRelToBase;
531 			RemoveBlood(sTileGridNo, pCorpseDef->bLevel);
532 		}
533 	}
534 
535 	return c;
536 }
537 catch (...) { return 0; }
538 
539 
FreeCorpsePalettes(ROTTING_CORPSE * pCorpse)540 static void FreeCorpsePalettes(ROTTING_CORPSE* pCorpse)
541 {
542 	INT32 cnt;
543 
544 	for ( cnt = 0; cnt < NUM_CORPSE_SHADES; cnt++ )
545 	{
546 		if ( pCorpse->pShades[ cnt ] != NULL )
547 		{
548 			delete[] pCorpse->pShades[ cnt ];
549 			pCorpse->pShades[ cnt ] = NULL;
550 		}
551 	}
552 }
553 
554 
555 static void RemoveCorpse(ROTTING_CORPSE* c);
556 
557 
RemoveCorpses()558 void RemoveCorpses( )
559 {
560 	FOR_EACH_ROTTING_CORPSE(c) RemoveCorpse(c);
561 	giNumRottingCorpse = 0;
562 }
563 
564 
RemoveCorpse(ROTTING_CORPSE * const c)565 static void RemoveCorpse(ROTTING_CORPSE* const c)
566 {
567 	Assert(gRottingCorpse <= c && c < endof(gRottingCorpse));
568 	Assert(c->fActivated);
569 
570 	c->fActivated = FALSE;
571 	DeleteAniTile(c->pAniTile);
572 	FreeCorpsePalettes(c);
573 }
574 
575 
CreateCorpsePalette(ROTTING_CORPSE * const c)576 static void CreateCorpsePalette(ROTTING_CORPSE* const c)
577 {
578 	char const* const substitution =
579 		c->def.ubType == ROTTING_STAGE2                  ? ""                   :
580 		c->def.usFlags & ROTTING_CORPSE_USE_CAMO_PALETTE ? ANIMSDIR "/camo.COL" :
581 		GetBodyTypePaletteSubstitution(0, c->def.ubBodyType);
582 
583 	const SGPPaletteEntry* pal;
584 	SGPPaletteEntry        tmp_pal[256];
585 	if (!substitution)
586 	{
587 		// Use palette from HVOBJECT, then use substitution for pants, etc
588 		memcpy(tmp_pal, gpTileCache[c->pAniTile->sCachedTileID].pImagery->vo->Palette(), sizeof(tmp_pal));
589 		SetPaletteReplacement(tmp_pal, c->def.HeadPal);
590 		SetPaletteReplacement(tmp_pal, c->def.VestPal);
591 		SetPaletteReplacement(tmp_pal, c->def.PantsPal);
592 		SetPaletteReplacement(tmp_pal, c->def.SkinPal);
593 		pal = tmp_pal;
594 	}
595 	else if (substitution[0] != '\0' && CreateSGPPaletteFromCOLFile(tmp_pal, substitution))
596 	{
597 		pal = tmp_pal;
598 	}
599 	else
600 	{
601 		// Use palette from hvobject
602 		pal = gpTileCache[c->pAniTile->sCachedTileID].pImagery->vo->Palette();
603 	}
604 
605 	CreateBiasedShadedPalettes(c->pShades, pal);
606 }
607 
608 
TurnSoldierIntoCorpse(SOLDIERTYPE & s)609 BOOLEAN TurnSoldierIntoCorpse(SOLDIERTYPE& s)
610 {
611 	ROTTING_CORPSE_DEFINITION Corpse;
612 	UINT8  ubType;
613 	UINT16 usItemFlags = 0; //WORLD_ITEM_DONTRENDER;
614 	UINT8  ubNumGoo;
615 	INT16  sNewGridNo;
616 	OBJECTTYPE ItemObject;
617 
618 
619 	if (s.sGridNo == NOWHERE)
620 	{
621 		return( FALSE );
622 	}
623 
624 	// ATE: Change to fix crash when item in hand
625 	if (gpItemPointer && gpItemPointerSoldier == &s)
626 	{
627 		CancelItemPointer( );
628 	}
629 
630 	// Setup some values!
631 	Corpse = ROTTING_CORPSE_DEFINITION{};
632 	Corpse.ubBodyType = s.ubBodyType;
633 	Corpse.sGridNo    = s.sGridNo;
634 	Corpse.bLevel     = s.bLevel;
635 	Corpse.ubProfile  = s.ubProfile;
636 
637 	if ( Corpse.bLevel > 0 )
638 	{
639 		Corpse.sHeightAdjustment = s.sHeightAdjustment - WALL_HEIGHT;
640 	}
641 
642 	Corpse.HeadPal  = s.HeadPal;
643 	Corpse.VestPal  = s.VestPal;
644 	Corpse.SkinPal  = s.SkinPal;
645 	Corpse.PantsPal = s.PantsPal;
646 
647 	if (s.bCamo != 0)
648 	{
649 		Corpse.usFlags |= ROTTING_CORPSE_USE_CAMO_PALETTE;
650 	}
651 
652 	// Determine corpse type!
653 	ubType = gubAnimSurfaceCorpseID[s.ubBodyType][s.usAnimState];
654 
655 	Corpse.bDirection	= s.bDirection;
656 
657 	// If we are a vehicle.... only use 1 direction....
658 	if (s.uiStatusFlags & SOLDIER_VEHICLE)
659 	{
660 		Corpse.usFlags |= ROTTING_CORPSE_VEHICLE;
661 
662 		if (s.ubBodyType != ICECREAMTRUCK && s.ubBodyType != HUMVEE)
663 		{
664 			Corpse.bDirection = 7;
665 		}
666 		else
667 		{
668 			Corpse.bDirection = gb2DirectionsFrom8[ Corpse.bDirection ];
669 		}
670 	}
671 
672 	if ( ubType == QUEEN_MONSTER_DEAD || ubType == BURNT_DEAD || ubType == EXPLODE_DEAD )
673 	{
674 		Corpse.bDirection = 7;
675 	}
676 
677 
678 	// ATE: If bDirection, get opposite
679 	//if ( ubType == SMERC_FALLF || ubType == MMERC_FALLF || ubType == FMERC_FALLF )
680 	//{
681 	//	Corpse.bDirection = OppositeDirection(Corpse.bDirection);
682 	//}
683 
684 	// Set time of death
685 	Corpse.uiTimeOfDeath = GetWorldTotalMin( );
686 
687 	// If corpse is not valid. make items visible
688 	if (ubType == NO_CORPSE && s.bTeam != OUR_TEAM)
689 	{
690 		usItemFlags &= (~WORLD_ITEM_DONTRENDER );
691 	}
692 
693 
694 	// ATE: If the queen is killed, she should
695 	// make items visible because it ruins end sequence....
696 	Visibility const bVisible = s.bTeam == OUR_TEAM ||
697 					s.ubProfile == QUEEN ?
698 					VISIBLE : INVISIBLE;
699 
700 	// Not for a robot...
701 	if (AM_A_ROBOT(&s))
702 	{
703 
704 	}
705 	else if ( ubType == QUEEN_MONSTER_DEAD )
706 	{
707 		gTacticalStatus.fLockItemLocators = FALSE;
708 
709 		ubNumGoo = 6 - ( gGameOptions.ubDifficultyLevel - DIF_LEVEL_EASY );
710 
711 		sNewGridNo = s.sGridNo + WORLD_COLS * 2;
712 
713 		for (INT32 cnt = 0; cnt < ubNumGoo; ++cnt)
714 		{
715 			CreateItem( JAR_QUEEN_CREATURE_BLOOD, 100, &ItemObject );
716 
717 			AddItemToPool(sNewGridNo, &ItemObject, bVisible, s.bLevel, usItemFlags, -1);
718 		}
719 	}
720 	else
721 	{
722 		// OK, Place what objects this guy was carrying on the ground!
723 		FOR_EACH_SOLDIER_INV_SLOT(pObj, s)
724 		{
725 			if ( pObj->usItem != NOTHING )
726 			{
727 				// Check if it's supposed to be dropped
728 				if (!(pObj->fFlags & OBJECT_UNDROPPABLE)
729 					|| (s.bTeam == OUR_TEAM)
730 					|| gamepolicy(f_drop_everything))
731 				{
732 					// and make sure that it really is a droppable item type
733 					if ( !(GCM->getItem(pObj->usItem)->getFlags() & ITEM_DEFAULT_UNDROPPABLE) )
734 					{
735 						ReduceAmmoDroppedByNonPlayerSoldiers(s, *pObj);
736 						Visibility vis = gamepolicy(f_all_dropped_visible) ? VISIBLE : bVisible;
737 						AddItemToPool(s.sGridNo, pObj, vis, s.bLevel, usItemFlags, -1);
738 					}
739 				}
740 			}
741 		}
742 
743 		DropKeysInKeyRing(s, s.sGridNo, s.bLevel, bVisible, false, 0, false);
744 	}
745 
746 	// Make team look for items
747 	AllSoldiersLookforItems();
748 
749 	// If not a player, you can completely remove soldiertype
750 	// otherwise, just remove their graphic
751 	if (s.bTeam != OUR_TEAM)
752 	{
753 		// Remove merc!
754 		// ATE: Remove merc slot first - will disappear if no corpse data found!
755 		TacticalRemoveSoldier(s);
756 	}
757 	else
758 	{
759 		RemoveSoldierFromGridNo(s);
760 	}
761 
762 	if (ubType == NO_CORPSE)
763 	{
764 		return( TRUE );
765 	}
766 
767 	// Set type
768 	Corpse.ubType	= ubType;
769 	ROTTING_CORPSE* const added_corpse = AddRottingCorpse(&Corpse);
770 
771 	// If this is our guy......make visible...
772 	//if (s.bTeam == OUR_TEAM)
773 	{
774 		added_corpse->def.bVisible = 1;
775 	}
776 
777 	return( TRUE );
778 }
779 
780 
FindNearestRottingCorpse(SOLDIERTYPE * pSoldier)781 INT16 FindNearestRottingCorpse( SOLDIERTYPE *pSoldier )
782 {
783 	INT32 uiLowestRange = 999999;
784 	INT16 sLowestGridNo = NOWHERE;
785 
786 	// OK, loop through our current listing of bodies
787 	CFOR_EACH_ROTTING_CORPSE(c)
788 	{
789 		// Check rotting state
790 		if (c->def.ubType == ROTTING_STAGE2)
791 		{
792 			const INT32 uiRange = GetRangeInCellCoordsFromGridNoDiff(pSoldier->sGridNo, c->def.sGridNo);
793 			if (uiRange < uiLowestRange)
794 			{
795 				sLowestGridNo = c->def.sGridNo;
796 				uiLowestRange = uiRange;
797 			}
798 		}
799 	}
800 
801 	return( sLowestGridNo );
802 }
803 
804 
AddCrowToCorpse(ROTTING_CORPSE * pCorpse)805 static void AddCrowToCorpse(ROTTING_CORPSE* pCorpse)
806 {
807 	SOLDIERCREATE_STRUCT MercCreateStruct;
808 	INT8 bBodyType = CROW;
809 
810 	// No crows inside :(
811 	if (GetRoom(pCorpse->def.sGridNo) != NO_ROOM) return;
812 
813 	// Put him flying over corpse pisition
814 	MercCreateStruct = SOLDIERCREATE_STRUCT{};
815 	MercCreateStruct.ubProfile = NO_PROFILE;
816 	MercCreateStruct.sSectorX = gWorldSectorX;
817 	MercCreateStruct.sSectorY = gWorldSectorY;
818 	MercCreateStruct.bSectorZ = gbWorldSectorZ;
819 	MercCreateStruct.bBodyType = bBodyType;
820 	MercCreateStruct.bDirection = SOUTH;
821 	MercCreateStruct.bTeam = CIV_TEAM;
822 	MercCreateStruct.sInsertionGridNo = pCorpse->def.sGridNo;
823 	RandomizeNewSoldierStats( &MercCreateStruct );
824 
825 	SOLDIERTYPE* const pSoldier = TacticalCreateSoldier(MercCreateStruct);
826 	if (pSoldier != NULL)
827 	{
828 		// Setup action data to point back to corpse....
829 		pSoldier->uiPendingActionData1 = CORPSE2ID(pCorpse);
830 		pSoldier->sPendingActionData2 = pCorpse->def.sGridNo;
831 
832 		pCorpse->def.bNumServicingCrows++;
833 
834 		const INT16 sGridNo = FindRandomGridNoFromSweetSpot(pSoldier, pCorpse->def.sGridNo, 2);
835 		if ( sGridNo != NOWHERE )
836 		{
837 			pSoldier->ubStrategicInsertionCode = INSERTION_CODE_GRIDNO;
838 			pSoldier->usStrategicInsertionData = sGridNo;
839 
840 			pSoldier->sInsertionGridNo = sGridNo;
841 			pSoldier->sDesiredHeight = 0;
842 
843 			AddSoldierToSector(pSoldier);
844 
845 			// Change to fly animation
846 			//sGridNo = FindRandomGridNoFromSweetSpot(pSoldier, pCorpse->def.sGridNo, 5);
847 			//pSoldier->usUIMovementMode = CROW_FLY;
848 			//EVENT_GetNewSoldierPath( pSoldier, sGridNo, pSoldier->usUIMovementMode );
849 		}
850 		else
851 		{
852 			TacticalRemoveSoldier(*pSoldier);
853 		}
854 
855 	}
856 
857 }
858 
HandleCrowLeave(SOLDIERTYPE * pSoldier)859 void HandleCrowLeave( SOLDIERTYPE *pSoldier )
860 {
861 	// Check if this crow is still referencing the same corpse...
862 	ROTTING_CORPSE* const pCorpse = ID2CORPSE(pSoldier->uiPendingActionData1);
863 
864 	// Double check grindo...
865 	if ( pSoldier->sPendingActionData2 == pCorpse->def.sGridNo )
866 	{
867 		// We have a match
868 		// Adjust crow servicing count...
869 		pCorpse->def.bNumServicingCrows--;
870 
871 		if ( pCorpse->def.bNumServicingCrows < 0 )
872 		{
873 			pCorpse->def.bNumServicingCrows = 0;
874 		}
875 	}
876 }
877 
878 
HandleCrowFlyAway(SOLDIERTYPE * pSoldier)879 void HandleCrowFlyAway( SOLDIERTYPE *pSoldier )
880 {
881 	// Set desired height
882 	pSoldier->sDesiredHeight = 100;
883 
884 	// Change to fly animation
885 	const INT16 sGridNo = FindRandomGridNoFromSweetSpot(pSoldier, pSoldier->sGridNo, 5);
886 	pSoldier->usUIMovementMode = CROW_FLY;
887 	SendGetNewSoldierPathEvent(pSoldier, sGridNo);
888 }
889 
890 
HandleRottingCorpses()891 void HandleRottingCorpses( )
892 {
893 	ROTTING_CORPSE *pCorpse;
894 	INT8   bNumCrows = 0;
895 	UINT32 uiChosenCorpseID;
896 
897 	// ATE: If it's too late, don't!
898 	if( NightTime() )
899 	{
900 		return;
901 	}
902 
903 	if ( gbWorldSectorZ > 0 )
904 	{
905 		return;
906 	}
907 
908 	// ATE: Check for multiple crows.....
909 	// Couint how many we have now...
910 	CFOR_EACH_IN_TEAM(s, CIV_TEAM)
911 	{
912 		if (s->bInSector &&
913 			s->bLife >= OKLIFE &&
914 			!(s->uiStatusFlags & SOLDIER_GASSED) &&
915 			s->ubBodyType == CROW)
916 		{
917 			bNumCrows++;
918 		}
919 	}
920 
921 	// Once population gets down to 0, we can add more again....
922 	if ( bNumCrows == 0 )
923 	{
924 		gTacticalStatus.fDontAddNewCrows = FALSE;
925 	}
926 
927 	if ( gTacticalStatus.fDontAddNewCrows )
928 	{
929 		return;
930 	}
931 
932 	if ( bNumCrows >= gTacticalStatus.ubNumCrowsPossible )
933 	{
934 		gTacticalStatus.fDontAddNewCrows = TRUE;
935 		return;
936 	}
937 
938 	if (IsTeamActive(CREATURE_TEAM))
939 	{
940 		// don't add any crows while there are predators around
941 		return;
942 	}
943 
944 	// Pick one to attact a crow...
945 	uiChosenCorpseID = Random( giNumRottingCorpse );
946 
947 	pCorpse = &(gRottingCorpse[ uiChosenCorpseID ] );
948 
949 	if ( pCorpse->fActivated )
950 	{
951 		if ( !( pCorpse->def.usFlags & ROTTING_CORPSE_VEHICLE ) )
952 		{
953 			if ( pCorpse->def.ubType == ROTTING_STAGE2 )
954 			{
955 				if ( GridNoOnScreen( pCorpse->def.sGridNo ) )
956 				{
957 					return;
958 				}
959 
960 				AddCrowToCorpse( pCorpse );
961 				AddCrowToCorpse( pCorpse );
962 			}
963 		}
964 	}
965 }
966 
967 
FindCorpseBasedOnStructure(GridNo const grid_no,STRUCTURE * const structure)968 static ROTTING_CORPSE* FindCorpseBasedOnStructure(GridNo const grid_no, STRUCTURE* const structure)
969 {
970 	for (LEVELNODE const* i = gpWorldLevelData[grid_no].pStructHead; i; i = i->pNext)
971 	{
972 		if (i->pStructureData != structure) continue;
973 		return ID2CORPSE(i->pAniTile->v.user.uiData);
974 	}
975 	return 0;
976 }
977 
978 
CorpseHit(INT16 sGridNo,UINT16 usStructureID)979 void CorpseHit( INT16 sGridNo, UINT16 usStructureID )
980 {
981 #if 0
982 	STRUCTURE *pStructure, *pBaseStructure;
983 	ROTTING_CORPSE *pCorpse = NULL;
984 	INT16 sBaseGridNo;
985 
986 	pStructure = FindStructureByID( sGridNo, usStructureID );
987 
988 	// Get base....
989 	pBaseStructure = FindBaseStructure( pStructure );
990 
991 	// Find base gridno...
992 	sBaseGridNo = pBaseStructure->sGridNo;
993 
994 	// Get corpse ID.....
995 	pCorpse = FindCorpseBasedOnStructure( sBaseGridNo, pBaseStructure );
996 
997 	if ( pCorpse == NULL )
998 	{
999 		SLOGD("Bullet hit corpse but corpse cannot be found at: %d", sBaseGridNo );
1000 		return;
1001 	}
1002 
1003 	// Twitch the bugger...
1004 	SLOGD("Corpse hit" );
1005 
1006 	if ( GridNoOnScreen( sBaseGridNo ) )
1007 	{
1008 		// Twitch....
1009 		// Set frame...
1010 		SetAniTileFrame(pCorpse->pAniTile, 1);
1011 
1012 		// Go reverse...
1013 		pCorpse->pAniTile->uiFlags |= ( ANITILE_BACKWARD | ANITILE_PAUSE_AFTER_LOOP );
1014 
1015 		// Turn off pause...
1016 		pCorpse->pAniTile->uiFlags &= (~ANITILE_PAUSED);
1017 	}
1018 
1019 	// PLay a sound....
1020 	PlayLocationJA2Sample(sGridNo, BULLET_IMPACT_2, MIDVOLUME, 1);
1021 #endif
1022 }
1023 
1024 
VaporizeCorpse(INT16 sGridNo,UINT16 usStructureID)1025 void VaporizeCorpse( INT16 sGridNo, UINT16 usStructureID )
1026 {
1027 	STRUCTURE *pStructure, *pBaseStructure;
1028 	ROTTING_CORPSE *pCorpse = NULL;
1029 	INT16 sBaseGridNo;
1030 	ANITILE_PARAMS AniParams;
1031 
1032 	pStructure = FindStructureByID( sGridNo, usStructureID );
1033 
1034 	// Get base....
1035 	pBaseStructure = FindBaseStructure( pStructure );
1036 
1037 	// Find base gridno...
1038 	sBaseGridNo = pBaseStructure->sGridNo;
1039 
1040 	// Get corpse ID.....
1041 	pCorpse = FindCorpseBasedOnStructure( sBaseGridNo, pBaseStructure );
1042 
1043 	if ( pCorpse == NULL )
1044 	{
1045 		SLOGW("Vaporize corpse but corpse cannot be found at: %d", sBaseGridNo );
1046 		return;
1047 	}
1048 
1049 	if ( pCorpse->def.usFlags & ROTTING_CORPSE_VEHICLE )
1050 	{
1051 		return;
1052 	}
1053 
1054 	if ( GridNoOnScreen( sBaseGridNo ) )
1055 	{
1056 		// Add explosion
1057 		AniParams = ANITILE_PARAMS{};
1058 		AniParams.sGridNo = sBaseGridNo;
1059 		AniParams.ubLevelID = ANI_STRUCT_LEVEL;
1060 		AniParams.sDelay = (INT16)( 80 );
1061 		AniParams.sStartFrame = 0;
1062 		AniParams.uiFlags = ANITILE_FORWARD;
1063 		AniParams.sX = CenterX( sBaseGridNo );
1064 		AniParams.sY = CenterY( sBaseGridNo );
1065 		AniParams.sZ = (INT16)pCorpse->def.sHeightAdjustment;
1066 		AniParams.zCachedFile = TILECACHEDIR "/gen_blow.sti";
1067 		CreateAnimationTile( &AniParams );
1068 
1069 		RemoveCorpse(pCorpse);
1070 		SetRenderFlags( RENDER_FLAG_FULL );
1071 
1072 		if ( pCorpse->def.bLevel == 0 )
1073 		{
1074 			// Set some blood......
1075 			SpreadEffect(sBaseGridNo, 2, 0, NULL, BLOOD_SPREAD_EFFECT, 0, NULL);
1076 		}
1077 	}
1078 
1079 	// PLay a sound....
1080 	PlayLocationJA2Sample(sGridNo, BODY_EXPLODE_1, HIGHVOLUME, 1);
1081 }
1082 
1083 
FindNearestAvailableGridNoForCorpse(ROTTING_CORPSE_DEFINITION * pDef,INT8 ubRadius)1084 INT16 FindNearestAvailableGridNoForCorpse( ROTTING_CORPSE_DEFINITION *pDef, INT8 ubRadius )
1085 {
1086 	INT16 sSweetGridNo;
1087 	INT16 sTop, sBottom;
1088 	INT16 sLeft, sRight;
1089 	INT16 cnt1, cnt2, cnt3;
1090 	INT16 sGridNo;
1091 	INT32 uiRange, uiLowestRange = 999999;
1092 	INT16 sLowestGridNo=0;
1093 	INT32 leftmost;
1094 	BOOLEAN fFound = FALSE;
1095 	SOLDIERTYPE soldier;
1096 	UINT8 ubSaveNPCAPBudget;
1097 	UINT8 ubSaveNPCDistLimit;
1098 	STRUCTURE_FILE_REF *pStructureFileRef = NULL;
1099 	UINT8 ubBestDirection=0;
1100 	BOOLEAN fSetDirection = FALSE;
1101 
1102 	cnt3 = 0;
1103 
1104 	// Get root filename... this removes path and extension
1105 	// Used to find struct data for this corpse...
1106 	ST::string zFilename(FileMan::getFileNameWithoutExt(zCorpseFilenames[pDef->ubType]));
1107 
1108 	pStructureFileRef = GetCachedTileStructureRefFromFilename( zFilename.c_str() );
1109 
1110 	sSweetGridNo = pDef->sGridNo;
1111 
1112 
1113 	//Save AI pathing vars.  changing the distlimit restricts how
1114 	//far away the pathing will consider.
1115 	ubSaveNPCAPBudget = gubNPCAPBudget;
1116 	ubSaveNPCDistLimit = gubNPCDistLimit;
1117 	gubNPCAPBudget = 0;
1118 	gubNPCDistLimit = ubRadius;
1119 
1120 	//create dummy soldier, and use the pathing to determine which nearby slots are
1121 	//reachable.
1122 	soldier = SOLDIERTYPE{};
1123 	soldier.bTeam = 1;
1124 	soldier.sGridNo = sSweetGridNo;
1125 
1126 	sTop    = ubRadius;
1127 	sBottom = -ubRadius;
1128 	sLeft   = - ubRadius;
1129 	sRight  = ubRadius;
1130 
1131 	//clear the mapelements of potential residue MAPELEMENT_REACHABLE flags
1132 	//in the square region.
1133 	for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
1134 	{
1135 		for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
1136 		{
1137 			sGridNo = sSweetGridNo + (WORLD_COLS * cnt1) + cnt2;
1138 			if ( sGridNo >=0 && sGridNo < WORLD_MAX )
1139 			{
1140 				gpWorldLevelData[ sGridNo ].uiFlags &= (~MAPELEMENT_REACHABLE);
1141 			}
1142 		}
1143 	}
1144 
1145 	//Now, find out which of these gridnos are reachable
1146 	//(use the fake soldier and the pathing settings)
1147 	FindBestPath( &soldier, NOWHERE, 0, WALKING, COPYREACHABLE, 0 );
1148 
1149 	uiLowestRange = 999999;
1150 
1151 	for( cnt1 = sBottom; cnt1 <= sTop; cnt1++ )
1152 	{
1153 		leftmost = ( ( sSweetGridNo + ( WORLD_COLS * cnt1 ) )/ WORLD_COLS ) * WORLD_COLS;
1154 
1155 		for( cnt2 = sLeft; cnt2 <= sRight; cnt2++ )
1156 		{
1157 			sGridNo = sSweetGridNo + ( WORLD_COLS * cnt1 ) + cnt2;
1158 			if (sGridNo >=0 && sGridNo < WORLD_MAX && sGridNo >= leftmost &&
1159 				sGridNo < (leftmost + WORLD_COLS) &&
1160 				gpWorldLevelData[sGridNo].uiFlags & MAPELEMENT_REACHABLE && !Water(sGridNo))
1161 			{
1162 				// Go on sweet stop
1163 				if ( NewOKDestination( &soldier, sGridNo, TRUE, soldier.bLevel ) )
1164 				{
1165 					BOOLEAN fDirectionFound = FALSE;
1166 					BOOLEAN	fCanSetDirection   = FALSE;
1167 
1168 					// Check each struct in each direction
1169 					if ( pStructureFileRef == NULL )
1170 					{
1171 						fDirectionFound = TRUE;
1172 					}
1173 					else
1174 					{
1175 						for( cnt3 = 0; cnt3 < 8; cnt3++ )
1176 						{
1177 							if (OkayToAddStructureToWorld(sGridNo, pDef->bLevel, &pStructureFileRef->pDBStructureRef[OneCDirection(cnt3)], INVALID_STRUCTURE_ID))
1178 							{
1179 								fDirectionFound = TRUE;
1180 								fCanSetDirection = TRUE;
1181 								break;
1182 							}
1183 						}
1184 					}
1185 
1186 					if ( fDirectionFound )
1187 					{
1188 						uiRange = GetRangeInCellCoordsFromGridNoDiff( sSweetGridNo, sGridNo );
1189 
1190 						if ( uiRange < uiLowestRange )
1191 						{
1192 							if ( fCanSetDirection )
1193 							{
1194 								ubBestDirection = (UINT8)cnt3;
1195 								fSetDirection   = TRUE;
1196 							}
1197 							sLowestGridNo = sGridNo;
1198 							uiLowestRange = uiRange;
1199 							fFound = TRUE;
1200 						}
1201 					}
1202 
1203 				}
1204 			}
1205 		}
1206 	}
1207 	gubNPCAPBudget = ubSaveNPCAPBudget;
1208 	gubNPCDistLimit = ubSaveNPCDistLimit;
1209 	if ( fFound )
1210 	{
1211 		if ( fSetDirection )
1212 		{
1213 			pDef->bDirection = ubBestDirection;
1214 		}
1215 
1216 		return sLowestGridNo;
1217 	}
1218 	return NOWHERE;
1219 }
1220 
1221 
IsValidDecapitationCorpse(const ROTTING_CORPSE * const c)1222 BOOLEAN IsValidDecapitationCorpse(const ROTTING_CORPSE* const c)
1223 {
1224 	if (c->def.fHeadTaken) return FALSE;
1225 	return gbCorpseValidForDecapitation[c->def.ubType];
1226 }
1227 
1228 
GetCorpseAtGridNo(INT16 sGridNo,INT8 bLevel)1229 ROTTING_CORPSE *GetCorpseAtGridNo( INT16 sGridNo, INT8 bLevel )
1230 {
1231 	STRUCTURE *pStructure, *pBaseStructure;
1232 
1233 	pStructure = FindStructure( sGridNo, STRUCTURE_CORPSE );
1234 
1235 	if ( pStructure != NULL )
1236 	{
1237 		// Get base....
1238 		pBaseStructure = FindBaseStructure( pStructure );
1239 
1240 		if ( pBaseStructure != NULL )
1241 		{
1242 			return( FindCorpseBasedOnStructure( pBaseStructure->sGridNo, pBaseStructure ) );
1243 		}
1244 	}
1245 
1246 	return( NULL );
1247 }
1248 
1249 
DecapitateCorpse(const INT16 sGridNo,const INT8 bLevel)1250 void DecapitateCorpse(const INT16 sGridNo, const INT8 bLevel)
1251 {
1252 	OBJECTTYPE Object;
1253 	ROTTING_CORPSE *pCorpse;
1254 	ROTTING_CORPSE_DEFINITION CorpseDef;
1255 
1256 	pCorpse = GetCorpseAtGridNo( sGridNo, bLevel );
1257 
1258 	if ( pCorpse == NULL )
1259 	{
1260 		return;
1261 	}
1262 
1263 	if ( IsValidDecapitationCorpse( pCorpse ) )
1264 	{
1265 		// Decapitate.....
1266 		// Copy corpse definition...
1267 		CorpseDef = pCorpse->def;
1268 
1269 		// Add new one...
1270 		CorpseDef.ubType = gDecapitatedCorpse[ CorpseDef.ubType ];
1271 
1272 		pCorpse->def.fHeadTaken = TRUE;
1273 
1274 		if ( CorpseDef.ubType != 0 )
1275 		{
1276 			RemoveCorpse(pCorpse);
1277 			AddRottingCorpse( &CorpseDef );
1278 		}
1279 
1280 		// Add head item.....
1281 
1282 		// Pick the head based on profile type...
1283 		UINT16 head_index;
1284 		switch (pCorpse->def.ubProfile)
1285 		{
1286 			case CHRIS:    head_index = HEAD_2; break;
1287 			case T_REX:    head_index = HEAD_3; break;
1288 			case SLAY:     head_index = HEAD_4; break;
1289 			case DRUGGIST: head_index = HEAD_5; break;
1290 			case ANNIE:    head_index = HEAD_6; break;
1291 			case TIFFANY:  head_index = HEAD_7; break;
1292 			default:       head_index = HEAD_1; break;
1293 		}
1294 
1295 		CreateItem(head_index, 100, &Object);
1296 		AddItemToPool( sGridNo, &Object, INVISIBLE, 0, 0, 0 );
1297 
1298 		// All teams lok for this...
1299 		NotifySoldiersToLookforItems( );
1300 	}
1301 
1302 }
1303 
1304 
GetBloodFromCorpse(SOLDIERTYPE * pSoldier)1305 void GetBloodFromCorpse( SOLDIERTYPE *pSoldier )
1306 {
1307 	const ROTTING_CORPSE* const pCorpse = ID2CORPSE(pSoldier->uiPendingActionData4);
1308 	INT8 bObjSlot;
1309 	OBJECTTYPE Object;
1310 
1311 	bObjSlot = FindObj( pSoldier, JAR );
1312 
1313 	// What kind of corpse ami I?
1314 	switch( pCorpse->def.ubType )
1315 	{
1316 		case ADULTMONSTER_DEAD:
1317 		case INFANTMONSTER_DEAD:
1318 
1319 			// Can get creature blood....
1320 			CreateItem( JAR_CREATURE_BLOOD, 100, &Object );
1321 			break;
1322 
1323 		case QUEEN_MONSTER_DEAD:
1324 			CreateItem( JAR_QUEEN_CREATURE_BLOOD, 100, &Object );
1325 			break;
1326 
1327 		default:
1328 
1329 			CreateItem( JAR_HUMAN_BLOOD, 100, &Object );
1330 			break;
1331 
1332 	}
1333 
1334 	if ( bObjSlot != NO_SLOT )
1335 	{
1336 		SwapObjs( &(pSoldier->inv[ bObjSlot ] ), &Object );
1337 	}
1338 }
1339 
1340 
ReduceAmmoDroppedByNonPlayerSoldiers(SOLDIERTYPE const & s,OBJECTTYPE & o)1341 void ReduceAmmoDroppedByNonPlayerSoldiers(SOLDIERTYPE const& s, OBJECTTYPE& o)
1342 {
1343 	if (s.bTeam == OUR_TEAM) return;
1344 	if (GCM->getItem(o.usItem)->getItemClass() != IC_AMMO) return;
1345 
1346 	// Don't drop all the clips, just a random # of them between 1 and how
1347 	// many there are
1348 	o.ubNumberOfObjects = 1 + Random(o.ubNumberOfObjects);
1349 	o.ubWeight          = CalculateObjectWeight(&o);
1350 }
1351 
1352 
LookForAndMayCommentOnSeeingCorpse(SOLDIERTYPE * pSoldier,INT16 sGridNo,UINT8 ubLevel)1353 void LookForAndMayCommentOnSeeingCorpse( SOLDIERTYPE *pSoldier, INT16 sGridNo, UINT8 ubLevel )
1354 {
1355 	ROTTING_CORPSE *pCorpse;
1356 	INT8            bToleranceThreshold = 0;
1357 
1358 	if ( QuoteExp_HeadShotOnly[ pSoldier->ubProfile ] == 1 )
1359 	{
1360 		return;
1361 	}
1362 
1363 	pCorpse = GetCorpseAtGridNo( sGridNo, ubLevel );
1364 
1365 	if ( pCorpse == NULL )
1366 	{
1367 		return;
1368 	}
1369 
1370 	if ( pCorpse->def.ubType != ROTTING_STAGE2 )
1371 	{
1372 		return;
1373 	}
1374 
1375 	// If servicing qrows, tolerance is now 1
1376 	if ( pCorpse->def.bNumServicingCrows > 0 )
1377 	{
1378 		bToleranceThreshold++;
1379 	}
1380 
1381 	// Check tolerance
1382 	if ( pSoldier->bCorpseQuoteTolerance <= bToleranceThreshold )
1383 	{
1384 		// Say quote...
1385 		TacticalCharacterDialogue( pSoldier, QUOTE_HEADSHOT );
1386 
1387 		BeginMultiPurposeLocator(sGridNo, ubLevel);
1388 
1389 		// Reset values....
1390 		pSoldier->bCorpseQuoteTolerance = (INT8)( Random(3) + 1 );
1391 
1392 		// 50% chance of adding 1 to other mercs....
1393 		if ( Random( 2 ) == 1 )
1394 		{
1395 			FOR_EACH_IN_TEAM(s, OUR_TEAM)
1396 			{
1397 				// ATE: Ok, lets check for some basic things here!
1398 				if (s->bLife >= OKLIFE && s->sGridNo != NOWHERE && s->bInSector)
1399 				{
1400 					++s->bCorpseQuoteTolerance;
1401 				}
1402 			}
1403 		}
1404 	}
1405 }
1406 
1407 
GetGridNoOfCorpseGivenProfileID(const UINT8 ubProfileID)1408 INT16 GetGridNoOfCorpseGivenProfileID(const UINT8 ubProfileID)
1409 {
1410 	// Loop through all corpses....
1411 	CFOR_EACH_ROTTING_CORPSE(c)
1412 	{
1413 		if (c->def.ubProfile == ubProfileID) return c->def.sGridNo;
1414 	}
1415 	return NOWHERE;
1416 }
1417 
1418 
DecayRottingCorpseAIWarnings(void)1419 void DecayRottingCorpseAIWarnings(void)
1420 {
1421 	FOR_EACH_ROTTING_CORPSE(c)
1422 	{
1423 		if (c->def.ubAIWarningValue > 0) --c->def.ubAIWarningValue;
1424 	}
1425 }
1426 
1427 
GetNearestRottingCorpseAIWarning(const INT16 sGridNo)1428 UINT8 GetNearestRottingCorpseAIWarning(const INT16 sGridNo)
1429 {
1430 	UINT8 ubHighestWarning = 0;
1431 	CFOR_EACH_ROTTING_CORPSE(c)
1432 	{
1433 		if (c->def.ubAIWarningValue > 0 &&
1434 			PythSpacesAway(sGridNo, c->def.sGridNo) <= CORPSE_WARNING_DIST &&
1435 			c->def.ubAIWarningValue > ubHighestWarning)
1436 		{
1437 			ubHighestWarning = c->def.ubAIWarningValue;
1438 		}
1439 	}
1440 	return ubHighestWarning;
1441 }
1442