/** * @brief Touch trigger * @sa SP_trigger_touch */ static bool Touch_TouchTrigger (Edict* self, Edict* activator) { /* these actors should really not be able to trigger this - they don't move anymore */ assert(!G_IsDead(activator)); self->_owner = G_EdictsFindTargetEntity(self->target); if (!self->owner()) { gi.DPrintf("Target '%s' wasn't found for %s\n", self->target, self->classname); G_FreeEdict(self); return false; } if (self->owner()->flags & FL_CLIENTACTION) { G_ActorSetClientAction(activator, self->owner()); } else if (!(self->spawnflags & TRIGGER_TOUCH_ONCE) || self->touchedNext == nullptr) { if (!self->owner()->use) { gi.DPrintf("Owner of %s doesn't have a use function\n", self->classname); G_FreeEdict(self); return false; } G_UseEdict(self->owner(), activator); } return false; }
static void Reset_TouchTrigger (Edict* self, Edict* activator) { /* fire the use function on leaving the trigger area */ if (activator != nullptr && (self->owner()->flags & FL_CLIENTACTION)) G_ActorSetClientAction(activator, nullptr); else if ((self->spawnflags & TRIGGER_TOUCH_ONCE) && self->touchedNext == nullptr) G_UseEdict(self->owner(), activator); }
/** * @brief This function 'uses' the edict. E.g. it opens the door when the player wants it to open * @sa PA_USE_DOOR * @param[in] player The player is trying to activate the door * @param[in,out] actor The actor the player is using to activate the entity * @param[in,out] edict The entity that is to be used * @todo Do we have to change the trigger position here, too? I don't think this is really needed. * @sa CL_ActorUse * @sa G_UseEdict */ bool G_ClientUseEdict (const player_t *player, edict_t *actor, edict_t *edict) { /* check whether the actor has sufficient TUs to 'use' this edicts */ if (!G_ActionCheckForCurrentTeam(player, actor, edict->TU)) return false; if (!G_UseEdict(edict, actor)) return false; /* using a group of edicts only costs TUs once (for the master) */ G_ActorUseTU(actor, edict->TU); /* send the new TUs */ G_SendStats(actor); G_EventEnd(); return true; }
/** * @brief func_door (0 .5 .8) ? * "health" if set, door is destroyable * @sa SV_SetModel * @sa LM_AddModel * @sa G_SendEdictsAndBrushModels */ void SP_func_door (edict_t *ent) { edict_t *other; ent->classname = "door"; ent->type = ET_DOOR; if (!ent->noise) ent->noise = "doors/open_close"; /* set an inline model */ gi.SetModel(ent, ent->model); ent->solid = SOLID_BSP; gi.LinkEdict(ent); ent->doorState = STATE_CLOSED; ent->dir = YAW; if (ent->spawnflags & REVERSE) ent->dir |= DOOR_OPEN_REVERSE; if (ent->HP) ent->flags |= FL_DESTROYABLE; ent->flags |= FL_CLIENTACTION; /* spawn the trigger entity */ other = G_TriggerSpawn(ent); other->touch = Touch_DoorTrigger; other->reset = Reset_DoorTrigger; ent->child = other; G_ActorSetTU(ent, TU_DOOR_ACTION); if (!ent->speed) ent->speed = 10; ent->use = Door_Use; /* the door should start opened */ if (ent->spawnflags & FL_TRIGGERED) G_UseEdict(ent, NULL); ent->destroy = Destroy_Breakable; }
/** * @brief Call the 'use' function for the given edict and all its group members * @param[in] ent The edict to call the use function for * @param[in] activator The edict that uses ent * @return qtrue when there is possibility to use edict being parameter. * @sa G_ClientUseEdict */ qboolean G_UseEdict (edict_t *ent, edict_t* activator) { if (!ent) return qfalse; /* no use function assigned */ if (!ent->use) return qfalse; if (!ent->use(ent, activator)) return qfalse; /* only the master edict is calling the opening for the other group parts */ if (!(ent->flags & FL_GROUPSLAVE)) { edict_t* chain = ent->groupChain; while (chain) { G_UseEdict(chain, activator); chain = chain->groupChain; } } return qtrue; }
/** * @note Think functions are only executed when the match is running * or in other word, the game has started */ void G_MissionThink (Edict* self) { if (!G_MatchIsRunning()) return; /* when every player has joined the match - spawn the mission target * particle (if given) to mark the trigger */ if (self->particle) { self->link = G_SpawnParticle(self->origin, self->spawnflags, self->particle); /* This is automatically freed on map shutdown */ self->particle = nullptr; } Edict* chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { if (chain->item) { const Item* ic; G_GetFloorItems(chain); ic = chain->getFloor(); if (!ic) { /* reset the counter if there is no item */ chain->count = 0; return; } for (; ic; ic = ic->getNext()) { const objDef_t* od = ic->def(); assert(od); /* not the item we are looking for */ if (Q_streq(od->id, chain->item)) break; } if (!ic) { /* reset the counter if it's not the searched item */ chain->count = 0; return; } } if (chain->time) { /* Check that the target zone is still occupied (last defender might have died) */ if (!chain->item && !G_MissionIsTouched(chain)) { chain->count = 0; } const int endTime = level.actualRound - chain->count; const int spawnIndex = (chain->getTeam() + level.teamOfs) % MAX_TEAMS; const int currentIndex = (level.activeTeam + level.teamOfs) % MAX_TEAMS; /* not every edict in the group chain has * been occupied long enough */ if (!chain->count || endTime < chain->time || (endTime == chain->time && spawnIndex < currentIndex)) return; } if (chain->target && !chain->time && !chain->item) { if (!G_MissionIsTouched(chain)) return; } } chain = chain->groupChain; } const bool endMission = self->target == nullptr; /* store team before the edict is released */ const int team = self->getTeam(); chain = self->groupMaster; if (!chain) chain = self; while (chain) { if (chain->type == ET_MISSION) { G_UseEdict(chain, nullptr); if (chain->item != nullptr) { Edict* item = G_GetEdictFromPos(chain->pos, ET_ITEM); if (item != nullptr) { if (!G_InventoryRemoveItemByID(chain->item, item, CID_FLOOR)) { Com_Printf("Could not remove item '%s' from floor edict %i\n", chain->item, item->getIdNum()); } else if (!item->getFloor()) { G_EventPerish(*item); G_FreeEdict(item); } } } if (chain->link != nullptr) { Edict* particle = G_GetEdictFromPos(chain->pos, ET_PARTICLE); if (particle != nullptr) { G_AppearPerishEvent(G_VisToPM(particle->visflags), false, *particle, nullptr); G_FreeEdict(particle); } chain->link = nullptr; } /* Display mission message */ if (G_ValidMessage(chain)) { const char* msg = chain->message; if (msg[0] == '_') ++msg; gi.BroadcastPrintf(PRINT_HUD, "%s", msg); } } Edict* ent = chain->groupChain; /* free the group chain */ G_FreeEdict(chain); chain = ent; } if (endMission) G_MatchEndTrigger(team, level.activeTeam == TEAM_ALIEN ? 10 : 3); }