/* Deals damage to an object * \param psObj object to deal damage to * \param damage amount of damage to deal * \param weaponClass the class of the weapon that deals the damage * \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, unsigned damage, unsigned originalhp, WEAPON_CLASS weaponClass, WEAPON_SUBCLASS weaponSubClass, bool isDamagePerSecond) { int actualDamage, armour, level = 1, lastHit = psObj->timeLastHit; // If the previous hit was by an EMP cannon and this one is not: // don't reset the weapon class and hit time // (Giel: I guess we need this to determine when the EMP-"shock" is over) if (psObj->lastHitWeapon != WSC_EMP || weaponSubClass == WSC_EMP) { psObj->timeLastHit = gameTime; psObj->lastHitWeapon = weaponSubClass; } // EMP cannons do no damage, if we are one return now if (weaponSubClass == WSC_EMP) { return 0; } // apply game difficulty setting damage = modifyForDifficultyLevel(damage, psObj->player != selectedPlayer); armour = psObj->armour[weaponClass]; if (psObj->type == OBJ_STRUCTURE || psObj->type == OBJ_DROID) { if (psObj->type == OBJ_STRUCTURE && ((STRUCTURE *)psObj)->status == SS_BEING_BUILT) { armour = 0; } // Force sending messages, even if messages were turned off, since a non-synchronised script will execute here. (Aaargh!) bool bMultiMessagesBackup = bMultiMessages; bMultiMessages = bMultiPlayer; clustObjectAttacked((BASE_OBJECT *)psObj); triggerEventAttacked(psObj, g_pProjLastAttacker, lastHit); bMultiMessages = bMultiMessagesBackup; } debug(LOG_ATTACK, "objDamage(%d): body %d armour %d damage: %d", psObj->id, psObj->body, armour, damage); if (psObj->type == OBJ_DROID) { DROID *psDroid = (DROID *)psObj; // Retrieve highest, applicable, experience level level = getDroidEffectiveLevel(psDroid); } // Reduce damage taken by EXP_REDUCE_DAMAGE % for each experience level actualDamage = (damage * (100 - EXP_REDUCE_DAMAGE * level)) / 100; // You always do at least a third of the experience modified damage actualDamage = MAX(actualDamage - armour, actualDamage / 3); // 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,isDps%d", psObj->id, damage, originalhp, weaponClass, weaponSubClass, armour, level, actualDamage, isDamagePerSecond); // for some odd reason, we have 0 hitpoints. if (!originalhp) { ASSERT(originalhp, "original hitpoints are 0 ?"); return -65536; // it is dead } // If the shell did sufficient damage to destroy the object, deal with it and return if (actualDamage >= psObj->body) { return -(int64_t)65536 * psObj->body / originalhp; } // Subtract the dealt damage from the droid's remaining body points psObj->body -= actualDamage; syncDebugObject(psObj, 'D'); return (int64_t)65536 * actualDamage / originalhp; }
// update the visibility change levels void visUpdateLevel(void) { visLevelInc = gameTimeAdjustedAverage(VIS_LEVEL_INC); visLevelDec = gameTimeAdjustedAverage(VIS_LEVEL_DEC); }