static void Reset_RescueTrigger (Edict* self, Edict* activator) { if (G_IsActor(activator)) { Actor* actor = makeActor(activator); if (self->isSameTeamAs(actor)) actor->setInRescueZone(false); } }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @todo use level.actualRound to determine the 'King of the Hill' time * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!self->owner()) return false; switch (self->owner()->getTeam()) { case TEAM_ALIEN: if (G_IsAlien(activator)) { if (!self->count) { self->count = level.actualRound; gi.BroadcastPrintf(PRINT_HUD, _("Aliens entered target zone!")); } return true; } else { /* reset king of the hill counter */ self->count = 0; } /* general case that also works for multiplayer teams */ default: if (!activator->isSameTeamAs(self->owner())) { /* reset king of the hill counter */ self->count = 0; return false; } if (self->owner()->count) return false; self->owner()->count = level.actualRound; if (!self->owner()->item) { gi.BroadcastPrintf(PRINT_HUD, _("Target zone is occupied!")); return true; } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = activator->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->owner()->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(makeActor(activator), cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); gi.BroadcastPrintf(PRINT_HUD, _("Item was placed.")); self->owner()->count = level.actualRound; return true; } } break; } return true; }
/** * @brief Rescue trigger * @sa SP_trigger_resuce */ static bool Touch_RescueTrigger (Edict* self, Edict* activator) { /* these actors should really not be able to trigger this - they don't move anymore */ assert(!G_IsDead(activator)); if (G_IsActor(activator)) { Actor* actor = makeActor(activator); if (self->isSameTeamAs(actor)) actor->setInRescueZone(true); } return false; }
/** * @brief Hurt trigger * @sa SP_trigger_hurt */ bool Touch_HurtTrigger (Edict* self, Edict* activator) { /* Dead actors should really not be able to trigger this - they can't be hurt anymore anyway */ if (!G_IsLivingActor(activator)) return false; /* If no damage is dealt don't count it as triggered */ const int damage = G_ApplyProtection(activator, self->dmgtype, self->dmg); if (damage == 0) return false; const bool stunEl = (self->dmgtype == gi.csi->damStunElectro); const bool stunGas = (self->dmgtype == gi.csi->damStunGas); const bool shock = (self->dmgtype == gi.csi->damShock); const bool isRobot = activator->chr.teamDef->robot; Actor* actor = makeActor(activator); if (stunEl || (stunGas && !isRobot)) { actor->addStun(damage); } else if (shock) { /** @todo Handle dazed via trigger_hurt */ } else { G_TakeDamage(actor, damage); } /* Play hurt sound unless this is shock damage -- it doesn't do anything * because we don't actually handle it above yet */ if (!shock) { const teamDef_t* teamDef = activator->chr.teamDef; const int gender = activator->chr.gender; const char* sound = teamDef->getActorSound(gender, SND_HURT); G_EventSpawnSound(G_PlayerToPM(activator->getPlayer()), *activator, nullptr, sound); } G_CheckDeathOrKnockout(actor, nullptr, nullptr, damage); return true; }
/** * @brief Mission trigger * @todo use level.nextmap to spawn another map when every living actor has touched the mission trigger * @note Don't set a client action here - otherwise the movement event might * be corrupted */ bool G_MissionTouch (Edict* self, Edict* activator) { if (!G_IsLivingActor(activator)) return false; Actor* actor = makeActor(activator); const char* const actorTeam = G_MissionGetTeamString(actor->getTeam()); if (!G_IsCivilian(actor) && self->isOpponent(actor)) { if (!self->item && self->count) { if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking the %s!"), actorTeam, self->targetname); } else { const char* const teamName = G_MissionGetTeamString(self->getTeam()); gi.BroadcastPrintf(PRINT_HUD, _("%s forces are attacking %s target zone!"), actorTeam, teamName); } /* reset king of the hill counter */ self->count = 0; } return false; } if (self->count) return false; if (self->isSameTeamAs(actor)) { self->count = level.actualRound; if (!self->item) { linkedList_t* touched = self->touchedList; while (touched) { const Edict* const ent = static_cast<const Edict* const>(touched->data); if (!self->isSameTeamAs(ent) && !G_IsDead(ent)) { return true; } touched = touched->next; } if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied the %s!"), actorTeam, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("%s forces have occupied their target zone!"), actorTeam); } return true; } } /* search the item in the activator's inventory */ /* ignore items linked from any temp container the actor must have this in his hands */ const Container* cont = nullptr; while ((cont = actor->chr.inv.getNextCont(cont))) { Item* item = nullptr; while ((item = cont->getNextItem(item))) { const objDef_t* od = item->def(); /* check whether we found the searched item in the actor's inventory */ if (!Q_streq(od->id, self->item)) continue; /* drop the weapon - even if out of TUs */ G_ActorInvMove(actor, cont->def(), item, INVDEF(CID_FLOOR), NONE, NONE, false); if (self->targetname) { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed at the %s."), item->def()->name, self->targetname); } else { gi.BroadcastPrintf(PRINT_HUD, _("The %s was placed."), item->def()->name); } self->count = level.actualRound; return true; } } return false; }
/** * @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 - nullptr for real shots * @param[in] impact impact location - @c nullptr for splash damage * @return @c true if damage could be dealt (even if it was 0) @c false otherwise * @sa G_SplashDamage * @sa G_TakeDamage * @sa G_PrintActorStats */ static bool G_Damage (Edict* target, const fireDef_t* fd, int damage, Actor* attacker, shot_mock_t* mock, const vec3_t impact) { assert(target); 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); /* Breakables */ if (G_IsBrushModel(target) && G_IsBreakable(target)) { /* Breakables are immune to stun & shock damage. */ if (stunEl || stunGas || shock || mock || smoke) return false; 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->getTeam(), 0, attacker); /* check if attacker appears/perishes for any other team */ G_CheckVis(attacker); } else { G_TakeDamage(target, damage); } return true; } /* Actors don't die again. */ if (!G_IsLivingActor(target)) return false; /* Now we know that the target is an actor */ Actor* victim = makeActor(target); /* only actors after this point - and they must have a teamdef */ assert(victim->chr.teamDef); const bool isRobot = CHRSH_IsTeamDefRobot(victim->chr.teamDef); /* Apply armour effects. */ if (damage > 0) { damage = G_ApplyProtection(victim, fd->dmgweight, damage); } else if (damage < 0) { /* Robots can't be healed. */ if (isRobot) return false; } Com_DPrintf(DEBUG_GAME, " Total damage: %d\n", damage); /* Apply difficulty settings. */ if (G_IsSinglePlayer()) { if (G_IsAlien(attacker) && !G_IsAlien(victim)) damage *= pow(1.18f, g_difficulty->value); else if (!G_IsAlien(attacker) && G_IsAlien(victim)) damage *= pow(1.18f, -g_difficulty->value); } assert(attacker->getTeam() >= 0 && attacker->getTeam() < MAX_TEAMS); assert(victim->getTeam() >= 0 && victim->getTeam() < MAX_TEAMS); if ((g_nodamage != nullptr && !g_nodamage->integer) || mock) { /* hit */ if (mock) { G_UpdateShotMock(mock, attacker, victim, damage); } else if (stunEl) { victim->addStun(damage); } else if (stunGas) { if (!isRobot) /* Can't stun robots with gas */ victim->addStun(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 && !victim->isSameTeamAs(attacker)) { /** @todo there should be a possible protection, too */ /* dazed entity wont reaction fire */ victim->removeReaction(); G_ActorReserveTUs(victim, 0, victim->chr.reservedTus.shot, victim->chr.reservedTus.crouch); /* flashbangs kill TUs */ G_ActorSetTU(victim, 0); G_SendStats(*victim); /* entity is dazed */ victim->setDazed(); G_EventSendState(G_VisToPM(victim->visflags), *victim); return !mock; } else { return false; } } else { if (damage < 0) { /* The 'attacker' is healing the victim. */ G_TreatActor(victim, fd, damage, attacker->getTeam()); } else { /* Real damage was dealt. */ G_DamageActor(victim, damage, impact); /* 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, victim, fd, damage); } } } if (mock) return false; G_CheckDeathOrKnockout(victim, attacker, fd, damage); return true; }