/** * @sa CL_BiggestItem * @param[in] le The local entity (ET_ITEM) with the floor container */ void LE_PlaceItem (le_t* le) { assert(LE_IsItem(le)); /* search owners (there can be many, some of them dead) */ le_t* actor = nullptr; while ((actor = LE_GetNextInUse(actor))) { if ((actor->type == ET_ACTOR || actor->type == ET_ACTOR2x2) && VectorCompare(actor->pos, le->pos)) { if (le->getFloorContainer()) actor->setFloor(le); } } /* the le is an ET_ITEM entity, this entity is there to render dropped items * if there are no items in the floor container, this entity can be * deactivated */ Item* floorCont = le->getFloorContainer(); if (floorCont) { const objDef_t* biggest = LE_BiggestItem(floorCont); le->model1 = cls.modelPool[biggest->idx]; if (!le->model1) Com_Error(ERR_DROP, "Model for item %s is not precached in the cls.model_weapons array", biggest->id); Grid_PosToVec(cl.mapData->routing, le->fieldSize, le->pos, le->origin); VectorSubtract(le->origin, biggest->center, le->origin); le->angles[ROLL] = 90; /*le->angles[YAW] = 10*(int)(le->origin[0] + le->origin[1] + le->origin[2]) % 360; */ le->origin[2] -= GROUND_DELTA; } else { /* If no items in floor inventory, don't draw this le - the container is * maybe empty because an actor picked up the last items here */ le->flags |= LE_REMOVE_NEXT_FRAME; } }
static inline void LE_GenerateInlineModelList (void) { le_t* le = nullptr; int i = 0; while ((le = LE_GetNextInUse(le))) { if (le->model1 && le->inlineModelName[0] == '*') cl.leInlineModelList[i++] = le->inlineModelName; } cl.leInlineModelList[i] = nullptr; }
/** * @brief Searches a local entity on a given grid field * @param[in] pos The grid pos to search for an item of the given type */ le_t* LE_GetFromPos (const pos3_t pos) { le_t* le = nullptr; while ((le = LE_GetNextInUse(le))) { if (VectorCompare(le->pos, pos)) return le; } /* didn't find it */ return nullptr; }
/** * @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(); }
/** * @brief Searches a local entity on a given grid field * @param[in] type Entity type * @param[in] pos The grid pos to search for an item of the given type */ le_t* LE_Find (entity_type_t type, const pos3_t pos) { le_t* le = nullptr; while ((le = LE_GetNextInUse(le))) { if (le->type == type && VectorCompare(le->pos, pos)) /* found the LE */ return le; } /* didn't find it */ return nullptr; }
/** * @sa CL_Frame */ void S_Frame (void) { if (snd_init && snd_init->modified) { S_Restart_f(); snd_init->modified = false; } if (!s_env.initialized) return; M_Frame(); if (CL_OnBattlescape()) { int i; s_channel_t *ch; le_t *le; /* update right angle for stereo panning */ VectorCopy(cl.cam.axis[AXIS_RIGHT], s_env.right); S_MumbleUpdate(cl.cam.camorg, cl.cam.axis[AXIS_FORWARD], cl.cam.axis[AXIS_RIGHT], cl.cam.axis[AXIS_UP]); /* update spatialization for current sounds */ ch = s_env.channels; for (i = 0; i < MAX_CHANNELS; i++, ch++) { if (!ch->sample) continue; /* reset channel's count for loop samples */ ch->count = 0; S_SpatializeChannel(ch); } /* ambient sounds */ le = NULL; while ((le = LE_GetNextInUse(le))) { if (le->type == ET_SOUND) { s_sample_t *sample = S_GetSample(le->sampleIdx); int j; for (j = 0; j < MAX_CHANNELS; j++) { if (s_env.channels[j].sample == sample) break; } if (j == MAX_CHANNELS) S_LoopSample(le->origin, sample, le->volume, le->attenuation); } } } }
/** * @brief Counts visible enemies on the battlescape * @return The amount of visible enemies (from all the other teams) */ int CL_CountVisibleEnemies (void) { le_t *le; int count; count = 0; le = NULL; while ((le = LE_GetNextInUse(le))) { if (LE_IsLivingAndVisibleActor(le) && le->team != cls.team && le->team != TEAM_CIVILIAN) count++; } return count; }
/** * @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 Searches all local entities for the one with the searched entnum * @param[in] entnum The entity number (server side) * @sa LE_Add */ le_t* LE_Get (int entnum) { le_t* le = nullptr; if (entnum == SKIP_LOCAL_ENTITY) return nullptr; while ((le = LE_GetNextInUse(le))) { if (le->entnum == entnum) /* found the LE */ return le; } /* didn't find it */ return nullptr; }
/** * @brief Returns entities that have origins within a spherical area. * @param[in] from The entity to start the search from. @c nullptr will start from the beginning. * @param[in] org The origin that is the center of the circle. * @param[in] rad radius to search an edict in. * @param[in] type Type of local entity. @c ET_NULL to ignore the type. */ le_t* LE_FindRadius (le_t* from, const vec3_t org, float rad, entity_type_t type) { le_t* le = from; while ((le = LE_GetNextInUse(le))) { if (type != ET_NULL && le->type != type) continue; vec3_t eorg; vec3_t leCenter; le->aabb.getCenter(leCenter); for (int j = 0; j < 3; j++) eorg[j] = org[j] - (le->origin[j] + leCenter[j]); if (VectorLength(eorg) > rad) continue; return le; } return nullptr; }
/** * @brief Clip against solid entities * @sa CL_Trace * @sa SV_ClipMoveToEntities */ static void CL_ClipMoveToLEs (MoveClipCL* clip) { if (clip->trace.allsolid) return; le_t* le = nullptr; while ((le = LE_GetNextInUse(le))) { int tile = 0; if (!(le->contents & clip->contentmask)) continue; if (le == clip->passle || le == clip->passle2) continue; vec3_t angles, shift; const int32_t headnode = CL_HullForEntity(le, &tile, shift, angles); assert(headnode < MAX_MAP_NODES); vec3_t origin; VectorCopy(le->origin, origin); trace_t trace = CM_HintedTransformedBoxTrace(cl.mapTiles->mapTiles[tile], clip->moveLine, clip->objBox, headnode, clip->contentmask, 0, origin, angles, shift, 1.0); if (trace.fraction < clip->trace.fraction) { /* make sure we keep a startsolid from a previous trace */ const bool oldStart = clip->trace.startsolid; trace.le = le; clip->trace = trace; clip->trace.startsolid |= oldStart; /* if true, plane is not valid */ } else if (trace.allsolid) { trace.le = le; clip->trace = trace; /* if true, the initial point was in a solid area */ } else if (trace.startsolid) { trace.le = le; clip->trace.startsolid = true; } } }
/** * @brief Searches a local entity at the given position. * @param[in] pos The grid position to search a local entity at * @param[in] includingStunned Also search for stunned actors if @c true. * @param[in] actor The current selected actor */ le_t* CL_BattlescapeSearchAtGridPos (const pos3_t pos, bool includingStunned, const le_t *actor) { le_t *le; le_t *nonActor = NULL; /* search for an actor on this field */ le = NULL; while ((le = LE_GetNextInUse(le))) { if (actor != NULL && le == actor->clientAction) { /* if the actor has a client action assigned and we click onto the actor, * we will trigger this client action */ if (VectorCompare(actor->pos, pos)) nonActor = le; } else if (le != actor && LE_IsLivingAndVisibleActor(le) && (includingStunned || !LE_IsStunned(le))) switch (le->fieldSize) { case ACTOR_SIZE_NORMAL: if (VectorCompare(le->pos, pos)) return le; break; case ACTOR_SIZE_2x2: { pos3_t actor2x2[3]; VectorSet(actor2x2[0], le->pos[0] + 1, le->pos[1], le->pos[2]); VectorSet(actor2x2[1], le->pos[0], le->pos[1] + 1, le->pos[2]); VectorSet(actor2x2[2], le->pos[0] + 1, le->pos[1] + 1, le->pos[2]); if (VectorCompare(le->pos, pos) || VectorCompare(actor2x2[0], pos) || VectorCompare(actor2x2[1], pos) || VectorCompare(actor2x2[2], pos)) return le; break; } default: Com_Error(ERR_DROP, "Grid_MoveCalc: unknown actor-size: %i!", le->fieldSize); } } return nonActor; }
/** * @sa CMod_GetMapSize * @note we only need to handle the 2d plane and can ignore the z level * @param[in] node Node description of the radar */ void uiRadarNode::draw (uiNode_t* node) { vec2_t pos; vec2_t screenPos; #ifdef RADARSIZE_DEBUG int textposy = 40; static const vec4_t red = {1, 0, 0, 0.5}; #endif static const vec4_t backgroundColor = {0.0, 0.0, 0.0, 1}; const float mapWidth = cl.mapData->mapBox.getWidthX(); const float mapHeight = cl.mapData->mapBox.getWidthY(); /** @todo use the same coef for x and y */ const float mapCoefX = (float) node->box.size[0] / (float) mapWidth; const float mapCoefY = (float) node->box.size[1] / (float) mapHeight; if (cls.state != ca_active) return; UI_GetNodeAbsPos(node, pos); UI_GetNodeScreenPos(node, screenPos); R_CleanupDepthBuffer(pos[0], pos[1], node->box.size[0], node->box.size[1]); UI_DrawFill(pos[0], pos[1], mapWidth * mapCoefX, mapHeight * mapCoefY, backgroundColor); #ifndef RADARSIZE_DEBUG UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]); #endif /* the cl struct is wiped with every new map */ if (!cl.radarInitialized) { UI_InitRadar(node); cl.radarInitialized = true; } /* update context */ radar.x = pos[0]; radar.y = pos[1]; radar.w = node->box.size[0]; radar.h = node->box.size[1]; if (radar.gridWidth < 6) radar.gridWidth = 6; if (radar.gridHeight < 6) radar.gridHeight = 6; #ifdef RADARSIZE_DEBUG UI_DrawStringInBox("f_small", ALIGN_UL, 50, textposy, 500, 25, va("%fx%f %fx%f map", cl.mapData->mapBox.getMinX(), cl.mapData->mapBox.getMinY(), cl.mapData->getMaxX(), cl.mapData->getMaxY())); textposy += 25; UI_DrawStringInBox("f_small", ALIGN_UL, 50, textposy, 500, 25, va("%fx%f map", mapWidth, mapHeight)); textposy += 25; #endif /* draw background */ for (int i = 0; i < radar.numImages; i++) { vec2_t imagePos; hudRadarImage_t* tile = &radar.images[i]; int maxlevel = cl_worldlevel->integer; /* check the max level value for this map tile */ if (maxlevel >= tile->maxlevel) maxlevel = tile->maxlevel - 1; assert(tile->path[maxlevel]); imagePos[0] = radar.x + mapCoefX * (tile->mapX - cl.mapData->mapBox.getMinX()); imagePos[1] = radar.y + mapCoefY * (tile->mapY - cl.mapData->mapBox.getMinY()); UI_DrawNormImageByName(false, imagePos[0], imagePos[1], mapCoefX * tile->mapWidth, mapCoefY * tile->mapHeight, 0, 0, 0, 0, tile->path[maxlevel]); #ifdef RADARSIZE_DEBUG UI_DrawStringInBox("f_small", ALIGN_UL, 50, textposy, 500, 25, va("%dx%d %dx%d %s", tile->x, tile->y, tile->width, tile->height, tile->path[maxlevel])); textposy += 25; UI_DrawStringInBox("f_small", ALIGN_UL, imagePos[0], imagePos[1], 500, 25, va("%dx%d", tile->gridX, tile->gridY)); #endif } #ifdef RADARSIZE_DEBUG UI_DrawFill(pos[0], pos[1], 100.0f * mapCoefX, 100.0f * mapCoefY, red); UI_DrawFill(pos[0], pos[1], UNIT_SIZE * mapCoefX, UNIT_SIZE * mapCoefY, red); #endif le_t* le = nullptr; while ((le = LE_GetNextInUse(le))) { vec3_t itempos; if (LE_IsInvisible(le)) continue; /* convert to radar area coordinates */ itempos[0] = pos[0] + (le->origin[0] - cl.mapData->mapBox.getMinX()) * mapCoefX; itempos[1] = pos[1] + (mapHeight - (le->origin[1] - cl.mapData->mapBox.getMinY())) * mapCoefY; switch (le->type) { case ET_ACTOR: case ET_ACTOR2x2: UI_RadarNodeDrawActor(le, itempos); break; case ET_ITEM: UI_RadarNodeDrawItem(le, itempos); break; default: break; } #ifdef RADARSIZE_DEBUG UI_DrawStringInBox("f_small", ALIGN_UL, 50, textposy, 500, 25, va("%fx%f %dx%d actor", le->origin[0], le->origin[1], le->pos[0], le->pos[1])); textposy += 25; UI_DrawFill(itempos[0], itempos[1], UNIT_SIZE * mapCoefX, 1, red); UI_DrawFill(itempos[0], itempos[1], 1, UNIT_SIZE * mapCoefY, red); #endif } #ifndef RADARSIZE_DEBUG UI_PopClipRect(); #endif }
/** * @brief Called whenever an entity disappears from view * @sa CL_EntAppear */ void CL_EntPerish (const eventRegister_t* self, dbuffer* msg) { int entnum; int type; NET_ReadFormat(msg, self->formatString, &entnum, &type); le_t* le = LE_Get(entnum); if (!le) LE_NotFoundWithTypeError(entnum, type); le_t* actor = nullptr; switch (le->type) { case ET_ITEM: cls.i.emptyContainer(&le->inv, CID_FLOOR); /* search owners (there can be many, some of them dead) */ 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"); actor->resetFloor(); } } break; case ET_ACTOR: case ET_ACTOR2x2: if (!cls.isOurRound() && le->team != TEAM_CIVILIAN) LE_CenterView(le); cls.i.destroyInventory(&le->inv); if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } /* Clear anim data to prevent actor "jumping" to new animation when it reappears, or worse animation issues. */ OBJZERO(le->as); 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: if (le->ptl) { CL_ParticleFree(le->ptl); le->ptl = nullptr; } else { Com_Printf("CL_EntPerish: Particle is nullptr for entnum %i!\n", entnum); } break; case ET_BREAKABLE: case ET_DOOR: case ET_DOOR_SLIDING: break; default: break; } le->flags |= LE_INVISIBLE; /* decrease the count of spotted aliens (also stunned) */ cl.numEnemiesSpotted = CL_CountVisibleEnemies(); Cvar_SetValue("mn_numaliensspotted", cl.numEnemiesSpotted); }
/** * @brief Call before entering a new level, or after vid_restart */ void CL_ViewLoadMedia (void) { le_t* le; int i, max; float loadingPercent; CL_ViewUpdateRenderData(); if (CL_GetConfigString(CS_TILES)[0] == '\0') return; /* no map loaded */ GAME_InitMissionBriefing(_(CL_GetConfigString(CS_MAPTITLE))); loadingPercent = 0; /* register models, pics, and skins */ SCR_DrawLoading(loadingPercent); R_ModBeginLoading(CL_GetConfigString(CS_TILES), CL_GetConfigStringInteger(CS_LIGHTMAP), CL_GetConfigString(CS_POSITIONS), CL_GetConfigString(CS_NAME), CL_GetConfigString(CS_MAPZONE)); CL_SpawnParseEntitystring(); loadingPercent += 10.0f; SCR_DrawLoading(loadingPercent); LM_Register(); CL_ParticleRegisterArt(); for (i = 1, max = 0; i < MAX_MODELS && CL_GetConfigString(CS_MODELS + i)[0] != '\0'; i++) max++; max += csi.numODs; for (i = 1; i < MAX_MODELS; i++) { const char* name = CL_GetConfigString(CS_MODELS + i); if (name[0] == '\0') break; SCR_DrawLoading(loadingPercent); cl.model_draw[i] = R_FindModel(name); if (!cl.model_draw[i]) { Cmd_ExecuteString("fs_info"); Com_Error(ERR_DROP, "Could not load model '%s'\n", name); } /* initialize clipping for bmodels */ if (name[0] == '*') cl.model_clip[i] = CM_InlineModel(cl.mapTiles, name); else cl.model_clip[i] = nullptr; loadingPercent += 100.0f / (float)max; } /* update le model references */ le = nullptr; while ((le = LE_GetNextInUse(le))) { if (le->modelnum1 > 0) le->model1 = LE_GetDrawModel(le->modelnum1); if (le->modelnum2 > 0) le->model2 = LE_GetDrawModel(le->modelnum2); } refdef.ready = true; /* waiting for EV_START */ SCR_EndLoadingPlaque(); }