New feature: advanced repair/dirt system

- items now have an internal repair value that can be lowered through damage taken. They can only be repaired up to that value by your mercs, onyl smiths can fully restore it to 100%
- guns get dirty over time, especially when shooting. Thsi can cause them to jam. Use cleaning kits (item #1576) to clean them by pressing 'Alt' + '.'
- more on this feature in http://www.bears-pit.com/board/ubbthreads.php/topics/308926/New_feature_advanced_repair_di.html#Post308926
- this requires GameDir revision 1496 or higher

WARING! THIS BREAKS SAVEGAME COMPATIBIITY!

git-svn-id: https://ja2svn.mooo.com/source/ja2/trunk/GameSource/ja2_v1.13/Build@5480 3b4a5df2-a311-0410-b5c6-a8a6f20db521
This commit is contained in:
Flugente
2012-08-18 00:03:40 +00:00
parent 50b849997b
commit 8275a1231b
39 changed files with 1285 additions and 377 deletions
+39 -23
View File
@@ -358,7 +358,7 @@ UINT8 GetMinHealingSkillNeeded( SOLDIERTYPE *pPatient );
UINT16 HealPatient( SOLDIERTYPE *pPatient, SOLDIERTYPE * pDoctor, UINT16 usHealAmount );
// can item be repaired?
BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus );
BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus, INT16 bThreshold );
// does another merc have a repairable item on them?
OBJECTTYPE* FindRepairableItemOnOtherSoldier( SOLDIERTYPE * pSoldier, UINT8 ubPassType );
@@ -3587,7 +3587,7 @@ static void CollectRepairableItems(const SOLDIERTYPE* pSoldier, RepairQueue& ite
foundItem = false;
for (UINT8 stackIndex = 0; stackIndex < pObj->ubNumberOfObjects; ++stackIndex) {
// Check the stack item itself
if (IsItemRepairable(pObj->usItem, (*pObj)[stackIndex]->data.objectStatus)) {
if (IsItemRepairable(pObj->usItem, (*pObj)[stackIndex]->data.objectStatus, (*pObj)[stackIndex]->data.sRepairThreshold)) {
RepairItem item(pObj, pSoldier, (INVENTORY_SLOT) pocketIndex);
itemsToFix.push(item);
break;
@@ -3596,7 +3596,7 @@ static void CollectRepairableItems(const SOLDIERTYPE* pSoldier, RepairQueue& ite
// Check for attachments (are there stackable items that can take attachments though?)
UINT8 attachmentIndex = 0;
for (attachmentList::const_iterator iter = (*pObj)[stackIndex]->attachments.begin(); iter != (*pObj)[stackIndex]->attachments.end(); ++iter, ++attachmentIndex) {
if (IsItemRepairable(iter->usItem, (*iter)[attachmentIndex]->data.objectStatus )) {
if (IsItemRepairable(iter->usItem, (*iter)[attachmentIndex]->data.objectStatus, (*iter)[attachmentIndex]->data.sRepairThreshold )) {
// Send the main item, not the attachment
RepairItem item(pObj, pSoldier, (INVENTORY_SLOT) pocketIndex);
itemsToFix.push(item);
@@ -3692,7 +3692,7 @@ OBJECTTYPE* FindRepairableItemInLBENODE( OBJECTTYPE * pObj, UINT8 subObject)
OBJECTTYPE* FindRepairableItemInSpecificPocket( OBJECTTYPE * pObj, UINT8 subObject)
{
AssertNotNIL(pObj);
if ( IsItemRepairable( pObj->usItem, (*pObj)[subObject]->data.objectStatus ) )
if ( IsItemRepairable( pObj->usItem, (*pObj)[subObject]->data.objectStatus, (*pObj)[subObject]->data.sRepairThreshold ) )
{
return( pObj );
}
@@ -3700,7 +3700,7 @@ OBJECTTYPE* FindRepairableItemInSpecificPocket( OBJECTTYPE * pObj, UINT8 subObje
// have to check for attachments after...
for (attachmentList::iterator iter = (*pObj)[subObject]->attachments.begin(); iter != (*pObj)[subObject]->attachments.end(); ++iter) {
// if it's repairable and NEEDS repairing
if ( IsItemRepairable( iter->usItem, (*iter)[subObject]->data.objectStatus ) && iter->exists() ) {
if ( IsItemRepairable( iter->usItem, (*iter)[subObject]->data.objectStatus, (*iter)[subObject]->data.sRepairThreshold ) && iter->exists() ) {
return( &(*iter) );
}
}
@@ -3708,8 +3708,8 @@ OBJECTTYPE* FindRepairableItemInSpecificPocket( OBJECTTYPE * pObj, UINT8 subObje
return( 0 );
}
void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UINT8 * pubRepairPtsLeft )
// Flugente: changed this function so that it repairs items up to a variable threshold instead of always 100%. This will only happen if the option gGameExternalOptions.fAdvRepairSystem is used
void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, INT16 sThreshold, UINT8 * pubRepairPtsLeft )
{
INT16 sRepairCostAdj;
UINT16 usDamagePts, usPtsFixed;
@@ -3719,7 +3719,7 @@ void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UI
AssertNotNIL (pubRepairPtsLeft);
// get item's repair ease, for each + point is 10% easier, each - point is 10% harder to repair
sRepairCostAdj = 100 - ( 10 * Item[ usItem ].bRepairEase );
sRepairCostAdj = sThreshold - ( 10 * Item[ usItem ].bRepairEase );
// make sure it ain't somehow gone too low!
if (sRepairCostAdj < 10)
@@ -3743,7 +3743,7 @@ void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UI
}
// how many points of damage is the item down by?
usDamagePts = 100 - *pbStatus;
usDamagePts = sThreshold - *pbStatus;
// adjust that by the repair cost adjustment percentage
usDamagePts = (usDamagePts * sRepairCostAdj) / 100;
@@ -3751,8 +3751,8 @@ void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UI
// do we have enough pts to fully repair the item?
if ( *pubRepairPtsLeft >= usDamagePts )
{
// fix it to 100%
*pbStatus = 100;
// fix it up to the threshold (max 100%)
*pbStatus = sThreshold;
*pubRepairPtsLeft -= usDamagePts;
}
else // not enough, partial fix only, if any at all
@@ -3766,10 +3766,10 @@ void DoActualRepair( SOLDIERTYPE * pSoldier, UINT16 usItem, INT16 * pbStatus, UI
{
*pbStatus += usPtsFixed;
// make sure we don't somehow end up over 100
if ( *pbStatus > 100 )
// make sure we don't somehow end up over the threshold
if ( *pbStatus > sThreshold )
{
*pbStatus = 100;
*pbStatus = sThreshold;
}
}
@@ -3790,22 +3790,32 @@ BOOLEAN RepairObject( SOLDIERTYPE * pSoldier, SOLDIERTYPE * pOwner, OBJECTTYPE *
for ( ubLoop = 0; ubLoop < ubItemsInPocket; ubLoop++ )
{
// if it's repairable and NEEDS repairing
if ( IsItemRepairable( pObj->usItem, (*pObj)[ubLoop]->data.objectStatus ) )
if ( IsItemRepairable( pObj->usItem, (*pObj)[ubLoop]->data.objectStatus, (*pObj)[ubLoop]->data.sRepairThreshold ) )
{
///////////////////////////////////////////////////////////////////////////////////////////////////////
// SANDRO - merc records, num items repaired
// Actually we check if we repaired at least 5% of status, otherwise the item is not considered broken
ubBeforeRepair = (UINT8)((*pObj)[ubLoop]->data.objectStatus);
// repairable, try to repair it
DoActualRepair( pSoldier, pObj->usItem, &((*pObj)[ubLoop]->data.objectStatus), pubRepairPtsLeft );
// Flugente: if using the new advanced repair system, we can only repair up to the repair threshold
INT16 threshold = 100;
if ( gGameExternalOptions.fAdvRepairSystem && ( (Item[pObj->usItem].usItemClass & IC_WEAPON) || (Item[pObj->usItem].usItemClass & IC_ARMOUR) ) )
threshold = (*pObj)[ubLoop]->data.sRepairThreshold;
// repairable, try to repair it
DoActualRepair( pSoldier, pObj->usItem, &((*pObj)[ubLoop]->data.objectStatus), threshold, pubRepairPtsLeft );
// if the item was repaired to full status and the repair wa at least 5%, add a point
if ( (*pObj)[ubLoop]->data.objectStatus == 100 && (((*pObj)[ubLoop]->data.objectStatus - ubBeforeRepair) > 4 ))
if ( (*pObj)[ubLoop]->data.objectStatus == threshold && (((*pObj)[ubLoop]->data.objectStatus - ubBeforeRepair) > 4 ))
{
gMercProfiles[ pSoldier->ubProfile ].records.usItemsRepaired++;
// if item was fully repaired, consider it cleaned
if ( gGameExternalOptions.fDirtSystem )
(*pObj)[ubLoop]->data.bDirtLevel = 0.0f;
}
// if the item was now repaired to a status of 96-99 and the repair was at least 2%, add a point, consider the item repaired (no points will be awarded for it anyway)
else if ( (*pObj)[ubLoop]->data.objectStatus > 95 && (*pObj)[ubLoop]->data.objectStatus < 100 && ((*pObj)[ubLoop]->data.objectStatus - ubBeforeRepair) > 1)
else if ( (*pObj)[ubLoop]->data.objectStatus > threshold - 5 && (*pObj)[ubLoop]->data.objectStatus < threshold && ((*pObj)[ubLoop]->data.objectStatus - ubBeforeRepair) > 1)
{
gMercProfiles[ pSoldier->ubProfile ].records.usItemsRepaired++;
}
@@ -4142,14 +4152,20 @@ void HandleRepairBySoldier( SOLDIERTYPE *pSoldier )
BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus )
BOOLEAN IsItemRepairable( UINT16 usItem, INT16 bStatus, INT16 bThreshold )
{
// check to see if item can/needs to be repaired
// if ( ( bStatus < 100) && ( Item[ usItem ].fFlags & ITEM_REPAIRABLE ) )
if ( ( bStatus < 100) && ( Item[ usItem ].repairable ) )
if ( ( bStatus < 100) && ( Item[ usItem ].repairable ) )
{
// yep
return ( TRUE );
if ( gGameExternalOptions.fAdvRepairSystem )
{
if ( ( ((Item[usItem].usItemClass & IC_WEAPON) != 0) || Item[usItem].usItemClass == IC_ARMOUR ) && bStatus < bThreshold )
return ( TRUE );
}
else
// yep
return ( TRUE );
}
// nope
@@ -48,6 +48,11 @@
class OBJECTTYPE;
class SOLDIERTYPE;
// Flugente: external sector data
extern SECTOR_EXT_DATA SectorExternalData[256][4];
extern void SetLastTimePlayerWasInSector(INT16 sMapX, INT16 sMapY, INT8 sMapZ); // Flugente: set last time sector was visited
//dnl ch51 081009
UINT8 gInventoryPoolIndex = '0';
std::vector<WORLDITEM> pInventoryPoolListQ[INVPOOLLISTNUM];
@@ -5069,6 +5074,14 @@ void SectorInventoryCooldownFunctions( INT16 sMapX, INT16 sMapY, INT16 sMapZ )
//Save the Items to the the file
SaveWorldItemsToTempItemFile( sMapX, sMapY, (INT8)sMapZ, uiTotalNumberOfRealItems, pTotalSectorList );
}
HandleSectorCooldownFunctions( sMapX, sMapY, (INT8)sMapZ, pTotalSectorList, uiTotalNumberOfRealItems, TRUE );
//Save the time the player was last in the sector
SetLastTimePlayerWasInSector( sMapX, sMapY, (INT8)sMapZ );
//Save the Items to the the file
SaveWorldItemsToTempItemFile( sMapX, sMapY, (INT8)sMapZ, uiTotalNumberOfRealItems, pTotalSectorList );
}
// Flugente: handle various cooldwon functions over an array of items in a specific sector.
@@ -5077,7 +5090,7 @@ void SectorInventoryCooldownFunctions( INT16 sMapX, INT16 sMapY, INT16 sMapZ )
void HandleSectorCooldownFunctions( INT16 sMapX, INT16 sMapY, INT8 sMapZ, WORLDITEM* pWorldItem, UINT32 size, BOOLEAN fWithMinutes )
{
// if not using overheating or food system, no point in all this
if ( !gGameOptions.fWeaponOverheating && !gGameOptions.fFoodSystem )
if ( !gGameOptions.fWeaponOverheating && !gGameExternalOptions.fDirtSystem && !gGameOptions.fFoodSystem )
return;
UINT32 tickspassed = 1;
@@ -5105,6 +5118,16 @@ void HandleSectorCooldownFunctions( INT16 sMapX, INT16 sMapY, INT8 sMapZ, WORLDI
if ( sMapZ > 0 )
foofdecaymod *= 0.8f;
// get sector-specific dirt threshold
UINT16 sectormod = 0;
UINT8 ubSectorId = SECTOR(sMapX, sMapY);
if ( sMapZ > 0 )
sectormod = 100;
else if ( ubSectorId >= 0 && ubSectorId < 256 )
{
sectormod = SectorExternalData[ubSectorId][sMapZ].usNaturalDirt;
}
for( UINT32 uiCount = 0; uiCount < size; ++uiCount ) // ... for all items in the world ...
{
if( pWorldItem[ uiCount ].fExists ) // ... if item exists ...
@@ -5158,6 +5181,24 @@ void HandleSectorCooldownFunctions( INT16 sMapX, INT16 sMapY, INT8 sMapZ, WORLDI
}
}
if ( gGameExternalOptions.fDirtSystem && ( (Item[pObj->usItem].usItemClass & IC_WEAPON) || (Item[pObj->usItem].usItemClass & IC_ARMOUR) ) )
{
FLOAT dirtincreasefactor = GetItemDirtIncreaseFactor(pObj, FALSE); // ... get dirt increase factor ...
// the current sector determines how much dirt increases
dirtincreasefactor *= (sectormod)/100;
dirtincreasefactor /= gGameExternalOptions.usSectorDirtDivider;
if ( dirtincreasefactor > 0.0f ) // ... item can get dirtier ...
{
for(INT16 i = 0; i < pObj->ubNumberOfObjects; ++i) // ... there might be multiple items here (item stack), so for each one ...
{
(*pObj)[i]->data.bDirtLevel = max(0.0f, min( OVERHEATING_MAX_TEMPERATURE, (*pObj)[i]->data.bDirtLevel + tickspassed * dirtincreasefactor) ); // set new temperature
}
}
}
if ( gGameOptions.fFoodSystem && Item[pWorldItem[ uiCount ].object.usItem].foodtype > 0 ) // ... if it is food and the food system is active ...
{
if ( Food[Item[pObj->usItem].foodtype].usDecayRate > 0.0f ) // ... if the food can decay...
+38 -15
View File
@@ -19,8 +19,8 @@
// Four different sector names, used at different times.
extern CHAR16 gzSectorNames[256][4][MAX_SECTOR_NAME_LENGTH];
// Flugente: To determine wether a sector has water in it.
extern UINT8 gSectorWaterType[256][4];
// Flugente: external sector data
extern SECTOR_EXT_DATA SectorExternalData[256][4];
// moved to lua
//extern CHAR16 gzSectorUndergroundNames1[256][4][MAX_SECTOR_NAME_LENGTH];
@@ -46,6 +46,7 @@ typedef struct
CHAR16 szCurExploredName[MAX_SECTOR_NAME_LENGTH];
CHAR16 szCurDetailedExploredName[MAX_SECTOR_NAME_LENGTH];
UINT8 sWaterType;
UINT16 usNaturalDirt;
UINT32 currentDepth;
UINT32 maxReadDepth;
} SectorNameParseData;
@@ -70,10 +71,15 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char **
// Initiate Array by setting first character to 0.
for (UINT16 x = 0; x < 256; x++)
{
gSectorWaterType[x][0] = 0;
gSectorWaterType[x][1] = 0;
gSectorWaterType[x][2] = 0;
gSectorWaterType[x][3] = 0;
SectorExternalData[x][0].usWaterType = 0;
SectorExternalData[x][1].usWaterType = 0;
SectorExternalData[x][2].usWaterType = 0;
SectorExternalData[x][3].usWaterType = 0;
SectorExternalData[x][0].usNaturalDirt = 0;
SectorExternalData[x][1].usNaturalDirt = 0;
SectorExternalData[x][2].usNaturalDirt = 0;
SectorExternalData[x][3].usNaturalDirt = 0;
if (Sector_Level == 0 )
{
@@ -121,7 +127,8 @@ SectorNameStartElementHandle(void *userData, const XML_Char *name, const char **
strcmp(name, "szDetailedUnexploredName") == 0 ||
strcmp(name, "szExploredName") == 0 ||
strcmp(name, "szDetailedExploredName") == 0 ||
strcmp(name, "sWaterType") == 0 ))
strcmp(name, "sWaterType") == 0 ||
strcmp(name, "usNaturalDirt") == 0 ))
{
pData->curElement = SECTORNAME_ELEMENT;
@@ -174,10 +181,15 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
wcscpy(gzSectorNames[ubSectorId][2], pData->szCurExploredName);
wcscpy(gzSectorNames[ubSectorId][3], pData->szCurDetailedExploredName);
gSectorWaterType[ubSectorId][0] = pData->sWaterType;
gSectorWaterType[ubSectorId][1] = pData->sWaterType;
gSectorWaterType[ubSectorId][2] = pData->sWaterType;
gSectorWaterType[ubSectorId][3] = pData->sWaterType;
SectorExternalData[ubSectorId][0].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][1].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][2].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][3].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][0].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][1].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][2].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][3].usNaturalDirt = pData->usNaturalDirt;
}
// moved to lua
//else if (Sector_Level == 1 )
@@ -211,10 +223,15 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
wcscpy(gzSectorNames[ubSectorId][2], pData->szCurExploredName);
wcscpy(gzSectorNames[ubSectorId][3], pData->szCurDetailedExploredName);
gSectorWaterType[ubSectorId][0] = pData->sWaterType;
gSectorWaterType[ubSectorId][1] = pData->sWaterType;
gSectorWaterType[ubSectorId][2] = pData->sWaterType;
gSectorWaterType[ubSectorId][3] = pData->sWaterType;
SectorExternalData[ubSectorId][0].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][1].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][2].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][3].usWaterType = pData->sWaterType;
SectorExternalData[ubSectorId][0].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][1].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][2].usNaturalDirt = pData->usNaturalDirt;
SectorExternalData[ubSectorId][3].usNaturalDirt = pData->usNaturalDirt;
}
// moved to lua
//else if (Sector_Level == 1 )
@@ -293,6 +310,12 @@ SectorNameEndElementHandle(void *userData, const XML_Char *name)
pData->sWaterType = (UINT8) atol(pData->szCharData);
}
else if(strcmp(name, "usNaturalDirt") == 0)
{
pData->curElement = SECTORNAME_ELEMENT_SECTOR;
pData->usNaturalDirt = (UINT16) atoi(pData->szCharData);
}
pData->maxReadDepth--;
}