Simplify incendary damage, fix incendary armour.

Apply incendary armour before converting from damage-per-second to damage-per-update,
such that incendary armour is now a per-second value, rather than a per-update value.

Dropped BASE_OBJECT::inFire, the value of which could be determined from BASE_OBJECT::burnStart.

Fixes ticket:3078.
master
Cyp 2012-01-27 18:11:53 +01:00
parent 4657de6ee7
commit 666ea1050e
13 changed files with 63 additions and 103 deletions

View File

@ -96,7 +96,6 @@ struct BASE_OBJECT : public SIMPLE_OBJECT
UBYTE cluster; ///< Which cluster the object is a member of
UBYTE visible[MAX_PLAYERS]; ///< Whether object is visible to specific player
UBYTE seenThisTick[MAX_PLAYERS]; ///< Whether object has been seen this tick by the specific player.
UBYTE inFire; ///< true if the object is in a fire
UWORD numWatchedTiles; ///< Number of watched tiles, zero for features
UDWORD lastEmission; ///< When did it last puff out smoke?
WEAPON_SUBCLASS lastHitWeapon; ///< The weapon that last hit it

View File

@ -420,7 +420,7 @@ void counterBatteryFire(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget)
* \param weaponSubClass the subclass of the weapon that deals the damage
* \return < 0 when the dealt damage destroys the object, > 0 when the object survives
*/
int32_t objDamage(BASE_OBJECT *psObj, UDWORD damage, UDWORD originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass)
int32_t objDamage(BASE_OBJECT *psObj, unsigned damage, unsigned originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, bool isDamagePerSecond)
{
int actualDamage, armour, level = 1;
@ -476,8 +476,19 @@ int32_t objDamage(BASE_OBJECT *psObj, UDWORD damage, UDWORD originalhp, WEAPON_C
// And at least MIN_WEAPON_DAMAGE points
actualDamage = MAX(actualDamage, MIN_WEAPON_DAMAGE);
if (isDamagePerSecond)
{
int deltaDamageRate = actualDamage - psObj->burnDamage;
if (deltaDamageRate <= 0)
{
return 0; // Did this much damage already, this tick, so don't do more.
}
actualDamage = gameTimeAdjustedAverage(deltaDamageRate);
psObj->burnDamage += deltaDamageRate;
}
objTrace(psObj->id, "objDamage: Penetrated %d", actualDamage);
syncDebug("damage%u dam%u,o%u,wc%d.%d,ar%d,lev%d,aDam%d", psObj->id, damage, originalhp, weaponClass, weaponSubClass, armour, level, actualDamage);
syncDebug("damage%u dam%u,o%u,wc%d.%d,ar%d,lev%d,aDam%d,isDps%d", psObj->id, damage, originalhp, weaponClass, weaponSubClass, armour, level, actualDamage, isDamagePerSecond);
// for some odd reason, we have 0 hitpoints.
if (!originalhp)

View File

@ -48,7 +48,7 @@ bool combFire(WEAPON *psWeap, BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget, in
if any support a counter battery sensor*/
void counterBatteryFire(BASE_OBJECT *psAttacker, BASE_OBJECT *psTarget);
int32_t objDamage(BASE_OBJECT *psObj, UDWORD damage, UDWORD originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass);
int32_t objDamage(BASE_OBJECT *psObj, unsigned damage, unsigned originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, bool isDamagePerSecond);
unsigned int objGuessFutureDamage(WEAPON_STATS *psStats, unsigned int player, BASE_OBJECT *psTarget);

View File

@ -153,7 +153,7 @@ bool droidInit(void)
*
* NOTE: This function will damage but _never_ destroy transports when in single player (campaign) mode
*/
int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime)
int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond)
{
int32_t relativeDamage;
@ -165,7 +165,7 @@ int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, W
damage *= 3;
}
relativeDamage = objDamage((BASE_OBJECT *)psDroid, damage, psDroid->originalBody, weaponClass, weaponSubClass);
relativeDamage = objDamage(psDroid, damage, psDroid->originalBody, weaponClass, weaponSubClass, isDamagePerSecond);
if (relativeDamage > 0)
{
@ -736,7 +736,6 @@ void droidUpdate(DROID *psDroid)
Vector3i dv;
UDWORD percentDamage, emissionInterval;
BASE_OBJECT *psBeingTargetted = NULL;
SDWORD damageToDo;
unsigned i;
CHECK_DROID(psDroid);
@ -836,43 +835,19 @@ void droidUpdate(DROID *psDroid)
// -----------------
/* Update the fire damage data */
if (psDroid->inFire & IN_FIRE)
if (psDroid->burnStart != 0 && psDroid->burnStart != gameTime - deltaGameTime) // -deltaGameTime, since projectiles are updated after droids.
{
/* Still in a fire, reset the fire flag to see if we get out this turn */
psDroid->inFire = 0;
}
else
{
/* The fire flag has not been set so we must be out of the fire */
if (psDroid->inFire & BURNING)
// The burnStart has been set, but is not from the previous tick, so we must be out of the fire.
psDroid->burnDamage = 0; // Reset burn damage done this tick.
if (psDroid->burnStart + BURN_TIME < gameTime)
{
if (psDroid->burnStart + BURN_TIME < gameTime)
{
// stop burning
psDroid->inFire = 0;
psDroid->burnStart = 0;
psDroid->burnDamage = 0;
}
else
{
// do burn damage
damageToDo = BURN_DAMAGE * ((SDWORD)gameTime - (SDWORD)psDroid->burnStart) /
GAME_TICKS_PER_SEC;
damageToDo -= (SDWORD)psDroid->burnDamage;
if (damageToDo > 0)
{
psDroid->burnDamage += damageToDo;
droidDamage(psDroid, damageToDo, WC_HEAT, WSC_FLAME, gameTime - deltaGameTime/2);
}
}
// Finished burning.
psDroid->burnStart = 0;
}
else if (psDroid->burnStart != 0)
else
{
// just left the fire
psDroid->inFire |= BURNING;
psDroid->burnStart = gameTime;
psDroid->burnDamage = 0;
// do burn damage
droidDamage(psDroid, BURN_DAMAGE, WC_HEAT, WSC_FLAME, gameTime - deltaGameTime/2, true);
}
}
@ -1968,7 +1943,6 @@ DROID *reallyBuildDroid(DROID_TEMPLATE *pTemplate, Position pos, UDWORD player,
}
memset(psDroid->seenThisTick, 0, sizeof(psDroid->seenThisTick));
psDroid->died = 0;
psDroid->inFire = 0;
psDroid->burnStart = 0;
psDroid->burnDamage = 0;
psDroid->sDisplay.screenX = OFF_SCREEN;

View File

@ -128,7 +128,7 @@ extern UDWORD calcTemplatePower(DROID_TEMPLATE *psTemplate);
bool idfDroid(DROID *psDroid);
/* Do damage to a droid */
int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime);
int32_t droidDamage(DROID *psDroid, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond);
/* The main update routine for all droids */
extern void droidUpdate(DROID *psDroid);

View File

@ -165,7 +165,7 @@ void featureStatsShutDown(void)
* \param weaponClass,weaponSubClass the class and subclass of the weapon that deals the damage
* \return < 0 never, >= 0 always
*/
int32_t featureDamage(FEATURE *psFeature, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime)
int32_t featureDamage(FEATURE *psFeature, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond)
{
int32_t relativeDamage;
@ -174,7 +174,7 @@ int32_t featureDamage(FEATURE *psFeature, unsigned damage, WEAPON_CLASS weaponCl
debug(LOG_ATTACK, "feature (id %d): body %d armour %d damage: %d",
psFeature->id, psFeature->body, psFeature->armour[weaponClass], damage);
relativeDamage = objDamage((BASE_OBJECT *)psFeature, damage, psFeature->psStats->body, weaponClass, weaponSubClass);
relativeDamage = objDamage(psFeature, damage, psFeature->psStats->body, weaponClass, weaponSubClass, isDamagePerSecond);
// If the shell did sufficient damage to destroy the feature
if (relativeDamage < 0)
@ -251,7 +251,8 @@ FEATURE * buildFeature(FEATURE_STATS *psStats, UDWORD x, UDWORD y,bool FromSave)
psFeature->rot.direction = 0;
}
psFeature->body = psStats->body;
psFeature->inFire = false;
psFeature->burnStart = 0;
psFeature->burnDamage = 0;
objSensorCache(psFeature, NULL);
objEcmCache(psFeature, NULL);
@ -355,6 +356,15 @@ void featureUpdate(FEATURE *psFeat)
// update the visibility for the feature
processVisibilityLevel((BASE_OBJECT *)psFeat);
/* Update the fire damage data */
if (psFeat->burnStart != 0 && psFeat->burnStart != gameTime - deltaGameTime) // -deltaGameTime, since projectiles are updated after features.
{
// The burnStart has been set, but is not from the previous tick, so we must be out of the fire.
psFeat->burnDamage = 0; // Reset burn damage done this tick.
// Finished burning.
psFeat->burnStart = 0;
}
syncDebugFeature(psFeat, '>');
}

View File

@ -54,7 +54,7 @@ bool destroyFeature(FEATURE *psDel, unsigned impactTime);
/* get a feature stat id from its name */
extern SDWORD getFeatureStatFromName(const char *pName);
int32_t featureDamage(FEATURE *psFeature, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime);
int32_t featureDamage(FEATURE *psFeature, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond);
extern void featureInitVars(void);

View File

@ -4213,7 +4213,6 @@ static bool loadSaveDroid(const char *pFileName, DROID **ppsCurrentDroidLists)
}
ASSERT(id != 0, "Droid ID should never be zero here");
psDroid->body = healthValue(ini, psDroid->originalBody);
psDroid->inFire = ini.value("inFire", 0).toInt();
psDroid->burnDamage = ini.value("burnDamage", 0).toInt();
psDroid->burnStart = ini.value("burnStart", 0).toInt();
psDroid->experience = ini.value("experience", 0).toInt();
@ -4397,7 +4396,6 @@ static bool writeDroid(WzConfig &ini, DROID *psCurr, bool onMission, int &counte
}
if (psCurr->died > 0) ini.setValue("died", psCurr->died);
if (psCurr->resistance > 0) ini.setValue("resistance", psCurr->resistance);
if (psCurr->inFire > 0) ini.setValue("inFire", psCurr->inFire);
if (psCurr->burnStart > 0) ini.setValue("burnStart", psCurr->burnStart);
if (psCurr->burnDamage > 0) ini.setValue("burnDamage", psCurr->burnDamage);
ini.setValue("droidType", psCurr->droidType);
@ -4611,7 +4609,6 @@ bool loadSaveStructure(char *pFileData, UDWORD filesize)
// The original code here didn't work and so the scriptwriters worked round it by using the module ID - so making it work now will screw up
// the scripts -so in ALL CASES overwrite the ID!
psStructure->id = psSaveStructure->id > 0 ? psSaveStructure->id : 0xFEDBCA98; // hack to remove struct id zero
psStructure->inFire = psSaveStructure->inFire;
psStructure->burnDamage = psSaveStructure->burnDamage;
burnTime = psSaveStructure->burnStart;
psStructure->burnStart = burnTime;
@ -4737,7 +4734,6 @@ static bool loadSaveStructure2(const char *pFileName, STRUCTURE **ppList)
{
psStructure->id = id; // force correct ID
}
psStructure->inFire = ini.value("inFire", 0).toInt();
psStructure->burnDamage = ini.value("burnDamage", 0).toInt();
psStructure->burnStart = ini.value("burnStart", 0).toInt();
memset(psStructure->visible, 0, sizeof(psStructure->visible));
@ -4953,7 +4949,6 @@ bool writeStructFile(const char *pFileName)
}
if (psCurr->died > 0) ini.setValue("died", psCurr->died);
if (psCurr->resistance > 0) ini.setValue("resistance", psCurr->resistance);
if (psCurr->inFire > 0) ini.setValue("inFire", psCurr->inFire);
if (psCurr->burnStart > 0) ini.setValue("burnStart", psCurr->burnStart);
if (psCurr->burnDamage > 0) ini.setValue("burnDamage", psCurr->burnDamage);
if (psCurr->status != SS_BUILT) ini.setValue("status", psCurr->status);
@ -5260,7 +5255,6 @@ bool loadSaveFeature(char *pFileData, UDWORD filesize)
//restore values
pFeature->id = psSaveFeature->id;
pFeature->rot.direction = DEG(psSaveFeature->direction);
pFeature->inFire = psSaveFeature->inFire;
pFeature->burnDamage = psSaveFeature->burnDamage;
if (psHeader->version >= VERSION_14)
{
@ -5331,7 +5325,6 @@ bool loadSaveFeature2(const char *pFileName)
//restore values
pFeature->id = ini.value("id").toInt();
pFeature->rot = ini.vector3i("rotation");
pFeature->inFire = ini.value("inFire", 0).toInt();
pFeature->burnDamage = ini.value("burnDamage", 0).toInt();
pFeature->burnStart = ini.value("burnStart", 0).toInt();
pFeature->born = ini.value("born", 2).toInt();
@ -5367,12 +5360,8 @@ bool writeFeatureFile(const char *pFileName)
ini.setValue("name", psCurr->psStats->pName);
ini.setVector3i("position", psCurr->pos);
ini.setVector3i("rotation", psCurr->rot);
if (psCurr->inFire)
{
ini.setValue("inFire", psCurr->inFire);
ini.setValue("burnDamage", psCurr->burnDamage);
ini.setValue("burnStart", psCurr->burnStart);
}
ini.setValue("burnDamage", psCurr->burnDamage);
ini.setValue("burnStart", psCurr->burnStart);
ini.setValue("health", psCurr->body);
ini.setValue("born", psCurr->born);
if (psCurr->selected) ini.setValue("selected", psCurr->selected);

View File

@ -2644,7 +2644,7 @@ void moveUpdateDroid(DROID *psDroid)
objTrace(psDroid->id, "MOVETURNTOTARGET complete");
}
if( (psDroid->inFire && psDroid->droidType != DROID_PERSON) && psDroid->visible[selectedPlayer])
if (psDroid->burnStart != 0 && psDroid->droidType != DROID_PERSON && psDroid->visible[selectedPlayer])
{
pos.x = psDroid->pos.x + (18-rand()%36);
pos.z = psDroid->pos.y + (18-rand()%36);

View File

@ -110,7 +110,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj );
static void proj_PostImpactFunc( PROJECTILE *psObj );
static void proj_checkBurnDamage(PROJECTILE *psProj);
static int32_t objectDamage(BASE_OBJECT *psObj, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime);
static int32_t objectDamage(BASE_OBJECT *psObj, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond);
static inline void setProjectileDestination(PROJECTILE *psProj, BASE_OBJECT *psObj)
@ -1152,7 +1152,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
psObj->psDest->id, psObj->psDest->player);
// Damage the object
relativeDamage = objectDamage(psObj->psDest, damage, psStats->weaponClass, psStats->weaponSubClass, psObj->time);
relativeDamage = objectDamage(psObj->psDest, damage, psStats->weaponClass, psStats->weaponSubClass, psObj->time, false);
proj_UpdateKills(psObj, relativeDamage);
@ -1234,7 +1234,7 @@ static void proj_ImpactFunc( PROJECTILE *psObj )
{
updateMultiStatsDamage(psObj->psSource->player, psCurr->player, damage);
}
int relativeDamage = objectDamage(psCurr, damage, psStats->weaponClass, psStats->weaponSubClass, psObj->time);
int relativeDamage = objectDamage(psCurr, damage, psStats->weaponClass, psStats->weaponSubClass, psObj->time, false);
proj_UpdateKills(psObj, relativeDamage);
}
}
@ -1383,32 +1383,16 @@ static void proj_checkBurnDamage(PROJECTILE *psProj)
continue; // Can't destroy oil wells.
}
/* The object is in the fire */
psCurr->inFire |= IN_FIRE;
if (psCurr->burnStart == 0 || (psCurr->inFire & BURNING) != 0)
if (psCurr->burnStart != gameTime)
{
/* This is the first turn the object is in the fire */
psCurr->burnStart = gameTime;
psCurr->burnDamage = 0;
psCurr->burnDamage = 0; // Reset burn damage done this tick.
}
else
{
// Calculate how much damage should have been done up till now.
unsigned damageSoFar = (gameTime - psCurr->burnStart) * weaponIncenDamage(psStats,psProj->player) / GAME_TICKS_PER_SEC;
int damageToDo = damageSoFar - psCurr->burnDamage;
if (damageToDo > 0)
{
int32_t relativeDamage;
debug(LOG_NEVER, "Burn damage of %d to object %d, player %d\n",
damageToDo, psCurr->id, psCurr->player);
unsigned damageRate = weaponIncenDamage(psStats,psProj->player);
debug(LOG_NEVER, "Burn damage of %d per second to object %d, player %d\n", damageRate, psCurr->id, psCurr->player);
relativeDamage = objectDamage(psCurr, damageToDo, psStats->weaponClass, psStats->weaponSubClass, gameTime - deltaGameTime/2);
psCurr->burnDamage += damageToDo;
proj_UpdateKills(psProj, relativeDamage);
}
// The damage could be negative if the object is being burnt by another fire with a higher burn damage.
}
int relativeDamage = objectDamage(psCurr, damageRate, psStats->weaponClass, psStats->weaponSubClass, gameTime - deltaGameTime/2, true);
proj_UpdateKills(psProj, relativeDamage);
}
}
@ -1536,20 +1520,20 @@ UDWORD calcDamage(UDWORD baseDamage, WEAPON_EFFECT weaponEffect, BASE_OBJECT *ps
* multiplied by -1, resulting in a negative number. Killed features do not
* result in negative numbers.
*/
static int32_t objectDamage(BASE_OBJECT *psObj, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime)
static int32_t objectDamage(BASE_OBJECT *psObj, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond)
{
switch (psObj->type)
{
case OBJ_DROID:
return droidDamage((DROID *)psObj, damage, weaponClass, weaponSubClass, impactTime);
return droidDamage((DROID *)psObj, damage, weaponClass, weaponSubClass, impactTime, isDamagePerSecond);
break;
case OBJ_STRUCTURE:
return structureDamage((STRUCTURE *)psObj, damage, weaponClass, weaponSubClass, impactTime);
return structureDamage((STRUCTURE *)psObj, damage, weaponClass, weaponSubClass, impactTime, isDamagePerSecond);
break;
case OBJ_FEATURE:
return featureDamage((FEATURE *)psObj, damage, weaponClass, weaponSubClass, impactTime);
return featureDamage((FEATURE *)psObj, damage, weaponClass, weaponSubClass, impactTime, isDamagePerSecond);
break;
case OBJ_PROJECTILE:

View File

@ -38,8 +38,6 @@ extern BASE_OBJECT *g_pProjLastAttacker; ///< The last unit that did damage - us
#define PROJ_MAX_PITCH 45
#define PROJ_ULTIMATE_PITCH 80
#define IN_FIRE 0x01 ///< Whether an object is in a fire.
#define BURNING 0x02 ///< Whether an object has just left the fire, but is still burning.
#define BURN_TIME 10000 ///< How long an object burns for after leaving a fire.
#define BURN_DAMAGE 15 ///< How much damaga a second an object takes when it is burning.
#define ACC_GRAVITY 1000 ///< Downward force against projectiles.

View File

@ -717,7 +717,7 @@ void handleAbandonedStructures()
* \param weaponSubClass the subclass of the weapon that deals the damage
* \return < 0 when the dealt damage destroys the structure, > 0 when the structure survives
*/
int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime)
int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond)
{
int32_t relativeDamage;
@ -726,7 +726,7 @@ int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS we
debug(LOG_ATTACK, "structure id %d, body %d, armour %d, damage: %d",
psStructure->id, psStructure->body, psStructure->armour[weaponClass], damage);
relativeDamage = objDamage(psStructure, damage, structureBody(psStructure), weaponClass, weaponSubClass);
relativeDamage = objDamage(psStructure, damage, structureBody(psStructure), weaponClass, weaponSubClass, isDamagePerSecond);
// If the shell did sufficient damage to destroy the structure
if (relativeDamage < 0)
@ -1473,7 +1473,6 @@ STRUCTURE* buildStructureDir(STRUCTURE_STATS *pStructureType, UDWORD x, UDWORD y
psBuilding->targetOrigin[i] = ORIGIN_UNKNOWN;
}
psBuilding->inFire = 0;
psBuilding->burnStart = 0;
psBuilding->burnDamage = 0;
@ -3679,16 +3678,12 @@ void structureUpdate(STRUCTURE *psBuilding, bool mission)
}
/* Update the fire damage data */
if (psBuilding->inFire & IN_FIRE)
if (psBuilding->burnStart != 0 && psBuilding->burnStart != gameTime - deltaGameTime) // -deltaGameTime, since projectiles are updated after structures.
{
/* Still in a fire, reset the fire flag to see if we get out this turn */
psBuilding->inFire = 0;
}
else
{
/* The fire flag has not been set so we must be out of the fire */
// The burnStart has been set, but is not from the previous tick, so we must be out of the fire.
psBuilding->burnDamage = 0; // Reset burn damage done this tick.
// Finished burning.
psBuilding->burnStart = 0;
psBuilding->burnDamage = 0;
}
//check the resistance level of the structure

View File

@ -112,7 +112,7 @@ extern bool structureStatsShutDown(void);
int requestOpenGate(STRUCTURE *psStructure);
int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime);
int32_t structureDamage(STRUCTURE *psStructure, unsigned damage, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, unsigned impactTime, bool isDamagePerSecond);
extern void structureBuild(STRUCTURE *psStructure, DROID *psDroid, int buildPoints, int buildRate = 1);
extern void structureDemolish(STRUCTURE *psStructure, DROID *psDroid, int buildPoints);
extern bool structureRepair(STRUCTURE *psStruct, DROID *psDroid, int buildPoints);