//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_ParseBSPEntities(void) { script_t *script; token_t token; bsp_entity_t *ent; bsp_epair_t *epair; script = LoadScriptMemory(bspworld.dentdata, bspworld.entdatasize, "entdata"); SetScriptFlags(script, SCFL_NOSTRINGWHITESPACES|SCFL_NOSTRINGESCAPECHARS);//SCFL_PRIMITIVE); bspworld.numentities = 1; while(PS_ReadToken(script, &token)) { if (strcmp(token.string, "{")) { ScriptError(script, "invalid %s\n", token.string); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if if (bspworld.numentities >= MAX_BSPENTITIES) { botimport.Print(PRT_MESSAGE, "too many entities in BSP file\n"); break; } //end if ent = &bspworld.entities[bspworld.numentities]; bspworld.numentities++; ent->epairs = NULL; while(PS_ReadToken(script, &token)) { if (!strcmp(token.string, "}")) break; epair = (bsp_epair_t *) GetClearedHunkMemory(sizeof(bsp_epair_t)); epair->next = ent->epairs; ent->epairs = epair; if (token.type != TT_STRING) { ScriptError(script, "invalid %s\n", token.string); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if StripDoubleQuotes(token.string); epair->key = (char *) GetHunkMemory(strlen(token.string) + 1); strcpy(epair->key, token.string); if (!PS_ExpectTokenType(script, TT_STRING, 0, &token)) { AAS_FreeBSPEntities(); FreeScript(script); return; } //end if StripDoubleQuotes(token.string); epair->value = (char *) GetHunkMemory(strlen(token.string) + 1); strcpy(epair->value, token.string); } //end while if (strcmp(token.string, "}")) { ScriptError(script, "missing }\n"); AAS_FreeBSPEntities(); FreeScript(script); return; } //end if } //end while FreeScript(script); } //end of the function AAS_ParseBSPEntities
//=========================================================================== // returns true if in Potentially Hearable Set // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== qbool AAS_inPVS(vec3_t p1, vec3_t p2) { return botimport.inPVS(p1, p2); } //end of the function AAS_InPVS
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_BSPModelMinsMaxsOrigin(int modelnum, vec3_t angles, vec3_t mins, vec3_t maxs, vec3_t origin) { botimport.BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin); } //end of the function AAS_BSPModelMinsMaxs
//=========================================================================== // traces axial boxes of any size through the world // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== bsp_trace_t AAS_Trace(vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { bsp_trace_t bsptrace; botimport.Trace(&bsptrace, start, mins, maxs, end, passent, contentmask); return bsptrace; } //end of the function AAS_Trace
//=========================================================================== // returns the contents at the given point // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_PointContents(vec3_t point) { return botimport.PointContents(point); } //end of the function AAS_PointContents
//=========================================================================== // predicts the movement // assumes regular bounding box sizes // NOTE: out of water jumping is not included // NOTE: grappling hook is not included // // Parameter: origin : origin to start with // presencetype : presence type to start with // velocity : velocity to start with // cmdmove : client command movement // cmdframes : number of frame cmdmove is valid // maxframes : maximum number of predicted frames // frametime : duration of one predicted frame // stopevent : events that stop the prediction // stopareanum : stop as soon as entered this area // Returns: aas_clientmove_t // Changes Globals: - //=========================================================================== int AAS_ClientMovementPrediction(struct aas_clientmove_s *move, int entnum, vec3_t origin, int presencetype, int onground, vec3_t velocity, vec3_t cmdmove, int cmdframes, int maxframes, float frametime, int stopevent, int stopareanum, vec3_t mins, vec3_t maxs, int visualize) { float phys_friction, phys_stopspeed, phys_gravity, phys_waterfriction; float phys_watergravity; float phys_walkaccelerate, phys_airaccelerate, phys_swimaccelerate; float phys_maxwalkvelocity, phys_maxcrouchvelocity, phys_maxswimvelocity; float phys_maxstep, phys_maxsteepness, phys_jumpvel, friction; float gravity, delta, maxvel, wishspeed, accelerate; //float velchange, newvel; //int ax; int n, i, j, pc, step, swimming, crouch, event, jump_frame, areanum; int areas[20], numareas; vec3_t points[20]; vec3_t org, end, feet, start, stepend, lastorg, wishdir; vec3_t frame_test_vel, old_frame_test_vel, left_test_vel; vec3_t up = {0, 0, 1}; aas_plane_t *plane, *plane2; aas_trace_t trace, steptrace; if (frametime <= 0) frametime = 0.1f; // phys_friction = aassettings.phys_friction; phys_stopspeed = aassettings.phys_stopspeed; phys_gravity = aassettings.phys_gravity; phys_waterfriction = aassettings.phys_waterfriction; phys_watergravity = aassettings.phys_watergravity; phys_maxwalkvelocity = aassettings.phys_maxwalkvelocity;// * frametime; phys_maxcrouchvelocity = aassettings.phys_maxcrouchvelocity;// * frametime; phys_maxswimvelocity = aassettings.phys_maxswimvelocity;// * frametime; phys_walkaccelerate = aassettings.phys_walkaccelerate; phys_airaccelerate = aassettings.phys_airaccelerate; phys_swimaccelerate = aassettings.phys_swimaccelerate; phys_maxstep = aassettings.phys_maxstep; phys_maxsteepness = aassettings.phys_maxsteepness; phys_jumpvel = aassettings.phys_jumpvel * frametime; // Com_Memset(move, 0, sizeof(aas_clientmove_t)); Com_Memset(&trace, 0, sizeof(aas_trace_t)); //start at the current origin VectorCopy(origin, org); org[2] += 0.25; //velocity to test for the first frame VectorScale(velocity, frametime, frame_test_vel); // jump_frame = -1; //predict a maximum of 'maxframes' ahead for (n = 0; n < maxframes; n++) { swimming = AAS_Swimming(org); //get gravity depending on swimming or not gravity = swimming ? phys_watergravity : phys_gravity; //apply gravity at the START of the frame frame_test_vel[2] = frame_test_vel[2] - (gravity * 0.1 * frametime); //if on the ground or swimming if (onground || swimming) { friction = swimming ? phys_friction : phys_waterfriction; //apply friction VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_ApplyFriction(frame_test_vel, friction, phys_stopspeed, frametime); VectorScale(frame_test_vel, frametime, frame_test_vel); } //end if crouch = qfalse; //apply command movement if (n < cmdframes) { //ax = 0; maxvel = phys_maxwalkvelocity; accelerate = phys_airaccelerate; VectorCopy(cmdmove, wishdir); if (onground) { if (cmdmove[2] < -300) { crouch = qtrue; maxvel = phys_maxcrouchvelocity; } //end if //if not swimming and upmove is positive then jump if (!swimming && cmdmove[2] > 1) { //jump velocity minus the gravity for one frame + 5 for safety frame_test_vel[2] = phys_jumpvel - (gravity * 0.1 * frametime) + 5; jump_frame = n; //jumping so air accelerate accelerate = phys_airaccelerate; } //end if else { accelerate = phys_walkaccelerate; } //end else //ax = 2; } //end if if (swimming) { maxvel = phys_maxswimvelocity; accelerate = phys_swimaccelerate; //ax = 3; } //end if else { wishdir[2] = 0; } //end else // wishspeed = VectorNormalize(wishdir); if (wishspeed > maxvel) wishspeed = maxvel; VectorScale(frame_test_vel, 1/frametime, frame_test_vel); AAS_Accelerate(frame_test_vel, frametime, wishdir, wishspeed, accelerate); VectorScale(frame_test_vel, frametime, frame_test_vel); /* for (i = 0; i < ax; i++) { velchange = (cmdmove[i] * frametime) - frame_test_vel[i]; if (velchange > phys_maxacceleration) velchange = phys_maxacceleration; else if (velchange < -phys_maxacceleration) velchange = -phys_maxacceleration; newvel = frame_test_vel[i] + velchange; // if (frame_test_vel[i] <= maxvel && newvel > maxvel) frame_test_vel[i] = maxvel; else if (frame_test_vel[i] >= -maxvel && newvel < -maxvel) frame_test_vel[i] = -maxvel; else frame_test_vel[i] = newvel; } //end for */ } //end if if (crouch) { presencetype = PRESENCE_CROUCH; } //end if else if (presencetype == PRESENCE_CROUCH) { if (AAS_PointPresenceType(org) & PRESENCE_NORMAL) { presencetype = PRESENCE_NORMAL; } //end if } //end else //save the current origin VectorCopy(org, lastorg); //move linear during one frame VectorCopy(frame_test_vel, left_test_vel); j = 0; do { VectorAdd(org, left_test_vel, end); //trace a bounding box trace = AAS_TraceClientBBox(org, end, presencetype, entnum); // //#ifdef AAS_MOVE_DEBUG if (visualize) { if (trace.startsolid) botimport.Print(PRT_MESSAGE, "PredictMovement: start solid\n"); AAS_DebugLine(org, trace.endpos, LINECOLOR_RED); } //end if //#endif //AAS_MOVE_DEBUG // if (stopevent & (SE_ENTERAREA|SE_TOUCHJUMPPAD|SE_TOUCHTELEPORTER|SE_TOUCHCLUSTERPORTAL)) { numareas = AAS_TraceAreas(org, trace.endpos, areas, points, 20); for (i = 0; i < numareas; i++) { if (stopevent & SE_ENTERAREA) { if (areas[i] == stopareanum) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_ENTERAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //NOTE: if not the first frame if ((stopevent & SE_TOUCHJUMPPAD) && n) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_JUMPPAD) { VectorCopy(points[i], move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->endarea = areas[i]; move->trace = trace; move->stopevent = SE_TOUCHJUMPPAD; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHTELEPORTER) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_TELEPORTER) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHTELEPORTER; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if if (stopevent & SE_TOUCHCLUSTERPORTAL) { if (aasworld.areasettings[areas[i]].contents & AREACONTENTS_CLUSTERPORTAL) { VectorCopy(points[i], move->endpos); move->endarea = areas[i]; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_TOUCHCLUSTERPORTAL; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end for } //end if // if (stopevent & SE_HITBOUNDINGBOX) { if (AAS_ClipToBBox(&trace, org, trace.endpos, presencetype, mins, maxs)) { VectorCopy(trace.endpos, move->endpos); move->endarea = AAS_PointAreaNum(move->endpos); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITBOUNDINGBOX; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if //move the entity to the trace end point VectorCopy(trace.endpos, org); //if there was a collision if (trace.fraction < 1.0) { //get the plane the bounding box collided with plane = AAS_PlaneFromNum(trace.planenum); // if (stopevent & SE_HITGROUNDAREA) { if (DotProduct(plane->normal, up) > phys_maxsteepness) { VectorCopy(org, start); start[2] += 0.5; if (AAS_PointAreaNum(start) == stopareanum) { VectorCopy(start, move->endpos); move->endarea = stopareanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDAREA; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if //assume there's no step step = qfalse; //if it is a vertical plane and the bot didn't jump recently if (plane->normal[2] == 0 && (jump_frame < 0 || n - jump_frame > 2)) { //check for a step VectorMA(org, -0.25, plane->normal, start); VectorCopy(start, stepend); start[2] += phys_maxstep; steptrace = AAS_TraceClientBBox(start, stepend, presencetype, entnum); // if (!steptrace.startsolid) { plane2 = AAS_PlaneFromNum(steptrace.planenum); if (DotProduct(plane2->normal, up) > phys_maxsteepness) { VectorSubtract(end, steptrace.endpos, left_test_vel); left_test_vel[2] = 0; frame_test_vel[2] = 0; //#ifdef AAS_MOVE_DEBUG if (visualize) { if (steptrace.endpos[2] - org[2] > 0.125) { VectorCopy(org, start); start[2] = steptrace.endpos[2]; AAS_DebugLine(org, start, LINECOLOR_BLUE); } //end if } //end if //#endif //AAS_MOVE_DEBUG org[2] = steptrace.endpos[2]; step = qtrue; } //end if } //end if } //end if // if (!step) { //velocity left to test for this frame is the projection //of the current test velocity into the hit plane VectorMA(left_test_vel, -DotProduct(left_test_vel, plane->normal), plane->normal, left_test_vel); //store the old velocity for landing check VectorCopy(frame_test_vel, old_frame_test_vel); //test velocity for the next frame is the projection //of the velocity of the current frame into the hit plane VectorMA(frame_test_vel, -DotProduct(frame_test_vel, plane->normal), plane->normal, frame_test_vel); //check for a landing on an almost horizontal floor if (DotProduct(plane->normal, up) > phys_maxsteepness) { onground = qtrue; } //end if if (stopevent & SE_HITGROUNDDAMAGE) { delta = 0; if (old_frame_test_vel[2] < 0 && frame_test_vel[2] > old_frame_test_vel[2] && !onground) { delta = old_frame_test_vel[2]; } //end if else if (onground) { delta = frame_test_vel[2] - old_frame_test_vel[2]; } //end else if (delta) { delta = delta * 10; delta = delta * delta * 0.0001; if (swimming) delta = 0; // never take falling damage if completely underwater /* if (ent->waterlevel == 3) return; if (ent->waterlevel == 2) delta *= 0.25; if (ent->waterlevel == 1) delta *= 0.5; */ if (delta > 40) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorCopy(frame_test_vel, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUNDDAMAGE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end if } //end if //extra check to prevent endless loop if (++j > 20) return qfalse; //while there is a plane hit } while(trace.fraction < 1.0); //if going down if (frame_test_vel[2] <= 10) { //check for a liquid at the feet of the bot VectorCopy(org, feet); feet[2] -= 22; pc = AAS_PointContents(feet); //get event from pc event = SE_NONE; if (pc & CONTENTS_LAVA) event |= SE_ENTERLAVA; if (pc & CONTENTS_SLIME) event |= SE_ENTERSLIME; if (pc & CONTENTS_WATER) event |= SE_ENTERWATER; // areanum = AAS_PointAreaNum(org); if (aasworld.areasettings[areanum].contents & AREACONTENTS_LAVA) event |= SE_ENTERLAVA; if (aasworld.areasettings[areanum].contents & AREACONTENTS_SLIME) event |= SE_ENTERSLIME; if (aasworld.areasettings[areanum].contents & AREACONTENTS_WATER) event |= SE_ENTERWATER; //if in lava or slime if (event & stopevent) { VectorCopy(org, move->endpos); move->endarea = areanum; VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = event & stopevent; move->presencetype = presencetype; move->endcontents = pc; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if // onground = AAS_OnGround(org, presencetype, entnum); //if onground and on the ground for at least one whole frame if (onground) { if (stopevent & SE_HITGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_HITGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if else if (stopevent & SE_LEAVEGROUND) { VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_LEAVEGROUND; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end else if else if (stopevent & SE_GAP) { aas_trace_t gaptrace; VectorCopy(org, start); VectorCopy(start, end); end[2] -= 48 + aassettings.phys_maxbarrier; gaptrace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, -1); //if solid is found the bot cannot walk any further and will not fall into a gap if (!gaptrace.startsolid) { //if it is a gap (lower than one step height) if (gaptrace.endpos[2] < org[2] - aassettings.phys_maxstep - 1) { if (!(AAS_PointContents(end) & CONTENTS_WATER)) { VectorCopy(lastorg, move->endpos); move->endarea = AAS_PointAreaNum(lastorg); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->trace = trace; move->stopevent = SE_GAP; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; return qtrue; } //end if } //end if } //end if } //end else if } //end for // VectorCopy(org, move->endpos); move->endarea = AAS_PointAreaNum(org); VectorScale(frame_test_vel, 1/frametime, move->velocity); move->stopevent = SE_NONE; move->presencetype = presencetype; move->endcontents = 0; move->time = n * frametime; move->frames = n; // return qtrue; } //end of the function AAS_ClientMovementPrediction
int BotExportTest(int parm0, char *parm1, vec3_t parm2, vec3_t parm3) { // return AAS_PointLight(parm2, NULL, NULL, NULL); #ifdef DEBUG static int area = -1; static int line[2]; int newarea, i, highlightarea, flood; // int reachnum; vec3_t eye, forward, right, end, origin; // vec3_t bottomcenter; // aas_trace_t trace; // aas_face_t *face; // aas_entity_t *ent; // bsp_trace_t bsptrace; // aas_reachability_t reach; // bot_goal_t goal; // clock_t start_time, end_time; vec3_t mins = {-16, -16, -24}; vec3_t maxs = {16, 16, 32}; // int areas[10], numareas; //return 0; if (!aasworld.loaded) return 0; /* if (parm0 & 1) { AAS_ClearShownPolygons(); AAS_FloodAreas(parm2); } //end if return 0; */ for (i = 0; i < 2; i++) if (!line[i]) line[i] = botimport.DebugLineCreate(); // AAS_ClearShownDebugLines(); //if (AAS_AgainstLadder(parm2)) botimport.Print(PRT_MESSAGE, "against ladder\n"); //BotOnGround(parm2, PRESENCE_NORMAL, 1, &newarea, &newarea); //botimport.Print(PRT_MESSAGE, "%f %f %f\n", parm2[0], parm2[1], parm2[2]); //* highlightarea = LibVarGetValue("bot_highlightarea"); if (highlightarea > 0) { newarea = highlightarea; } //end if else { VectorCopy(parm2, origin); origin[2] += 0.5; //newarea = AAS_PointAreaNum(origin); newarea = BotFuzzyPointReachabilityArea(origin); } //end else botimport.Print(PRT_MESSAGE, "\rtravel time to goal (%d) = %d ", botlibglobals.goalareanum, AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT)); //newarea = BotReachabilityArea(origin, qtrue); if (newarea != area) { botimport.Print(PRT_MESSAGE, "origin = %f, %f, %f\n", origin[0], origin[1], origin[2]); area = newarea; botimport.Print(PRT_MESSAGE, "new area %d, cluster %d, presence type %d\n", area, AAS_AreaCluster(area), AAS_PointPresenceType(origin)); botimport.Print(PRT_MESSAGE, "area contents: "); if (aasworld.areasettings[area].contents & AREACONTENTS_WATER) { botimport.Print(PRT_MESSAGE, "water &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_LAVA) { botimport.Print(PRT_MESSAGE, "lava &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_SLIME) { botimport.Print(PRT_MESSAGE, "slime &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_JUMPPAD) { botimport.Print(PRT_MESSAGE, "jump pad &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_CLUSTERPORTAL) { botimport.Print(PRT_MESSAGE, "cluster portal &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_VIEWPORTAL) { botimport.Print(PRT_MESSAGE, "view portal &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_DONOTENTER) { botimport.Print(PRT_MESSAGE, "do not enter &"); } //end if if (aasworld.areasettings[area].contents & AREACONTENTS_MOVER) { botimport.Print(PRT_MESSAGE, "mover &"); } //end if if (!aasworld.areasettings[area].contents) { botimport.Print(PRT_MESSAGE, "empty"); } //end if botimport.Print(PRT_MESSAGE, "\n"); botimport.Print(PRT_MESSAGE, "travel time to goal (%d) = %d\n", botlibglobals.goalareanum, AAS_AreaTravelTimeToGoalArea(newarea, origin, botlibglobals.goalareanum, TFL_DEFAULT|TFL_ROCKETJUMP)); /* VectorCopy(origin, end); end[2] += 5; numareas = AAS_TraceAreas(origin, end, areas, NULL, 10); AAS_TraceClientBBox(origin, end, PRESENCE_CROUCH, -1); botimport.Print(PRT_MESSAGE, "num areas = %d, area = %d\n", numareas, areas[0]); */ /* botlibglobals.goalareanum = newarea; VectorCopy(parm2, botlibglobals.goalorigin); botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", origin[0], origin[1], origin[2], newarea); */ } //end if //* flood = LibVarGetValue("bot_flood"); if (parm0 & 1) { if (flood) { AAS_ClearShownPolygons(); AAS_ClearShownDebugLines(); AAS_FloodAreas(parm2); } else { botlibglobals.goalareanum = newarea; VectorCopy(parm2, botlibglobals.goalorigin); botimport.Print(PRT_MESSAGE, "new goal %2.1f %2.1f %2.1f area %d\n", origin[0], origin[1], origin[2], newarea); } } //end if*/ if (flood) return 0; // if (parm0 & BUTTON_USE) // { // botlibglobals.runai = !botlibglobals.runai; // if (botlibglobals.runai) botimport.Print(PRT_MESSAGE, "started AI\n"); // else botimport.Print(PRT_MESSAGE, "stopped AI\n"); //* / /* goal.areanum = botlibglobals.goalareanum; reachnum = BotGetReachabilityToGoal(parm2, newarea, 1, ms.avoidreach, ms.avoidreachtimes, &goal, TFL_DEFAULT); if (!reachnum) { botimport.Print(PRT_MESSAGE, "goal not reachable\n"); } //end if else { AAS_ReachabilityFromNum(reachnum, &reach); AAS_ClearShownDebugLines(); AAS_ShowArea(area, qtrue); AAS_ShowArea(reach.areanum, qtrue); AAS_DrawCross(reach.start, 6, LINECOLOR_BLUE); AAS_DrawCross(reach.end, 6, LINECOLOR_RED); // if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR) { ElevatorBottomCenter(&reach, bottomcenter); AAS_DrawCross(bottomcenter, 10, LINECOLOR_GREEN); } //end if } //end else*/ // botimport.Print(PRT_MESSAGE, "travel time to goal = %d\n", // AAS_AreaTravelTimeToGoalArea(area, origin, botlibglobals.goalareanum, TFL_DEFAULT)); // botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); // AAS_Reachability_WeaponJump(703, 716); // } //end if*/ /* face = AAS_AreaGroundFace(newarea, parm2); if (face) { AAS_ShowFace(face - aasworld.faces); } //end if*/ /* AAS_ClearShownDebugLines(); AAS_ShowArea(newarea, parm0 & BUTTON_USE); AAS_ShowReachableAreas(area); */ AAS_ClearShownPolygons(); AAS_ClearShownDebugLines(); AAS_ShowAreaPolygons(newarea, 1, parm0 & 4); if (parm0 & 2) AAS_ShowReachableAreas(area); else { static int lastgoalareanum, lastareanum; static int avoidreach[MAX_AVOIDREACH]; static float avoidreachtimes[MAX_AVOIDREACH]; static int avoidreachtries[MAX_AVOIDREACH]; int reachnum, resultFlags; bot_goal_t goal; aas_reachability_t reach; /* goal.areanum = botlibglobals.goalareanum; VectorCopy(botlibglobals.goalorigin, goal.origin); reachnum = BotGetReachabilityToGoal(origin, newarea, lastgoalareanum, lastareanum, avoidreach, avoidreachtimes, avoidreachtries, &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, NULL, 0, &resultFlags); AAS_ReachabilityFromNum(reachnum, &reach); AAS_ShowReachability(&reach); */ int curarea; vec3_t curorigin; goal.areanum = botlibglobals.goalareanum; VectorCopy(botlibglobals.goalorigin, goal.origin); VectorCopy(origin, curorigin); curarea = newarea; for ( i = 0; i < 100; i++ ) { if ( curarea == goal.areanum ) { break; } reachnum = BotGetReachabilityToGoal(curorigin, curarea, lastgoalareanum, lastareanum, avoidreach, avoidreachtimes, avoidreachtries, &goal, TFL_DEFAULT|TFL_FUNCBOB|TFL_ROCKETJUMP, NULL, 0, &resultFlags); AAS_ReachabilityFromNum(reachnum, &reach); AAS_ShowReachability(&reach); VectorCopy(reach.end, origin); lastareanum = curarea; curarea = reach.areanum; } } //end else VectorClear(forward); //BotGapDistance(origin, forward, 0); /* if (parm0 & BUTTON_USE) { botimport.Print(PRT_MESSAGE, "test rj from 703 to 716\n"); AAS_Reachability_WeaponJump(703, 716); } //end if*/ AngleVectors(parm3, forward, right, NULL); //get the eye 16 units to the right of the origin VectorMA(parm2, 8, right, eye); //get the eye 24 units up eye[2] += 24; //get the end point for the line to be traced VectorMA(eye, 800, forward, end); // AAS_TestMovementPrediction(1, parm2, forward); /* //trace the line to find the hit point trace = AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); if (!line[0]) line[0] = botimport.DebugLineCreate(); botimport.DebugLineShow(line[0], eye, trace.endpos, LINECOLOR_BLUE); // AAS_ClearShownDebugLines(); if (trace.ent) { ent = &aasworld.entities[trace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if */ /* start_time = clock(); for (i = 0; i < 2000; i++) { AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); // AAS_TraceClientBBox(eye, end, PRESENCE_NORMAL, 1); } //end for end_time = clock(); botimport.Print(PRT_MESSAGE, "me %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); start_time = clock(); for (i = 0; i < 2000; i++) { AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); } //end for end_time = clock(); botimport.Print(PRT_MESSAGE, "id %lu clocks, %lu CLOCKS_PER_SEC\n", end_time - start_time, CLOCKS_PER_SEC); */ // TTimo: nested comments are BAD for gcc -Werror, use #if 0 instead.. #if 0 AAS_ClearShownDebugLines(); //bsptrace = AAS_Trace(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); bsptrace = AAS_Trace(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); if (!line[0]) line[0] = botimport.DebugLineCreate(); botimport.DebugLineShow(line[0], eye, bsptrace.endpos, LINECOLOR_YELLOW); if (bsptrace.fraction < 1.0) { face = AAS_TraceEndFace(&trace); if (face) { AAS_ShowFace(face - aasworld.faces); } //end if AAS_DrawPlaneCross(bsptrace.endpos, bsptrace.plane.normal, bsptrace.plane.dist + bsptrace.exp_dist, bsptrace.plane.type, LINECOLOR_GREEN); if (trace.ent) { ent = &aasworld.entities[trace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if } //end if //bsptrace = AAS_Trace2(eye, NULL, NULL, end, 1, MASK_PLAYERSOLID); bsptrace = AAS_Trace2(eye, mins, maxs, end, 1, MASK_PLAYERSOLID); botimport.DebugLineShow(line[1], eye, bsptrace.endpos, LINECOLOR_BLUE); if (bsptrace.fraction < 1.0) { AAS_DrawPlaneCross(bsptrace.endpos, bsptrace.plane.normal, bsptrace.plane.dist,// + bsptrace.exp_dist, bsptrace.plane.type, LINECOLOR_RED); if (bsptrace.ent) { ent = &aasworld.entities[bsptrace.ent]; AAS_ShowBoundingBox(ent->origin, ent->mins, ent->maxs); } //end if } //end if #endif #endif return 0; } //end of the function BotExportTest
void AAS_AddTeleporterPortals(void) { int j, area2num, facenum, otherareanum; char *target, *targetname, *classname; bsp_entity_t *entities, *ent, *dest; vec3_t origin, destorigin, mins, maxs, end; vec3_t bbmins, bbmaxs; aas_area_t *area; aas_face_t *face; aas_trace_t trace; aas_link_t *areas, *link; entities = AAS_ParseBSPEntities(); for (ent = entities; ent; ent = ent->next) { classname = AAS_ValueForBSPEpairKey(ent, "classname"); if (classname && !strcmp(classname, "misc_teleporter")) { if (!AAS_VectorForBSPEpairKey(ent, "origin", origin)) { botimport.Print(PRT_ERROR, "teleporter (%s) without origin\n", target); continue; } //end if // target = AAS_ValueForBSPEpairKey(ent, "target"); if (!target) { botimport.Print(PRT_ERROR, "teleporter (%s) without target\n", target); continue; } //end if for (dest = entities; dest; dest = dest->next) { classname = AAS_ValueForBSPEpairKey(dest, "classname"); if (classname && !strcmp(classname, "misc_teleporter_dest")) { targetname = AAS_ValueForBSPEpairKey(dest, "targetname"); if (targetname && !strcmp(targetname, target)) { break; } //end if } //end if } //end for if (!dest) { botimport.Print(PRT_ERROR, "teleporter without destination (%s)\n", target); continue; } //end if if (!AAS_VectorForBSPEpairKey(dest, "origin", destorigin)) { botimport.Print(PRT_ERROR, "teleporter destination (%s) without origin\n", target); continue; } //end if destorigin[2] += 24; //just for q2e1m2, the dork has put the telepads in the ground VectorCopy(destorigin, end); end[2] -= 100; trace = AAS_TraceClientBBox(destorigin, end, PRESENCE_CROUCH, -1); if (trace.startsolid) { botimport.Print(PRT_ERROR, "teleporter destination (%s) in solid\n", target); continue; } //end if VectorCopy(trace.endpos, destorigin); area2num = AAS_PointAreaNum(destorigin); //reset all cluster fields for (j = 0; j < aasworld.numareas; j++) { aasworld.areasettings[j].cluster = 0; } //end for // VectorSet(mins, -8, -8, 8); VectorSet(maxs, 8, 8, 24); // AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, bbmins, bbmaxs); // VectorAdd(origin, mins, mins); VectorAdd(origin, maxs, maxs); //add bounding box size VectorSubtract(mins, bbmaxs, mins); VectorSubtract(maxs, bbmins, maxs); //link an invalid (-1) entity areas = AAS_AASLinkEntity(mins, maxs, -1); // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents |= AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL; } //end for // for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[link->areanum]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != link->areanum) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if // AAS_FloodCluster_r(otherareanum, 1); } //end for } //end for //if the teleport destination IS in the same cluster if (aasworld.areasettings[area2num].cluster) { for (link = areas; link; link = link->next_area) { if (!AAS_AreaGrounded(link->areanum)) continue; //add the teleporter portal mark aasworld.areasettings[link->areanum].contents &= ~(AREACONTENTS_CLUSTERPORTAL | AREACONTENTS_TELEPORTAL); } //end for } //end if } //end if } //end for AAS_FreeBSPEntities(entities); } //end of the function AAS_AddTeleporterPortals
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_InitClustering(void) { int i, removedPortalAreas; int n, total, numreachabilityareas; if (!aasworld.loaded) return; //if there are clusters if (aasworld.numclusters >= 1) { #ifndef BSPC //if clustering isn't forced if (!((int)LibVarGetValue("forceclustering")) && !((int)LibVarGetValue("forcereachability"))) return; #endif } //end if //set all view portals as cluster portals in case we re-calculate the reachabilities and clusters (with -reach) AAS_SetViewPortalsAsClusterPortals(); //count the number of forced cluster portals AAS_CountForcedClusterPortals(); //remove all area cluster marks AAS_RemoveClusterAreas(); //find possible cluster portals AAS_FindPossiblePortals(); //craete portals to for the bot view AAS_CreateViewPortals(); //remove all portals that are not closing a cluster //AAS_RemoveNotClusterClosingPortals(); //initialize portal memory if (aasworld.portals) FreeMemory(aasworld.portals); aasworld.portals = (aas_portal_t *) GetClearedMemory(AAS_MAX_PORTALS * sizeof(aas_portal_t)); //initialize portal index memory if (aasworld.portalindex) FreeMemory(aasworld.portalindex); aasworld.portalindex = (aas_portalindex_t *) GetClearedMemory(AAS_MAX_PORTALINDEXSIZE * sizeof(aas_portalindex_t)); //initialize cluster memory if (aasworld.clusters) FreeMemory(aasworld.clusters); aasworld.clusters = (aas_cluster_t *) GetClearedMemory(AAS_MAX_CLUSTERS * sizeof(aas_cluster_t)); // removedPortalAreas = 0; botimport.Print(PRT_MESSAGE, "\r%6d removed portal areas", removedPortalAreas); while(1) { botimport.Print(PRT_MESSAGE, "\r%6d", removedPortalAreas); //initialize the number of portals and clusters aasworld.numportals = 1; //portal 0 is a dummy aasworld.portalindexsize = 0; aasworld.numclusters = 1; //cluster 0 is a dummy //create the portals from the portal areas AAS_CreatePortals(); // removedPortalAreas++; //find the clusters if (!AAS_FindClusters()) continue; //test the portals if (!AAS_TestPortals()) continue; // break; } //end while botimport.Print(PRT_MESSAGE, "\n"); //the AAS file should be saved aasworld.savefile = qtrue; //write the portal areas to the log file for (i = 1; i < aasworld.numportals; i++) { Log_Write("portal %d: area %d\r\n", i, aasworld.portals[i].areanum); } //end for // report cluster info botimport.Print(PRT_MESSAGE, "%6d portals created\n", aasworld.numportals); botimport.Print(PRT_MESSAGE, "%6d clusters created\n", aasworld.numclusters); for (i = 1; i < aasworld.numclusters; i++) { botimport.Print(PRT_MESSAGE, "cluster %d has %d reachability areas\n", i, aasworld.clusters[i].numreachabilityareas); } //end for // report AAS file efficiency numreachabilityareas = 0; total = 0; for (i = 0; i < aasworld.numclusters; i++) { n = aasworld.clusters[i].numreachabilityareas; numreachabilityareas += n; total += n * n; } total += numreachabilityareas * aasworld.numportals; // botimport.Print(PRT_MESSAGE, "%6i total reachability areas\n", numreachabilityareas); botimport.Print(PRT_MESSAGE, "%6i AAS memory/CPU usage (the lower the better)\n", total * 3); } //end of the function AAS_InitClustering
void AAS_RemoveNotClusterClosingPortals(void) { int i, j, facenum, otherareanum, nonclosingportals, numseperatedclusters; aas_area_t *area; aas_face_t *face; AAS_RemoveTeleporterPortals(); // nonclosingportals = 0; for (i = 1; i < aasworld.numareas; i++) { if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; // numseperatedclusters = 0; //reset all cluster fields AAS_RemoveClusterAreas(); //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[i]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; //if not solid at the other side of the face if (!otherareanum) continue; //don't flood into other portals if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; //another cluster is seperated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); AAS_FloodClusterReachabilities(numseperatedclusters); } //end for //use the reachabilities to flood into other areas for (j = 0; j < aasworld.areasettings[i].numreachableareas; j++) { otherareanum = aasworld.reachability[ aasworld.areasettings[i].firstreachablearea + j].areanum; //this should never be qtrue but we check anyway if (!otherareanum) continue; //don't flood into other portals if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) continue; //if the area already has a cluster set if (aasworld.areasettings[otherareanum].cluster) continue; //another cluster is seperated by this portal numseperatedclusters++; //flood the cluster AAS_FloodCluster_r(otherareanum, numseperatedclusters); AAS_FloodClusterReachabilities(numseperatedclusters); } //end for //a portal must seperate no more and no less than 2 clusters if (numseperatedclusters != 2) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; nonclosingportals++; //recheck all the other portals again i = 0; } //end if } //end for botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); } //end of the function AAS_RemoveNotClusterClosingPortals
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== void AAS_RemoveNotClusterClosingPortals(void) { int i, j, k, facenum, otherareanum, nonclosingportals; aas_area_t *area; aas_face_t *face; AAS_RemoveTeleporterPortals(); // nonclosingportals = 0; for (i = 1; i < aasworld.numareas; i++) { if (!(aasworld.areasettings[i].contents & AREACONTENTS_CLUSTERPORTAL)) continue; //find a non-portal area adjacent to the portal area and flood //the cluster from there area = &aasworld.areas[i]; for (j = 0; j < area->numfaces; j++) { facenum = abs(aasworld.faceindex[area->firstface + j]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if //reset all cluster fields AAS_RemoveClusterAreas(); // AAS_FloodCluster_r(otherareanum, 1); AAS_FloodClusterReachabilities(1); //check if all adjacent non-portal areas have a cluster set for (k = 0; k < area->numfaces; k++) { facenum = abs(aasworld.faceindex[area->firstface + k]); face = &aasworld.faces[facenum]; // if (face->frontarea != i) otherareanum = face->frontarea; else otherareanum = face->backarea; // if (!otherareanum) continue; // if (aasworld.areasettings[otherareanum].contents & AREACONTENTS_CLUSTERPORTAL) { continue; } //end if // if (!aasworld.areasettings[otherareanum].cluster) break; } //end for //if all adjacent non-portal areas have a cluster set then the portal //didn't seal a cluster if (k >= area->numfaces) { aasworld.areasettings[i].contents &= ~AREACONTENTS_CLUSTERPORTAL; nonclosingportals++; //recheck all the other portals again i = 0; break; } //end if } //end for } //end for botimport.Print(PRT_MESSAGE, "\r%6d non closing portals removed\n", nonclosingportals); } //end of the function AAS_RemoveNotClusterClosingPortals
//=========================================================================== // recursive subdivision of the line by the BSP tree. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int AAS_TraceAreas(vec3_t start, vec3_t end, int *areas, vec3_t *points, int maxareas) { int side, nodenum, tmpplanenum; int numareas; float front, back, frac; vec3_t cur_start, cur_end, cur_mid; aas_tracestack_t tracestack[127]; aas_tracestack_t *tstack_p; aas_node_t *aasnode; aas_plane_t *plane; numareas = 0; areas[0] = 0; if (!aasworld.loaded) return numareas; tstack_p = tracestack; //we start with the whole line on the stack VectorCopy(start, tstack_p->start); VectorCopy(end, tstack_p->end); tstack_p->planenum = 0; //start with node 1 because node zero is a dummy for a solid leaf tstack_p->nodenum = 1; //starting at the root of the tree tstack_p++; while (1) { //pop up the stack tstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (tstack_p < tracestack) { return numareas; } //end if //number of the current node to test the line against nodenum = tstack_p->nodenum; //if it is an area if (nodenum < 0) { #ifdef AAS_SAMPLE_DEBUG if (-nodenum > aasworld.numareasettings) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: -nodenum = %d out of range\n", -nodenum); return numareas; } //end if #endif //AAS_SAMPLE_DEBUG //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); areas[numareas] = -nodenum; if (points) VectorCopy(tstack_p->start, points[numareas]); numareas++; if (numareas >= maxareas) return numareas; continue; } //end if //if it is a solid leaf if (!nodenum) { continue; } //end if #ifdef AAS_SAMPLE_DEBUG if (nodenum > aasworld.numnodes) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: nodenum out of range\n"); return numareas; } //end if #endif //AAS_SAMPLE_DEBUG //the node to test against aasnode = &aasworld.nodes[nodenum]; //start point of current line to test against node VectorCopy(tstack_p->start, cur_start); //end point of the current line to test against node VectorCopy(tstack_p->end, cur_end); //the current node plane plane = &aasworld.planes[aasnode->planenum]; // switch(plane->type) {/*FIXME: wtf doesn't this work? obviously the node planes aren't always facing positive!!! //check for axial planes case PLANE_X: { front = cur_start[0] - plane->dist; back = cur_end[0] - plane->dist; break; } //end case case PLANE_Y: { front = cur_start[1] - plane->dist; back = cur_end[1] - plane->dist; break; } //end case case PLANE_Z: { front = cur_start[2] - plane->dist; back = cur_end[2] - plane->dist; break; } //end case*/ // default: //gee it's not an axial plane { front = DotProduct(cur_start, plane->normal) - plane->dist; back = DotProduct(cur_end, plane->normal) - plane->dist; // break; } //end default } //end switch //if the whole to be traced line is totally at the front of this node //only go down the tree with the front child if (front > 0 && back > 0) { //keep the current start and end point on the stack //and go down the tree with the front child tstack_p->nodenum = aasnode->children[0]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end if //if the whole to be traced line is totally at the back of this node //only go down the tree with the back child else if (front <= 0 && back <= 0) { //keep the current start and end point on the stack //and go down the tree with the back child tstack_p->nodenum = aasnode->children[1]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end if //go down the tree both at the front and back of the node else { tmpplanenum = tstack_p->planenum; //calculate the hitpoint with the node (split point of the line) //put the crosspoint TRACEPLANE_EPSILON pixels on the near side if (front < 0) frac = (front)/(front-back); else frac = (front)/(front-back); if (frac < 0) frac = 0; else if (frac > 1) frac = 1; //frac = front / (front-back); // cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); //side the front part of the line is on side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); //not necesary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if //now put the part near the start of the line on the stack so we will //continue with thats part first. This way we'll find the first //hit of the bbox VectorCopy(cur_start, tstack_p->start); VectorCopy(cur_mid, tstack_p->end); tstack_p->planenum = tmpplanenum; tstack_p->nodenum = aasnode->children[side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceAreas: stack overflow\n"); return numareas; } //end if } //end else } //end while // return numareas; } //end of the function AAS_TraceAreas
//=========================================================================== // recursive subdivision of the line by the BSP tree. // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== aas_trace_t AAS_TraceClientBBox(vec3_t start, vec3_t end, int presencetype, int passent) { int side, nodenum, tmpplanenum; float front, back, frac; vec3_t cur_start, cur_end, cur_mid, v1, v2; aas_tracestack_t tracestack[127]; aas_tracestack_t *tstack_p; aas_node_t *aasnode; aas_plane_t *plane; aas_trace_t trace; //clear the trace structure Com_Memset(&trace, 0, sizeof(aas_trace_t)); if (!aasworld.loaded) return trace; tstack_p = tracestack; //we start with the whole line on the stack VectorCopy(start, tstack_p->start); VectorCopy(end, tstack_p->end); tstack_p->planenum = 0; //start with node 1 because node zero is a dummy for a solid leaf tstack_p->nodenum = 1; //starting at the root of the tree tstack_p++; while (1) { //pop up the stack tstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (tstack_p < tracestack) { tstack_p++; //nothing was hit trace.startsolid = qfalse; trace.fraction = 1.0; //endpos is the end of the line VectorCopy(end, trace.endpos); //nothing hit trace.ent = 0; trace.area = 0; trace.planenum = 0; return trace; } //end if //number of the current node to test the line against nodenum = tstack_p->nodenum; //if it is an area if (nodenum < 0) { #ifdef AAS_SAMPLE_DEBUG if (-nodenum > aasworld.numareasettings) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: -nodenum out of range\n"); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //botimport.Print(PRT_MESSAGE, "areanum = %d, must be %d\n", -nodenum, AAS_PointAreaNum(start)); //if can't enter the area because it hasn't got the right presence type if (!(aasworld.areasettings[-nodenum].presencetype & presencetype)) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if (tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2]) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract(end, start, v1); VectorSubtract(tstack_p->start, start, v2); trace.fraction = VectorLength(v2) / VectorNormalize(v1); VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); } //end else VectorCopy(tstack_p->start, trace.endpos); trace.ent = 0; trace.area = -nodenum; // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &aasworld.planes[trace.planenum]; if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; return trace; } //end if else { if (passent >= 0) { if (AAS_AreaEntityCollision(-nodenum, tstack_p->start, tstack_p->end, presencetype, passent, &trace)) { if (!trace.startsolid) { VectorSubtract(end, start, v1); VectorSubtract(trace.endpos, start, v2); trace.fraction = VectorLength(v2) / VectorLength(v1); } //end if return trace; } //end if } //end if } //end else trace.lastarea = -nodenum; continue; } //end if //if it is a solid leaf if (!nodenum) { //if the start point is still the initial start point //NOTE: no need for epsilons because the points will be //exactly the same when they're both the start point if (tstack_p->start[0] == start[0] && tstack_p->start[1] == start[1] && tstack_p->start[2] == start[2]) { trace.startsolid = qtrue; trace.fraction = 0.0; VectorClear(v1); } //end if else { trace.startsolid = qfalse; VectorSubtract(end, start, v1); VectorSubtract(tstack_p->start, start, v2); trace.fraction = VectorLength(v2) / VectorNormalize(v1); VectorMA(tstack_p->start, -0.125, v1, tstack_p->start); } //end else VectorCopy(tstack_p->start, trace.endpos); trace.ent = 0; trace.area = 0; //hit solid leaf // VectorSubtract(end, start, v1); trace.planenum = tstack_p->planenum; //always take the plane with normal facing towards the trace start plane = &aasworld.planes[trace.planenum]; if (DotProduct(v1, plane->normal) > 0) trace.planenum ^= 1; return trace; } //end if #ifdef AAS_SAMPLE_DEBUG if (nodenum > aasworld.numnodes) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: nodenum out of range\n"); return trace; } //end if #endif //AAS_SAMPLE_DEBUG //the node to test against aasnode = &aasworld.nodes[nodenum]; //start point of current line to test against node VectorCopy(tstack_p->start, cur_start); //end point of the current line to test against node VectorCopy(tstack_p->end, cur_end); //the current node plane plane = &aasworld.planes[aasnode->planenum]; // switch(plane->type) {/*FIXME: wtf doesn't this work? obviously the axial node planes aren't always facing positive!!! //check for axial planes case PLANE_X: { front = cur_start[0] - plane->dist; back = cur_end[0] - plane->dist; break; } //end case case PLANE_Y: { front = cur_start[1] - plane->dist; back = cur_end[1] - plane->dist; break; } //end case case PLANE_Z: { front = cur_start[2] - plane->dist; back = cur_end[2] - plane->dist; break; } //end case*/ // default: //gee it's not an axial plane { front = DotProduct(cur_start, plane->normal) - plane->dist; back = DotProduct(cur_end, plane->normal) - plane->dist; // break; } //end default } //end switch // bk010221 - old location of FPE hack and divide by zero expression //if the whole to be traced line is totally at the front of this node //only go down the tree with the front child if ((front >= -ON_EPSILON && back >= -ON_EPSILON)) { //keep the current start and end point on the stack //and go down the tree with the front child tstack_p->nodenum = aasnode->children[0]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end if //if the whole to be traced line is totally at the back of this node //only go down the tree with the back child else if ((front < ON_EPSILON && back < ON_EPSILON)) { //keep the current start and end point on the stack //and go down the tree with the back child tstack_p->nodenum = aasnode->children[1]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end if //go down the tree both at the front and back of the node else { tmpplanenum = tstack_p->planenum; // bk010221 - new location of divide by zero (see above) if ( front == back ) front -= 0.001f; // bk0101022 - hack/FPE //calculate the hitpoint with the node (split point of the line) //put the crosspoint TRACEPLANE_EPSILON pixels on the near side if (front < 0) frac = (front + TRACEPLANE_EPSILON)/(front-back); else frac = (front - TRACEPLANE_EPSILON)/(front-back); // bk010221 // if (frac < 0) frac = 0.001f; //0 else if (frac > 1) frac = 0.999f; //1 //frac = front / (front-back); // cur_mid[0] = cur_start[0] + (cur_end[0] - cur_start[0]) * frac; cur_mid[1] = cur_start[1] + (cur_end[1] - cur_start[1]) * frac; cur_mid[2] = cur_start[2] + (cur_end[2] - cur_start[2]) * frac; // AAS_DrawPlaneCross(cur_mid, plane->normal, plane->dist, plane->type, LINECOLOR_RED); //side the front part of the line is on side = front < 0; //first put the end part of the line on the stack (back side) VectorCopy(cur_mid, tstack_p->start); //not necesary to store because still on stack //VectorCopy(cur_end, tstack_p->end); tstack_p->planenum = aasnode->planenum; tstack_p->nodenum = aasnode->children[!side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if //now put the part near the start of the line on the stack so we will //continue with thats part first. This way we'll find the first //hit of the bbox VectorCopy(cur_start, tstack_p->start); VectorCopy(cur_mid, tstack_p->end); tstack_p->planenum = tmpplanenum; tstack_p->nodenum = aasnode->children[side]; tstack_p++; if (tstack_p >= &tracestack[127]) { botimport.Print(PRT_ERROR, "AAS_TraceBoundingBox: stack overflow\n"); return trace; } //end if } //end else } //end while // return trace; } //end of the function AAS_TraceClientBBox
aas_link_t *AAS_AASLinkEntity(vec3_t absmins, vec3_t absmaxs, int entnum) { int side, nodenum; aas_linkstack_t linkstack[128]; aas_linkstack_t *lstack_p; aas_node_t *aasnode; aas_plane_t *plane; aas_link_t *link, *areas; if (!aasworld.loaded) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: aas not loaded\n"); return NULL; } //end if areas = NULL; // lstack_p = linkstack; //we start with the whole line on the stack //start with node 1 because node zero is a dummy used for solid leafs lstack_p->nodenum = 1; //starting at the root of the tree lstack_p++; while (1) { //pop up the stack lstack_p--; //if the trace stack is empty (ended up with a piece of the //line to be traced in an area) if (lstack_p < linkstack) break; //number of the current node to test the line against nodenum = lstack_p->nodenum; //if it is an area if (nodenum < 0) { //NOTE: the entity might have already been linked into this area // because several node children can point to the same area for (link = aasworld.arealinkedentities[-nodenum]; link; link = link->next_ent) { if (link->entnum == entnum) break; } //end for if (link) continue; // link = AAS_AllocAASLink(); if (!link) return areas; link->entnum = entnum; link->areanum = -nodenum; //put the link into the double linked area list of the entity link->prev_area = NULL; link->next_area = areas; if (areas) areas->prev_area = link; areas = link; //put the link into the double linked entity list of the area link->prev_ent = NULL; link->next_ent = aasworld.arealinkedentities[-nodenum]; if (aasworld.arealinkedentities[-nodenum]) aasworld.arealinkedentities[-nodenum]->prev_ent = link; aasworld.arealinkedentities[-nodenum] = link; // continue; } //end if //if solid leaf if (!nodenum) continue; //the node to test against aasnode = &aasworld.nodes[nodenum]; //the current node plane plane = &aasworld.planes[aasnode->planenum]; //get the side(s) the box is situated relative to the plane side = AAS_BoxOnPlaneSide2(absmins, absmaxs, plane); //if on the front side of the node if (side & 1) { lstack_p->nodenum = aasnode->children[0]; lstack_p++; } //end if if (lstack_p >= &linkstack[127]) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); break; } //end if //if on the back side of the node if (side & 2) { lstack_p->nodenum = aasnode->children[1]; lstack_p++; } //end if if (lstack_p >= &linkstack[127]) { botimport.Print(PRT_ERROR, "AAS_LinkEntity: stack overflow\n"); break; } //end if } //end while return areas; } //end of the function AAS_AASLinkEntity