/** * @brief Dumps contents of the entire client routing table to CSV file. * @sa CL_InitLocal */ void Grid_DumpClientRoutes_f (void) { ipos3_t wpMins, wpMaxs; VecToPos(cl.mapData->mapBox.mins, wpMins); VecToPos(cl.mapData->mapBox.maxs, wpMaxs); RT_WriteCSVFiles(cl.mapData->routing, "ufoaiclient", GridBox(wpMins, wpMaxs)); }
/** * @note This is only working for one z-level. But our models should be * split for each level anyway. * @param ent The edict to fill the forbidden list for */ static void G_BuildForbiddenListForEntity (edict_t *ent) { pos3_t mins, maxs, origin; vec3_t center, shiftedMins, shiftedMaxs; int xDelta, yDelta, size, i, j; VectorAdd(ent->absmin, ent->origin, shiftedMins); VectorAdd(ent->absmax, ent->origin, shiftedMaxs); VectorCenterFromMinsMaxs(shiftedMins, shiftedMaxs, center); VecToPos(shiftedMins, mins); VecToPos(shiftedMaxs, maxs); VecToPos(center, origin); xDelta = std::max(1, maxs[0] - mins[0]); yDelta = std::max(1, maxs[1] - mins[1]); size = xDelta * yDelta; ent->forbiddenListPos = (pos3_t *)G_TagMalloc(size * sizeof(pos3_t), TAG_LEVEL); ent->forbiddenListSize = size; for (i = 0; i < xDelta; i++) { for (j = 0; j < yDelta; j++) { const pos_t x = mins[0] + i; const pos_t y = mins[1] + j; const pos_t z = origin[2]; VectorSet(ent->forbiddenListPos[i], x, y, z); } } }
/** * @brief Dumps contents of the entire client routing table to CSV file. * @sa CL_InitLocal */ void Grid_DumpClientRoutes_f (void) { ipos3_t wpMins, wpMaxs; VecToPos(cl.mapData->mapMin, wpMins); VecToPos(cl.mapData->mapMax, wpMaxs); RT_WriteCSVFiles(cl.mapData->map, "ufoaiclient", wpMins, wpMaxs); }
/** * @brief Only moves the camera to the given target location if its not yet close enough */ void CL_CheckCameraRoute (const pos3_t from, const pos3_t target) { pos3_t current; VecToPos(cl.cam.origin, current); const float minDistToMove = 4.0f; const float dist = Vector2Dist(target, current); if (dist < minDistToMove) { if (target[2] != current[2]) Cvar_SetValue("cl_worldlevel", target[2]); return; } CL_CameraRoute(from, target); }
/** * @brief Center the camera on the local entity's origin * @param le The local entity which origin is used to center the camera * @sa CL_CenterView * @sa CL_ViewCenterAtGridPosition * @sa CL_CameraRoute */ void LE_CenterView (const le_t* le) { if (!cl_centerview->integer) return; assert(le); if (le->team == cls.team) { const float minDistToMove = 4.0f * UNIT_SIZE; const float dist = Vector2Dist(cl.cam.origin, le->origin); if (dist < minDistToMove) { pos3_t currentCamPos; VecToPos(cl.cam.origin, currentCamPos); if (le->pos[2] != currentCamPos[2]) Cvar_SetValue("cl_worldlevel", le->pos[2]); return; } VectorCopy(le->origin, cl.cam.origin); } else { pos3_t pos; VecToPos(cl.cam.origin, pos); CL_CheckCameraRoute(pos, le->pos); } }
/** * This is only for particles that are spawned during a match - not for map particles. * @return A particle edict */ Edict* G_SpawnParticle (const vec3_t origin, int spawnflags, const char* particle) { Edict* ent = G_Spawn("particle"); ent->type = ET_PARTICLE; VectorCopy(origin, ent->origin); /* Set the position of the entity */ VecToPos(ent->origin, ent->pos); ent->particle = particle; ent->spawnflags = spawnflags; G_CheckVis(ent); return ent; }
/** * @brief Register this think function once you would like to end the match * This think function will register the touch callback and spawns the particles for * the client to see the next map trigger. */ void Think_NextMapTrigger (Edict* self) { vec3_t center; pos3_t centerPos; self->absBox.getCenter(center); /* spawn the particle to mark the trigger */ G_SpawnParticle(center, self->spawnflags, self->particle); VecToPos(center, centerPos); G_EventCenterViewAt(PM_ALL, centerPos); gi.BroadcastPrintf(PRINT_HUD, _("You are now ready to switch the map.")); self->setTouch(Touch_NextMapTrigger); self->think = nullptr; }
/** * @brief Applies morale changes to actors who find themselves in the general direction of a shot. * @param shooter The shooting actor. * @param fd The firedef used to shoot. * @param from The weapon's muzzle location. * @param weapon The weapon used to shoot. * @param impact The shoot's impact location. */ static void G_ShotMorale (const Actor* shooter, const fireDef_t* fd, const vec3_t from, const Item* weapon, const vec3_t impact) { /* Skip not detectable shoots */ if (weapon->def()->dmgtype == gi.csi->damLaser || fd->irgoggles) return; Actor* check = nullptr; const float minDist = UNIT_SIZE * 1.5f; while ((check = G_EdictsGetNextLivingActor(check))) { /* Skip yourself */ if (check == shooter) continue; pos3_t target; VecToPos(impact, target); /* Skip the hit actor -- morale was already handled */ if (check->isSamePosAs(target)) continue; vec3_t dir1, dir2; VectorSubtract(check->origin, from, dir1); VectorSubtract(impact, from, dir2); const float len1 = VectorLength(dir1); const float len2 = VectorLength(dir2); const float dot = DotProduct(dir1, dir2); if (dot / (len1 * len2) < 0.7f) continue; /* Skip if shooting next or over an ally */ if (check->isSameTeamAs(shooter) && VectorDistSqr(check->origin, shooter->origin) <= minDist * minDist) continue; vec3_t vec1; if (len1 > len2) { VectorSubtract(dir2, dir1, vec1); } else { VectorScale(dir2, dot / (len2 * len2), vec1); VectorSubtract(dir1, vec1, vec1); } const float morDist = (check->isSamePosAs(target) ? UNIT_SIZE * 0.5f : minDist); if (VectorLengthSqr(vec1) <= morDist * morDist) { /* @todo Add a visibility check here? */ G_Morale(ML_SHOOT, check, shooter, fd->damage[0]); } } }
static void G_SpawnSmoke (const vec3_t vec, const char *particle, int rounds) { pos3_t pos; edict_t *ent; VecToPos(vec, pos); ent = G_GetEdictFromPos(pos, ET_SMOKE); if (ent == NULL) { pos_t z = gi.GridFall(gi.routingMap, ACTOR_SIZE_NORMAL, pos); if (z != pos[2]) return; ent = G_Spawn(); VectorCopy(pos, ent->pos); G_EdictCalcOrigin(ent); ent->spawnflags = G_GetLevelFlagsFromPos(pos); ent->particle = particle; SP_misc_smoke(ent); } ent->count = rounds; }
static void G_SpawnStunSmoke (const vec3_t vec, const char* particle, int rounds, int damage) { pos3_t pos; Edict* ent; VecToPos(vec, pos); ent = G_GetEdictFromPos(pos, ET_SMOKESTUN); if (ent == nullptr) { pos_t z = gi.GridFall(ACTOR_SIZE_NORMAL, pos); if (z != pos[2]) return; ent = G_Spawn(); VectorCopy(pos, ent->pos); VectorCopy(vec, ent->origin); ent->dmg = damage; ent->particle = particle; ent->spawnflags = G_GetLevelFlagsFromPos(pos); SP_misc_smokestun(ent); } ent->count = rounds; }
void G_InitCamera (Edict* ent, camera_type_t cameraType, float angle, bool rotate) { switch (cameraType) { CAMERAMODEL(CAMERA_MOBILE, 0); CAMERAMODEL(CAMERA_STATIONARY, 1); default: gi.DPrintf("unknown camera type given: %i\n", cameraType); G_FreeEdict(ent); return; } AABB modelAabb; if (gi.LoadModelAABB(ent->model, 0, modelAabb)) { ent->entBox.set(modelAabb); ent->camera.cameraType = cameraType; ent->camera.rotate = rotate; ent->classname = "misc_camera"; ent->type = ET_CAMERA; ent->solid = SOLID_BBOX; ent->flags |= FL_DESTROYABLE; ent->material = MAT_ELECTRICAL; ent->fieldSize = ACTOR_SIZE_NORMAL; ent->destroy = Destroy_Camera; ent->use = G_CameraUse; ent->dir = AngleToDir(angle); /* Set the position of the entity */ VecToPos(ent->origin, ent->pos); gi.LinkEdict(ent); } else { gi.DPrintf("Could not get bounding box for model '%s'\n", ent->model); G_FreeEdict(ent); } }
static void G_SpawnFire (const vec3_t vec, const char *particle, int rounds, int damage) { pos3_t pos; edict_t *ent; VecToPos(vec, pos); ent = G_GetEdictFromPos(pos, ET_FIRE); if (ent == NULL) { pos_t z = gi.GridFall(gi.routingMap, ACTOR_SIZE_NORMAL, pos); if (z != pos[2]) return; ent = G_Spawn(); VectorCopy(pos, ent->pos); VectorCopy(vec, ent->origin); ent->dmg = damage; ent->particle = particle; ent->spawnflags = G_GetLevelFlagsFromPos(pos); SP_misc_fire(ent); } ent->count = rounds; }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] routing The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] box The box around the inline model (alternative to name) * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, Routing &routing, const char *name, const GridBox &box, const char **list) { if (box.isZero()) { pos3_t min, max; vec3_t absmin, absmax; const cBspModel_t *model; unsigned int i; /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } #if 1 /* An attempt to fix the 'doors starting opened' bug (# 3456). * The main difference is the (missing) rotation of the halfVec. * The results are better, but do not fix the problem. */ CalculateMinsMaxs(model->angles, model->mins, model->maxs, model->origin, absmin, absmax); #else /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, absmin); VectorAdd(maxVec, model->origin, absmax); } else { /* normal */ /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, absmin); VectorAdd(model->maxs, model->origin, absmax); } #endif VecToPos(absmin, min); VecToPos(absmax, max); /* fit min/max into the world size */ max[0] = std::min(max[0], (pos_t)(PATHFINDING_WIDTH - 1)); max[1] = std::min(max[1], (pos_t)(PATHFINDING_WIDTH - 1)); max[2] = std::min(max[2], (pos_t)(PATHFINDING_HEIGHT - 1)); for (i = 0; i < 3; i++) min[i] = std::max(min[i], (pos_t)0); /* We now have the dimensions, call the generic rerouting function. */ GridBox rerouteBox(min, max); Grid_RecalcBoxRouting(mapTiles, routing, rerouteBox, list); } else { /* use the passed box */ Grid_RecalcBoxRouting(mapTiles, routing, box, list); } }
static void testMove (void) { vec3_t vec; pos3_t pos; pos_t gridPos; if (FS_CheckFile("maps/%s.bsp", mapName) != -1) { char entityString[MAX_TOKEN_CHARS]; CM_LoadMap(mapName, true, "", entityString, &mapData, &mapTiles); CM_LoadMap(mapName, true, "", entityString, &mapData, &mapTiles); } else { UFO_CU_FAIL_MSG_FATAL(va("Map resource '%s.bsp' for test is missing.", mapName)); } VectorSet(vec, 16, 16, 48); VecToPos(vec, pos); CU_ASSERT_EQUAL(pos[0], 128); CU_ASSERT_EQUAL(pos[1], 128); CU_ASSERT_EQUAL(pos[2], 0); VectorSet(vec, 80, 16, 80); VecToPos(vec, pos); CU_ASSERT_EQUAL(pos[0], 130); CU_ASSERT_EQUAL(pos[1], 128); CU_ASSERT_EQUAL(pos[2], 1); gridPos = Grid_Fall(mapData.routing, ACTOR_SIZE_NORMAL, pos); CU_ASSERT_EQUAL(gridPos, 1); { const byte crouchingState = 0; const int maxTUs = MAX_ROUTE_TUS; int lengthStored; pos3_t to; pathing_t* path = Mem_AllocType(pathing_t); VectorSet(vec, 80, 80, 32); VecToPos(vec, pos); Grid_CalcPathing(mapData.routing, ACTOR_SIZE_NORMAL, path, pos, maxTUs, nullptr); Grid_MoveStore(path); /* move downwards */ { int lengthUnstored; VectorSet(vec, 80, 48, 32); VecToPos(vec, to); lengthUnstored = Grid_MoveLength(path, to, crouchingState, false); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthUnstored, lengthStored); CU_ASSERT_EQUAL(lengthStored, TU_MOVE_STRAIGHT); } /* try to move three steps upwards - there is a brush*/ { VectorSet(vec, 80, 176, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, ROUTING_NOT_REACHABLE); } /* try move into the nodraw */ { VectorSet(vec, 48, 16, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, ROUTING_NOT_REACHABLE); } /* move into the lightclip */ { VectorSet(vec, 48, 48, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, TU_MOVE_DIAGONAL); } /* move into the passable */ { VectorSet(vec, 144, 48, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, TU_MOVE_DIAGONAL + TU_MOVE_STRAIGHT); } /* go to the other side - diagonal, followed by six straight moves */ { VectorSet(vec, -16, 48, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 6 * TU_MOVE_STRAIGHT + TU_MOVE_DIAGONAL); } /* try to walk out of the map */ { VectorSet(vec, 48, 272, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, ROUTING_NOT_REACHABLE); } /* walk to the map border */ { VectorSet(vec, 48, 240, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 4 * TU_MOVE_STRAIGHT + TU_MOVE_DIAGONAL); } /* walk a level upwards */ { VectorSet(vec, 240, 80, 96); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 5 * TU_MOVE_STRAIGHT); } /* move to the door (not a func_door) */ { VectorSet(vec, 176, -80, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 4 * TU_MOVE_STRAIGHT + 2 * TU_MOVE_DIAGONAL); } /* move into the trigger_touch */ { VectorSet(vec, -48, -80, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 5 * TU_MOVE_STRAIGHT + 3 * TU_MOVE_DIAGONAL); } /* try to walk into the actorclip */ { VectorSet(vec, -48, -48, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, ROUTING_NOT_REACHABLE); } } }
static void testMoveEntities (void) { pos3_t pos; vec3_t vec; pathing_t* path = Mem_AllocType(pathing_t); forbiddenList_t forbiddenList; const byte crouchingState = 0; const int maxTUs = MAX_ROUTE_TUS; forbiddenList.reset(); SV_Map(true, mapName, nullptr); /* starting point */ VectorSet(vec, 240, -144, 32); VecToPos(vec, pos); G_CompleteRecalcRouting(); { Edict* ent = nullptr; while ((ent = G_EdictsGetNextInUse(ent))) { /* Dead 2x2 unit will stop walking, too. */ if (ent->type == ET_SOLID) { int j; for (j = 0; j < ent->forbiddenListSize; j++) { forbiddenList.add(ent->forbiddenListPos[j], (byte*) &ent->fieldSize); } } } } { int lengthStored; pos3_t to; Grid_CalcPathing(sv->mapData.routing, ACTOR_SIZE_NORMAL, path, pos, maxTUs, &forbiddenList); Grid_MoveStore(path); /* walk onto the func_breakable */ { VectorSet(vec, 112, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 4 * TU_MOVE_STRAIGHT); } /* walk over the func_breakable */ { VectorSet(vec, 80, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 5 * TU_MOVE_STRAIGHT); } /* walk over the func_breakable */ { VectorSet(vec, 16, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 7 * TU_MOVE_STRAIGHT); } } /* starting point */ VectorSet(vec, 144, 144, 32); VecToPos(vec, pos); { int lengthStored; pos3_t to; Grid_CalcPathing(sv->mapData.routing, ACTOR_SIZE_NORMAL, path, pos, maxTUs, &forbiddenList); Grid_MoveStore(path); /* walk through the opened door */ { VectorSet(vec, 112, 144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, TU_MOVE_STRAIGHT); } /* walk around the opened door */ { VectorSet(vec, 144, 208, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, true); CU_ASSERT_EQUAL(lengthStored, 2 * TU_MOVE_STRAIGHT + TU_MOVE_DIAGONAL); } } SV_ShutdownGameProgs(); }
/** * @brief Register local entities for SOLID_BSP models like func_breakable or func_door * @note func_breakable, func_door * @sa G_SendEdictsAndBrushModels * @sa EV_ADD_BRUSH_MODEL * @sa CL_SpawnParseEntitystring */ void CL_AddBrushModel (const eventRegister_t *self, struct dbuffer *msg) { le_t *le; int entnum, modelnum1, levelflags, speed, dir; entity_type_t type; const cBspModel_t *model; int angle; vec3_t origin, angles; NET_ReadFormat(msg, self->formatString, &type, &entnum, &modelnum1, &levelflags, &origin, &angles, &speed, &angle, &dir); if (type != ET_BREAKABLE && type != ET_DOOR && type != ET_ROTATING && type != ET_DOOR_SLIDING && type != ET_TRIGGER_RESCUE && type != ET_TRIGGER_NEXTMAP) Com_Error(ERR_DROP, "Invalid le announced via EV_ADD_BRUSH_MODEL type: %i\n", type); else if (modelnum1 > MAX_MODELS || modelnum1 < 1) Com_Error(ERR_DROP, "Invalid le modelnum1 announced via EV_ADD_BRUSH_MODEL\n"); /* check if the ent is already visible */ le = LE_Get(entnum); if (le) Com_Error(ERR_DROP, "le announced a second time - le for entnum %i (type: %i) already exists (via EV_ADD_BRUSH_MODEL)\n", entnum, type); le = LE_Add(entnum); assert(le); le->rotationSpeed = speed / 100.0f; le->slidingSpeed = speed; le->angle = angle; le->dir = dir; le->type = type; le->modelnum1 = modelnum1; le->levelflags = levelflags; le->addFunc = LE_BrushModelAction; LE_SetThink(le, LET_BrushModel); /* The origin and angles are REQUIRED for doors to work! */ VectorCopy(origin, le->origin); /* store the initial position - needed for sliding doors */ VectorCopy(le->origin, le->oldOrigin); VectorCopy(angles, le->angles); Com_sprintf(le->inlineModelName, sizeof(le->inlineModelName), "*%i", le->modelnum1); model = LE_GetClipModel(le); le->model1 = R_FindModel(le->inlineModelName); if (!le->model1) Com_Error(ERR_DROP, "CL_AddBrushModel: Could not register inline model %i", le->modelnum1); /* Transfer model mins and maxs to entity */ VectorCopy(model->mins, le->mins); VectorCopy(model->maxs, le->maxs); VectorSubtract(le->maxs, le->mins, le->size); VecToPos(le->origin, le->pos); /* to allow tracing against this le */ if (!LE_IsNotSolid(le)) { /* This is to help the entity collision code out */ /* Copy entity origin and angles to model*/ CM_SetInlineModelOrientation(cl.mapTiles, le->inlineModelName, le->origin, le->angles); le->contents = CONTENTS_SOLID; CL_RecalcRouting(le); } }
/** * @brief Creates a server's entity / program execution context * by parsing textual entity definitions out of an ent file. * @sa CM_EntityString * @sa SV_SpawnServer */ void G_SpawnEntities (const char *mapname, bool day, const char *entities) { int entnum; G_FreeTags(TAG_LEVEL); OBJZERO(level); level.pathingMap = (pathing_t *)G_TagMalloc(sizeof(*level.pathingMap), TAG_LEVEL); G_EdictsReset(); /* initialize reactionFire data */ G_ReactionFireTargetsInit(); Q_strncpyz(level.mapname, mapname, sizeof(level.mapname)); level.day = day; G_ResetClientData(); level.activeTeam = TEAM_NO_ACTIVE; level.actualRound = 1; level.hurtAliens = sv_hurtaliens->integer; ai_waypointList = NULL; /* parse ents */ entnum = 0; while (1) { edict_t *ent; /* parse the opening brace */ const char *token = Com_Parse(&entities); if (!entities) break; if (token[0] != '{') gi.Error("ED_LoadFromFile: found %s when expecting {", token); ent = G_Spawn(); entities = ED_ParseEdict(entities, ent); ent->mapNum = entnum++; /* Set the position of the entity */ VecToPos(ent->origin, ent->pos); /* Call this entity's specific initializer (sets ent->type) */ ED_CallSpawn(ent); /* if this entity is an bbox (e.g. actor), then center its origin based on its position */ if (ent->solid == SOLID_BBOX) G_EdictCalcOrigin(ent); } /* spawn ai players, if needed */ if (level.num_spawnpoints[TEAM_CIVILIAN]) { if (AI_CreatePlayer(TEAM_CIVILIAN) == NULL) gi.DPrintf("Could not create civilian\n"); } if ((sv_maxclients->integer == 1 || ai_numactors->integer) && level.num_spawnpoints[TEAM_ALIEN]) { if (AI_CreatePlayer(TEAM_ALIEN) == NULL) gi.DPrintf("Could not create alien\n"); } Com_Printf("Used inventory slots after ai spawn: %i\n", game.i.GetUsedSlots(&game.i)); G_FindEdictGroups(); }
static void testMoveEntities (void) { routing_t *routing; pos3_t pos; vec3_t vec; pathing_t *path = Mem_AllocType(pathing_t); pos_t *forbiddenList[MAX_FORBIDDENLIST]; int forbiddenListLength = 0; const byte crouchingState = 0; const int distance = MAX_ROUTE; SV_Map(qtrue, mapName, NULL); /* starting point */ VectorSet(vec, 240, -144, 32); VecToPos(vec, pos); routing = &sv->mapData.map[ACTOR_SIZE_NORMAL - 1]; G_CompleteRecalcRouting(); { edict_t *ent = NULL; while ((ent = G_EdictsGetNextInUse(ent))) { /* Dead 2x2 unit will stop walking, too. */ if (ent->type == ET_SOLID) { int j; for (j = 0; j < ent->forbiddenListSize; j++) { forbiddenList[forbiddenListLength++] = ent->forbiddenListPos[j]; forbiddenList[forbiddenListLength++] = (byte*) &ent->fieldSize; } } } } { int lengthStored; pos3_t to; Grid_MoveCalc(routing, ACTOR_SIZE_NORMAL, path, pos, crouchingState, distance, forbiddenList, forbiddenListLength); Grid_MoveStore(path); /* walk onto the func_breakable */ { VectorSet(vec, 112, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, qtrue); CU_ASSERT_EQUAL(lengthStored, 4 * TU_MOVE_STRAIGHT); } /* walk over the func_breakable */ { VectorSet(vec, 80, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, qtrue); CU_ASSERT_EQUAL(lengthStored, 5 * TU_MOVE_STRAIGHT); } /* walk over the func_breakable */ { VectorSet(vec, 16, -144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, qtrue); CU_ASSERT_EQUAL(lengthStored, 7 * TU_MOVE_STRAIGHT); } } /* starting point */ VectorSet(vec, 144, 144, 32); VecToPos(vec, pos); { int lengthStored; pos3_t to; Grid_MoveCalc(routing, ACTOR_SIZE_NORMAL, path, pos, crouchingState, distance, forbiddenList, forbiddenListLength); Grid_MoveStore(path); /* walk through the opened door */ { VectorSet(vec, 112, 144, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, qtrue); CU_ASSERT_EQUAL(lengthStored, TU_MOVE_STRAIGHT); } /* walk around the opened door */ { VectorSet(vec, 144, 208, 32); VecToPos(vec, to); lengthStored = Grid_MoveLength(path, to, crouchingState, qtrue); CU_ASSERT_EQUAL(lengthStored, 2 * TU_MOVE_STRAIGHT + TU_MOVE_DIAGONAL); } } SV_ShutdownGameProgs(); }
/** * @brief Calculates the routing of a map * @sa CheckUnit * @sa CheckConnections * @sa ProcessWorldModel */ void DoRouting (void) { int i; byte* data; vec3_t mins, maxs; pos3_t pos; /* Turn on trace debugging if requested. */ if (config.generateDebugTrace) debugTrace = true; /* Record the current mapTiles[0] state so we can remove all CLIPS when done. */ PushInfo(); /* build tracing structure */ EmitBrushes(); EmitPlanes(); /** This is needed for tracing to work!!! */ /** @note LEVEL_TRACING is not an actual level */ MakeTracingNodes(LEVEL_ACTORCLIP + 1); /* Reset the whole block of map data to 0 */ Nmap.init(); /* get world bounds for optimizing */ RT_GetMapSize(&mapTiles, mins, maxs); /* Com_Printf("Vectors: (%f, %f, %f) to (%f, %f, %f)\n", mins[0], mins[1], mins[2], maxs[0], maxs[1], maxs[2]); */ VecToPos(mins, wpMins); VecToPos(maxs, wpMaxs); /* Verify the world extents are not lopsided. */ assert(wpMins[0] <= wpMaxs[0]); assert(wpMins[1] <= wpMaxs[1]); assert(wpMins[2] <= wpMaxs[2]); /* scan area heights */ RunSingleThreadOn(CheckUnitThread, PATHFINDING_WIDTH * PATHFINDING_WIDTH * ACTOR_MAX_SIZE, config.verbosity >= VERB_NORMAL, "UNITCHECK"); /* scan connections */ RunSingleThreadOn(CheckConnectionsThread, PATHFINDING_WIDTH * PATHFINDING_WIDTH * (CORE_DIRECTIONS / (1 + RT_IS_BIDIRECTIONAL)) * (ACTOR_MAX_SIZE), config.verbosity >= VERB_NORMAL, "CONNCHECK"); /* Try to shrink the world bounds along the x and y coordinates */ for (i = 0; i < 2; i++) { /* for x and y, but not z */ int j = i ^ 1; /* if i points to x, j points to y and vice versa */ /* Increase the mins */ while (wpMaxs[j] > wpMins[j]) { VectorSet(pos, wpMins[0], wpMins[1], wpMaxs[2]); for (pos[i] = wpMins[i]; pos[i] <= wpMaxs[i]; pos[i]++) { /* for all cells in an x or y row */ if (Nmap.getFloor(1, pos[0], pos[1], wpMaxs[2]) + wpMaxs[2] * CELL_HEIGHT != -1) /* no floor ? */ break; } if (pos[i] <= wpMaxs[i]) /* found a floor before the end of the row ? */ break; /* break while */ wpMins[j]++; /* if it was an x-row, increase y-value of mins and vice versa */ } /* Decrease the maxs */ while (wpMaxs[j] > wpMins[j]) { VectorCopy(wpMaxs, pos); for (pos[i] = wpMins[i]; pos[i] <= wpMaxs[i]; pos[i]++) { if (Nmap.getFloor(1, pos[0], pos[1], wpMaxs[2]) + wpMaxs[2] * CELL_HEIGHT != -1) break; } if (pos[i] <= wpMaxs[i]) break; wpMaxs[j]--; } } /* Output the floor trace file if set */ if (config.generateTraceFile) { RT_WriteCSVFiles(Nmap, baseFilename, wpMins, wpMaxs); } /* store the data */ data = curTile->routedata; for (i = 0; i < 3; i++) wpMins[i] = LittleLong(wpMins[i]); data = CompressRouting((byte*)wpMins, data, sizeof(wpMins)); for (i = 0; i < 3; i++) wpMaxs[i] = LittleLong(wpMaxs[i]); data = CompressRouting((byte*)wpMaxs, data, sizeof(wpMaxs)); data = CompressRouting((byte*)&Nmap, data, sizeof(Nmap)); curTile->routedatasize = data - curTile->routedata; /* Ensure that we did not exceed our allotment of memory for this data. */ assert(curTile->routedatasize <= MAX_MAP_ROUTING); /* Remove the CLIPS fom the tracing structure by resetting it. */ PopInfo(); }
/** * @brief This function recalculates the routing surrounding the entity name. * @sa CM_InlineModel * @sa CM_CheckUnit * @sa CM_UpdateConnection * @sa CMod_LoadSubmodels * @sa Grid_RecalcBoxRouting * @param[in] mapTiles List of tiles the current (RMA-)map is composed of * @param[in] map The routing map (either server or client map) * @param[in] name Name of the inline model to compute the mins/maxs for * @param[in] list The local models list (a local model has a name starting with * followed by the model number) */ void Grid_RecalcRouting (mapTiles_t *mapTiles, routing_t *map, const char *name, const char **list) { const cBspModel_t *model; pos3_t min, max; unsigned int i; double start, end; start = time(NULL); /* get inline model, if it is one */ if (*name != '*') { Com_Printf("Called Grid_RecalcRouting with no inline model\n"); return; } model = CM_InlineModel(mapTiles, name); if (!model) { Com_Printf("Called Grid_RecalcRouting with invalid inline model name '%s'\n", name); return; } Com_DPrintf(DEBUG_PATHING, "Model:%s origin(%f,%f,%f) angles(%f,%f,%f) mins(%f,%f,%f) maxs(%f,%f,%f)\n", name, model->origin[0], model->origin[1], model->origin[2], model->angles[0], model->angles[1], model->angles[2], model->mins[0], model->mins[1], model->mins[2], model->maxs[0], model->maxs[1], model->maxs[2]); /* get the target model's dimensions */ if (VectorNotEmpty(model->angles)) { vec3_t minVec, maxVec; vec3_t centerVec, halfVec, newCenterVec; vec3_t m[3]; /* Find the center of the extents. */ VectorCenterFromMinsMaxs(model->mins, model->maxs, centerVec); /* Find the half height and half width of the extents. */ VectorSubtract(model->maxs, centerVec, halfVec); /* Rotate the center about the origin. */ VectorCreateRotationMatrix(model->angles, m); VectorRotate(m, centerVec, newCenterVec); /* Set minVec and maxVec to bound around newCenterVec at halfVec size. */ VectorSubtract(newCenterVec, halfVec, minVec); VectorAdd(newCenterVec, halfVec, maxVec); /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(minVec, model->origin, minVec); VecToPos(minVec, min); VectorAdd(maxVec, model->origin, maxVec); VecToPos(maxVec, max); } else { /* normal */ vec3_t temp; /* Now offset by origin then convert to position (Doors do not have 0 origins) */ VectorAdd(model->mins, model->origin, temp); VecToPos(temp, min); VectorAdd(model->maxs, model->origin, temp); VecToPos(temp, max); } /* fit min/max into the world size */ max[0] = min(max[0] + 1, PATHFINDING_WIDTH - 1); max[1] = min(max[1] + 1, PATHFINDING_WIDTH - 1); max[2] = min(max[2] + 1, PATHFINDING_HEIGHT - 1); for (i = 0; i < 3; i++) min[i] = max(min[i] - 1, 0); /* We now have the dimensions, call the generic rerouting function. */ Grid_RecalcBoxRouting(mapTiles, map, min, max, list); end = time(NULL); Com_DPrintf(DEBUG_ROUTING, "Retracing for model %s between (%i, %i, %i) and (%i, %i %i) in %5.1fs\n", name, min[0], min[1], min[2], max[0], max[1], max[2], end - start); }