/** * @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; }
void CL_InvAmmo (const eventRegister_t *self, dbuffer *msg) { invList_t *ic; le_t *le; int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le = LE_Get(number); if (!le) { Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n"); return; } if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y); if (!ic) return; /* set new ammo */ ic->item.ammoLeft = ammo; ic->item.ammo = INVSH_GetItemByIDX(type); }
/** * @brief Adds server side edicts to the client for displaying them * @sa EV_ADD_EDICT * @sa CL_EntAppear */ void CL_AddEdict (const eventRegister_t *self, struct dbuffer * msg) { le_t *le; int entnum; entity_type_t type; vec3_t mins, maxs; NET_ReadFormat(msg, self->formatString, &entnum, &type, &mins, &maxs); /* use an offset to ensure that we don't conflict with any other solid edict that is already spawned */ le = LE_Get(entnum + MAX_EDICTS); if (!le) { le = LE_Add(entnum + MAX_EDICTS); } else { Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: Entity appearing already visible... overwriting the old one\n"); le->inuse = qtrue; } VectorCopy(mins, le->mins); VectorCopy(maxs, le->maxs); le->addFunc = CL_AddEdictFunc; le->type = type; Com_DPrintf(DEBUG_CLIENT, "CL_AddEdict: entnum: %i - type: %i\n", entnum, type); }
/** * @brief Adds an hidden actor to the list of le's * @sa CL_ActorAppear * @note Actor is invisible until CL_ActorAppear (EV_ACTOR_APPEAR) was triggered * @note EV_ACTOR_ADD * @sa G_SendInvisible */ void CL_ActorAdd (const eventRegister_t* self, dbuffer* msg) { /* check if the actor is already visible */ const int entnum = NET_ReadShort(msg); le_t* le = LE_Get(entnum); if (le) { Com_Printf("CL_ActorAdd: Actor with number %i already exists\n", entnum); NET_SkipFormat(msg, self->formatString); return; } le = LE_Add(entnum); /* get the info */ int teamDefID; NET_ReadFormat(msg, self->formatString, &le->team, &teamDefID, &le->gender, &le->pnum, &le->pos, &le->state, &le->fieldSize); if (teamDefID < 0 || teamDefID > csi.numTeamDefs) Com_Printf("CL_ActorAdd: Invalid teamDef index\n"); else le->teamDef = &csi.teamDef[teamDefID]; /*Com_Printf("CL_ActorAdd: Add number: %i\n", entnum);*/ le->type = ET_ACTORHIDDEN; Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin); le->flags = LE_INVISIBLE; }
void CL_InvAmmo (const eventRegister_t* self, dbuffer* msg) { int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le_t* le = LE_Get(number); if (!le) { Com_DPrintf(DEBUG_CLIENT, "InvAmmo message ignored... LE not found\n"); return; } if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); Item* item = le->inv.getItemAtPos(INVDEF(container), x, y); if (!item) return; /* set new ammo */ item->setAmmoLeft(ammo); item->setAmmoDef(INVSH_GetItemByIDX(type)); }
/** * @brief Adds a camera edicts to the client for displaying them * @sa EV_CAMERA_APPEAR */ void CL_CameraAppear (const eventRegister_t *self, dbuffer *msg) { int entnum; int team; int levelflags; int dir; int rotate; vec3_t origin; camera_type_t cameraType; NET_ReadFormat(msg, self->formatString, &entnum, &origin, &team, &dir, &cameraType, &levelflags, &rotate); le_t *le = LE_Get(entnum); if (!le) { le = LE_Add(entnum); } else { le->inuse = true; } VectorCopy(origin, le->origin); le->type = ET_CAMERA; le->team = team; le->angles[YAW] = directionAngles[le->angle]; le->flags |= LE_CHECK_LEVELFLAGS; le->levelflags = levelflags; le->model1 = R_FindModel(va("objects/cameras/camera%i", cameraType)); if (rotate) { const char *rotateAnim = "rotate"; R_AnimChange(&le->as, le->model1, rotateAnim); } Com_DPrintf(DEBUG_CLIENT, "CL_CameraAppear: entnum: %i\n", entnum); }
/** * @brief Play a sound on the client side * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa EV_SOUND * @note if the last character of the sound file string that was sent by the * server is a '+', we will select a random sound file by replacing the '+' * character with a number between 01..99 */ void CL_SoundEvent (const eventRegister_t* self, dbuffer* msg) { char sound[MAX_QPATH]; vec3_t origin; int number; int step; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &step, &sound, sizeof(sound)); le_t* le = LE_Get(number); if (le) { if (LE_IsLivingActor(le) && le->team != cls.team) { /** @todo render */ } else if (LE_IsDoor(le) || LE_IsBreakable(le)) { /** @todo render */ } } const char* file = CL_ConvertSoundFromEvent(sound, sizeof(sound)); Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", file, origin[0], origin[1], origin[2]); if (step >= 0 && step < MAX_ROUTE) { le_t* closest = CL_ActorGetClosest(origin, cls.team); if (closest != nullptr) { vec3_t tmp; VectorCopy(cl.cam.camorg, tmp); VectorCopy(closest->origin, cl.cam.camorg); S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); VectorCopy(tmp, cl.cam.camorg); } return; } S_LoadAndPlaySample(file, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); }
/** * @brief Callback for EV_DOOR_CLOSE event - rotates the inline model and recalc routing * @sa EV_DOOR_CLOSE * @sa G_ClientUseEdict * @sa Touch_DoorTrigger */ void CL_DoorClose (const eventRegister_t *self, struct dbuffer *msg) { /* get local entity */ int number; le_t *le; NET_ReadFormat(msg, self->formatString, &number); le = LE_Get(number); if (!le) LE_NotFoundError(number); if (le->type == ET_DOOR) { if (le->dir & DOOR_OPEN_REVERSE) le->angles[le->dir & 3] += DOOR_ROTATION_ANGLE; else le->angles[le->dir & 3] -= DOOR_ROTATION_ANGLE; CM_SetInlineModelOrientation(cl.mapTiles, le->inlineModelName, le->origin, le->angles); CL_RecalcRouting(le); } else if (le->type == ET_DOOR_SLIDING) { LE_SetThink(le, LET_DoorSlidingClose); le->think(le); } else { Com_Error(ERR_DROP, "Invalid door entity found of type: %i", le->type); } }
/** * @brief Let an entity appear - like an item on the ground that just got visible * @sa EV_ENT_APPEAR * @sa CL_EntPerish * @sa CL_AddEdict */ void CL_EntAppear (const eventRegister_t* self, dbuffer* msg) { int entnum; entity_type_t type; pos3_t pos; NET_ReadFormat(msg, self->formatString, &entnum, &type, &pos); /* check if the ent is already visible */ le_t* le = LE_Get(entnum); if (!le) { le = LE_Add(entnum); } else { Com_DPrintf(DEBUG_CLIENT, "CL_EntAppear: Entity appearing already visible... overwriting the old one\n"); le->inuse = true; } le->type = type; /* the default is invisible - another event will follow which spawns not * only the le, but also the particle. The visibility is set there, too */ if (le->type == ET_PARTICLE) LE_SetInvisible(le); VectorCopy(pos, le->pos); Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin); }
void CL_ActorStateChange (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, state; character_t *chr; NET_ReadFormat(msg, self->formatString, &entnum, &state); le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); if (!LE_IsActor(le)) { Com_Printf("StateChange message ignored... LE is no actor (number: %i, state: %i, type: %i)\n", entnum, state, le->type); return; } /* If standing up or crouching down remove the reserved-state for crouching. */ if (((state & STATE_CROUCHED) && !LE_IsCrouched(le)) || (!(state & STATE_CROUCHED) && LE_IsCrouched(le))) { if (CL_ActorUsableTUs(le) < TU_CROUCH && CL_ActorReservedTUs(le, RES_CROUCH) >= TU_CROUCH) { /* We have not enough non-reserved TUs, * but some reserved for crouching/standing up. * i.e. we only reset the reservation for crouching if it's the very last attempt. */ CL_ActorReserveTUs(le, RES_CROUCH, 0); /* Reset reserved TUs (0 TUs) */ } } /* killed by the server: no animation is played, etc. */ if ((state & STATE_DEAD) && LE_IsLivingActor(le)) { le->state = state; FLOOR(le) = NULL; LE_SetThink(le, NULL); VectorCopy(player_dead_maxs, le->maxs); CL_ActorRemoveFromTeamList(le); return; } else { le->state = state; LE_SetThink(le, LET_StartIdle); } /* save those states that the actor should also carry over to other missions */ chr = CL_ActorGetChr(le); if (!chr) return; chr->state = (le->state & STATE_REACTION); /* change reaction button state */ if (!(le->state & STATE_REACTION)) { UI_ExecuteConfunc("disable_reaction"); } else { UI_ExecuteConfunc("startreaction"); } /* state change may have affected move length */ CL_ActorConditionalMoveCalc(le); }
/** * @brief Network event function for reaction fire target handling. Responsible for updating * the HUD with the information that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from */ void CL_ActorReactionFireTargetUpdate (const eventRegister_t* self, dbuffer* msg) { int shooterEntNum; int targetEntNum; // if these TUs have arrived at 0, the reaction fire can be triggered int tusUntilTriggered; int unused; NET_ReadFormat(msg, self->formatString, &shooterEntNum, &targetEntNum, &tusUntilTriggered, &unused); const le_t* shooter = LE_Get(shooterEntNum); if (!shooter) LE_NotFoundError(shooterEntNum); const le_t* target = LE_Get(targetEntNum); if (!target) LE_NotFoundError(targetEntNum); const bool outOfRange = CL_ActorIsReactionFireOutOfRange(shooter, target); UI_ExecuteConfunc("reactionfire_updatetarget %i %i %i %i", shooterEntNum, target->entnum, tusUntilTriggered, outOfRange); }
/** * @brief Called whenever an entity disappears from view * @sa CL_EntAppear */ void CL_EntPerish (const eventRegister_t *self, struct dbuffer *msg) { int entnum; int type; le_t *le, *actor; NET_ReadFormat(msg, self->formatString, &entnum, &type); le = LE_Get(entnum); if (!le) LE_NotFoundWithTypeError(entnum, type); switch (le->type) { case ET_ITEM: cls.i.EmptyContainer(&cls.i, &le->i, INVDEF(csi.idFloor)); /* search owners (there can be many, some of them dead) */ actor = NULL; while ((actor = LE_GetNextInUse(actor))) { if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2) && VectorCompare(actor->pos, le->pos)) { Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: le of type ET_ITEM hidden\n"); FLOOR(actor) = NULL; } } break; case ET_ACTOR: case ET_ACTOR2x2: cls.i.DestroyInventory(&cls.i, &le->i); break; #ifdef DEBUG case ET_ACTORHIDDEN: Com_DPrintf(DEBUG_CLIENT, "CL_EntPerish: It should not happen that we perish a hidden actor\n"); return; #endif case ET_PARTICLE: CL_ParticleFree(le->ptl); le->ptl = NULL; break; case ET_BREAKABLE: case ET_DOOR: case ET_DOOR_SLIDING: break; default: break; } le->invis = qtrue; /* decrease the count of spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); }
/** * @sa CL_InvDel * @sa G_SendInventory * @sa EV_INV_ADD */ void CL_InvAdd (const eventRegister_t *self, dbuffer *msg) { const int number = NET_ReadShort(msg); le_t *le = LE_Get(number); int nr = NET_ReadShort(msg); if (!le) LE_NotFoundError(number); le->removeNextFrame = false; for (; nr-- > 0;) { item_t item; containerIndex_t container; int x, y; CL_NetReceiveItem(msg, &item, &container, &x, &y); if (LE_IsItem(le)) { if (container != csi.idFloor) Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container); } else if (INVDEF(container)->temp) { Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container); } if (cls.i.AddToInventory(&cls.i, &le->i, &item, INVDEF(container), x, y, item.amount) == NULL) Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i", item.amount, item.item->id, container); if (container == csi.idRight) le->right = item.item->idx; else if (container == csi.idLeft) le->left = item.item->idx; else if (container == csi.idExtension) le->extension = item.item->idx; else if (container == csi.idHeadgear) le->headgear = item.item->idx; } switch (le->type) { case ET_ACTOR: case ET_ACTOR2x2: LE_SetThink(le, LET_StartIdle); break; case ET_ITEM: LE_PlaceItem(le); break; default: break; } }
/** * @brief Reads the entity number for client interaction * @sa EV_CLIENT_ACTION * @sa Touch_DoorTrigger * @sa CL_ActorUse * @todo Hud should have a button that should be activated now */ void CL_ActorClientAction (const eventRegister_t *self, dbuffer *msg) { le_t* le; int number, actionEntityNumber; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &actionEntityNumber); /* get actor le */ le = LE_Get(number); if (!le) LE_NotFoundError(number); /* set client action entity */ le->clientAction = LE_Get(actionEntityNumber); if (!le->clientAction) LE_NotFoundError(actionEntityNumber); UI_ExecuteConfunc("enable_clientaction"); Com_DPrintf(DEBUG_CLIENT, "CL_ActorClientAction: Set entity number: %i (for actor with entnum %i)\n", actionEntityNumber, number); }
/** * @note e.g. func_breakable or func_door with health * @sa EV_MODEL_EXPLODE */ void CL_Explode (const eventRegister_t *self, struct dbuffer *msg) { const int entnum = NET_ReadShort(msg); le_t *le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); le->inuse = false; if (le->modelnum1 > 0) cl.model_clip[le->modelnum1] = NULL; /* Recalc the client routing table because this le (and the inline model) is now gone */ CL_RecalcRouting(le); }
/** * @brief Network event function for reaction fire target handling. Responsible for updating * the HUD with the information that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from */ void CL_ActorReactionFireRemoveTarget (const eventRegister_t* self, dbuffer* msg) { int shooterEntNum; int targetEntNum; int unused; NET_ReadFormat(msg, self->formatString, &shooterEntNum, &targetEntNum, &unused); const le_t* target = LE_Get(targetEntNum); if (!target) LE_NotFoundError(targetEntNum); UI_ExecuteConfunc("reactionfire_removetarget %i %i", shooterEntNum, target->entnum); }
/** * @sa CL_InvDel * @sa G_SendInventory * @sa EV_INV_ADD */ void CL_InvAdd (const eventRegister_t* self, dbuffer* msg) { const int number = NET_ReadShort(msg); le_t* le = LE_Get(number); int nr = NET_ReadShort(msg); if (!le) LE_NotFoundError(number); le->flags &= ~LE_REMOVE_NEXT_FRAME; for (; nr-- > 0;) { Item item; containerIndex_t container; int x, y; CL_NetReceiveItem(msg, &item, &container, &x, &y); if (LE_IsItem(le)) { if (container != CID_FLOOR) Com_Error(ERR_DROP, "InvAdd for ET_ITEM but target container is not the floor but %i", container); } else if (INVDEF(container)->temp) { Com_Error(ERR_DROP, "InvAdd for %i to temp container %i", le->type, container); } if (cls.i.addToInventory(&le->inv, &item, INVDEF(container), x, y, item.getAmount()) == nullptr) Com_Error(ERR_DROP, "InvAdd failed - could not add %i item(s) of %s to container %i", item.getAmount(), item.def()->id, container); if (container == CID_RIGHT) le->right = item.def()->idx; else if (container == CID_LEFT) le->left = item.def()->idx; else if (container == CID_HEADGEAR) le->headgear = item.def()->idx; } switch (le->type) { case ET_ACTOR: case ET_ACTOR2x2: if (LE_IsSelected(le)) Cmd_ExecuteString("hud_updateactorload"); LE_SetThink(le, LET_StartIdle); break; case ET_ITEM: LE_PlaceItem(le); break; default: break; } }
/** * @brief Network event function for TU reservation. Responsible for updating the HUD with the information * that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from */ void CL_ActorReservationChange (const eventRegister_t* self, dbuffer* msg) { int entnum, reaction, shot, crouch; NET_ReadFormat(msg, self->formatString, &entnum, &reaction, &shot, &crouch); const le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); character_t* chr = CL_ActorGetChr(le); if (!chr) return; chr->reservedTus.reaction = reaction; chr->reservedTus.shot = shot; chr->reservedTus.crouch = crouch; }
int CL_ActorReactionFireRemoveTargetTime (const eventRegister_t* self, dbuffer* msg, eventTiming_t* eventTiming) { int targetEntNum; int unused; int step; NET_ReadFormat(msg, self->formatString, &unused, &targetEntNum, &step); const le_t* target = LE_Get(targetEntNum); if (!target) LE_NotFoundError(targetEntNum); if (step >= MAX_ROUTE) return eventTiming->nextTime; const int stepTime = CL_GetStepTime(eventTiming, target, step); if (eventTiming->shootTime > stepTime) return eventTiming->impactTime; return stepTime; }
/** * @sa CL_InvAdd */ void CL_InvDel (const eventRegister_t *self, dbuffer *msg) { le_t *le; int number; int x, y; containerIndex_t container; invList_t *ic; NET_ReadFormat(msg, self->formatString, &number, &container, &x, &y); le = LE_Get(number); if (!le) Com_Error(ERR_DROP, "InvDel message ignored... LE not found\n"); /* update the local entity to ensure that the correct weapon/item is rendered in the battlescape */ if (container == csi.idRight) le->right = NONE; else if (container == csi.idLeft) le->left = NONE; else if (container == csi.idExtension) le->extension = NONE; else if (container == csi.idHeadgear) le->headgear = NONE; if (le->type == ET_ACTOR || le->type == ET_ACTOR2x2) LE_SetThink(le, LET_StartIdle); ic = INVSH_SearchInInventory(&le->i, INVDEF(container), x, y); /* ic can be null for other team actors - we don't the full inventory of them, only * the object index */ if (!ic) return; if (!cls.i.RemoveFromInventory(&cls.i, &le->i, INVDEF(container), ic)) Com_Error(ERR_DROP, "CL_InvDel: No item was removed from container %i", container); if (le == selActor) Cmd_ExecuteString("hud_updateactorload"); /* update the rendered item after it was removed from the floor container */ if (LE_IsItem(le)) LE_PlaceItem(le); }
int CL_SoundEventTime (const struct eventRegister_s* self, dbuffer* msg, eventTiming_t* eventTiming) { char sound[MAX_QPATH]; vec3_t origin; int number; int step; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &step, &sound, sizeof(sound)); const le_t* le = LE_Get(number); if (!le) LE_NotFoundError(number); if (step >= MAX_ROUTE) return eventTiming->nextTime; const int stepTime = CL_GetStepTime(eventTiming, le, step); if (eventTiming->shootTime > stepTime) return eventTiming->impactTime; return stepTime; }
/** * @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]; }
/** * @brief Network event function for reaction fire mode changes. Responsible for updating * the HUD with the information that were received from the server * @param self The event pointer * @param msg The network message to parse the event data from * @sa HUD_UpdateReactionFiremodes */ void CL_ActorReactionFireChange (const eventRegister_t* self, dbuffer* msg) { actorHands_t hand; int entnum, fmIdx, odIdx; NET_ReadFormat(msg, self->formatString, &entnum, &fmIdx, &hand, &odIdx); const le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); character_t* chr = CL_ActorGetChr(le); if (!chr) return; const objDef_t* od = INVSH_GetItemByIDX(odIdx); chr->RFmode.set(hand, fmIdx, od); UI_ExecuteConfunc("reactionfire_updated"); }
/** * @brief Decides if following events should be delayed. The delay is the amount of time the actor needs to walk * from the start to the end pos. */ int CL_ActorDoMoveTime (const eventRegister_t *self, dbuffer *msg, eventTiming_t *eventTiming) { int time = 0; const int eventTime = eventTiming->nextTime; const int number = NET_ReadShort(msg); /* get le */ const le_t *le = LE_Get(number); if (!le) LE_NotFoundError(number); pos3_t pos; VectorCopy(le->pos, pos); byte crouchingState = LE_IsCrouched(le) ? 1 : 0; /* the end of this event is marked with a 0 */ while (NET_PeekLong(msg) != 0) { const dvec_t dvec = NET_ReadShort(msg); const byte dir = getDVdir(dvec); pos3_t oldPos; VectorCopy(pos, oldPos); PosAddDV(pos, crouchingState, dvec); time += LE_ActorGetStepTime(le, pos, oldPos, dir, NET_ReadShort(msg)); NET_ReadShort(msg); } /* skip the end of move marker */ NET_ReadLong(msg); /* Also skip the final position */ NET_ReadByte(msg); NET_ReadByte(msg); NET_ReadByte(msg); assert(NET_PeekByte(msg) == EV_NULL); eventTiming->nextTime += time + 400; return eventTime; }
/** * @brief Callback for EV_DOOR_CLOSE event - rotates the inline model and recalc routing * @sa EV_DOOR_CLOSE * @sa G_ClientUseEdict * @sa Touch_DoorTrigger */ void CL_DoorClose (const eventRegister_t* self, dbuffer* msg) { /* get local entity */ int number; NET_ReadFormat(msg, self->formatString, &number); le_t* le = LE_Get(number); if (!le) LE_NotFoundError(number); if (le->type == ET_DOOR) { LE_SetThink(le, LET_DoorRotatingClose); le->think(le); } else if (le->type == ET_DOOR_SLIDING) { LE_SetThink(le, LET_DoorSlidingClose); le->think(le); } else { Com_Error(ERR_DROP, "Invalid door entity found of type: %i", le->type); } }
/** * @brief Starts shooting with actor. * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg The netchannel message * @sa CL_ActorShootHidden * @sa CL_ActorShoot * @sa CL_ActorDoShoot * @todo Improve detection of left- or right animation. * @sa EV_ACTOR_START_SHOOT */ void CL_ActorStartShoot (const eventRegister_t *self, dbuffer *msg) { le_t *le; pos3_t from, target; int entnum; shoot_types_t shootType; NET_ReadFormat(msg, self->formatString, &entnum, &shootType, &from, &target); /* shooting actor */ le = LE_Get(entnum); /* center view (if wanted) */ if (cl.actTeam != cls.team) CL_CameraRoute(from, target); /* actor dependent stuff following */ if (!le) /* it's OK, the actor is not visible */ return; if (!LE_IsLivingActor(le) || LE_IsStunned(le)) { Com_Printf("CL_ActorStartShoot: LE (%i) not a living actor (type: %i)\n", entnum, le->type); return; } /* ET_ACTORHIDDEN actors don't have a model yet */ if (le->type == ET_ACTORHIDDEN) return; /* Animate - we have to check if it is right or left weapon usage. */ if (IS_SHOT_RIGHT(shootType)) { R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->right, le->left, le->state)); } else if (IS_SHOT_LEFT(shootType)) { R_AnimChange(&le->as, le->model1, LE_GetAnim("move", le->left, le->right, le->state)); } else if (!IS_SHOT_HEADGEAR(shootType)) { /* no animation for headgear (yet) */ Com_Error(ERR_DROP, "CL_ActorStartShoot: Invalid shootType given (entnum: %i, shootType: %i).\n", shootType, entnum); } }
/** * @brief Let a particle appear for the client * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa CL_ParticleSpawn * @sa EV_PARTICLE_APPEAR */ void CL_ParticleAppear (const eventRegister_t* self, dbuffer* msg) { char particle[MAX_VAR]; int entnum, levelflags; vec3_t origin; /* read data */ NET_ReadFormat(msg, self->formatString, &entnum, &levelflags, origin, particle, sizeof(particle)); le_t* le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); /* particles don't have a model to add to the scene - we mark them as invisible and * only render the particle */ LE_SetInvisible(le); le->levelflags = levelflags; le->particleID = Mem_PoolStrDup(particle, cl_genericPool, 0); le->ptl = CL_ParticleSpawn(le->particleID, le->levelflags, origin); if (!le->ptl) Com_Printf("Could not spawn particle: '%s'\n", le->particleID); }
void CL_InvReload (const eventRegister_t* self, dbuffer* msg) { int number; int ammo, type, x, y; containerIndex_t container; NET_ReadFormat(msg, self->formatString, &number, &ammo, &type, &container, &x, &y); le_t* le = LE_Get(number); if (!le) return; if (le->team != cls.team) return; assert(container >= 0); assert(container < MAX_INVDEFS); Item* ic = le->inv.getItemAtPos(INVDEF(container), x, y); if (!ic) return; S_LoadAndPlaySample(ic->def()->reloadSound, le->origin, ic->def()->reloadAttenuation, SND_VOLUME_WEAPONS); /* if the displaced clip had any remaining bullets * store them as loose, unless the removed clip was full */ equipDef_t* ed = GAME_GetEquipmentDefinition(); if (ed && ic->getAmmoLeft() > 0 && ic->getAmmoLeft() != ic->def()->ammo) { assert(ammo == ic->def()->ammo); /* Accumulate loose ammo into clips (only accessible post-mission) */ ed->addClip(ic); } /* set new ammo */ ic->setAmmoLeft(ammo); ic->setAmmoDef(INVSH_GetItemByIDX(type)); if (le == selActor) Cmd_ExecuteString("hud_updateactorload"); }
/** * @brief Play a sound on the client side * @param[in] self Pointer to the event structure that is currently executed * @param[in] msg holds the network data * @sa EV_SOUND * @note if the last character of the sound file string that was sent by the * server is a '+', we will select a random sound file by replacing the '+' * character with a number between 01..99 */ void CL_SoundEvent (const eventRegister_t *self, struct dbuffer *msg) { char sound[MAX_QPATH]; vec3_t origin; int number; le_t *le; size_t length; /* read data */ NET_ReadFormat(msg, self->formatString, &number, &origin, &sound, sizeof(sound)); le = LE_Get(number); if (le) { if (LE_IsLivingActor(le) && le->team != cls.team) { /** @todo render */ } else if (LE_IsDoor(le) || LE_IsBreakable(le)) { /** @todo render */ } } length = strlen(sound) - 1; if (sound[length] == '+') { int i; sound[length] = '\0'; for (i = 1; i <= 99; i++) { if (FS_CheckFile("sounds/%s%02i", sound, i) == -1) break; } Com_sprintf(sound + length, sizeof(sound) - length, "%02i", rand() % i + 1); } Com_DPrintf(DEBUG_SOUND, "Play network sample %s at (%f:%f:%f)\n", sound, origin[0], origin[1], origin[2]); S_LoadAndPlaySample(sound, origin, SOUND_ATTN_NORM, SND_VOLUME_DEFAULT); }
/** * @brief Parses the actor wound stats that come from the netchannel * @sa CL_ParseEvent * @sa G_SendStats */ void CL_ActorWound (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, bodyPart, wounds, treatment; NET_ReadFormat(msg, self->formatString, &entnum, &bodyPart, &wounds, &treatment); le = LE_Get(entnum); if (!le) LE_NotFoundError(entnum); switch (le->type) { case ET_ACTORHIDDEN: case ET_ACTOR: case ET_ACTOR2x2: break; default: Com_Printf("CL_ActorWound: LE (%i) not an actor (type: %i)\n", entnum, le->type); return; } le->wounds.woundLevel[bodyPart] = wounds; le->wounds.treatmentLevel[bodyPart] = treatment; }