mirror of
https://github.com/1dot13/source.git
synced 2026-07-22 13:40:22 +02:00
- mercs can refuse to eat if they do not like the food (works only once) git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5422 3b4a5df2-a311-0410-b5c6-a8a6f20db521
997 lines
37 KiB
C++
997 lines
37 KiB
C++
#ifdef PRECOMPILEDHEADERS
|
|
#include "Tactical All.h"
|
|
#else
|
|
#include <math.h>
|
|
#include "sgp.h"
|
|
#include "soldier profile.h"
|
|
#include "Food.h"
|
|
#include "items.h"
|
|
#include "morale.h"
|
|
#include "points.h"
|
|
#include "message.h"
|
|
#include "GameSettings.h" // SANDRO - had to add this, dammit!
|
|
#include "Random.h"
|
|
#include "Text.h"
|
|
#include "Interface.h"
|
|
#include "Dialogue Control.h"
|
|
#include "Sound Control.h"
|
|
#include "Assignments.h"
|
|
#include "Overhead.h"
|
|
#include "worldman.h"
|
|
#include "Isometric Utils.h"
|
|
#include "Campaign Types.h"
|
|
#include "Drugs And Alcohol.h"
|
|
#include "environment.h"
|
|
//#include "Game Clock.h"
|
|
#include "WorldDat.h"
|
|
#include "Facilities.h"
|
|
#endif
|
|
|
|
//forward declarations of common classes to eliminate includes
|
|
class OBJECTTYPE;
|
|
class SOLDIERTYPE;
|
|
|
|
extern MoraleEvent gbMoraleEvent[NUM_MORALE_EVENTS];
|
|
|
|
UINT8 gSectorWaterType[256][4];
|
|
|
|
//extern BOOLEAN HandleSoldierDeath( SOLDIERTYPE *pSoldier , BOOLEAN *pfMadeCorpse );
|
|
//extern BOOLEAN TacticalRemoveSoldier( UINT16 usSoldierIndex );
|
|
extern BOOLEAN GetSectorFlagStatus( INT16 sMapX, INT16 sMapY, UINT8 bMapZ, UINT32 uiFlagToSet );
|
|
extern void BuildStashForSelectedSectorAndDecayFood( INT16 sMapX, INT16 sMapY, INT16 sMapZ );
|
|
|
|
FoodMoraleMod FoodMoraleMods[NUM_FOOD_MORALE_TYPES] =
|
|
{
|
|
{ 100000, -10, -3, - -75, -75, 2}, // FOOD_STUFFED
|
|
{ 5000, -5, -2, -5, -5, 0}, // FOOD_EXTREMELY_FULL
|
|
{ 2500, 2, -1, -1, 0, 0}, // FOOD_FULL
|
|
{ 1000, 5, 0, 0, 0, 0}, // FOOD_SLIGHTLY_FULL
|
|
|
|
{ 0, 0, 0, 0, 0, 0}, // FOOD_NORMAL
|
|
|
|
{ -1000, 0, 0, 0, 0, 0}, // FOOD_LOW
|
|
{ -2500, -2, 0, 0, 0, 0}, // FOOD_EVEN_LOWER
|
|
{ -5000, -10, -1, -10, -10, 5}, // FOOD_VERY_LOW
|
|
{ -7500, -20, -2, -25, -25, 25}, // FOOD_DANGER
|
|
{ -8750, -30, -2, -50, -50, 75}, // FOOD_DESPERATE
|
|
{ -10000, -50, -3, -80, -75, 100}, // FOOD_STARVING
|
|
};
|
|
|
|
|
|
BOOLEAN ApplyFood( SOLDIERTYPE *pSoldier, OBJECTTYPE *pObject, BOOLEAN fForce )
|
|
{
|
|
// static variables to remember the last food someone was forced to eat
|
|
static UINT8 lasteater = 0;
|
|
static UINT16 lastitem = 0;
|
|
|
|
// how did this even happen?
|
|
if ( !pSoldier || !pObject || !(pObject->exists() ) || (*pObject)[0]->data.objectStatus < 1 )
|
|
return( FALSE);
|
|
|
|
UINT32 foodtype = Item[pObject->usItem].foodtype;
|
|
|
|
// if not a food item, nothing to see here
|
|
if ( foodtype == 0 || foodtype > FOOD_TYPE_MAX )
|
|
return( FALSE);
|
|
|
|
// workaround: canteens with 1% status are treated as 'empty'. They cannot be consumed, but refilled
|
|
if ( Item[pObject->usItem].canteen == TRUE && (*pObject)[0]->data.objectStatus == 1 )
|
|
return( FALSE);
|
|
|
|
// do we eat or drink this stuff?
|
|
UINT8 type = AP_EAT;
|
|
if ( Food[foodtype].bDrinkPoints > Food[foodtype].bFoodPoints )
|
|
type = AP_DRINK;
|
|
|
|
// return if we don't have enough APs
|
|
if (!EnoughPoints( pSoldier, APBPConstants[type], 0, TRUE ) )
|
|
{
|
|
return( FALSE );
|
|
}
|
|
|
|
// check if we are willing to eat this: if we're filled, the merc refuses
|
|
if ( ( ( Food[foodtype].bFoodPoints > 0 && pSoldier->bFoodLevel > FoodMoraleMods[FOOD_EXTREMELY_FULL].bThreshold ) || Food[foodtype].bFoodPoints <= 0 ) && ( ( Food[foodtype].bDrinkPoints > 0 && pSoldier->bDrinkLevel > FoodMoraleMods[FOOD_EXTREMELY_FULL].bThreshold ) || Food[foodtype].bDrinkPoints <= 0 ) )
|
|
{
|
|
// Say quote!
|
|
TacticalCharacterDialogue( pSoldier, 61 );
|
|
|
|
return( FALSE);
|
|
}
|
|
|
|
// we have to determine wether the food is rotten, that might influence its condition and poison us
|
|
FLOAT foodcondition = (*pObject)[0]->data.bTemperature / OVERHEATING_MAX_TEMPERATURE;
|
|
|
|
// food in bad condition is harmful!
|
|
if ( foodcondition < FOOD_BAD_THRESHOLD )
|
|
{
|
|
// if we can choose to reject a food and haven't yet done so with this type of bad food, do so. Works only once
|
|
if ( !fForce && ( lasteater != pSoldier->ubID || lastitem != pObject->usItem) )
|
|
{
|
|
lasteater = pSoldier->ubID;
|
|
lastitem = pObject->usItem;
|
|
|
|
// notification
|
|
if ( type == AP_EAT )
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not want to eat %s", pSoldier->name, Item[pObject->usItem].szItemName );
|
|
else
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s does not want to drink %s", pSoldier->name, Item[pObject->usItem].szItemName );
|
|
|
|
// Say quote!
|
|
TacticalCharacterDialogue( pSoldier, 61 );
|
|
|
|
return( FALSE);
|
|
}
|
|
|
|
// determine the max nutritional value
|
|
INT32 maxpts = max(Food[foodtype].bFoodPoints, Food[foodtype].bDrinkPoints);
|
|
|
|
// divide by 100 (poison sum is in [0, 99]
|
|
// multiply with FOOD_BAD_THRESHOLD for more reasonable values
|
|
INT8 poisonadd = (INT8)(0.01 * maxpts * (1.0 - foodcondition) * FOOD_BAD_THRESHOLD );
|
|
|
|
pSoldier->AddPoison(poisonadd);
|
|
}
|
|
|
|
FLOAT conditionmodifier = 0.5f * (1.0f + sqrt(foodcondition));
|
|
|
|
FLOAT percentualsize = min(Food[foodtype].ubPortionSize, (*pObject)[0]->data.objectStatus) / 100.0f;
|
|
|
|
INT32 foodpts = (INT32) (Food[foodtype].bFoodPoints * percentualsize * conditionmodifier );
|
|
INT32 drinkpts = (INT32) (Food[foodtype].bDrinkPoints * percentualsize * conditionmodifier );
|
|
|
|
// eat it!
|
|
pSoldier->bFoodLevel = min(pSoldier->bFoodLevel + foodpts, FOOD_MAX);
|
|
pSoldier->bFoodLevel = max(pSoldier->bFoodLevel, FOOD_MIN);
|
|
|
|
pSoldier->bDrinkLevel = min(pSoldier->bDrinkLevel + drinkpts, DRINK_MAX);
|
|
pSoldier->bDrinkLevel = max(pSoldier->bDrinkLevel, DRINK_MIN);
|
|
|
|
/////////////////// MORALE //////////////////////
|
|
INT8 moralemod = Food[foodtype].bMoraleMod;
|
|
|
|
if ( moralemod > 0 )
|
|
// morale is lower if food is rotten, can even become negative
|
|
moralemod = (INT8) ( moralemod * (FOOD_BAD_THRESHOLD_INVERSE*foodcondition - 1.0f) );
|
|
else
|
|
// if we hate the food anyway, give even lower morale if its rotten
|
|
moralemod = (INT8) ( moralemod * (2.0f - foodcondition) );
|
|
|
|
if ( moralemod > 0 )
|
|
{
|
|
while ( moralemod > 0 && moralemod >= gbMoraleEvent[MORALE_GOOD_FOOD].bChange )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_GOOD_FOOD, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
moralemod -= gbMoraleEvent[MORALE_GOOD_FOOD].bChange;
|
|
}
|
|
|
|
while ( moralemod > 0 && moralemod >= gbMoraleEvent[MORALE_FOOD].bChange )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_FOOD, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
moralemod -= gbMoraleEvent[MORALE_FOOD].bChange;
|
|
}
|
|
}
|
|
else if ( moralemod < 0 )
|
|
{
|
|
while ( moralemod < 0 && moralemod <= gbMoraleEvent[MORALE_LOATHSOME_FOOD].bChange )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_LOATHSOME_FOOD, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
moralemod -= gbMoraleEvent[MORALE_LOATHSOME_FOOD].bChange;
|
|
}
|
|
|
|
while ( moralemod < 0 && moralemod <= gbMoraleEvent[MORALE_BAD_FOOD].bChange )
|
|
{
|
|
HandleMoraleEvent( pSoldier, MORALE_BAD_FOOD, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
moralemod -= gbMoraleEvent[MORALE_BAD_FOOD].bChange;
|
|
}
|
|
}
|
|
/////////////////// MORALE //////////////////////
|
|
|
|
// notification
|
|
if ( type == AP_EAT )
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s ate %s", pSoldier->name, Item[pObject->usItem].szItemName );
|
|
else
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s drank %s", pSoldier->name, Item[pObject->usItem].szItemName );
|
|
|
|
// now remove a portion of the food item (or the whole item altogether)
|
|
UINT16 ptsconsumed = UseKitPoints( pObject, Food[foodtype].ubPortionSize, pSoldier );
|
|
|
|
INT32 sBPAdjustment = 0;
|
|
// if the food is more of a drink, we also restore breath points
|
|
if ( Food[foodtype].bDrinkPoints > Food[foodtype].bFoodPoints )
|
|
{
|
|
//UINT16 ptsconsumed = Food[foodtype].ubPortionSize - ptsleft;
|
|
|
|
INT16 usTotalKitPoints = TotalPoints( pObject );
|
|
|
|
INT16 sPointsToUse = __min( ptsconsumed, usTotalKitPoints );
|
|
|
|
sBPAdjustment = 2 * sPointsToUse * -(100 - pSoldier->bBreath);
|
|
}
|
|
|
|
DeductPoints( pSoldier, APBPConstants[type], sBPAdjustment );
|
|
|
|
// let it be known that we are eating
|
|
if ( pSoldier->bTeam == gbPlayerNum )
|
|
{
|
|
if ( type == AP_EAT )
|
|
{
|
|
// Play sound
|
|
PlayJA2SampleFromFile( "Sounds\\eat1.wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
else
|
|
{
|
|
if ( gMercProfiles[ pSoldier->ubProfile ].bSex == MALE )
|
|
{
|
|
PlayJA2Sample( DRINK_CANTEEN_MALE, RATE_11025, MIDVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
else
|
|
{
|
|
PlayJA2Sample( DRINK_CANTEEN_FEMALE, RATE_11025, MIDVOLUME, 1, MIDDLEPAN );
|
|
}
|
|
}
|
|
}
|
|
|
|
return( TRUE );
|
|
}
|
|
|
|
UINT8 GetFoodSituation( SOLDIERTYPE *pSoldier )
|
|
{
|
|
UINT8 foodsituation = FOOD_NORMAL;
|
|
|
|
if ( !pSoldier )
|
|
return( foodsituation );
|
|
|
|
// The lowest value determines how bad we feel
|
|
INT32 missamount = min( pSoldier->bFoodLevel, pSoldier->bDrinkLevel);
|
|
|
|
// old code
|
|
/*INT32 missamount = 0;
|
|
if ( pSoldier->bFoodLevel < 0 )
|
|
missamount += pSoldier->bFoodLevel;
|
|
|
|
if ( pSoldier->bDrinkLevel < 0 )
|
|
missamount += (INT32)(FOOD_MORALE_DRINK_TO_FOOD_RATIO * pSoldier->bDrinkLevel);*/
|
|
|
|
for ( UINT8 i = FOOD_STUFFED; i < NUM_FOOD_MORALE_TYPES; ++i )
|
|
{
|
|
if ( missamount < FoodMoraleMods[i].bThreshold )
|
|
foodsituation = i;
|
|
else
|
|
break;
|
|
}
|
|
|
|
return( foodsituation );
|
|
}
|
|
|
|
void FoodMaxMoraleModifiy( SOLDIERTYPE *pSoldier, UINT8* pubMaxMorale )
|
|
{
|
|
if ( !pSoldier )
|
|
return;
|
|
|
|
UINT8 foodsituation = GetFoodSituation( pSoldier );
|
|
|
|
INT8 mod = FoodMoraleMods[foodsituation].bMoraleModifier;
|
|
|
|
if ( (*pubMaxMorale) + mod > 0 )
|
|
(*pubMaxMorale) += mod;
|
|
else
|
|
(*pubMaxMorale) = 0;
|
|
}
|
|
|
|
void FoodNeedForSleepModifiy( SOLDIERTYPE *pSoldier, UINT8* pubNeedForSleep )
|
|
{
|
|
if ( !pSoldier )
|
|
return;
|
|
|
|
UINT8 foodsituation = GetFoodSituation( pSoldier );
|
|
|
|
(*pubNeedForSleep) += max(1, (INT16)((*pubNeedForSleep) - FoodMoraleMods[foodsituation].bSleepModifier));
|
|
}
|
|
|
|
void ReducePointsForHunger( SOLDIERTYPE *pSoldier, UINT32 *pusPoints )
|
|
{
|
|
UINT8 foodsituation = GetFoodSituation( pSoldier );
|
|
|
|
INT8 mod = FoodMoraleMods[foodsituation].bAssignmentEfficiencyModifier;
|
|
|
|
*pusPoints = (UINT32)((*pusPoints) * (100 - mod)/100);
|
|
}
|
|
|
|
void ReduceBPRegenForHunger( SOLDIERTYPE *pSoldier, INT32 *psPoints )
|
|
{
|
|
UINT8 foodsituation = GetFoodSituation( pSoldier );
|
|
|
|
INT8 mod = FoodMoraleMods[foodsituation].bBreathRegenModifier;
|
|
|
|
*psPoints = (INT32)((*psPoints) * (100 - mod)/100);
|
|
}
|
|
|
|
void HourlyFoodSituationUpdate( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( !pSoldier )
|
|
return;
|
|
|
|
// determine our current activity level
|
|
FLOAT activitymodifier = gGameExternalOptions.sFoodDigestionOnDuty;
|
|
if ( pSoldier->flags.fMercAsleep == TRUE )
|
|
activitymodifier = gGameExternalOptions.sFoodDigestionSleep;
|
|
else if ( !pSoldier->bInSector )
|
|
{
|
|
if( pSoldier->bAssignment == VEHICLE )
|
|
activitymodifier = gGameExternalOptions.sFoodDigestionTravelVehicle;
|
|
else
|
|
activitymodifier = gGameExternalOptions.sFoodDigestionTravel;
|
|
}
|
|
else if ( pSoldier->bAssignment > DOCTOR )
|
|
activitymodifier = gGameExternalOptions.sFoodDigestionAssignment;
|
|
else if ( (gTacticalStatus.uiFlags & INCOMBAT) )
|
|
activitymodifier = gGameExternalOptions.sFoodDigestionCombat;
|
|
|
|
// for some odd reason, the time isn't even needed here, so we just use 0 :-)
|
|
INT8 sectortemperaturemod = SectorTemperature( 0, pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ );
|
|
|
|
// if we are heat intolerant, increase modifier
|
|
if ( sectortemperaturemod > 0 && (gMercProfiles[ pSoldier->ubProfile ].bDisability == HEAT_INTOLERANT || MercUnderTheInfluence(pSoldier, DRUG_TYPE_HEATINTOLERANT) ) )
|
|
++sectortemperaturemod;
|
|
|
|
FLOAT temperaturemodifier = (FLOAT)(3 + sectortemperaturemod)/3;
|
|
|
|
// due to digestion, reduce our food and drink levels
|
|
pSoldier->bFoodLevel = max(pSoldier->bFoodLevel - (INT32) (activitymodifier * gGameExternalOptions.usFoodDigestionHourlyBaseFood), FOOD_MIN);
|
|
pSoldier->bDrinkLevel = max(pSoldier->bDrinkLevel - (INT32) (activitymodifier * temperaturemodifier * gGameExternalOptions.usFoodDigestionHourlyBaseDrink), DRINK_MIN);
|
|
|
|
// there is a chance that we take damage to our health and strength stats if we are starving (or insanely obese :-) )
|
|
UINT8 foodsituation = GetFoodSituation( pSoldier );
|
|
|
|
UINT8 statdamagechance = FoodMoraleMods[foodsituation].ubStatDamageChance;
|
|
|
|
if ( statdamagechance > 0 )
|
|
{
|
|
// these reductions can be healed, but only if we are in a sufficient food situation again
|
|
// damage strength
|
|
if ( Random(100) < statdamagechance )
|
|
{
|
|
UINT8 numberofreduces = 1;
|
|
// if starving, we lose stats a LOT faster
|
|
if ( foodsituation == FOOD_STARVING )
|
|
numberofreduces += Random(2);
|
|
|
|
INT8 oldval = pSoldier->stats.bStrength;
|
|
pSoldier->stats.bStrength = max(1, pSoldier->stats.bStrength - numberofreduces);
|
|
pSoldier->usStarveDamageStrength += oldval - pSoldier->stats.bStrength;
|
|
|
|
// Update Profile
|
|
gMercProfiles[ pSoldier->ubProfile ].bStrength = pSoldier->stats.bStrength;
|
|
gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++;
|
|
|
|
// make stat RED for a while...
|
|
pSoldier->timeChanges.uiChangeStrengthTime = GetJA2Clock();
|
|
pSoldier->usValueGoneUp &= ~( STRENGTH_INCREASE );
|
|
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s's strength was damaged due to lack of nutrition!", pSoldier->name );
|
|
}
|
|
|
|
// damage health
|
|
if ( Random(100) < statdamagechance )
|
|
{
|
|
UINT8 numberofreduces = 1;
|
|
// if starving, we lose stats a LOT faster
|
|
if ( foodsituation == FOOD_STARVING )
|
|
numberofreduces += 1 + 2 * Random(4);
|
|
|
|
INT8 oldlife = pSoldier->stats.bLife;
|
|
|
|
pSoldier->stats.bLifeMax = max(2, pSoldier->stats.bLifeMax - numberofreduces);
|
|
pSoldier->stats.bLife = min(pSoldier->stats.bLife, pSoldier->stats.bLifeMax);
|
|
pSoldier->bBleeding = min(pSoldier->bBleeding, pSoldier->stats.bLifeMax);
|
|
|
|
// adjust poison values
|
|
pSoldier->bPoisonSum = min(pSoldier->bPoisonSum, pSoldier->stats.bLifeMax);
|
|
pSoldier->bPoisonLife = min(pSoldier->bPoisonLife, pSoldier->bPoisonSum);
|
|
pSoldier->bPoisonBleeding = min(pSoldier->bPoisonBleeding, pSoldier->bBleeding);
|
|
|
|
pSoldier->usStarveDamageHealth += oldlife - pSoldier->stats.bLifeMax;
|
|
|
|
// Update Profile
|
|
gMercProfiles[ pSoldier->ubProfile ].bLifeMax = pSoldier->stats.bLifeMax;
|
|
gMercProfiles[ pSoldier->ubProfile ].records.usTimesStatDamaged++;
|
|
|
|
ScreenMsg( FONT_MCOLOR_LTYELLOW, MSG_INTERFACE, L"%s's health was damaged due to lack of nutrition!", pSoldier->name );
|
|
|
|
// if we fall below OKLIFE, we start bleeding...
|
|
// Reason for this is that
|
|
if ( pSoldier->stats.bLife < OKLIFE )
|
|
{
|
|
// handling a soldiers death doesn't work somehow - let them bleed to death instead
|
|
/*BOOLEAN fMadeCorpse;
|
|
HandleSoldierDeath( pSoldier, &fMadeCorpse );
|
|
|
|
//remove the merc from the tactical
|
|
TacticalRemoveSoldier( pSoldier->ubID );*/
|
|
|
|
pSoldier->bBleeding = max(1, pSoldier->stats.bLife - 1);
|
|
pSoldier->stats.bLife = 1;
|
|
|
|
// Update Profile
|
|
gMercProfiles[ pSoldier->ubProfile ].bLifeMax = pSoldier->stats.bLifeMax;
|
|
pSoldier->usStarveDamageHealth += oldlife - pSoldier->stats.bLifeMax;
|
|
|
|
return;
|
|
}
|
|
|
|
// make stat RED for a while...
|
|
pSoldier->timeChanges.uiChangeStrengthTime = GetJA2Clock();
|
|
pSoldier->usValueGoneUp &= ~( HEALTH_INCREASE );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void HourlyFoodAutoDigestion( SOLDIERTYPE *pSoldier )
|
|
{
|
|
if ( !pSoldier )
|
|
return;
|
|
|
|
// don't eat if not necessary ( note that if the play decides to eat manually, he can achieve better results. This is intended to award micro-management)
|
|
if ( pSoldier->bFoodLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && pSoldier->bDrinkLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold )
|
|
return;
|
|
|
|
// if were a prisoner, we can't feed ourself, and the player can't do that either. Instead the army provides food (not much and of bad quality)
|
|
if (pSoldier->bAssignment == ASSIGNMENT_POW)
|
|
{
|
|
INT16 powwater = gGameExternalOptions.usFoodDigestionHourlyBaseDrink * FOOD_POW_MULTIPLICATOR;
|
|
INT16 powfoodadd = powwater * gGameExternalOptions.usFoodDigestionHourlyBaseFood / gGameExternalOptions.usFoodDigestionHourlyBaseDrink;
|
|
|
|
// if we're thirsty or hungry, and this is nutritious, consume it
|
|
if ( pSoldier->bDrinkLevel < FoodMoraleMods[FOOD_VERY_LOW].bThreshold )
|
|
{
|
|
pSoldier->bDrinkLevel = min(pSoldier->bDrinkLevel + powwater, DRINK_MAX);
|
|
pSoldier->bDrinkLevel = max(pSoldier->bDrinkLevel, DRINK_MIN);
|
|
}
|
|
|
|
if ( pSoldier->bFoodLevel < FoodMoraleMods[FOOD_VERY_LOW].bThreshold )
|
|
{
|
|
pSoldier->bFoodLevel = min(pSoldier->bFoodLevel + powfoodadd, FOOD_MAX);
|
|
pSoldier->bFoodLevel = max(pSoldier->bFoodLevel, FOOD_MIN);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no eating if not able to!
|
|
if ( pSoldier->flags.fMercAsleep == TRUE || pSoldier->stats.bLife < OKLIFE )
|
|
return;
|
|
|
|
// In certain facilities, we can also eat
|
|
for (UINT16 cnt = 0; cnt < NUM_FACILITY_TYPES; ++cnt)
|
|
{
|
|
// Is this facility here?
|
|
if (gFacilityLocations[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)][cnt].fFacilityHere)
|
|
{
|
|
// Does it allow training militia?
|
|
if (gFacilityTypes[cnt].AssignmentData[FAC_FOOD].sCantinaFoodModifier > 0)
|
|
{
|
|
if (cnt == (UINT16)pSoldier->sFacilityTypeOperated && // Soldier is operating this facility
|
|
GetSoldierFacilityAssignmentIndex( pSoldier ) != -1)
|
|
{
|
|
INT16 cantinafoodadd = gFacilityTypes[cnt].AssignmentData[FAC_FOOD].sCantinaFoodModifier;
|
|
INT16 cantinawater = cantinafoodadd * FOOD_FACILITY_WATER_FACTOR;
|
|
|
|
// if we're thirsty or hungry, and this is nutritious, consume it. When in a cantina, we are willing to eat a bit more
|
|
if ( pSoldier->bDrinkLevel < FoodMoraleMods[FOOD_MERC_STOP_FACILITY].bThreshold )
|
|
{
|
|
pSoldier->bDrinkLevel = min(pSoldier->bDrinkLevel + cantinawater, DRINK_MAX);
|
|
pSoldier->bDrinkLevel = max(pSoldier->bDrinkLevel, DRINK_MIN);
|
|
}
|
|
|
|
if ( pSoldier->bFoodLevel < FoodMoraleMods[FOOD_MERC_STOP_FACILITY].bThreshold )
|
|
{
|
|
pSoldier->bFoodLevel = min(pSoldier->bFoodLevel + cantinafoodadd, FOOD_MAX);
|
|
pSoldier->bFoodLevel = max(pSoldier->bFoodLevel, FOOD_MIN);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// search for food in our inventory
|
|
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
|
|
|
// on the first loop, we omit food in bad condition, and refillable canteens and canned food
|
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
|
{
|
|
// ... if Item exists and is food ...
|
|
if (pSoldier->inv[bLoop].exists() == true && Item[pSoldier->inv[bLoop].usItem].foodtype > 0 )
|
|
{
|
|
OBJECTTYPE * pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
|
{
|
|
UINT32 foodtype = Item[pObj->usItem].foodtype;
|
|
|
|
// omit non-degrading food (save it for later!)
|
|
if ( Food[foodtype].usDecayRate <= 0.0f )
|
|
continue;
|
|
|
|
// omit bad food (we don't like that and will eat it only if we have to)
|
|
FLOAT foodcondition = (*pObj)[0]->data.bTemperature / OVERHEATING_MAX_TEMPERATURE;
|
|
if ( foodcondition < FOOD_BAD_THRESHOLD )
|
|
continue;
|
|
|
|
if ( Item[pObj->usItem].canteen == TRUE )
|
|
continue;
|
|
|
|
// if we're thirsty or hungry, and this is nutritious, consume it
|
|
if ( ( pSoldier->bDrinkLevel < FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && Food[foodtype].bDrinkPoints > 0 ) || ( pSoldier->bFoodLevel < FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && Food[foodtype].bFoodPoints > 0 ) )
|
|
{
|
|
while ( (*pObj)[0]->data.objectStatus > 1 )
|
|
{
|
|
// if food is also a drug, ApplyDrugs will also call ApplyFood
|
|
if ( Item[pObj->usItem].drugtype > 0 )
|
|
ApplyDrugs( pSoldier, pObj );
|
|
else
|
|
ApplyFood( pSoldier, pObj, TRUE ); // cannot reject to eat this, we chose to eat this ourself!
|
|
|
|
// if we're full, finish
|
|
if ( pSoldier->bFoodLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && pSoldier->bDrinkLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold )
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// second loop: consume anything to feed
|
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
|
{
|
|
// ... if Item exists and is food ...
|
|
if (pSoldier->inv[bLoop].exists() == true && Item[pSoldier->inv[bLoop].usItem].foodtype > 0 )
|
|
{
|
|
OBJECTTYPE * pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
|
{
|
|
UINT32 foodtype = Item[pObj->usItem].foodtype;
|
|
|
|
// if we're thirsty or hungry, and this is nutritious, consume it
|
|
if ( ( pSoldier->bDrinkLevel < FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && Food[foodtype].bDrinkPoints > 0 ) || ( pSoldier->bFoodLevel < FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && Food[foodtype].bFoodPoints > 0 ) )
|
|
{
|
|
while ( (*pObj)[0]->data.objectStatus > 1 )
|
|
{
|
|
// if food is also a drug, ApplyDrugs will also call ApplyFood
|
|
if ( Item[pObj->usItem].drugtype > 0 )
|
|
ApplyDrugs( pSoldier, pObj );
|
|
else
|
|
ApplyFood( pSoldier, pObj, TRUE ); // cannot reject to eat this, we chose to eat this ourself!
|
|
|
|
// if we're full, finish
|
|
if ( pSoldier->bFoodLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold && pSoldier->bDrinkLevel > FoodMoraleMods[FOOD_MERC_START_CONSUME].bThreshold )
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void HourlyFoodUpdate( void )
|
|
{
|
|
INT8 bMercID, bLastTeamID;
|
|
SOLDIERTYPE * pSoldier = NULL;
|
|
|
|
bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID;
|
|
|
|
// loop through all mercs to calculate their morale
|
|
for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; ++bMercID, ++pSoldier)
|
|
{
|
|
//if the merc is active, and in Arulco
|
|
if ( pSoldier && pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && !(pSoldier->bAssignment == IN_TRANSIT || pSoldier->bAssignment == ASSIGNMENT_DEAD ) )
|
|
{
|
|
// digestion
|
|
HourlyFoodSituationUpdate( pSoldier );
|
|
|
|
// if hungry, eat something automatically from the inventory
|
|
HourlyFoodAutoDigestion( pSoldier );
|
|
}
|
|
}
|
|
|
|
// decay food everywhere
|
|
HourlyFoodDecay();
|
|
}
|
|
|
|
UINT8 GetWaterQuality(INT16 asMapX, INT16 asMapY, INT8 asMapZ)
|
|
{
|
|
UINT8 waterquality = WATER_NONE;
|
|
|
|
// for now, we assume that only sectors on the surface have access to water
|
|
UINT8 ubSectorId = SECTOR(asMapX, asMapY);
|
|
if (asMapZ == 0 && ubSectorId >= 0 && ubSectorId < 256 )
|
|
{
|
|
waterquality = gSectorWaterType[ubSectorId][asMapZ];
|
|
}
|
|
|
|
return waterquality;
|
|
}
|
|
|
|
|
|
// a function that tries to fill up all canteens in this sector
|
|
void SectorFillCanteens( void )
|
|
{
|
|
// no functionality if not in tactical or in combat, or nobody is here
|
|
if ( guiCurrentScreen != GAME_SCREEN || (gTacticalStatus.uiFlags & INCOMBAT) || gusSelectedSoldier == NOBODY )
|
|
return;
|
|
|
|
// determine if there are any patches of water in this sector.
|
|
// If so, fill up all refillable water containers (= canteens) (there is no way to check if this is actually fresh water, we just assume it is)
|
|
// If not, see if there is a water drum, and fill up the canteens from that one
|
|
UINT8 waterquality = GetWaterQuality(gWorldSectorX, gWorldSectorY, gbWorldSectorZ);
|
|
|
|
if ( waterquality == WATER_DRINKABLE || waterquality == WATER_POISONOUS )
|
|
{
|
|
// the temperature of the water in this sector (temperature reflects the quality)
|
|
FLOAT addtemperature = OVERHEATING_MAX_TEMPERATURE;
|
|
|
|
// if the water in this sector is poisoned, we add a different temperature - resulting in worsening of the item's decay status
|
|
if ( waterquality == WATER_POISONOUS )
|
|
addtemperature = 0.0f;
|
|
|
|
// first step: fill all canteens in inventories
|
|
INT8 bMercID, bLastTeamID;
|
|
SOLDIERTYPE * pSoldier = NULL;
|
|
|
|
bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID;
|
|
|
|
// loop through all mercs
|
|
for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; bMercID++, pSoldier++)
|
|
{
|
|
//if the merc is in this sector
|
|
if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && pSoldier->bInSector && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) )
|
|
{
|
|
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
|
|
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
|
{
|
|
// ... if Item exists and is canteen (that can have drink points) ...
|
|
if (pSoldier->inv[bLoop].exists() == true && Item[pSoldier->inv[bLoop].usItem].canteen && Food[Item[pSoldier->inv[bLoop].usItem].foodtype].bDrinkPoints > 0)
|
|
{
|
|
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
|
{
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
UINT16 status = (*pObj)[i]->data.objectStatus;
|
|
UINT16 statusmmissing = max(0, 100 - status);
|
|
FLOAT temperature = (*pObj)[i]->data.bTemperature;
|
|
|
|
(*pObj)[i]->data.objectStatus = 100; // refill canteen
|
|
(*pObj)[i]->data.bTemperature = (status * temperature + statusmmissing * addtemperature)/100;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// second step: fill canteens in sector
|
|
for( UINT32 uiCount = 0; uiCount < guiNumWorldItems; ++uiCount ) // ... for all items in the world ...
|
|
{
|
|
if( gWorldItems[ uiCount ].fExists ) // ... if item exists ...
|
|
{
|
|
// ... if Item exists and is a canteen (only those are refillable) ...
|
|
if ( Item[gWorldItems[ uiCount ].object.usItem].canteen && Food[Item[gWorldItems[ uiCount ].object.usItem].foodtype].bDrinkPoints > 0)
|
|
{
|
|
OBJECTTYPE* pObj = &(gWorldItems[ uiCount ].object); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
|
{
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
UINT16 status = (*pObj)[i]->data.objectStatus;
|
|
UINT16 statusmmissing = max(0, 100 - status);
|
|
FLOAT temperature = (*pObj)[i]->data.bTemperature;
|
|
|
|
(*pObj)[i]->data.objectStatus = 100; // refill canteen
|
|
(*pObj)[i]->data.bTemperature = (status * temperature + statusmmissing * addtemperature)/100;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
OBJECTTYPE* pWaterDrum = GetUsableWaterDrumInSector();
|
|
if ( !pWaterDrum || !(pWaterDrum->exists()) )
|
|
return;
|
|
|
|
INT32 drumsize = Food[Item[pWaterDrum->usItem].foodtype].bDrinkPoints;
|
|
|
|
// first step: fill all canteens in inventories
|
|
INT8 bMercID, bLastTeamID;
|
|
SOLDIERTYPE * pSoldier = NULL;
|
|
|
|
bMercID = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
bLastTeamID = gTacticalStatus.Team[ gbPlayerNum ].bLastID;
|
|
|
|
// loop through all mercs
|
|
for ( pSoldier = MercPtrs[ bMercID ]; bMercID <= bLastTeamID; bMercID++, pSoldier++)
|
|
{
|
|
//if the merc is in this sector
|
|
if ( pSoldier->bActive && pSoldier->ubProfile != NO_PROFILE && pSoldier->bInSector && ( pSoldier->sSectorX == gWorldSectorX ) && ( pSoldier->sSectorY == gWorldSectorY ) && ( pSoldier->bSectorZ == gbWorldSectorZ) )
|
|
{
|
|
INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly
|
|
|
|
for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ...
|
|
{
|
|
// ... if Item exists and is canteen and is NOT a water drum...
|
|
if (pSoldier->inv[bLoop].exists() == true && Item[pSoldier->inv[bLoop].usItem].canteen && (Food[Item[pSoldier->inv[bLoop].usItem].foodtype].bDrinkPoints > 0) && !HasItemFlag(pSoldier->inv[bLoop].usItem, (WATER_DRUM)))
|
|
{
|
|
OBJECTTYPE* pObj = &(pSoldier->inv[bLoop]); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ...
|
|
{
|
|
INT32 canteensize = Food[Item[pObj->usItem].foodtype].bDrinkPoints;
|
|
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
if ( (*pObj)[i]->data.objectStatus < 100 ) // ... and status is > 1 (1 means that it is empty, but still usable)
|
|
{
|
|
INT32 ptsneeded = (INT32)((100 - (*pObj)[i]->data.objectStatus) * canteensize / 100);
|
|
INT32 ptsinwaterdrum = (INT32)(((*pWaterDrum)[i]->data.objectStatus - 1) * drumsize / 100); // -1 because 1% is the status of an empty water drum
|
|
|
|
if ( ptsneeded < ptsinwaterdrum )
|
|
{
|
|
(*pObj)[i]->data.objectStatus = 100;
|
|
|
|
(*pWaterDrum)[i]->data.objectStatus = max(1, (INT16)((100 * ( ptsinwaterdrum - ptsneeded )) / drumsize) );
|
|
}
|
|
else
|
|
{
|
|
(*pObj)[i]->data.objectStatus += (INT16)((100 * ptsinwaterdrum) / canteensize);
|
|
|
|
(*pWaterDrum)[i]->data.objectStatus = 1;
|
|
}
|
|
|
|
(*pObj)[i]->data.bTemperature = ((*pObj)[i]->data.bTemperature + (*pWaterDrum)[0]->data.bTemperature) / 2; // water now has mixed freshness
|
|
|
|
if ( (*pWaterDrum)[0]->data.objectStatus == 1 )
|
|
{
|
|
// get a new water drum!
|
|
pWaterDrum = GetUsableWaterDrumInSector();
|
|
if ( !pWaterDrum || !(pWaterDrum->exists()) )
|
|
return;
|
|
|
|
drumsize = Food[Item[pWaterDrum->usItem].foodtype].bDrinkPoints;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// second step: fill canteens in sector
|
|
for( UINT32 uiCount = 0; uiCount < guiNumWorldItems; ++uiCount ) // ... for all items in the world ...
|
|
{
|
|
if( gWorldItems[ uiCount ].fExists ) // ... if item exists ...
|
|
{
|
|
// ... if Item exists and is a canteen (only those are refillable) ...
|
|
if ( Item[gWorldItems[ uiCount ].object.usItem].canteen )
|
|
{
|
|
OBJECTTYPE* pObj = &(gWorldItems[ uiCount ].object); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ...
|
|
{
|
|
if ( (Food[pObj->usItem].bDrinkPoints > 0) && !HasItemFlag(pObj->usItem, (WATER_DRUM)) ) // ... if item is NOT a water drum...
|
|
{
|
|
INT32 canteensize = Food[Item[pObj->usItem].foodtype].bDrinkPoints;
|
|
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
if ( (*pObj)[i]->data.objectStatus < 100 ) // ... and status is > 1 (1 means that it is empty, but still usable)
|
|
{
|
|
INT32 ptsneeded = (INT32)((100 - (*pObj)[i]->data.objectStatus) * canteensize / 100);
|
|
INT32 ptsinwaterdrum = (INT32)(((*pWaterDrum)[i]->data.objectStatus - 1) * drumsize / 100); // -1 because 1% is the status of an empty water drum
|
|
|
|
if ( ptsneeded < ptsinwaterdrum )
|
|
{
|
|
(*pObj)[i]->data.objectStatus = 100;
|
|
|
|
(*pWaterDrum)[i]->data.objectStatus = max(1, (INT16)((100 * ( ptsinwaterdrum - ptsneeded )) / drumsize) );
|
|
}
|
|
else
|
|
{
|
|
(*pObj)[i]->data.objectStatus += (INT16)((100 * ptsinwaterdrum) / canteensize);
|
|
|
|
(*pWaterDrum)[i]->data.objectStatus = 1;
|
|
}
|
|
|
|
(*pObj)[0]->data.bTemperature = ((*pObj)[0]->data.bTemperature + (*pWaterDrum)[0]->data.bTemperature) / 2; // water now has mixed freshness
|
|
|
|
if ( (*pWaterDrum)[i]->data.objectStatus == 1 )
|
|
{
|
|
// get a new water drum!
|
|
pWaterDrum = GetUsableWaterDrumInSector();
|
|
if ( !pWaterDrum || !(pWaterDrum->exists()) )
|
|
return;
|
|
|
|
drumsize = Food[Item[pWaterDrum->usItem].foodtype].bDrinkPoints;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
OBJECTTYPE* GetUsableWaterDrumInSector( void )
|
|
{
|
|
for( UINT32 uiCount = 0; uiCount < guiNumWorldItems; ++uiCount ) // ... for all items in the world ...
|
|
{
|
|
if( gWorldItems[ uiCount ].fExists ) // ... if item exists ...
|
|
{
|
|
// ... if Item exists and is a canteen (only those are refillable) ...
|
|
if ( Item[gWorldItems[ uiCount ].object.usItem].canteen )
|
|
{
|
|
OBJECTTYPE* pObj = &(gWorldItems[ uiCount ].object); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL && pObj->exists() ) // ... if pointer is not obviously useless ...
|
|
{
|
|
if ( HasItemFlag(pObj->usItem, (WATER_DRUM)) ) // ... if item is a water drum...
|
|
{
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
if ( (*pObj)[i]->data.objectStatus > 1 ) // ... and status is > 1 (1 means that it is empty, but still usable)
|
|
{
|
|
return( pObj );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
void HourlyFoodDecay( void )
|
|
{
|
|
// decay food in all inventories
|
|
UINT32 cnt = gTacticalStatus.Team[ gbPlayerNum ].bFirstID;
|
|
for ( SOLDIERTYPE* pSoldier = MercPtrs[ cnt ]; cnt <= gTacticalStatus.Team[ gbPlayerNum ].bLastID; cnt++, pSoldier++)
|
|
{
|
|
if ( pSoldier && pSoldier->bActive )
|
|
{
|
|
pSoldier->SoldierInventoryFoodDecay();
|
|
}
|
|
}
|
|
|
|
if ( gGameExternalOptions.fFoodDecayInSectors )
|
|
{
|
|
INT16 currentSectorX = gWorldSectorX;
|
|
INT16 currentSectorY = gWorldSectorY;
|
|
INT8 currentSectorZ = gbWorldSectorZ;
|
|
|
|
// decay food in all visited sectors
|
|
for ( INT16 sbMapX = 1; sbMapX < MAP_WORLD_X - 1; ++sbMapX )
|
|
{
|
|
for ( INT16 sbMapY = 1; sbMapY < MAP_WORLD_Y - 1; ++sbMapY )
|
|
{
|
|
for ( UINT8 ubMapZ = 0; ubMapZ < 4; ++ubMapZ )
|
|
{
|
|
// only if sector has already been visisted
|
|
if( GetSectorFlagStatus( sbMapX, sbMapY, ubMapZ, SF_ALREADY_VISITED ) == TRUE )
|
|
{
|
|
BuildStashForSelectedSectorAndDecayFood(sbMapX, sbMapY, (INT16)ubMapZ);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SectorFoodDecay( WORLDITEM* pWorldItem, UINT32 size )
|
|
{
|
|
// one hour has 60 minutes, with 12 5-second-intervals (cooldown values are based on 5-second values)
|
|
FLOAT decaymod = 12*60*gGameExternalOptions.sFoodDecayModificator;
|
|
|
|
for( UINT32 uiCount = 0; uiCount < size; ++uiCount ) // ... for all items in the world ...
|
|
{
|
|
if( pWorldItem[ uiCount ].fExists ) // ... if item exists ...
|
|
{
|
|
if ( Item[pWorldItem[ uiCount ].object.usItem].foodtype > 0 ) // ... if is food...
|
|
{
|
|
OBJECTTYPE* pObj = &(pWorldItem[ uiCount ].object); // ... get pointer for this item ...
|
|
|
|
if ( pObj != NULL ) // ... if pointer is not obviously useless ...
|
|
{
|
|
if ( Food[Item[pObj->usItem].foodtype].usDecayRate > 0.0f ) // ... if the food can decay...
|
|
{
|
|
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
|
|
{
|
|
(*pObj)[i]->data.bTemperature = max(0.0f, (*pObj)[i]->data.bTemperature - decaymod * Food[Item[pObj->usItem].foodtype].usDecayRate); // set new temperature
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// commented out, as we now look for an xml value. Might be relevant in the future, though
|
|
/*BOOLEAN DrinkableWaterInSector(INT16 asMapX, INT16 asMapY, INT8 asMapZ)
|
|
{
|
|
// static variables allow us to remember the result of the last time this function was called - this way, only one call per sector is required
|
|
static INT16 sMapX = 0;
|
|
static INT16 sMapY = 0;
|
|
static INT8 sMapZ = 0;
|
|
static BOOLEAN drinkablewaterfound = FALSE;
|
|
|
|
if ( asMapX != sMapX || asMapY != sMapY || asMapZ != sMapZ )
|
|
{
|
|
drinkablewaterfound = FALSE;
|
|
|
|
for( INT32 sGridNo = 0; sGridNo < MAX_MAP_POS; ++sGridNo )
|
|
{
|
|
if ( Water(sGridNo) || IsKitchenFurnitureWaterSource(sGridNo) )
|
|
{
|
|
drinkablewaterfound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
UINT8 ubSectorId = SECTOR(asMapX, asMapY);
|
|
if (ubSectorId >= 0 && ubSectorId < 256 && gSectorWaterType[ubSectorId][asMapZ] == WATER_DRINKABLE )
|
|
{
|
|
drinkablewaterfound = TRUE;
|
|
}
|
|
|
|
sMapX = asMapX;
|
|
sMapY = asMapY;
|
|
sMapZ = asMapZ;
|
|
}
|
|
|
|
return drinkablewaterfound;
|
|
}*/
|
|
|
|
// commented out, as we now look for an xml value. Might be relevant in the future, though
|
|
/*// determine wether there is a kitechen furniture here (we assume its a sink, so it'll have water)
|
|
BOOLEAN IsKitchenFurnitureWaterSource( INT32 sGridNo )
|
|
{
|
|
STRUCTURE* pStruct = FindStructure(sGridNo, STRUCTURE_GENERIC);
|
|
|
|
if ( pStruct != NULL )
|
|
{
|
|
// Get LEVELNODE for struct and remove!
|
|
LEVELNODE* pNode = FindLevelNodeBasedOnStructure( pStruct->sGridNo, pStruct );
|
|
|
|
if ( pNode )
|
|
{
|
|
UINT32 uiTileType = 0;
|
|
if ( GetTileType( pNode->usIndex, &uiTileType ) )
|
|
{
|
|
UINT16 usIndex = pNode->usIndex;
|
|
|
|
// Check if we are a sandbag
|
|
if ( ( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "furn_6.sti", 10) == 0 ) ||
|
|
( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "old_furn.sti", 12) == 0 ) ||
|
|
( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "furnmix.sti", 11) == 0 ) ||
|
|
( _strnicmp( gTilesets[ giCurrentTilesetID ].TileSurfaceFilenames[ uiTileType ], "furn_mix.sti", 12) == 0 ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}*/
|