/** * @brief Checks whether there are still actors to fight with left. If none * are the match end will be triggered. * @sa G_MatchEndTrigger */ void G_MatchEndCheck (void) { int activeTeams; int i, last; if (level.intermissionTime > 0.0) /* already decided */ return; if (!level.numplayers) { G_MatchEndTrigger(0, 0); return; } /** @todo count from 0 to get the civilians for objectives */ for (i = 1, activeTeams = 0, last = 0; i < MAX_TEAMS; i++) { edict_t *ent = NULL; /* search for living but not stunned actors - there must at least be one actor * that is still able to attack or defend himself */ while ((ent = G_EdictsGetNextLivingActorOfTeam(ent, i)) != NULL) { if (!G_IsStunned(ent)) { last = i; activeTeams++; break; } } } /** @todo < 2 does not work when we count civilians */ /* prepare for sending results */ if (activeTeams < 2) { const int timeGap = (level.activeTeam == TEAM_ALIEN ? 10.0 : 3.0); G_MatchEndTrigger(activeTeams == 1 ? last : 0, timeGap); } }
void G_ActorCheckRevitalise (Edict* ent) { if (G_IsStunned(ent) && ent->STUN < ent->HP) { /* check that we could move after we stood up */ Edict* otherActor = nullptr; while ((otherActor = G_EdictsGetNextInUse(otherActor))) { if (!VectorCompare(ent->pos, otherActor->pos)) continue; if (G_IsBlockingMovementActor(otherActor)) return; } G_ActorRevitalise(ent); G_EventActorRevitalise(*ent); G_SendStats(*ent); } }
/** * @brief Heals a target and treats wounds. * @param[in,out] target Pointer to the actor who we want to treat. * @param[in] fd Pointer to the firedef used to heal the target. * @param[in] heal The value of the damage to heal. * @param[in] healerTeam The index of the team of the healer. */ void G_TreatActor (Edict* target, const fireDef_t* const fd, const int heal, const int healerTeam) { assert(target->chr.teamDef); /* Treat wounds */ if (fd->dmgweight == gi.csi->damNormal) { int bodyPart, mostWounded = 0; woundInfo_t* wounds = &target->chr.wounds; /* Find the worst not treated wound */ for (bodyPart = 0; bodyPart < target->chr.teamDef->bodyTemplate->numBodyParts(); ++bodyPart) if (wounds->woundLevel[bodyPart] > wounds->woundLevel[mostWounded]) mostWounded = bodyPart; if (wounds->woundLevel[mostWounded] > 0) { const int woundsHealed = std::min(static_cast<int>(abs(heal) / target->chr.teamDef->bodyTemplate->bleedingFactor(mostWounded)), wounds->woundLevel[mostWounded]); G_TakeDamage(target, heal); wounds->woundLevel[mostWounded] -= woundsHealed; wounds->treatmentLevel[mostWounded] += woundsHealed; /* Update stats here to get info on how many HP the target received. */ if (target->chr.scoreMission) target->chr.scoreMission->heal += abs(heal); } } /* Treat stunned actors */ if (fd->dmgweight == gi.csi->damStunElectro && G_IsStunned(target)) { if (CHRSH_IsTeamDefAlien(target->chr.teamDef) && target->team != healerTeam) /** @todo According to specs it should only be possible to use the medikit to keep an alien sedated when * 'live alien' is researched, is it possible to find if a tech is researched here? */ target->STUN = std::min(255, target->STUN - heal); else target->STUN = std::max(0, target->STUN + heal); G_ActorCheckRevitalise(target); } /* Increase morale */ if (fd->dmgweight == gi.csi->damShock) target->morale = std::min(GET_MORALE(target->chr.score.skills[ABILITY_MIND]), target->morale - heal); G_SendWoundStats(target); }
static void G_ActorRevitalise (Edict* ent) { if (G_IsStunned(ent)) { G_RemoveStunned(ent); /** @todo have a look at the morale value of * the ent and maybe get into rage or panic? */ G_ActorModifyCounters(ent->link, ent, 1, 0, -1); G_GetFloorItems(ent); } G_ActorSetMaxs(ent); /* check if the player appears/perishes, seen from other teams */ G_CheckVis(ent); /* calc new vis for this player */ G_CheckVisTeamAll(ent->team, 0, ent); G_PrintStats("%s is revitalized.", ent->chr.name); }
/** * @brief Update character stats for this mission after successful shoot. * @note Mind you that this code is always from the view of PHALANX soldiers right now, not anybody else! * @param[in,out] attacker Pointer to attacker. * @param[in] fd Pointer to fireDef_t used in shoot. * @param[in] target Pointer to target. * @sa G_UpdateCharacterSkills */ static void G_UpdateCharacterBodycount (edict_t *attacker, const fireDef_t *fd, const edict_t *target) { chrScoreMission_t *scoreMission; chrScoreGlobal_t *scoreGlobal; killtypes_t type; if (!attacker || !target) return; scoreGlobal = &attacker->chr.score; scoreMission = attacker->chr.scoreMission; /* only phalanx soldiers have this */ if (!scoreMission) return; switch (target->team) { case TEAM_ALIEN: type = KILLED_ENEMIES; if (fd) { assert(fd->weaponSkill >= 0); assert(fd->weaponSkill < lengthof(scoreMission->skillKills)); scoreMission->skillKills[fd->weaponSkill]++; } break; case TEAM_CIVILIAN: type = KILLED_CIVILIANS; break; case TEAM_PHALANX: type = KILLED_TEAM; break; default: return; } if (G_IsStunned(target)) { scoreMission->stuns[type]++; scoreGlobal->stuns[type]++; } else if (G_IsDead(target)) { scoreMission->kills[type]++; scoreGlobal->kills[type]++; } }
/** * @brief Reports and handles death or stun of an actor. If the HP of an actor is zero the actor * will die, otherwise the actor will get stunned. * @param[in] ent Pointer to an entity being killed or stunned actor. * @param[in] attacker Pointer to attacker - it must be notified about state of victim. * @todo Discuss whether stunned actor should really drop everything to floor. Maybe * it should drop only what he has in hands? Stunned actor can wake later during mission. */ bool G_ActorDieOrStun (Edict* ent, Edict* attacker) { bool state; if (ent->HP == 0) state = G_ActorDie(ent, attacker); else state = G_ActorStun(ent, attacker); /* no state change performed? */ if (!state) { gi.DPrintf("G_ActorDieOrStun: State wasn't changed\n"); return false; } if (!G_IsStunned(ent)) ent->solid = SOLID_NOT; /* send death */ G_EventActorDie(*ent, attacker != nullptr); /* handle inventory - drop everything but the armour to the floor */ G_InventoryToFloor(ent); G_ClientStateChange(ent->getPlayer(), ent, ~STATE_REACTION, false); /* check if the player appears/perishes, seen from other teams */ G_CheckVis(ent); /* check if the attacker appears/perishes, seen from other teams */ if (attacker) G_CheckVis(attacker); /* calc new vis for this player */ G_CheckVisTeamAll(ent->team, 0, attacker); /* unlink the floor container */ ent->resetFloor(); G_ReactionFireOnDead(ent); return true; }
static bool G_ActorDie (Edict* ent, const Edict* attacker) { const bool stunned = G_IsStunned(ent); G_RemoveStunned(ent); if (G_IsDead(ent)) return false; G_SetState(ent, 1 + rand() % MAX_DEATH); G_ActorSetMaxs(ent); if (stunned) { G_ActorModifyCounters(attacker, ent, 0, 1, 0); G_ActorModifyCounters(ent->link, ent, 0, 0, -1); } else { G_ActorModifyCounters(attacker, ent, -1, 1, 0); } return true; }
/** * @brief Checks whether the requested action is possible * @param[in] player Which player (human player) is trying to do the action * @param[in] ent Which of his units is trying to do the action. */ static bool G_ActionCheck (const player_t *player, edict_t *ent) { /* don't check for a player - but maybe a server action */ if (!player) return true; if (!ent || !ent->inuse) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - object not present!")); return false; } if (ent->type != ET_ACTOR && ent->type != ET_ACTOR2x2) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not an actor!")); return false; } if (G_IsStunned(ent)) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is stunned!")); return false; } if (G_IsDead(ent)) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - actor is dead!")); return false; } if (ent->team != player->pers.team) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - not on same team!")); return false; } if (ent->pnum != player->num) { G_ClientPrintf(player, PRINT_HUD, _("Can't perform action - no control over allied actors!")); return false; } /* could be possible */ return true; }
/** * @brief Checks whether the given edict is a living actor * @param[in] ent The edict to perform the check for * @sa LE_IsLivingActor */ bool G_IsLivingActor (const Edict* ent) { return G_IsActor(ent) && (G_IsStunned(ent) || !G_IsDead(ent)); }
/** * @brief Deals damage of a give type and amount to a target. * @param[in,out] target What we want to damage. * @param[in] fd The fire definition that defines what type of damage is dealt. * @param[in] damage The value of the damage. * @param[in] attacker The attacker. * @param[in] mock pseudo shooting - only for calculating mock values - NULL for real shots * @sa G_SplashDamage * @sa G_TakeDamage * @sa G_PrintActorStats */ static void G_Damage (edict_t *target, const fireDef_t *fd, int damage, edict_t *attacker, shot_mock_t *mock) { const bool stunEl = (fd->obj->dmgtype == gi.csi->damStunElectro); const bool stunGas = (fd->obj->dmgtype == gi.csi->damStunGas); const bool shock = (fd->obj->dmgtype == gi.csi->damShock); const bool smoke = (fd->obj->dmgtype == gi.csi->damSmoke); bool isRobot; assert(target); /* Breakables */ if (G_IsBrushModel(target) && G_IsBreakable(target)) { /* Breakables are immune to stun & shock damage. */ if (stunEl || stunGas || shock || mock || smoke) return; if (damage >= target->HP) { /* don't reset the HP value here, this value is used to distinguish * between triggered destroy and a shoot */ assert(target->destroy); target->destroy(target); /* maybe the attacker is seeing something new? */ G_CheckVisTeamAll(attacker->team, false, attacker); /* check if attacker appears/perishes for any other team */ G_CheckVis(attacker, true); } else { G_TakeDamage(target, damage); } return; } /* Actors don't die again. */ if (!G_IsLivingActor(target)) return; /* only actors after this point - and they must have a teamdef */ assert(target->chr.teamDef); isRobot = CHRSH_IsTeamDefRobot(target->chr.teamDef); /* Apply armour effects. */ if (damage > 0) { const int nd = target->chr.teamDef->resistance[fd->dmgweight]; if (CONTAINER(target, gi.csi->idArmour)) { const objDef_t *ad = CONTAINER(target, gi.csi->idArmour)->item.t; damage = std::max(1, damage - ad->protection[fd->dmgweight] - nd); } else { damage = std::max(1, damage - nd); } } else if (damage < 0) { /* Robots can't be healed. */ if (isRobot) return; } Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage); /* Apply difficulty settings. */ if (sv_maxclients->integer == 1) { if (G_IsAlien(attacker) && !G_IsAlien(target)) damage *= pow(1.18, g_difficulty->value); else if (!G_IsAlien(attacker) && G_IsAlien(target)) damage *= pow(1.18, -g_difficulty->value); } assert(attacker->team >= 0 && attacker->team < MAX_TEAMS); assert(target->team >= 0 && target->team < MAX_TEAMS); if (g_nodamage != NULL && !g_nodamage->integer) { /* hit */ if (mock) { G_UpdateShotMock(mock, attacker, target, damage); } else if (stunEl) { target->STUN += damage; } else if (stunGas) { if (!isRobot) /* Can't stun robots with gas */ target->STUN += damage; } else if (shock) { /* Only do this if it's not one from our own team ... they should have known that there was a flashbang coming. */ if (!isRobot && target->team != attacker->team) { /** @todo there should be a possible protection, too */ /* dazed entity wont reaction fire */ G_RemoveReaction(target); G_ActorReserveTUs(target, 0, target->chr.reservedTus.shot, target->chr.reservedTus.crouch); /* flashbangs kill TUs */ G_ActorSetTU(target, 0); G_SendStats(target); /* entity is dazed */ G_SetDazed(target); G_ClientPrintf(G_PLAYER_FROM_ENT(target), PRINT_HUD, _("Soldier is dazed!\nEnemy used flashbang!")); return; } } else { G_TakeDamage(target, damage); if (damage < 0) { /* The 'attacker' is healing the target. */ /* Update stats here to get info on how many TUs the target received. */ if (target->chr.scoreMission) target->chr.scoreMission->heal += abs(damage); /** @todo Also increase the morale a little bit when * soldier gets healing and morale is lower than max possible? */ if (G_IsStunned(target)) { /* reduce STUN */ target->STUN += damage; G_ActorCheckRevitalise(target); } } else { /* Real damage was dealt. */ /* Update overall splash damage for stats/score. */ if (!mock && damage > 0 && fd->splrad) /**< Check for >0 and splrad to not count this as direct hit. */ G_UpdateHitScore(attacker, target, fd, damage); } } } if (mock) return; G_CheckDeathOrKnockout(target, attacker, fd, damage); }