/** * @brief Change the animation of an actor to the idle animation (which can be * panic, dead or stand) * @note We have more than one animation for dead - the index is given by the * state of the local entity * @note Think function * @note See the *.anm files in the models dir */ void LET_StartIdle (le_t* le) { /* hidden actors don't have models assigned, thus we can not change the * animation for any model */ if (!LE_IsInvisible(le)) { if (LE_IsDead(le)) R_AnimChange(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le))); else if (LE_IsPanicked(le)) R_AnimChange(&le->as, le->model1, "panic0"); else R_AnimChange(&le->as, le->model1, LE_GetAnim("stand", le->right, le->left, le->state)); } le->pathPos = le->pathLength = 0; if (le->stepList != nullptr) { leStep_t* step = le->stepList->next; Mem_Free(le->stepList); le->stepList = step; if (step != nullptr) { le->stepIndex--; } else if (le->stepIndex != 0) { Com_Error(ERR_DROP, "stepindex for entnum %i is out of sync (%i should be 0)\n", le->entnum, le->stepIndex); } } /* keep this animation until something happens */ LE_SetThink(le, nullptr); }
/** * @brief Moves actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa LET_PathMove * @note EV_ACTOR_MOVE */ void CL_ActorDoMove (const eventRegister_t* self, dbuffer* msg) { const int number = NET_ReadShort(msg); /* get le */ le_t* le = LE_Get(number); if (!le) LE_NotFoundError(number); if (!LE_IsActor(le)) Com_Error(ERR_DROP, "Can't move, LE doesn't exist or is not an actor (entnum: %i, type: %i)\n", number, le->type); if (LE_IsDead(le)) Com_Error(ERR_DROP, "Can't move, actor on team %i dead (entnum: %i)", le->team, number); /* lock this le for other events, the corresponding unlock is in LE_DoEndPathMove() */ LE_Lock(le); if (le->isMoving()) { if (le->pathLength == le->pathPos) { LE_DoEndPathMove(le); } else { Com_Error(ERR_DROP, "Actor (entnum: %i) on team %i is still moving (%i steps left). Times: %i, %i, %i", le->entnum, le->team, le->pathLength - le->pathPos, le->startTime, le->endTime, cl.time); } } int i = 0; /* the end of this event is marked with a 0 */ while (NET_PeekLong(msg) != 0) { NET_ReadByte(msg); le->dvtab[i] = NET_ReadShort(msg); /** Don't adjust dv values here- the whole thing is needed to move the actor! */ le->speed[i] = NET_ReadShort(msg); le->pathContents[i] = NET_ReadShort(msg); i++; } le->pathLength = i; if (le->pathLength > MAX_ROUTE) Com_Error(ERR_DROP, "Overflow in pathLength (entnum: %i)", number); /* skip the end of move marker */ NET_ReadLong(msg); /* Also get the final position */ NET_ReadGPos(msg, le->newPos); if (VectorCompare(le->newPos, le->pos)) Com_Error(ERR_DROP, "start and end pos are the same (entnum: %i)", number); /* activate PathMove function */ le->resetFloor(); if (LE_IsInvisible(le)) /* Hack: this relies on the visibility events interrupting the EV_ACTOR_MOVE event */ LE_SetThink(le, LET_HiddenMove); else LE_SetThink(le, LET_StartPathMove); le->pathPos = 0; le->startTime = cl.time; le->endTime = cl.time; }
/** * @brief Collecting stunned aliens and alien bodies after the mission. * @param[in] aircraft Pointer to the aircraft with cargo. * @sa CL_ParseResults * @sa CL_GameAutoGo */ void AL_CollectingAliens (aircraft_t *aircraft) { le_t *le = NULL; while ((le = LE_GetNextInUse(le))) { if (LE_IsActor(le) && LE_IsAlien(le)) { assert(le->teamDef); if (LE_IsStunned(le)) AL_AddAlienTypeToAircraftCargo(aircraft, le->teamDef, 1, qfalse); else if (LE_IsDead(le)) AL_AddAlienTypeToAircraftCargo(aircraft, le->teamDef, 1, qtrue); } } }
/** * @brief Turns actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message */ void CL_ActorDoTurn (const eventRegister_t* self, dbuffer* msg) { int entnum, dir; NET_ReadFormat(msg, self->formatString, &entnum, &dir); /* get le */ le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsActor(le)) Com_Error(ERR_DROP, "Can't turn, LE doesn't exist or is not an actor (number: %i, type: %i)\n", entnum, le->type); if (LE_IsDead(le)) Com_Error(ERR_DROP, "Can't turn, actor dead\n"); le->angle = dir; le->angles[YAW] = directionAngles[le->angle]; }
static void UI_RadarNodeGetActorColor (const le_t* le, vec4_t color) { const int actorLevel = le->pos[2]; Vector4Set(color, 0, 1, 0, 1); /* use different alpha values for different levels */ if (actorLevel < cl_worldlevel->integer) color[3] = 0.5; else if (actorLevel > cl_worldlevel->integer) color[3] = 0.3; /* use different colors for different teams */ if (LE_IsCivilian(le)) { color[0] = 1; } else if (le->team != cls.team) { color[1] = 0; color[0] = 1; } /* show dead actors in full black */ if (LE_IsDead(le)) { Vector4Set(color, 0, 0, 0, 0.3); } }
static void UI_RadarNodeDrawActor (const le_t* le, const vec3_t pos) { vec2_t coords[4]; vec2_t vertices[4]; int i; const float size = 10; const int tileSize = 28; int tilePos = 4; const image_t* image; vec4_t color; const float pov = directionAngles[le->angle] * torad + M_PI; image = UI_LoadImage("ui/radar"); if (image == nullptr) return; /* draw FOV */ if (!LE_IsDead(le)) { vertices[0][0] = - size * 4; vertices[0][1] = + 0; vertices[1][0] = + size * 4; vertices[1][1] = + 0; vertices[2][0] = + size * 4; vertices[2][1] = - size * 4; vertices[3][0] = - size * 4; vertices[3][1] = - size * 4; coords[0][0] = (7) / 128.0f; coords[0][1] = (37 + 63) / 128.0f; coords[1][0] = (7 + 114) / 128.0f; coords[1][1] = (37 + 63) / 128.0f; coords[2][0] = (7 + 114) / 128.0f; coords[2][1] = (37) / 128.0f; coords[3][0] = (7) / 128.0f; coords[3][1] = (37) / 128.0f; /* affine transformation */ for (i = 0; i < 4; i++) { const float dx = vertices[i][0]; const float dy = vertices[i][1]; vertices[i][0] = pos[0] + dx * sin(pov) + dy * cos(pov); vertices[i][1] = pos[1] + dx * cos(pov) - dy * sin(pov); } UI_RadarNodeGetActorColor(le, color); if (LE_IsSelected(le)) { color[3] *= 0.75; } else { color[3] = 0.1f; } UI_RadarNodeDrawArrays(color, coords, vertices, image); } if (LE_IsDead(le)) tilePos = 4; else if (LE_IsSelected(le)) tilePos = 66; else tilePos = 36; /* a 0,0 centered square */ vertices[0][0] = - size; vertices[0][1] = + size; vertices[1][0] = + size; vertices[1][1] = + size; vertices[2][0] = + size; vertices[2][1] = - size; vertices[3][0] = - size; vertices[3][1] = - size; coords[0][0] = (tilePos) / 128.0f; coords[0][1] = (5 + tileSize) / 128.0f; coords[1][0] = (tilePos + tileSize) / 128.0f; coords[1][1] = (5 + tileSize) / 128.0f; coords[2][0] = (tilePos + tileSize) / 128.0f; coords[2][1] = (5) / 128.0f; coords[3][0] = (tilePos) / 128.0f; coords[3][1] = (5) / 128.0f; /* affine transformation */ for (i = 0; i < 4; i++) { const float dx = vertices[i][0]; const float dy = vertices[i][1]; vertices[i][0] = pos[0] + dx * sin(pov) + dy * cos(pov); vertices[i][1] = pos[1] + dx * cos(pov) - dy * sin(pov); } UI_RadarNodeGetActorColor(le, color); UI_RadarNodeDrawArrays(color, coords, vertices, image); }
/** * @brief Shoot with weapon. * @sa CL_ActorShoot * @sa CL_ActorShootHidden * @todo Improve detection of left- or right animation. * @sa EV_ACTOR_SHOOT */ void CL_ActorDoShoot (const eventRegister_t* self, dbuffer* msg) { vec3_t muzzle, impact; int flags, normal, shooterEntnum, victimEntnum; int objIdx; int first; weaponFireDefIndex_t weapFdsIdx; fireDefIndex_t fdIdx; int surfaceFlags; shoot_types_t shootType; /* read data */ NET_ReadFormat(msg, self->formatString, &shooterEntnum, &victimEntnum, &first, &objIdx, &weapFdsIdx, &fdIdx, &shootType, &flags, &surfaceFlags, &muzzle, &impact, &normal); le_t* leVictim; if (victimEntnum != SKIP_LOCAL_ENTITY) { leVictim = LE_Get(victimEntnum); if (!leVictim) LE_NotFoundError(victimEntnum); } else { leVictim = nullptr; } /* get shooter le */ le_t* leShooter = LE_Get(shooterEntnum); /* get the fire def */ const objDef_t* obj = INVSH_GetItemByIDX(objIdx); const fireDef_t* fd = FIRESH_GetFiredef(obj, weapFdsIdx, fdIdx); CL_ActorGetMuzzle(leShooter, muzzle, shootType); /* add effect le */ LE_AddProjectile(fd, flags, muzzle, impact, normal, leVictim); /* start the sound */ if ((first || !fd->soundOnce) && fd->fireSound != nullptr && !(flags & SF_BOUNCED)) S_LoadAndPlaySample(fd->fireSound, muzzle, fd->fireAttenuation, SND_VOLUME_WEAPONS); /* do actor related stuff */ if (!leShooter) return; /* maybe hidden or inuse is false? */ if (!LE_IsActor(leShooter)) Com_Error(ERR_DROP, "Can't shoot, LE not an actor (type: %i)", leShooter->type); /* no animations for hidden actors */ if (leShooter->type == ET_ACTORHIDDEN) return; if (LE_IsDead(leShooter)) { Com_DPrintf(DEBUG_CLIENT, "Can't shoot, actor dead or stunned.\n"); return; } /* Animate - we have to check if it is right or left weapon usage. */ if (IS_SHOT_RIGHT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->right, leShooter->left, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->right, leShooter->left, leShooter->state)); } else if (IS_SHOT_LEFT(shootType)) { R_AnimChange(&leShooter->as, leShooter->model1, LE_GetAnim("shoot", leShooter->left, leShooter->right, leShooter->state)); R_AnimAppend(&leShooter->as, leShooter->model1, LE_GetAnim("stand", leShooter->left, leShooter->right, leShooter->state)); } else if (IS_SHOT_HEADGEAR(shootType)) { if (fd->irgoggles) { leShooter->state |= RF_IRGOGGLESSHOT; if (LE_IsSelected(leShooter)) refdef.rendererFlags |= RDF_IRGOGGLES; } } else { /* no animation for headgear (yet) */ Com_Error(ERR_DROP, "CL_ActorDoShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, shooterEntnum); } }
/** * @brief Adds an UGV to the render entities. * @param[in] le The local entity the UGV should be created from * @param[out] ent * @sa CL_AddActor */ bool CL_AddUGV (le_t * le, entity_t * ent) { entity_t add; if (!LE_IsDead(le)) { /* add weapon */ if (le->left != NONE) { OBJZERO(add); add.model = cls.modelPool[le->left]; add.tagent = R_GetFreeEntity() + 2 + (le->right != NONE); add.tagname = "tag_lweapon"; R_AddEntity(&add); } /* add weapon */ if (le->right != NONE) { OBJZERO(add); add.alpha = le->alpha; add.model = cls.modelPool[le->right]; add.tagent = R_GetFreeEntity() + 2; add.tagname = "tag_rweapon"; R_AddEntity(&add); } } /* add head */ OBJZERO(add); add.alpha = le->alpha; add.model = le->model2; add.skinnum = le->bodySkin; /** @todo */ add.tagent = R_GetFreeEntity() + 1; add.tagname = "tag_head"; R_AddEntity(&add); /* add actor special effects */ ent->flags |= RF_SHADOW; ent->flags |= RF_ACTOR; if (!LE_IsDead(le)) { if (LE_IsSelected(le)) ent->flags |= RF_SELECTED; if (le->team == cls.team) { if (le->pnum == cl.pnum) ent->flags |= RF_MEMBER; if (le->pnum != cl.pnum) ent->flags |= RF_ALLIED; } if (le->team == TEAM_CIVILIAN) ent->flags |= RF_NEUTRAL; } return true; }
/** * @brief Kills an actor (all that is needed is the local entity state set to STATE_DEAD). * @note Also changes the animation to a random death sequence and appends the dead animation * @param[in] msg The netchannel message * @param[in] self Pointer to the event structure that is currently executed */ void CL_ActorDie (const eventRegister_t *self, dbuffer *msg) { le_t *le; int entnum, state, playerNum; NET_ReadFormat(msg, self->formatString, &entnum, &state, &playerNum); /* get les */ le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsLivingActor(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, LE is not an actor (type: %i)", le->type); if (!LE_IsStunned(le) && LE_IsDead(le)) Com_Error(ERR_DROP, "CL_ActorDie: Can't kill, actor already dead"); LE_Lock(le); /* set relevant vars */ FLOOR(le) = NULL; le->state = state; /* count spotted aliens */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); /* play animation */ LE_SetThink(le, NULL); R_AnimChange(&le->as, le->model1, va("death%i", LE_GetAnimationIndexForDeath(le))); R_AnimAppend(&le->as, le->model1, va("dead%i", LE_GetAnimationIndexForDeath(le))); /* Print some info about the death or stun. */ if (le->team == cls.team) { if (playerNum != le->pnum) { const char *playerName = CL_PlayerGetName(playerNum); char tmpbuf[128]; Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s lost a soldier\n"), playerName); HUD_DisplayMessage(tmpbuf); } else { const character_t *chr = CL_ActorGetChr(le); if (chr) { char tmpbuf[128]; if (LE_IsStunned(le)) { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was stunned\n"), chr->name); } else { Com_sprintf(tmpbuf, lengthof(tmpbuf), _("%s was killed\n"), chr->name); } HUD_DisplayMessage(tmpbuf); } } } else { switch (le->team) { case (TEAM_CIVILIAN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A civilian was stunned.")); else HUD_DisplayMessage(_("A civilian was killed.")); break; case (TEAM_ALIEN): if (LE_IsStunned(le)) HUD_DisplayMessage(_("An alien was stunned.")); else HUD_DisplayMessage(_("An alien was killed.")); break; case (TEAM_PHALANX): if (LE_IsStunned(le)) HUD_DisplayMessage(_("A soldier was stunned.")); else HUD_DisplayMessage(_("A soldier was killed.")); break; default: if (LE_IsStunned(le)) HUD_DisplayMessage(va(_("A member of team %i was stunned."), le->team)); else HUD_DisplayMessage(va(_("A member of team %i was killed."), le->team)); break; } } /** * @todo CHRSH_IsTeamDefRobot: spawn smoke particles for robots */ CL_ActorPlaySound(le, SND_DEATH); VectorCopy(player_dead_maxs, le->maxs); if (!LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; CL_ActorRemoveFromTeamList(le); /* update pathing as we maybe can walk onto the dead actor now */ CL_ActorConditionalMoveCalc(selActor); LE_Unlock(le); }
/** * @sa CL_ActorAddToTeamList * @sa G_AppearPerishEvent * @sa CL_ActorAdd * @note EV_ACTOR_APPEAR */ void CL_ActorAppear (const eventRegister_t *self, struct dbuffer *msg) { le_t *le, *leResponsible; int entnum, entnumResponsible, modelnum1, modelnum2; int teamDefID = -1; /* check if the actor is already visible */ entnum = NET_ReadShort(msg); entnumResponsible = NET_ReadShort(msg); le = LE_Get(entnum); leResponsible = LE_Get(entnumResponsible); if (entnumResponsible != SKIP_LOCAL_ENTITY && !leResponsible) LE_NotFoundError(entnumResponsible); /* mission start - no actor is spawned yet - so create it */ if (!le) le = LE_Add(entnum); /* Locking should be unnecessary if CL_CheckDefault filters this call, since this event starts and * ends in this function only. Adding lock/unlock just to be sure. */ LE_Lock(le); /* maybe added via CL_ActorAdd before */ le->invis = false; /* get the info */ NET_ReadFormat(msg, self->formatString, &le->team, &teamDefID, &le->gender, &le->ucn, &le->pnum, &le->pos, &le->angle, &le->right, &le->left, &modelnum1, &modelnum2, &le->bodySkin, &le->headSkin, &le->state, &le->fieldSize, &le->maxTU, &le->maxMorale, &le->maxHP); if (teamDefID < 0 || teamDefID > csi.numTeamDefs) Com_Printf("CL_ActorAppear: Invalid teamDef index\n"); else le->teamDef = &csi.teamDef[teamDefID]; switch (le->fieldSize) { case ACTOR_SIZE_NORMAL: le->addFunc = CL_AddActor; le->type = ET_ACTOR; break; case ACTOR_SIZE_2x2: le->addFunc = CL_AddUGV; le->type = ET_ACTOR2x2; break; default: Com_Error(ERR_DROP, "Unknown fieldSize for le in CL_ActorAppear (EV_ACTOR_APPEAR)"); } le->modelnum1 = modelnum1; le->modelnum2 = modelnum2; le->model1 = LE_GetDrawModel(modelnum1); le->model2 = LE_GetDrawModel(modelnum2); Grid_PosToVec(cl.mapData->map, le->fieldSize, le->pos, le->origin); le->angles[YAW] = directionAngles[le->angle]; if (LE_IsDead(le) && !LE_IsStunned(le)) le->contents = CONTENTS_DEADACTOR; else le->contents = CONTENTS_ACTOR; VectorCopy(player_mins, le->mins); if (LE_IsDead(le)) VectorCopy(player_dead_maxs, le->maxs); else VectorCopy(player_maxs, le->maxs); LE_SetThink(le, LET_StartIdle); /* count spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); if (LE_IsLivingActor(le)) { if (cl.actTeam != cls.team) { /* center view (if wanted) */ LE_CenterView(le); } /* draw line of sight */ if (le->team != cls.team) { if (leResponsible) CL_DrawLineOfSight(leResponsible, le); /* message */ if (le->team != TEAM_CIVILIAN) { if (GAME_TeamIsKnown(le->teamDef)) { char tmpbuf[128]; Com_sprintf(tmpbuf, sizeof(tmpbuf), _("Enemy spotted: %s!"), _(le->teamDef->name)); HUD_DisplayMessage(tmpbuf); } else HUD_DisplayMessage(_("Unknown enemy spotted!")); } else HUD_DisplayMessage(_("Civilian spotted.")); /* update pathing as new actor could block path */ CL_ActorConditionalMoveCalc(leResponsible ? leResponsible : selActor); } } /* add team members to the actor list */ CL_ActorAddToTeamList(le); LE_Unlock(le); }
/** * @brief Checks whether the given le is a living actor (but might be hidden) * @param[in] le The local entity to perform the check for * @sa G_IsLivingActor * @sa LE_IsActor */ bool LE_IsLivingActor (const le_t* le) { assert(le); return LE_IsActor(le) && (LE_IsStunned(le) || !LE_IsDead(le)); }