// get the maximum group size for a command droid unsigned int cmdDroidMaxGroup(const DROID* psCommander) { return getDroidLevel(psCommander) * 2 + MIN_CMD_GROUP_DROIDS; }
/* Calculates attack priority for a certain target */ static SDWORD targetAttackWeight(BASE_OBJECT *psTarget, BASE_OBJECT *psAttacker, SDWORD weapon_slot) { SDWORD targetTypeBonus=0, damageRatio=0, attackWeight=0, noTarget=-1; UDWORD weaponSlot; DROID *targetDroid=NULL,*psAttackerDroid=NULL,*psGroupDroid,*psDroid; STRUCTURE *targetStructure=NULL; WEAPON_EFFECT weaponEffect; WEAPON_STATS *attackerWeapon; BOOL bEmpWeap=false,bCmdAttached=false,bTargetingCmd=false; if (psTarget == NULL || psAttacker == NULL || aiObjectIsProbablyDoomed(psTarget)) { return noTarget; } ASSERT(psTarget != psAttacker, "targetAttackWeight: Wanted to evaluate the worth of attacking ourselves..."); targetTypeBonus = 0; //Sensors/ecm droids, non-military structures get lower priority /* Get attacker weapon effect */ if(psAttacker->type == OBJ_DROID) { psAttackerDroid = (DROID *)psAttacker; attackerWeapon = (WEAPON_STATS *)(asWeaponStats + psAttackerDroid->asWeaps[weapon_slot].nStat); //check if this droid is assigned to a commander bCmdAttached = hasCommander(psAttackerDroid); //find out if current target is targeting our commander if(bCmdAttached) { if(psTarget->type == OBJ_DROID) { psDroid = (DROID *)psTarget; //go through all enemy weapon slots for(weaponSlot = 0; !bTargetingCmd && weaponSlot < ((DROID *)psTarget)->numWeaps; weaponSlot++) { //see if this weapon is targeting our commander if (psDroid->psActionTarget[weaponSlot] == (BASE_OBJECT *)psAttackerDroid->psGroup->psCommander) { bTargetingCmd = true; } } } else { if(psTarget->type == OBJ_STRUCTURE) { //go through all enemy weapons for(weaponSlot = 0; !bTargetingCmd && weaponSlot < ((STRUCTURE *)psTarget)->numWeaps; weaponSlot++) { if (((STRUCTURE *)psTarget)->psTarget[weaponSlot] == (BASE_OBJECT *)psAttackerDroid->psGroup->psCommander) { bTargetingCmd = true; } } } } } } else if(psAttacker->type == OBJ_STRUCTURE) { attackerWeapon = ((WEAPON_STATS *)(asWeaponStats + ((STRUCTURE *)psAttacker)->asWeaps[weapon_slot].nStat)); } else /* feature */ { ASSERT(!"invalid attacker object type", "targetAttackWeight: Invalid attacker object type"); return noTarget; } //Get weapon effect weaponEffect = attackerWeapon->weaponEffect; //See if attacker is using an EMP weapon bEmpWeap = (attackerWeapon->weaponSubClass == WSC_EMP); /* Calculate attack weight */ if(psTarget->type == OBJ_DROID) { targetDroid = (DROID *)psTarget; if (targetDroid->died) { debug(LOG_NEVER, "Target droid is dead, skipping invalid droid.\n"); return noTarget; } /* Calculate damage this target suffered */ if (targetDroid->originalBody == 0) // FIXME Somewhere we get 0HP droids from { damageRatio = 0; debug(LOG_ERROR, "targetAttackWeight: 0HP droid detected!"); debug(LOG_ERROR, " Type: %i Name: \"%s\" Owner: %i \"%s\")", targetDroid->droidType, targetDroid->aName, targetDroid->player, getPlayerName(targetDroid->player)); } else { damageRatio = 1 - targetDroid->body / targetDroid->originalBody; } assert(targetDroid->originalBody != 0); // Assert later so we get the info from above /* See if this type of a droid should be prioritized */ switch (targetDroid->droidType) { case DROID_SENSOR: case DROID_ECM: case DROID_PERSON: case DROID_TRANSPORTER: case DROID_DEFAULT: case DROID_ANY: break; case DROID_CYBORG: case DROID_WEAPON: case DROID_CYBORG_SUPER: targetTypeBonus = WEIGHT_WEAPON_DROIDS; break; case DROID_COMMAND: targetTypeBonus = WEIGHT_COMMAND_DROIDS; break; case DROID_CONSTRUCT: case DROID_REPAIR: case DROID_CYBORG_CONSTRUCT: case DROID_CYBORG_REPAIR: targetTypeBonus = WEIGHT_SERVICE_DROIDS; break; } /* Now calculate the overall weight */ attackWeight = asWeaponModifier[weaponEffect][(asPropulsionStats + targetDroid->asBits[COMP_PROPULSION].nStat)->propulsionType] // Our weapon's effect against target + WEIGHT_DIST_TILE_DROID * psAttacker->sensorRange/TILE_UNITS - WEIGHT_DIST_TILE_DROID * map_coord(iHypot(psAttacker->pos.x - targetDroid->pos.x, psAttacker->pos.y - targetDroid->pos.y)) // farer droids are less attractive + WEIGHT_HEALTH_DROID * damageRatio // we prefer damaged droids + targetTypeBonus; // some droid types have higher priority /* If attacking with EMP try to avoid targets that were already "EMPed" */ if(bEmpWeap && (targetDroid->lastHitWeapon == WSC_EMP) && ((gameTime - targetDroid->timeLastHit) < EMP_DISABLE_TIME)) //target still disabled { attackWeight /= EMP_DISABLED_PENALTY_F; } } else if(psTarget->type == OBJ_STRUCTURE) { targetStructure = (STRUCTURE *)psTarget; /* Calculate damage this target suffered */ damageRatio = 1 - targetStructure->body / structureBody(targetStructure); /* See if this type of a structure should be prioritized */ switch(targetStructure->pStructureType->type) { case REF_DEFENSE: targetTypeBonus = WEIGHT_WEAPON_STRUCT; break; case REF_RESOURCE_EXTRACTOR: targetTypeBonus = WEIGHT_DERRICK_STRUCT; break; case REF_FACTORY: case REF_CYBORG_FACTORY: case REF_REPAIR_FACILITY: targetTypeBonus = WEIGHT_MILITARY_STRUCT; break; default: break; } /* Now calculate the overall weight */ attackWeight = asStructStrengthModifier[weaponEffect][targetStructure->pStructureType->strength] // Our weapon's effect against target + WEIGHT_DIST_TILE_STRUCT * psAttacker->sensorRange/TILE_UNITS - WEIGHT_DIST_TILE_STRUCT * map_coord(iHypot(psAttacker->pos.x - targetStructure->pos.x, psAttacker->pos.y - targetStructure->pos.y)) // farer structs are less attractive + WEIGHT_HEALTH_STRUCT * damageRatio // we prefer damaged structures + targetTypeBonus; // some structure types have higher priority /* Go for unfinished structures only if nothing else found (same for non-visible structures) */ if(targetStructure->status != SS_BUILT) //a decoy? { attackWeight /= WEIGHT_STRUCT_NOTBUILT_F; } /* EMP should only attack structures if no enemy droids are around */ if(bEmpWeap) { attackWeight /= EMP_STRUCT_PENALTY_F; } } else //a feature { return 1; } /* We prefer objects we can see and can attack immediately */ if(!visibleObject((BASE_OBJECT *)psAttacker, psTarget, true)) { attackWeight /= WEIGHT_NOT_VISIBLE_F; } /* Commander-related criterias */ if(bCmdAttached) //attached to a commander and don't have a target assigned by some order { ASSERT(psAttackerDroid->psGroup->psCommander != NULL, "Commander is NULL"); //if commander is being targeted by our target, try to defend the commander if(bTargetingCmd) { attackWeight += WEIGHT_CMD_RANK * ( 1 + getDroidLevel(psAttackerDroid->psGroup->psCommander)); } //fire support - go through all droids assigned to the commander for (psGroupDroid = psAttackerDroid->psGroup->psList; psGroupDroid; psGroupDroid = psGroupDroid->psGrpNext) { for(weaponSlot = 0; weaponSlot < psGroupDroid->numWeaps; weaponSlot++) { //see if this droid is currently targeting current target if(psGroupDroid->psTarget == psTarget || psGroupDroid->psActionTarget[weaponSlot] == psTarget) { //we prefer targets that are already targeted and hence will be destroyed faster attackWeight += WEIGHT_CMD_SAME_TARGET; } } } } return attackWeight; }
/** This function returns the maximum group size of the command droid.*/ unsigned int cmdDroidMaxGroup(const DROID* psCommander) { return getDroidLevel(psCommander) * getBrainStats(const_cast<DROID*>(psCommander))->maxDroidsMult + getBrainStats(const_cast<DROID*>(psCommander))->maxDroids; }