qbool SV_CheckBottom (edict_t *ent) { vec3_t mins, maxs, start, stop; trace_t trace; int x, y; float mid, bottom; VectorAdd (ent->v.origin, ent->v.mins, mins); VectorAdd (ent->v.origin, ent->v.maxs, maxs); // if all of the points under the corners are solid world, don't bother // with the tougher checks // the corners must be within 16 of the midpoint start[2] = mins[2] - 1; for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = x ? maxs[0] : mins[0]; start[1] = y ? maxs[1] : mins[1]; if (SV_PointContents (start) != CONTENTS_SOLID) goto realcheck; } return true; // we got out easy realcheck: // // check it for real... // start[2] = mins[2]; // the midpoint must be within 16 of the bottom start[0] = stop[0] = (mins[0] + maxs[0])*0.5; start[1] = stop[1] = (mins[1] + maxs[1])*0.5; stop[2] = start[2] - 2*STEPSIZE; trace = SV_Trace (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction == 1.0) return false; mid = bottom = trace.endpos[2]; // the corners must be within 16 of the midpoint for (x=0 ; x<=1 ; x++) for (y=0 ; y<=1 ; y++) { start[0] = stop[0] = x ? maxs[0] : mins[0]; start[1] = stop[1] = y ? maxs[1] : mins[1]; trace = SV_Trace (start, vec3_origin, vec3_origin, stop, true, ent); if (trace.fraction != 1.0 && trace.endpos[2] > bottom) bottom = trace.endpos[2]; if (trace.fraction == 1.0 || mid - trace.endpos[2] > STEPSIZE) return false; } return true; }
static void predict_move(sharedEntity_t *ent, float frametime, trajectory_t *tr, vec3_t result) { float stepSize; vec3_t start_o, start_v, down, up; trace_t trace; VectorCopy(tr->trBase, result); // assume the move fails if (zero_vector(tr->trDelta)) // not moving { return; } if (predict_slide_move(ent, frametime, tr, result)) // move completed { return; } VectorCopy(tr->trBase, start_o); VectorCopy(tr->trDelta, start_v); VectorCopy(start_o, up); up[2] += STEPSIZE; // test the player position if they were a stepheight higher SV_Trace(&trace, start_o, ent->r.mins, ent->r.maxs, up, ent->s.number, CONTENTS_SOLID, qfalse); if (trace.allsolid) // can't step up { return; } stepSize = trace.endpos[2] - start_o[2]; // try slidemove from this position VectorCopy(trace.endpos, tr->trBase); VectorCopy(start_v, tr->trDelta); predict_slide_move(ent, frametime, tr, result); // push down the final amount VectorCopy(tr->trBase, down); down[2] -= stepSize; SV_Trace(&trace, tr->trBase, ent->r.mins, ent->r.maxs, down, ent->s.number, CONTENTS_SOLID, qfalse); if (!trace.allsolid) { VectorCopy(trace.endpos, result); } }
/* ============ SV_TestEntityPosition A small wrapper around SV_BoxInSolidEntity that never clips against the supplied entity. ============ */ edict_t *SV_TestEntityPosition (edict_t *ent) { trace_t trace; if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NOMONSTERS, ent); else trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, ent->v.origin, MOVE_NORMAL, ent); if (trace.startsolid) return sv.edicts; return NULL; }
/* * Kills all entities that would touch the * proposed new positioning of ent. Ent s * hould be unlinked before calling this! */ qboolean KillBox(edict_t *ent) { trace_t tr; if (!ent) { return false; } while (1) { tr = SV_Trace(ent->s.origin, ent->mins, ent->maxs, ent->s.origin, NULL, MASK_PLAYERSOLID); if (!tr.ent) { break; } /* nail it */ T_Damage(tr.ent, ent, ent, vec3_origin, ent->s.origin, vec3_origin, 100000, 0, DAMAGE_NO_PROTECTION, MOD_TELEFRAG); /* if we didn't kill it, fail */ if (tr.ent->solid) { return false; } } return true; /* all clear */ }
int SV_OrgVisibleBox(vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore, int rmg) { trace_t tr; if (rmg) { SV_Trace(&tr, org1, NULL, NULL, org2, ignore, MASK_SOLID, 0, 0, 10); } else { SV_Trace(&tr, org1, mins, maxs, org2, ignore, MASK_SOLID, 0, 0, 10); } if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid) { return 1; } return 0; }
/* ================ FinishSpawningItem Traces down to find where an item should rest, instead of letting them free fall from their spawn points ================ */ void GameItem::Think_FinishSpawningItem::execute() { trace_t tr; vec3_t dest; VectorSet( self_->r.mins, -ITEM_RADIUS, -ITEM_RADIUS, -ITEM_RADIUS ); VectorSet( self_->r.maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); self_->s.eType = ET_ITEM; self_->s.modelindex = self_->item_ - bg_itemlist; // store item number in modelindex self_->s.modelindex2 = 0; // zero indicates this isn't a dropped item self_->r.contents = CONTENTS_TRIGGER; self_->setTouch(new Touch_ItemTouch); // useing an item causes it to respawn self_->setUse(new Use_ItemUse); if( self_->spawnflags_ & 1 ) { // suspended G_SetOrigin( self_, self_->s.origin ); } else { // drop to floor VectorSet( dest, self_->s.origin[0], self_->s.origin[1], self_->s.origin[2] - 4096 ); SV_Trace( &tr, self_->s.origin, self_->r.mins, self_->r.maxs, dest, self_->s.number, MASK_SOLID, false ); if( tr.startsolid ) { Com_Printf ("FinishSpawningItem: %s startsolid at %s\n", self_->classname_, vtos(self_->s.origin)); //theLevel.removeEntity(self_); self_->freeUp(); return; } // allow to ride movers self_->s.groundEntityNum = tr.entityNum; G_SetOrigin( self_, tr.endpos ); } // team slaves and targeted items aren't present at start if( ( self_->flags_ & FL_TEAMSLAVE ) || self_->targetname_ ) { self_->s.eFlags |= EF_NODRAW; self_->r.contents = 0; return; } SV_LinkEntity( self_ ); }
/* ============ SV_PushEntity Does not change the entities velocity at all ============ */ trace_t SV_PushEntity (edict_t *ent, vec3_t push) { trace_t trace; vec3_t end; VectorAdd (ent->v.origin, push, end); if (ent->v.movetype == MOVETYPE_FLYMISSILE) trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_MISSILE, ent); else if (ent->v.solid == SOLID_TRIGGER || ent->v.solid == SOLID_NOT) // only clip against bmodels trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NOMONSTERS, ent); else trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, MOVE_NORMAL, ent); VectorCopy (trace.endpos, ent->v.origin); SV_LinkEdict (ent, true); if (trace.e.ent) SV_Impact (ent, trace.e.ent); return trace; }
/* ================== BotImport_Trace ================== */ static void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { trace_t trace; SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse); //copy the trace information bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; VectorCopy(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; VectorCopy(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; }
/* * BotImport_Trace */ static void BotImport_Trace(bsp_Trace *bsptrace, Vec3 start, Vec3 mins, Vec3 maxs, Vec3 end, int passent, int contentmask) { Trace trace; SV_Trace(&trace, start, mins, maxs, end, passent, contentmask, qfalse); /* copy the trace information */ bsptrace->allsolid = trace.allsolid; bsptrace->startsolid = trace.startsolid; bsptrace->fraction = trace.fraction; copyv3(trace.endpos, bsptrace->endpos); bsptrace->plane.dist = trace.plane.dist; copyv3(trace.plane.normal, bsptrace->plane.normal); bsptrace->plane.signbits = trace.plane.signbits; bsptrace->plane.type = trace.plane.type; bsptrace->surface.value = trace.surfaceFlags; bsptrace->ent = trace.entityNum; bsptrace->exp_dist = 0; bsptrace->sidenum = 0; bsptrace->contents = 0; }
/* ============= SV_movestep Called by monster program code. The move will be adjusted for slopes and stairs, but if the move isn't possible, no move is done, false is returned, and pr_global_struct->trace_normal is set to the normal of the blocking wall ============= */ qbool SV_movestep (edict_t *ent, vec3_t move, qbool relink) { float dz; vec3_t oldorg, neworg, end; trace_t trace; int i; edict_t *enemy; // try the move VectorCopy (ent->v.origin, oldorg); VectorAdd (ent->v.origin, move, neworg); // flying monsters don't step up if ( (int)ent->v.flags & (FL_SWIM | FL_FLY) ) { // try one move with vertical motion, then one without for (i=0 ; i<2 ; i++) { VectorAdd (ent->v.origin, move, neworg); enemy = PROG_TO_EDICT(ent->v.enemy); if (i == 0 && enemy != sv.edicts) { dz = ent->v.origin[2] - PROG_TO_EDICT(ent->v.enemy)->v.origin[2]; if (dz > 40) neworg[2] -= 8; if (dz < 30) neworg[2] += 8; } trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, neworg, false, ent); if (trace.fraction == 1) { if ( ((int)ent->v.flags & FL_SWIM) && SV_PointContents(trace.endpos) == CONTENTS_EMPTY ) return false; // swim monster left water VectorCopy (trace.endpos, ent->v.origin); if (relink) SV_LinkEdict (ent, true); return true; } if (enemy == sv.edicts) break; } return false; } // push down from a step height above the wished position neworg[2] += STEPSIZE; VectorCopy (neworg, end); end[2] -= STEPSIZE*2; trace = SV_Trace (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid) return false; if (trace.startsolid) { neworg[2] -= STEPSIZE; trace = SV_Trace (neworg, ent->v.mins, ent->v.maxs, end, false, ent); if (trace.allsolid || trace.startsolid) return false; } if (trace.fraction == 1) { // if monster had the ground pulled out, go ahead and fall if ( (int)ent->v.flags & FL_PARTIALGROUND ) { VectorAdd (ent->v.origin, move, ent->v.origin); if (relink) SV_LinkEdict (ent, true); ent->v.flags = (int)ent->v.flags & ~FL_ONGROUND; // Com_Printf ("fall down\n"); return true; } return false; // walked off an edge } // check point traces down for dangling corners VectorCopy (trace.endpos, ent->v.origin); if (!SV_CheckBottom (ent)) { if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // entity had floor mostly pulled out from underneath it // and is trying to correct if (relink) SV_LinkEdict (ent, true); return true; } VectorCopy (oldorg, ent->v.origin); return false; } if ( (int)ent->v.flags & FL_PARTIALGROUND ) { // Com_Printf ("back on ground\n"); ent->v.flags = (int)ent->v.flags & ~FL_PARTIALGROUND; } ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); // the move is ok if (relink) SV_LinkEdict (ent, true); return true; }
void updateTargetTracking( GameEntity *ent ) { unsigned long targetcat = 0; trace_t tr; vec3_t forward, endpos, dir; float radarrange = 0; bool buildings = false, groundinstallations = false; GameEntity *test; float cone = 0.0f; // Com_Printf( "updateTargetTracking for %s\n", ent->client->pers.netname ); // what can we lock on ? if( ent->client_->ps_.ONOFF & OO_RADAR_AIR ) { targetcat = CAT_PLANE|CAT_HELO; radarrange = availableVehicles[ent->client_->vehicle_].radarRange; cone = availableVehicles[ent->client_->vehicle_].trackCone; } else if( ent->client_->ps_.ONOFF & OO_RADAR_GROUND ) { targetcat = CAT_GROUND|CAT_BOAT; buildings = true; groundinstallations = true; radarrange = availableVehicles[ent->client_->vehicle_].radarRange2; cone = availableVehicles[ent->client_->vehicle_].trackCone2; } if( !targetcat ) { if( ent->client_->ps_.stats[STAT_LOCKINFO] ) untrack(ent); return; } // weapon range and vehicle direction if( (availableVehicles[ent->client_->vehicle_].cat & CAT_GROUND) || (availableVehicles[ent->client_->vehicle_].cat & CAT_BOAT)) { vec3_t right, up, temp; AngleVectors( ent->client_->ps_.vehicleAngles, forward, right, up ); RotatePointAroundVector( temp, up, forward, ent->client_->ps_.turretAngle ); CrossProduct( up, temp, right ); RotatePointAroundVector( dir, right, temp, ent->client_->ps_.gunAngle ); VectorCopy( dir, forward ); } else { VectorCopy( ent->s.angles, dir ); AngleVectors( dir, forward, 0, 0 ); } // no target yet if( !ent->tracktarget_ ) { VectorMA( ent->r.currentOrigin, radarrange, forward, endpos ); SV_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, endpos, ent->s.number, MASK_ALL, false ); // nothing found if( tr.entityNum == ENTITYNUM_NONE ) return; // found something test = theLevel.getEntity(tr.entityNum);// &g_entities[tr.entityNum]; if( test->s.eType == ET_VEHICLE && test->client_ && (availableVehicles[test->client_->vehicle_].cat & targetcat) ) { track(ent, test); } else if( test->s.eType == ET_MISC_VEHICLE ) { if( (test->s.modelindex == 255 && groundinstallations) || // ground installations (availableVehicles[test->s.modelindex].cat & targetcat) ) track(ent, test); } else { if( buildings ) { if( test->s.eType == ET_EXPLOSIVE && test->takedamage_ ) track(ent, test); } else { return; } } } else { // update existing target vec3_t diff; float dot, dist; int actualcat = 0; if( ent->tracktarget_->s.eType == ET_EXPLOSIVE ) { vec3_t mid; actualcat = CAT_GROUND; VectorAdd( ent->tracktarget_->r.absmax, ent->tracktarget_->r.absmin, mid ); VectorScale( mid, 0.5f, mid ); VectorSubtract( mid, ent->r.currentOrigin, diff ); } else { if( ent->tracktarget_->s.eType == ET_MISC_VEHICLE ) { if( ent->tracktarget_->s.modelindex == 255 )// groundinstallation { actualcat = CAT_GROUND; groundinstallations = true; } else actualcat = availableVehicles[ent->tracktarget_->s.modelindex].cat; } else if( ent->tracktarget_->s.eType == ET_VEHICLE && ent->tracktarget_->client_ ) { actualcat = availableVehicles[ent->tracktarget_->client_->vehicle_].cat; } VectorSubtract( ent->tracktarget_->r.currentOrigin, ent->r.currentOrigin, diff ); } if( !actualcat ) { untrack(ent); return; } // check within range dist = VectorNormalize( diff ); if( dist > radarrange ) { untrack(ent); return; } // check within cone dot = DotProduct( forward, diff ); if( dot < cone ) { untrack(ent); return; } else if( dot < availableWeapons[ent->s.weaponIndex].lockcone ) { if( ent->client_->ps_.stats[STAT_LOCKINFO] & LI_LOCKING ) unlock(ent); ent->locktime_ = theLevel.time_; return; } // check LOS VectorMA( ent->r.currentOrigin, dist, diff, endpos ); SV_Trace( &tr, ent->r.currentOrigin, 0, 0, endpos, ent->s.number, MASK_PLAYERSOLID, false ); if( tr.fraction < 1 && tr.entityNum != ent->s.tracktarget ) { untrack(ent); return; } // only weapons that can lock on if( availableWeapons[ent->s.weaponIndex].type != WT_GUIDEDBOMB && availableWeapons[ent->s.weaponIndex].type != WT_ANTIAIRMISSILE && availableWeapons[ent->s.weaponIndex].type != WT_ANTIGROUNDMISSILE && availableWeapons[ent->s.weaponIndex].type != WT_ANTIRADARMISSILE ) { //untrack(ent); if( ent->client_->ps_.stats[STAT_LOCKINFO] & LI_LOCKING ) unlock(ent); return; } // what can we lock on ? targetcat = 0; if( availableWeapons[ent->s.weaponIndex].type == WT_ANTIAIRMISSILE && (ent->client_->ps_.ONOFF & OO_RADAR_AIR) ) { targetcat = CAT_PLANE|CAT_HELO; } else if( availableWeapons[ent->s.weaponIndex].type != WT_ANTIAIRMISSILE && (ent->client_->ps_.ONOFF & OO_RADAR_GROUND) ) { targetcat = CAT_GROUND|CAT_BOAT; buildings = true; } if( !targetcat ) { unlock(ent); //untrack(ent); return; } else if( !(targetcat & actualcat) ) { unlock(ent); return; } // check time if( theLevel.time_ > ent->locktime_ + availableWeapons[ent->s.weaponIndex].lockdelay ) { ent->client_->ps_.stats[STAT_LOCKINFO] |= LI_LOCKING; } // update target if( ent->tracktarget_->client_ && (ent->client_->ps_.stats[STAT_LOCKINFO] & LI_LOCKING) ) { ent->tracktarget_->client_->ps_.stats[STAT_LOCKINFO] |= LI_BEING_LOCKED; } } }
/** * @brief This test cycles through the list of map definitions found in the maps.ufo script * and tries to find surfaces to stand on with no sound assigned to them. * * This test takes too long to be run every time testall is run. So it's set up almost like a game: * After 5 maps (the first of them is fully checked) with missing sounds, the test stops. * If we manage to 'clean' one of those 5 maps, the next map will show up in the next run. */ TEST_F(FootStepTest, DISABLED_MapDefsFootSteps) { const char* filterId = TEST_GetStringProperty("mapdef-id"); const mapDef_t* md; int mapCount = 0; // the number of maps read int badMapCount = 0; const int skipCount = 20; // to skip the first n mapDefs const int badMapCountMax = 25; // # of maps with missing sounds before this test stops const int mapCountMax = 150; // should of cause be higher than skip + bad const int texCountMax = 30; char texNames[texCountMax][MAX_QPATH]; bool done = false; OBJZERO(texNames); ASSERT_TRUE(csi.numMDs > 0); MapDef_Foreach(md) { if (md->mapTheme[0] == '.') continue; if (filterId && !Q_streq(filterId, md->id)) continue; mapCount++; if (mapCount <= skipCount) continue; /* use a known seed to reproduce an error */ unsigned int seed; if (TEST_ExistsProperty("mapdef-seed")) { seed = TEST_GetLongProperty("mapdef-seed"); } else { seed = (unsigned int) time(nullptr); } srand(seed); int count = 0; Com_Printf("testMapDefsFootSteps: Mapdef %s (seed %u)\n", md->id, seed); const char* asmName = (const char*)LIST_GetByIdx(md->params, 0); SV_Map(true, md->mapTheme, asmName); /* now that we have loaded the map, check all cells for walkable places */ GridBox mBox(sv->mapData.mapBox); // test ALL the cells #if !FOOTSTEPS_FULL if (mapCount >= skipCount + 4) { // after the first 4 maps, reduce the testing area const pos3_t center = {148, 128, 0}; mBox.set(center, center); // the box on the map we're testing mBox.expandXY(10); // just test a few cells around the center of the map mBox.maxs[2] = 2; // and 3 levels high } #endif mBox.clipToMaxBoundaries(); for (int x = mBox.getMinX(); x <= mBox.getMaxX() && !done; x++) { for (int y = mBox.getMinY(); y <= mBox.getMaxY() && !done; y++) { for (int z = mBox.getMinZ(); z <= mBox.getMaxZ(); z++) { const int floor = sv->mapData.routing.getFloor(1, x, y, z); if (floor < 0) // if we have a floor in that cell continue; const AABB noBox(vec3_origin, vec3_origin); // we're doing a point-trace const pos3_t cellPos = {(pos_t)x, (pos_t)y, (pos_t)z}; // the cell in question vec3_t from, to; PosToVec(cellPos, from); // the center of the cell VectorCopy(from, to); // also base for the endpoint of the trace from[2] -= UNIT_HEIGHT / 2; // bottom of the cell from[2] += (floor + 2) * QUANT; // add the height of the floor plus 2 QUANTS to[2] -= 2 * UNIT_HEIGHT; // we should really hit the ground with this const trace_t trace = SV_Trace(Line(from, to), noBox, nullptr, MASK_SOLID); if (!trace.surface) continue; const char* snd = SV_GetFootstepSound(trace.surface->name); if (snd) continue; for (int i = 0; i < texCountMax; ++i) { if (!texNames[i][0]) { // found a free slot ? Q_strncpyz(texNames[i], trace.surface->name, sizeof(texNames[i])); count++; break; } if (Q_streq(trace.surface->name, texNames[i])) // already there ? break; } if (count > texCountMax) { done = true; break; // the z-loop } } } } if (!texNames[0][0]) { Com_Printf("In map %s, asm %s: Nothing detected\n", md->mapTheme, asmName); } else { ++badMapCount; for (int i = 0; i < texCountMax; ++i) { if (texNames[i][0]) { Com_Printf("In map %s, asm %s: No sound for: %s\n", md->mapTheme, asmName, texNames[i]); } } } OBJZERO(texNames); SV_ShutdownGameProgs(); if (done || mapCount >= mapCountMax || badMapCount >= badMapCountMax) break; } }
int SV_FlyMove (edict_t *ent, float time, trace_t *steptrace, int type) { int bumpcount, numbumps; vec3_t dir; float d; int numplanes; vec3_t planes[MAX_CLIP_PLANES]; vec3_t primal_velocity, original_velocity, new_velocity; int i, j; trace_t trace; vec3_t end; float time_left; int blocked; numbumps = 4; blocked = 0; VectorCopy (ent->v.velocity, original_velocity); VectorCopy (ent->v.velocity, primal_velocity); numplanes = 0; time_left = time; for (bumpcount=0 ; bumpcount<numbumps ; bumpcount++) { for (i=0 ; i<3 ; i++) end[i] = ent->v.origin[i] + time_left * ent->v.velocity[i]; trace = SV_Trace (ent->v.origin, ent->v.mins, ent->v.maxs, end, type, ent); if (trace.allsolid) { // entity is trapped in another solid VectorClear (ent->v.velocity); return 3; } if (trace.fraction > 0) { // actually covered some distance VectorCopy (trace.endpos, ent->v.origin); VectorCopy (ent->v.velocity, original_velocity); numplanes = 0; } if (trace.fraction == 1) break; // moved the entire distance if (!trace.e.ent) Host_Error ("SV_FlyMove: !trace.e.ent"); if (trace.plane.normal[2] > 0.7) { blocked |= 1; // floor if (trace.e.ent->v.solid == SOLID_BSP) { ent->v.flags = (int)ent->v.flags | FL_ONGROUND; ent->v.groundentity = EDICT_TO_PROG(trace.e.ent); } } if (!trace.plane.normal[2]) { blocked |= 2; // step if (steptrace) *steptrace = trace; // save for player extrafriction } // // run the impact function // SV_Impact (ent, trace.e.ent); if (!ent->inuse) break; // removed by the impact function time_left -= time_left * trace.fraction; // cliped to another plane if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorClear (ent->v.velocity); return 3; } VectorCopy (trace.plane.normal, planes[numplanes]); numplanes++; // // modify original_velocity so it parallels all of the clip planes // for (i=0 ; i<numplanes ; i++) { ClipVelocity (original_velocity, planes[i], new_velocity, 1); for (j=0 ; j<numplanes ; j++) if (j != i) { if (DotProduct (new_velocity, planes[j]) < 0) break; // not ok } if (j == numplanes) break; } if (i != numplanes) { // go along this plane VectorCopy (new_velocity, ent->v.velocity); } else { // go along the crease if (numplanes != 2) { // Com_Printf ("clip velocity, numplanes == %i\n",numplanes); VectorClear (ent->v.velocity); return 7; } CrossProduct (planes[0], planes[1], dir); d = DotProduct (dir, ent->v.velocity); VectorScale (dir, d, ent->v.velocity); } // // if original velocity is against the original velocity, stop dead // to avoid tiny occilations in sloping corners // if (DotProduct (ent->v.velocity, primal_velocity) <= 0) { VectorClear (ent->v.velocity); return blocked; } } return blocked; }
/* * Tests whether the player entity ent is visible from the point origin. */ qboolean SV_IsPlayerVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, sharedEntity_t *ent, vec3_t diff) { int i,contents_mask,goal_ent,viewer_clnum,ent_clnum,tries; trace_t tr; vec3_t start,end,dir,entangles,angles,temp,forward; sharedEntity_t *viewer_ent; client_t *viewer_cl; // client_t *ent_cl; playerState_t *viewer_ps, *ent_ps; float pitch; viewer_clnum = frame->ps.clientNum; // get the client number of the viewer ent_clnum = ent->s.clientNum; // get the client number of the other player if (viewer_clnum == ent_clnum) { // in case the viewer is the player entity return qtrue; // we don't need to hide us from ourselves } viewer_ps = &frame->ps; ent_ps = SV_GameClientNum(ent_clnum); if (viewer_ps->pm_type != PM_NORMAL) { // if the viewer is dead or spectating return qtrue; // let every entity be visible } if (ent_ps->pm_type != PM_NORMAL || (ent->s.weapon == WP_NONE)) { // if the player entity is dead or spectating return qtrue; } viewer_cl = svs.clients+viewer_clnum; // get the client of the viewer // ent_cl = svs.clients+ent_clnum; // get the client of the other player // if (viewer_clnum > ent_clnum) { // if viewer_clnum > ent_clnum, we have already tested whether ent_clnum is able to see viewer_clnum. // if (ent_cl->tracetimer[viewer_clnum] > sv.time) return qtrue; // and we could assume symmetry of SV_IsPlayerVisibleFromPoint // } if (viewer_cl->tracetimer[ent_clnum] > sv.time+MEMORY+10) { // if the sv.time has been reset viewer_cl->tracetimer[ent_clnum] = sv.time; // reset the tracetimer } else if (viewer_cl->tracetimer[ent_clnum] > (sv.time+MEMORY-10)) { // if we have recently seen this entity, we are lazy and assume it is still visible // Com_Printf(va("client: %i, seen: %i\n", ent_clnum, viewer_cl->tracetimer[ent_clnum])); return qtrue; } goal_ent = SV_NumForGentity(ent); // this might always be the same as ent_clnum viewer_ent = SV_GentityNum(viewer_clnum); contents_mask = CONTENTS_SOLID;// |CONTENTS_BODY will work for doors, but also for windows |CONTENTS_PLAYERCLIP|CONTENTS_SOLID|CONTENTS_MOVER|CONTENTS_PLAYERCLIP // if (seen->v.movetype == MOVETYPE_PUSH ) { //don't cull doors and plats :( // return false; // } // if (sv_antiwallhack.value == 1) //1 only check player models, 2 = check all ents // if (strcmp(pr_strings + seen->v.classname, "player")) // return qfalse; // get camera origin (according to \cg_drawdebug 1) start[0] = origin[0]; start[1] = origin[1]; start[2] = origin[2]+3.0f; VectorCopy(viewer_ps->viewangles, angles); AnglesNormalize180(angles); pitch = angles[PITCH]; angles[PITCH] = 0; angles[ROLL] = 0; AngleVectors(angles, forward, NULL, NULL); VectorScale(forward, (pitch/3.5f), temp); VectorAdd( start, temp, start); // if there is sufficient distance between viewer and player entity, check if player entity is within viewer's field of vision VectorSubtract(ent->r.currentOrigin, start, dir); // VectorAdd(ent->r.currentOrigin,dir,diff);// fill diff VectorCopy(viewer_ent->s.pos.trBase,diff);// fill diff vectoangles(dir, entangles); dir[2]=0; // pretend, players are on the same level (the height should no be taken into account) if (VectorLength(dir) > 1024) {// if it is not within close range (x,y-wise, not z-wise) if (!InFieldOfVision(viewer_ps->viewangles, 60.f, entangles, ent_clnum)) {// If the player entity is not in the field of vision of the viewer // Com_Printf( va("behind: %i vorg: %f,%f,%f vang: %f,%f,%f eorg: %f,%f,%f dir: %f,%f,%f eang: %f,%f,%f ent: %i\n", viewer_clnum,origin[0],origin[1],origin[2],viewer_ps->viewangles[0],viewer_ps->viewangles[1],viewer_ps->viewangles[2],ent->r.currentOrigin[0],ent->r.currentOrigin[1],ent->r.currentOrigin[2],dir[0],dir[1],dir[2],entangles[0],entangles[1],entangles[2],ent_clnum)); return qtrue; // if the player entity is behind the viewer, abstain from any computations (and transmit the entity to hear sounds) // } else { // Com_Printf( va("front: %i vorg: %f,%f,%f vang: %f,%f,%f eorg: %f,%f,%f dir: %f,%f,%f eang: %f,%f,%f ent: %i\n", viewer_clnum,origin[0],origin[1],origin[2],viewer_ps->viewangles[0],viewer_ps->viewangles[1],viewer_ps->viewangles[2],ent->r.currentOrigin[0],ent->r.currentOrigin[1],ent->r.currentOrigin[2],dir[0],dir[1],dir[2],entangles[0],entangles[1],entangles[2],ent_clnum)); } } // aim straight at the head of the entity from our eyes end[0] = ent->r.currentOrigin[0]; end[1] = ent->r.currentOrigin[1]; end[2] = ent->r.currentOrigin[2]+ent->r.maxs[2];// "+3.0f" doesn't do it. "+ent->r.maxs[2]" is at the top of the BBox VectorCopy(ent_ps->viewangles, angles); AnglesNormalize180(angles); pitch = angles[PITCH]; angles[PITCH] = 0; angles[ROLL] = 0; AngleVectors(angles, forward, NULL, NULL); VectorScale(forward, (pitch/3.5f), temp); VectorAdd( end, temp, end); memset (&tr, 0, sizeof(tr)); tr.fraction = 1; // check the head SV_Trace(&tr, start, NULL, NULL, end, viewer_clnum, contents_mask, qfalse); // Com_Printf(va("client: %i, trace: %f\n", ent->s.clientNum, tr.fraction)); if (tr.fraction == 1 || tr.entityNum==goal_ent) {// tr.fraction == 1 || if there is a line of sight to the entity viewer_cl->tracetimer[ent_clnum] = sv.time + MEMORY;// update the trace timer so the entity will be visible the next 200 time units return qtrue;// and signal the entity is visible } // Com_Printf(va("origin(%f %f %f) min(%f %f %f) max(%f %f %f)\n", start[0], start[1], start[2], mins[0],mins[1],mins[2],maxs[0],maxs[1],maxs[2])); // check the last good offset VectorAdd(ent->r.currentOrigin,viewer_cl->lasttrace[ent_clnum],end); SV_Trace(&tr, start, NULL, NULL, end, viewer_clnum, contents_mask, qfalse); // Com_Printf(va("client: %i, trace: %f\n", ent->s.clientNum, tr.fraction)); if (tr.fraction == 1 || tr.entityNum==goal_ent) {// tr.fraction == 1 || if there is a line of sight to the entity viewer_cl->tracetimer[ent_clnum] = sv.time + MEMORY;// update the trace timer so the entity will be visible the next 200 time units return qtrue;// and signal the entity is visible } // random tries = (viewer_cl->tracetimer[ent_clnum]+(MEMORY*20) > sv.time)?8:2;// if we have seen an entity recently, we try hard to locate it again for (i=0; i<tries; i++) {// Even if the head is not visible, other body parts might be. Let's check a few randomly selected points end[0] = ent->r.currentOrigin[0] + offsetrandom(ent->r.mins[0], ent->r.maxs[0]); end[1] = ent->r.currentOrigin[1] + offsetrandom(ent->r.mins[1], ent->r.maxs[1]); end[2] = ent->r.currentOrigin[2] + offsetrandom(ent->r.mins[2], ent->r.maxs[2]+3.f); SV_Trace(&tr, start, NULL, NULL, end, viewer_clnum, contents_mask, qfalse); if (tr.fraction == 1 || tr.entityNum==goal_ent) {// if there is a line of sight to the entity // Com_Printf(va("found ent in %i hits\n", i)); viewer_cl->tracetimer[ent_clnum] = sv.time + MEMORY;// update the trace timer so the entity will be visible the next 200 time units VectorSubtract(end, ent->r.currentOrigin, viewer_cl->lasttrace[ent_clnum]);// remember the offset return qtrue;// and signal the entity is visible } } viewer_cl->lasttrace[ent_clnum][0] = offsetrandom(ent->r.mins[0], ent->r.maxs[0]); viewer_cl->lasttrace[ent_clnum][1] = offsetrandom(ent->r.mins[1], ent->r.maxs[1]); viewer_cl->lasttrace[ent_clnum][2] = offsetrandom(ent->r.mins[2], ent->r.maxs[2]+3.f); return (viewer_cl->tracetimer[ent_clnum] > sv.time);// returns true if the entity was visible within the last 200 time units }
/* ================ G_RunItem ================ */ void G_RunItem( GameEntity *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == -1 ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = theLevel.time_; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); return; } // get current position BG_EvaluateTrajectory( &ent->s.pos, theLevel.time_, origin ); // trace a line from the previous position to the current position if ( ent->clipmask_ ) mask = ent->clipmask_; else mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID; SV_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask, false ); VectorCopy( tr.endpos, ent->r.currentOrigin ); if ( tr.startsolid ) tr.fraction = 0; SV_LinkEntity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) return; // if it is in a nodrop volume, remove it contents = SV_PointContents( ent->r.currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { if (ent->item_ && ent->item_->giType == IT_TEAM) Team_FreeEntity(ent); else ent->freeUp(); return; } G_BounceItem( ent, &tr ); }
/* ======================================================================================================================================= BotImport_Trace ======================================================================================================================================= */ static void BotImport_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) { SV_Trace(bsptrace, start, mins, maxs, end, passent, contentmask, qfalse); }
void SV_PostprocessFrame() { edict_t *ent; int e, i; client_t *cl; guard(SV_PostprocessFrame); for (e = 0; e < ge->num_edicts; e++) { ent = EDICT_NUM(e); // ignore ents without visible models if (ent->svflags & SVF_NOCLIENT) continue; if (ent->s.event == EV_FOOTSTEP || ent->s.event == EV_FALLSHORT) { CVec3 point; point[0] = ent->s.origin[0]; point[1] = ent->s.origin[1]; point[2] = ent->s.origin[2] - 64; trace_t trace; SV_Trace(trace, ent->s.origin, point, ent->bounds, ent, MASK_PLAYERSOLID|MASK_MONSTERSOLID|MASK_WATER); if (trace.fraction < 1) { int footsteptype = trace.surface->material - 1; if (footsteptype < 0)//?? || footsteptype >= MATERIAL_COUNT) // i.e. MATERIAL_SILENT (== 0) or incorrect ent->s.event = 0; // EV_FOOTSTEP ? /* else if (footsteptype > MATERIAL_WATER) { //!! DEBUG !! (and hack) - should not happen developer->integer = 2; Com_DPrintf(S_RED"Bad material sound: idx=%d", footsteptype); ent->s.event = 0; } */ else if (ent->s.event == EV_FOOTSTEP) ent->s.event = EV_FOOTSTEP0 + footsteptype; else ent->s.event = EV_FALLSHORT0 + footsteptype; } } } // work only in deathmatch game, not in single-player or coop game if (sv_deathmatch->integer) //!! should be another variable; campersounds - below!! && sv_campersounds->integer) { unsigned t = appMilliseconds(); for (i = 0, cl = svs.clients; i < sv_maxclients->integer; i++, cl++) { if (cl->state != cs_spawned && cl->state != cs_connected) { cl->last_velocity2 = 0; continue; // dead / not connected player } ent = cl->edict; pmove_state_t *pm = &ent->client->ps.pmove; int prev_vel = cl->last_velocity2; int curr_vel = pm->velocity[2]; cl->last_velocity2 = curr_vel; if (cl->screaming && (curr_vel >= FALLING_SCREAM_VELOCITY1 || prev_vel >= FALLING_SCREAM_VELOCITY1 || pm->pm_type != PM_NORMAL)) // killed { // stop scream SV_StartSoundNew(NULL, ent, CHAN_BODY, SV_SoundIndex("*falling1.wav"), 0.0f, ATTN_NORM, 0); // volume=0 cl->screaming = false; } if (ent->s.event) continue; // have a sound event /* if (pm->pm_type == PM_SPECTATOR) //!! don't works - server doesn't send info about spectators (need another way) { sfxstate = 1; if (cl->sfxstate != sfxstate) { // just changed to spectator cl->sfxstate = sfxstate; cl->nextsfxtime = t + 1000 + (rand()&0x7FF); // ~1-3 sec. continue; } if (cl->nextsfxtime > t) continue; // waiting for sfx time cl->nextsfxtime = t + 1000 + (rand()&0x7FF); ent->s.event = EV_SPECTATOR0 + (rand()&3); appPrintf("%i:SPECT-SFX\n",i); } else*/ if (pm->pm_type == PM_NORMAL) { CVec3 pm_origin; pm_origin[0] = pm->origin[0] / 8.0f; pm_origin[1] = pm->origin[1] / 8.0f; pm_origin[2] = pm->origin[2] / 8.0f; // check for falling sounds if (!cl->screaming) { if (curr_vel < FALLING_SCREAM_VELOCITY1 && prev_vel < FALLING_SCREAM_VELOCITY1) { CVec3 end = pm_origin; end[2] = pm_origin[2] - FALLING_SCREAM_HEIGHT_WATER; trace_t trace; static const CBox bounds = {{-20, -20, -10}, {20, 20, 10}}; SV_Trace(trace, pm_origin, end, bounds, NULL, CONTENTS_WATER); if (trace.fraction == 1.0 && !trace.startsolid) // no water and start not in water { end[2] = pm_origin[2] - FALLING_SCREAM_HEIGHT_SOLID; SV_Trace(trace, pm_origin, end, bounds, NULL, CONTENTS_SOLID|CONTENTS_LAVA); if (trace.fraction == 1.0 || (!trace.ent && trace.plane.normal[2] < 0.5) || trace.contents & CONTENTS_LAVA || trace.surface->flags & SURF_SKY) cl->screaming = true; } } if (cl->screaming) { SV_StartSoundNew(NULL, ent, CHAN_BODY, SV_SoundIndex("*falling1.wav"), 1, ATTN_NORM, 0); continue; } } else if (curr_vel >= FALLING_SCREAM_VELOCITY1 || prev_vel >= FALLING_SCREAM_VELOCITY1) { // stop scream if (cl->screaming) SV_StartSoundNew(NULL, ent, CHAN_BODY, SV_SoundIndex("*falling1.wav"), 0.0, ATTN_NORM, 0); cl->screaming = false; } // check for idle (camper) sounds const CBspLeaf *leaf = CM_FindLeaf(pm_origin); int clust = leaf->cluster; int sfxstate = 2; if (!clust) continue; // possibly map without visinfo - no cluster partition (cannot detect campers with this way) if (clust != cl->lastcluster || cl->sfxstate != sfxstate || (leaf->contents & MASK_WATER)) { // changed cluster or state - not camping cl->sfxstate = sfxstate; cl->lastcluster = clust; cl->nextsfxtime = t + CAMPER_TIMEOUT + rand()%CAMPER_TIMEOUT_DELTA; continue; } if (cl->nextsfxtime > t) continue; // waiting for sfx time cl->nextsfxtime = t + CAMPER_REPEAT + rand()%CAMPER_REPEAT_DELTA; int sfx0, sfxn; if (pm->pm_flags & PMF_DUCKED) { sfx0 = EV_CAMPER0; sfxn = 2; } else { sfx0 = EV_CAMPER0+2; sfxn = 7; } ent->s.event = sfx0 + rand() % sfxn; } else { cl->sfxstate = 0; continue; } } } unguard; }
/* ==================== SV_GameSystemCalls The module is making a system call ==================== */ intptr_t SV_GameSystemCalls(intptr_t * args) { switch (args[0]) { case G_PRINT: Com_Printf("%s", (char *)VMA(1)); return 0; case G_ERROR: Com_Error(ERR_DROP, "%s", (char *)VMA(1)); return 0; case G_MILLISECONDS: return Sys_Milliseconds(); case G_CVAR_REGISTER: Cvar_Register((vmCvar_t*)VMA(1), (char*)VMA(2), (char*)VMA(3), args[4]); return 0; case G_CVAR_UPDATE: Cvar_Update((vmCvar_t*)VMA(1)); return 0; case G_CVAR_SET: Cvar_Set((const char *)VMA(1), (const char *)VMA(2)); return 0; case G_CVAR_VARIABLE_INTEGER_VALUE: return Cvar_VariableIntegerValue((const char *)VMA(1)); case G_CVAR_VARIABLE_STRING_BUFFER: Cvar_VariableStringBuffer((char *)VMA(1), (char*)VMA(2), args[3]); return 0; case G_CVAR_LATCHEDVARIABLESTRINGBUFFER: Cvar_LatchedVariableStringBuffer((char *)VMA(1), (char*)VMA(2), args[3]); return 0; case G_ARGC: return Cmd_Argc(); case G_ARGV: Cmd_ArgvBuffer(args[1], (char*)VMA(2), args[3]); return 0; case G_SEND_CONSOLE_COMMAND: Cbuf_ExecuteText(args[1], (char *)VMA(2)); return 0; case G_FS_FOPEN_FILE: return FS_FOpenFileByMode((char *)VMA(1), (fileHandle_t*)VMA(2), (fsMode_t)args[3]); case G_FS_READ: FS_Read2(VMA(1), args[2], args[3]); return 0; case G_FS_WRITE: return FS_Write(VMA(1), args[2], args[3]); case G_FS_RENAME: FS_Rename((char *)VMA(1), (char *)VMA(2)); return 0; case G_FS_FCLOSE_FILE: FS_FCloseFile(args[1]); return 0; case G_FS_GETFILELIST: return FS_GetFileList((char *)VMA(1), (char *)VMA(2), (char*)VMA(3), args[4]); case G_LOCATE_GAME_DATA: SV_LocateGameData((sharedEntity_t*)VMA(1), args[2], args[3], (playerState_t*)VMA(4), args[5]); return 0; case G_DROP_CLIENT: SV_GameDropClient(args[1], (char*)VMA(2), args[3]); return 0; case G_SEND_SERVER_COMMAND: SV_GameSendServerCommand(args[1], (char*)VMA(2)); return 0; case G_LINKENTITY: SV_LinkEntity((sharedEntity_t*)VMA(1)); return 0; case G_UNLINKENTITY: SV_UnlinkEntity((sharedEntity_t*)VMA(1)); return 0; case G_ENTITIES_IN_BOX: return SV_AreaEntities((float*)VMA(1), (float*)VMA(2), (int*)VMA(3), args[4]); case G_ENTITY_CONTACT: return SV_EntityContact((float*)VMA(1), (float*)VMA(2), (sharedEntity_t*)VMA(3), TT_AABB); case G_ENTITY_CONTACTCAPSULE: return SV_EntityContact((float*)VMA(1), (float*)VMA(2), (sharedEntity_t*)VMA(3), TT_CAPSULE); case G_TRACE: SV_Trace((trace_t*)VMA(1), (float*)VMA(2), (float*)VMA(3), (float*)VMA(4), (float*)VMA(5), args[6], args[7], TT_AABB); return 0; case G_TRACECAPSULE: SV_Trace((trace_t*)VMA(1), (float*)VMA(2), (float*)VMA(3), (float*)VMA(4), (float*)VMA(5), args[6], args[7], TT_CAPSULE); return 0; case G_POINT_CONTENTS: return SV_PointContents((float*)VMA(1), args[2]); case G_SET_BRUSH_MODEL: SV_SetBrushModel((sharedEntity_t*)VMA(1), (char*)VMA(2)); return 0; case G_IN_PVS: return SV_inPVS((float*)VMA(1), (float*)VMA(2)); case G_IN_PVS_IGNORE_PORTALS: return SV_inPVSIgnorePortals((float*)VMA(1), (float*)VMA(2)); case G_SET_CONFIGSTRING: SV_SetConfigstring(args[1], (char*)VMA(2)); return 0; case G_GET_CONFIGSTRING: SV_GetConfigstring(args[1], (char*)VMA(2), args[3]); return 0; case G_SET_CONFIGSTRING_RESTRICTIONS: SV_SetConfigstringRestrictions( args[1], (clientList_t*)VMA(2) ); return 0; case G_SET_USERINFO: SV_SetUserinfo(args[1], (char*)VMA(2)); return 0; case G_GET_USERINFO: SV_GetUserinfo(args[1], (char*)VMA(2), args[3]); return 0; case G_GET_SERVERINFO: SV_GetServerinfo((char*)VMA(1), args[2]); return 0; case G_ADJUST_AREA_PORTAL_STATE: SV_AdjustAreaPortalState((sharedEntity_t*)VMA(1),(bool)args[2]); return 0; case G_AREAS_CONNECTED: return CM_AreasConnected(args[1], args[2]); case G_UPDATE_SHARED_CONFIG: SV_UpdateSharedConfig( args[1], (char*)VMA(2) ); return 0; case G_BOT_ALLOCATE_CLIENT: return SV_BotAllocateClient(args[1]); case G_BOT_FREE_CLIENT: SV_BotFreeClient(args[1]); return 0; case G_GET_USERCMD: SV_GetUsercmd(args[1], (usercmd_t*)VMA(2)); return 0; case G_GET_ENTITY_TOKEN: { const char *s; s = COM_Parse(&sv.entityParsePoint); Q_strncpyz((char*)VMA(1), s, args[2]); if(!sv.entityParsePoint && !s[0]) { return false; } else { return true; } } case G_DEBUG_POLYGON_CREATE: return BotImport_DebugPolygonCreate(args[1], args[2], (vec3_t*)VMA(3)); case G_DEBUG_POLYGON_DELETE: BotImport_DebugPolygonDelete(args[1]); return 0; case G_REAL_TIME: return Com_RealTime((qtime_t*)VMA(1)); case G_SNAPVECTOR: Q_SnapVector((float*)VMA(1)); return 0; case G_SEND_GAMESTAT: SV_MasterGameStat( (char*)VMA(1) ); return 0; case G_ADDCOMMAND: Cmd_AddCommand( (char*)VMA(1), NULL, (char*)VMA(3) ); return 0; case G_REMOVECOMMAND: Cmd_RemoveCommand( (char*)VMA(1) ); return 0; case G_GETTAG: return SV_GetTag(args[1], args[2], (char*)VMA(3), (orientation_t*)VMA(4)); case G_REGISTERTAG: return SV_LoadTag((char*)VMA(1)); case G_REGISTERSOUND: return S_RegisterSound((char*)VMA(1), (bool)args[2]); case G_GET_SOUND_LENGTH: return S_GetSoundLength(args[1]); case G_PARSE_ADD_GLOBAL_DEFINE: return Parse_AddGlobalDefine( (char*)VMA(1) ); case G_PARSE_LOAD_SOURCE: return Parse_LoadSourceHandle( (char*)VMA(1) ); case G_PARSE_FREE_SOURCE: return Parse_FreeSourceHandle( args[1] ); case G_PARSE_READ_TOKEN: return Parse_ReadTokenHandle( args[1], (pc_token_t*)VMA(2) ); case G_PARSE_SOURCE_FILE_AND_LINE: return Parse_SourceFileAndLine( args[1], (char*)VMA(2), (int*)VMA(3) ); case BOTLIB_SETUP: return SV_BotLibSetup(); case BOTLIB_SHUTDOWN: return SV_BotLibShutdown(); case BOTLIB_LIBVAR_SET: return botlib_export->BotLibVarSet((char*)VMA(1), (char*)VMA(2)); case BOTLIB_LIBVAR_GET: return botlib_export->BotLibVarGet((char*)VMA(1), (char*)VMA(2), args[3]); case BOTLIB_PC_ADD_GLOBAL_DEFINE: return Parse_AddGlobalDefine( (char*)VMA(1) ); case BOTLIB_PC_LOAD_SOURCE: return Parse_LoadSourceHandle((char*)VMA(1)); case BOTLIB_PC_FREE_SOURCE: return Parse_FreeSourceHandle(args[1]); case BOTLIB_PC_READ_TOKEN: return Parse_ReadTokenHandle(args[1], (pc_token_t*)VMA(2)); case BOTLIB_PC_SOURCE_FILE_AND_LINE: return Parse_SourceFileAndLine(args[1], (char*)VMA(2), (int*)VMA(3)); case BOTLIB_PC_UNREAD_TOKEN: Parse_UnreadLastTokenHandle(args[1]); return 0; case BOTLIB_START_FRAME: return botlib_export->BotLibStartFrame(VMF(1)); case BOTLIB_LOAD_MAP: return botlib_export->BotLibLoadMap((char*)VMA(1)); case BOTLIB_UPDATENTITY: return botlib_export->BotLibUpdateEntity(args[1], (bot_entitystate_t*)VMA(2)); case BOTLIB_TEST: return botlib_export->Test( args[1], (char*)VMA(2), (float*)VMA(3), (float*)VMA(4) ); case BOTLIB_GET_SNAPSHOT_ENTITY: return SV_BotGetSnapshotEntity(args[1], args[2]); case BOTLIB_GET_CONSOLE_MESSAGE: return SV_BotGetConsoleMessage(args[1], (char*)VMA(2), args[3]); case BOTLIB_USER_COMMAND: SV_ClientThink(&svs.clients[args[1]], (usercmd_t*)VMA(2)); return 0; case BOTLIB_AAS_ENTITY_INFO: botlib_export->aas.AAS_EntityInfo(args[1], (aas_entityinfo_s*)VMA(2)); return 0; case BOTLIB_AAS_INITIALIZED: return botlib_export->aas.AAS_Initialized(); case BOTLIB_AAS_PRESENCE_TYPE_BOUNDING_BOX: botlib_export->aas.AAS_PresenceTypeBoundingBox( args[1], (float*)VMA(2), (float*)VMA(3) ); return 0; case BOTLIB_AAS_TIME: return FloatAsInt(botlib_export->aas.AAS_Time()); case BOTLIB_AAS_SETCURRENTWORLD: botlib_export->aas.AAS_SetCurrentWorld(args[1]); return 0; case BOTLIB_AAS_POINT_AREA_NUM: return botlib_export->aas.AAS_PointAreaNum( (float*)VMA(1) ); case BOTLIB_AAS_TRACE_AREAS: return botlib_export->aas.AAS_TraceAreas( (float*)VMA(1), (float*)VMA(2), (int*)VMA(3), (vec3_t*)VMA(4), args[5] ); case BOTLIB_AAS_BBOX_AREAS: return botlib_export->aas.AAS_BBoxAreas( (float*)VMA(1), (float*)VMA(2), (int*)VMA(3), args[4] ); case BOTLIB_AAS_AREA_CENTER: botlib_export->aas.AAS_AreaCenter(args[1], (float*)VMA(2)); return 0; case BOTLIB_AAS_AREA_WAYPOINT: return botlib_export->aas.AAS_AreaWaypoint(args[1], (float*)VMA(2)); case BOTLIB_AAS_POINT_CONTENTS: return botlib_export->aas.AAS_PointContents((float*)VMA(1)); case BOTLIB_AAS_NEXT_BSP_ENTITY: return botlib_export->aas.AAS_NextBSPEntity(args[1]); case BOTLIB_AAS_VALUE_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_ValueForBSPEpairKey(args[1], (char*)VMA(2), (char*)VMA(3), args[4]); case BOTLIB_AAS_VECTOR_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_VectorForBSPEpairKey(args[1], (char*)VMA(2), (float*)VMA(3)); case BOTLIB_AAS_FLOAT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_FloatForBSPEpairKey(args[1], (char*)VMA(2), (float*)VMA(3)); case BOTLIB_AAS_INT_FOR_BSP_EPAIR_KEY: return botlib_export->aas.AAS_IntForBSPEpairKey(args[1], (char*)VMA(2), (int*)VMA(3)); case BOTLIB_AAS_AREA_REACHABILITY: return botlib_export->aas.AAS_AreaReachability(args[1]); case BOTLIB_AAS_AREA_LADDER: return botlib_export->aas.AAS_AreaLadder(args[1]); case BOTLIB_AAS_AREA_TRAVEL_TIME_TO_GOAL_AREA: return botlib_export->aas.AAS_AreaTravelTimeToGoalArea(args[1], (float*)VMA(2), args[3], args[4]); case BOTLIB_AAS_SWIMMING: return botlib_export->aas.AAS_Swimming((float*)VMA(1)); case BOTLIB_AAS_PREDICT_CLIENT_MOVEMENT: return botlib_export->aas.AAS_PredictClientMovement((aas_clientmove_s*)VMA(1), args[2], (float*)VMA(3), args[4], args[5], (float*)VMA(6), (float*)VMA(7), args[8], args[9], VMF(10), args[11], args[12], args[13]); case BOTLIB_AAS_RT_SHOWROUTE: botlib_export->aas.AAS_RT_ShowRoute((float*)VMA(1), args[2], args[3]); return 0; case BOTLIB_AAS_NEARESTHIDEAREA: return botlib_export->aas.AAS_NearestHideArea(args[1], (float*)VMA(2), args[3], args[4], (float*)VMA(5), args[6], args[7], VMF(8), (float*)VMA(9)); case BOTLIB_AAS_LISTAREASINRANGE: return botlib_export->aas.AAS_ListAreasInRange((float*)VMA(1), args[2], VMF(3), args[4], (vec3_t*)VMA(5), args[6]); case BOTLIB_AAS_AVOIDDANGERAREA: return botlib_export->aas.AAS_AvoidDangerArea((float*)VMA(1), args[2], (float*)VMA(3), args[4], VMF(5), args[6]); case BOTLIB_AAS_RETREAT: return botlib_export->aas.AAS_Retreat((int*)VMA(1), args[2], (float*)VMA(3), args[4], (float*)VMA(5), args[6], VMF(7), VMF(8), args[9]); case BOTLIB_AAS_ALTROUTEGOALS: return botlib_export->aas.AAS_AlternativeRouteGoals((float*)VMA(1), (float*)VMA(2), args[3], (aas_altroutegoal_t*)VMA(4), args[5], args[6]); case BOTLIB_AAS_SETAASBLOCKINGENTITY: botlib_export->aas.AAS_SetAASBlockingEntity((float*)VMA(1), (float*)VMA(2), args[3]); return 0; case BOTLIB_AAS_RECORDTEAMDEATHAREA: botlib_export->aas.AAS_RecordTeamDeathArea((float*)VMA(1), args[2], args[3], args[4], args[5]); return 0; case BOTLIB_EA_SAY: botlib_export->ea.EA_Say(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_SAY_TEAM: botlib_export->ea.EA_SayTeam(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_USE_ITEM: botlib_export->ea.EA_UseItem(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_DROP_ITEM: botlib_export->ea.EA_DropItem(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_USE_INV: botlib_export->ea.EA_UseInv(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_DROP_INV: botlib_export->ea.EA_DropInv(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_GESTURE: botlib_export->ea.EA_Gesture(args[1]); return 0; case BOTLIB_EA_COMMAND: botlib_export->ea.EA_Command(args[1], (char*)VMA(2)); return 0; case BOTLIB_EA_SELECT_WEAPON: botlib_export->ea.EA_SelectWeapon(args[1], args[2]); return 0; case BOTLIB_EA_TALK: botlib_export->ea.EA_Talk(args[1]); return 0; case BOTLIB_EA_ATTACK: botlib_export->ea.EA_Attack(args[1]); return 0; case BOTLIB_EA_RELOAD: botlib_export->ea.EA_Reload(args[1]); return 0; case BOTLIB_EA_USE: botlib_export->ea.EA_Use(args[1]); return 0; case BOTLIB_EA_RESPAWN: botlib_export->ea.EA_Respawn(args[1]); return 0; case BOTLIB_EA_JUMP: botlib_export->ea.EA_Jump(args[1]); return 0; case BOTLIB_EA_DELAYED_JUMP: botlib_export->ea.EA_DelayedJump(args[1]); return 0; case BOTLIB_EA_CROUCH: botlib_export->ea.EA_Crouch(args[1]); return 0; case BOTLIB_EA_WALK: botlib_export->ea.EA_Walk(args[1]); return 0; case BOTLIB_EA_MOVE_UP: botlib_export->ea.EA_MoveUp(args[1]); return 0; case BOTLIB_EA_MOVE_DOWN: botlib_export->ea.EA_MoveDown(args[1]); return 0; case BOTLIB_EA_MOVE_FORWARD: botlib_export->ea.EA_MoveForward(args[1]); return 0; case BOTLIB_EA_MOVE_BACK: botlib_export->ea.EA_MoveBack(args[1]); return 0; case BOTLIB_EA_MOVE_LEFT: botlib_export->ea.EA_MoveLeft(args[1]); return 0; case BOTLIB_EA_MOVE_RIGHT: botlib_export->ea.EA_MoveRight(args[1]); return 0; case BOTLIB_EA_MOVE: botlib_export->ea.EA_Move(args[1], (float*)VMA(2), VMF(3)); return 0; case BOTLIB_EA_VIEW: botlib_export->ea.EA_View(args[1], (float*)VMA(2)); return 0; case BOTLIB_EA_PRONE: botlib_export->ea.EA_Prone(args[1]); return 0; case BOTLIB_EA_END_REGULAR: botlib_export->ea.EA_EndRegular(args[1], VMF(2)); return 0; case BOTLIB_EA_GET_INPUT: botlib_export->ea.EA_GetInput(args[1], VMF(2), (bot_input_t*)VMA(3)); return 0; case BOTLIB_EA_RESET_INPUT: botlib_export->ea.EA_ResetInput(args[1], (bot_input_t*)VMA(2)); return 0; case BOTLIB_AI_LOAD_CHARACTER: return botlib_export->ai.BotLoadCharacter((char*)VMA(1), args[2]); case BOTLIB_AI_FREE_CHARACTER: botlib_export->ai.BotFreeCharacter(args[1]); return 0; case BOTLIB_AI_CHARACTERISTIC_FLOAT: return FloatAsInt(botlib_export->ai.Characteristic_Float(args[1], args[2])); case BOTLIB_AI_CHARACTERISTIC_BFLOAT: return FloatAsInt(botlib_export->ai.Characteristic_BFloat(args[1], args[2], VMF(3), VMF(4))); case BOTLIB_AI_CHARACTERISTIC_INTEGER: return botlib_export->ai.Characteristic_Integer(args[1], args[2]); case BOTLIB_AI_CHARACTERISTIC_BINTEGER: return botlib_export->ai.Characteristic_BInteger(args[1], args[2], args[3], args[4]); case BOTLIB_AI_CHARACTERISTIC_STRING: botlib_export->ai.Characteristic_String(args[1], args[2], (char*)VMA(3), args[4]); return 0; case BOTLIB_AI_ALLOC_CHAT_STATE: return botlib_export->ai.BotAllocChatState(); case BOTLIB_AI_FREE_CHAT_STATE: botlib_export->ai.BotFreeChatState(args[1]); return 0; case BOTLIB_AI_QUEUE_CONSOLE_MESSAGE: botlib_export->ai.BotQueueConsoleMessage(args[1], args[2], (char*)VMA(3)); return 0; case BOTLIB_AI_REMOVE_CONSOLE_MESSAGE: botlib_export->ai.BotRemoveConsoleMessage(args[1], args[2]); return 0; case BOTLIB_AI_NEXT_CONSOLE_MESSAGE: return botlib_export->ai.BotNextConsoleMessage(args[1], (bot_consolemessage_s*)VMA(2)); case BOTLIB_AI_NUM_CONSOLE_MESSAGE: return botlib_export->ai.BotNumConsoleMessages(args[1]); case BOTLIB_AI_INITIAL_CHAT: botlib_export->ai.BotInitialChat(args[1], (char*)VMA(2), args[3], (char*)VMA(4), (char*)VMA(5), (char*)VMA(6), (char*)VMA(7), (char*)VMA(8), (char*)VMA(9), (char*)VMA(10), (char*)VMA(11)); return 0; case BOTLIB_AI_NUM_INITIAL_CHATS: return botlib_export->ai.BotNumInitialChats(args[1], (char*)VMA(2)); case BOTLIB_AI_REPLY_CHAT: return botlib_export->ai.BotReplyChat(args[1], (char*)VMA(2), args[3], args[4], (char*)VMA(5), (char*)VMA(6), (char*)VMA(7), (char*)VMA(8), (char*)VMA(9), (char*)VMA(10), (char*)VMA(11), (char*)VMA(12)); case BOTLIB_AI_CHAT_LENGTH: return botlib_export->ai.BotChatLength(args[1]); case BOTLIB_AI_ENTER_CHAT: botlib_export->ai.BotEnterChat(args[1], args[2], args[3]); return 0; case BOTLIB_AI_GET_CHAT_MESSAGE: botlib_export->ai.BotGetChatMessage(args[1], (char*)VMA(2), args[3]); return 0; case BOTLIB_AI_STRING_CONTAINS: return botlib_export->ai.StringContains((char*)VMA(1), (char*)VMA(2), args[3]); case BOTLIB_AI_FIND_MATCH: return botlib_export->ai.BotFindMatch((char*)VMA(1), (bot_match_s*)VMA(2), args[3]); case BOTLIB_AI_MATCH_VARIABLE: botlib_export->ai.BotMatchVariable((bot_match_s*)VMA(1), args[2], (char*)VMA(3), args[4]); return 0; case BOTLIB_AI_UNIFY_WHITE_SPACES: botlib_export->ai.UnifyWhiteSpaces((char*)VMA(1)); return 0; case BOTLIB_AI_REPLACE_SYNONYMS: botlib_export->ai.BotReplaceSynonyms((char*)VMA(1), args[2]); return 0; case BOTLIB_AI_LOAD_CHAT_FILE: return botlib_export->ai.BotLoadChatFile(args[1], (char*)VMA(2), (char*)VMA(3)); case BOTLIB_AI_SET_CHAT_GENDER: botlib_export->ai.BotSetChatGender(args[1], args[2]); return 0; case BOTLIB_AI_SET_CHAT_NAME: botlib_export->ai.BotSetChatName(args[1], (char*)VMA(2)); return 0; case BOTLIB_AI_RESET_GOAL_STATE: botlib_export->ai.BotResetGoalState(args[1]); return 0; case BOTLIB_AI_RESET_AVOID_GOALS: botlib_export->ai.BotResetAvoidGoals(args[1]); return 0; case BOTLIB_AI_REMOVE_FROM_AVOID_GOALS: botlib_export->ai.BotRemoveFromAvoidGoals(args[1], args[2]); return 0; case BOTLIB_AI_PUSH_GOAL: botlib_export->ai.BotPushGoal(args[1], (bot_goal_s*)VMA(2)); return 0; case BOTLIB_AI_POP_GOAL: botlib_export->ai.BotPopGoal(args[1]); return 0; case BOTLIB_AI_EMPTY_GOAL_STACK: botlib_export->ai.BotEmptyGoalStack(args[1]); return 0; case BOTLIB_AI_DUMP_AVOID_GOALS: botlib_export->ai.BotDumpAvoidGoals(args[1]); return 0; case BOTLIB_AI_DUMP_GOAL_STACK: botlib_export->ai.BotDumpGoalStack(args[1]); return 0; case BOTLIB_AI_GOAL_NAME: botlib_export->ai.BotGoalName(args[1], (char*)VMA(2), args[3]); return 0; case BOTLIB_AI_GET_TOP_GOAL: return botlib_export->ai.BotGetTopGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_GET_SECOND_GOAL: return botlib_export->ai.BotGetSecondGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_CHOOSE_LTG_ITEM: return botlib_export->ai.BotChooseLTGItem(args[1], (float*)VMA(2), (int*)VMA(3), args[4]); case BOTLIB_AI_CHOOSE_NBG_ITEM: return botlib_export->ai.BotChooseNBGItem(args[1], (float*)VMA(2), (int*)VMA(3), args[4], (bot_goal_s*)VMA(5), VMF(6)); case BOTLIB_AI_TOUCHING_GOAL: return botlib_export->ai.BotTouchingGoal((float*)VMA(1), (bot_goal_s*)VMA(2)); case BOTLIB_AI_ITEM_GOAL_IN_VIS_BUT_NOT_VISIBLE: return botlib_export->ai.BotItemGoalInVisButNotVisible(args[1], (float*)VMA(2), (float*)VMA(3), (bot_goal_s*)VMA(4)); case BOTLIB_AI_GET_LEVEL_ITEM_GOAL: return botlib_export->ai.BotGetLevelItemGoal(args[1], (char*)VMA(2), (bot_goal_s*)VMA(3)); case BOTLIB_AI_GET_NEXT_CAMP_SPOT_GOAL: return botlib_export->ai.BotGetNextCampSpotGoal(args[1], (bot_goal_s*)VMA(2)); case BOTLIB_AI_GET_MAP_LOCATION_GOAL: return botlib_export->ai.BotGetMapLocationGoal((char*)VMA(1), (bot_goal_s*)VMA(2)); case BOTLIB_AI_AVOID_GOAL_TIME: return FloatAsInt(botlib_export->ai.BotAvoidGoalTime(args[1], args[2])); case BOTLIB_AI_INIT_LEVEL_ITEMS: botlib_export->ai.BotInitLevelItems(); return 0; case BOTLIB_AI_UPDATE_ENTITY_ITEMS: botlib_export->ai.BotUpdateEntityItems(); return 0; case BOTLIB_AI_LOAD_ITEM_WEIGHTS: return botlib_export->ai.BotLoadItemWeights(args[1], (char*)VMA(2)); case BOTLIB_AI_FREE_ITEM_WEIGHTS: botlib_export->ai.BotFreeItemWeights(args[1]); return 0; case BOTLIB_AI_INTERBREED_GOAL_FUZZY_LOGIC: botlib_export->ai.BotInterbreedGoalFuzzyLogic(args[1], args[2], args[3]); return 0; case BOTLIB_AI_SAVE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotSaveGoalFuzzyLogic(args[1], (char*)VMA(2)); return 0; case BOTLIB_AI_MUTATE_GOAL_FUZZY_LOGIC: botlib_export->ai.BotMutateGoalFuzzyLogic(args[1], VMF(2)); return 0; case BOTLIB_AI_ALLOC_GOAL_STATE: return botlib_export->ai.BotAllocGoalState(args[1]); case BOTLIB_AI_FREE_GOAL_STATE: botlib_export->ai.BotFreeGoalState(args[1]); return 0; case BOTLIB_AI_RESET_MOVE_STATE: botlib_export->ai.BotResetMoveState(args[1]); return 0; case BOTLIB_AI_MOVE_TO_GOAL: botlib_export->ai.BotMoveToGoal((bot_moveresult_s*)VMA(1), args[2], (bot_goal_s*)VMA(3), args[4]); return 0; case BOTLIB_AI_MOVE_IN_DIRECTION: return botlib_export->ai.BotMoveInDirection(args[1], (float*)VMA(2), VMF(3), args[4]); case BOTLIB_AI_RESET_AVOID_REACH: botlib_export->ai.BotResetAvoidReach(args[1]); return 0; case BOTLIB_AI_RESET_LAST_AVOID_REACH: botlib_export->ai.BotResetLastAvoidReach(args[1]); return 0; case BOTLIB_AI_REACHABILITY_AREA: return botlib_export->ai.BotReachabilityArea((float*)VMA(1), args[2]); case BOTLIB_AI_MOVEMENT_VIEW_TARGET: return botlib_export->ai.BotMovementViewTarget(args[1], (bot_goal_s*)VMA(2), args[3], VMF(4), (float*)VMA(5)); case BOTLIB_AI_PREDICT_VISIBLE_POSITION: return botlib_export->ai.BotPredictVisiblePosition((float*)VMA(1), args[2], (bot_goal_s*)VMA(3), args[4], (vec_t*)VMA(5)); case BOTLIB_AI_ALLOC_MOVE_STATE: return botlib_export->ai.BotAllocMoveState(); case BOTLIB_AI_FREE_MOVE_STATE: botlib_export->ai.BotFreeMoveState(args[1]); return 0; case BOTLIB_AI_INIT_MOVE_STATE: botlib_export->ai.BotInitMoveState(args[1], (bot_initmove_s*)VMA(2)); return 0; case BOTLIB_AI_INIT_AVOID_REACH: botlib_export->ai.BotInitAvoidReach(args[1]); return 0; case BOTLIB_AI_CHOOSE_BEST_FIGHT_WEAPON: return botlib_export->ai.BotChooseBestFightWeapon(args[1], (int*)VMA(2)); case BOTLIB_AI_GET_WEAPON_INFO: botlib_export->ai.BotGetWeaponInfo(args[1], args[2], (weaponinfo_s*)VMA(3)); return 0; case BOTLIB_AI_LOAD_WEAPON_WEIGHTS: return botlib_export->ai.BotLoadWeaponWeights(args[1], (char*)VMA(2)); case BOTLIB_AI_ALLOC_WEAPON_STATE: return botlib_export->ai.BotAllocWeaponState(); case BOTLIB_AI_FREE_WEAPON_STATE: botlib_export->ai.BotFreeWeaponState(args[1]); return 0; case BOTLIB_AI_RESET_WEAPON_STATE: botlib_export->ai.BotResetWeaponState(args[1]); return 0; case BOTLIB_AI_GENETIC_PARENTS_AND_CHILD_SELECTION: return botlib_export->ai.GeneticParentsAndChildSelection(args[1], (float*)VMA(2), (int*)VMA(3), (int*)VMA(4), (int*)VMA(5)); case G_ADD_PHYSICS_ENTITY: #ifdef USE_PHYSICS CMod_PhysicsAddEntity((sharedEntity_t*)VMA(1)); #endif return 0; case G_ADD_PHYSICS_STATIC: #ifdef USE_PHYSICS CMod_PhysicsAddStatic((sharedEntity_t*)VMA(1)); #endif return 0; case TRAP_MEMSET: memset(VMA(1), args[2], args[3]); return 0; case TRAP_MEMCPY: memcpy(VMA(1), VMA(2), args[3]); return 0; case TRAP_STRNCPY: return (intptr_t)strncpy( (char*)VMA( 1 ), (char*)VMA( 2 ), args[3] ); case TRAP_SIN: return FloatAsInt(sin(VMF(1))); case TRAP_COS: return FloatAsInt(cos(VMF(1))); case TRAP_ATAN2: return FloatAsInt(atan2(VMF(1), VMF(2))); case TRAP_SQRT: return FloatAsInt(sqrt(VMF(1))); case TRAP_MATRIXMULTIPLY: AxisMultiply((vec3_t*)VMA(1), (vec3_t*)VMA(2), (vec3_t*)VMA(3)); return 0; case TRAP_ANGLEVECTORS: AngleVectors((vec_t*)VMA(1), (vec_t*)VMA(2), (vec_t*)VMA(3), (vec_t*)VMA(4)); return 0; case TRAP_PERPENDICULARVECTOR: PerpendicularVector((vec_t*)VMA(1), (vec_t*)VMA(2)); return 0; case TRAP_FLOOR: return FloatAsInt(floor(VMF(1))); case TRAP_CEIL: return FloatAsInt(ceil(VMF(1))); case G_SENDMESSAGE: SV_SendBinaryMessage(args[1], (char*)VMA(2), args[3]); return 0; case G_MESSAGESTATUS: return SV_BinaryMessageStatus(args[1]); #if defined(ET_MYSQL) case G_SQL_RUNQUERY: return OW_RunQuery( (char*)VMA(1) ); case G_SQL_FINISHQUERY: OW_FinishQuery( args[1] ); return 0; case G_SQL_NEXTROW: return OW_NextRow( args[1] ); case G_SQL_ROWCOUNT: return OW_RowCount( args[1] ); case G_SQL_GETFIELDBYID: OW_GetFieldByID( args[1], args[2], (char*)VMA(3), args[4] ); return 0; case G_SQL_GETFIELDBYNAME: OW_GetFieldByName( args[1], (char*)VMA(2), (char*)VMA(3), args[4] ); return 0; case G_SQL_GETFIELDBYID_INT: return OW_GetFieldByID_int( args[1], args[2] ); case G_SQL_GETFIELDBYNAME_INT: return OW_GetFieldByName_int( args[1], (char*)VMA(2) ); case G_SQL_FIELDCOUNT: return OW_FieldCount( args[1] ); case G_SQL_CLEANSTRING: OW_CleanString( (char*)VMA(1), (char*)VMA(2), args[3] ); return 0; #endif case G_RSA_GENMSG: return SV_RSAGenMsg( (char*)VMA(1), (char*)VMA(2), (char*)VMA(3) ); default: Com_Error( ERR_DROP, "Bad game system trap: %ld", (long int) args[0] ); } return -1; }
static int predict_slide_move(sharedEntity_t *ent, float frametime, trajectory_t *tr, vec3_t result) { int count, numplanes = 0, i, j, k; float d, time_left = frametime, into; vec3_t planes[MAX_CLIP_PLANES], velocity, origin, clipVelocity, endVelocity, endClipVelocity, dir, end; trace_t trace; VectorCopy(tr->trBase, origin); origin[2] += Z_ADJUST; // move it off the floor VectorCopy(tr->trDelta, velocity); VectorCopy(tr->trDelta, endVelocity); for (count = 0; count < NUMBUMPS; count++) { // calculate position we are trying to move to VectorMA(origin, time_left, velocity, end); // see if we can make it there SV_Trace(&trace, origin, ent->r.mins, ent->r.maxs, end, ent->s.number, CONTENTS_SOLID, qfalse); if (trace.allsolid) { // entity is completely trapped in another solid VectorCopy(origin, result); return 0; } if (trace.fraction > 0.99) // moved the entire distance { VectorCopy(trace.endpos, result); return 1; } if (trace.fraction > 0) // covered some distance { VectorCopy(trace.endpos, origin); } time_left -= time_left * trace.fraction; if (numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen VectorCopy(origin, result); return 0; } // if this is the same plane we hit before, nudge velocity // out along it, which fixes some epsilon issues with // non-axial planes for (i = 0; i < numplanes; i++) { if (DotProduct(trace.plane.normal, planes[i]) > 0.99) { VectorAdd(trace.plane.normal, velocity, velocity); break; } } if (i < numplanes) { continue; } VectorCopy(trace.plane.normal, planes[numplanes]); numplanes++; // modify velocity so it parallels all of the clip planes // find a plane that it enters for (i = 0; i < numplanes; i++) { into = DotProduct(velocity, planes[i]); if (into >= 0.1) // move doesn't interact with the plane { continue; } // slide along the plane predict_clip_velocity(velocity, planes[i], clipVelocity); // slide along the plane predict_clip_velocity(endVelocity, planes[i], endClipVelocity); // see if there is a second plane that the new move enters for (j = 0; j < numplanes; j++) { if (j == i) { continue; } if (DotProduct(clipVelocity, planes[j]) >= 0.1) // move doesn't interact with the plane { continue; } // try clipping the move to the plane predict_clip_velocity(clipVelocity, planes[j], clipVelocity); predict_clip_velocity(endClipVelocity, planes[j], endClipVelocity); // see if it goes back into the first clip plane if (DotProduct(clipVelocity, planes[i]) >= 0) { continue; } // slide the original velocity along the crease vec3_cross(planes[i], planes[j], dir); vec3_norm(dir); d = DotProduct(dir, velocity); VectorScale(dir, d, clipVelocity); vec3_cross(planes[i], planes[j], dir); vec3_norm(dir); d = DotProduct(dir, endVelocity); VectorScale(dir, d, endClipVelocity); // see if there is a third plane the new move enters for (k = 0; k < numplanes; k++) { if (k == i || k == j) { continue; } if (DotProduct(clipVelocity, planes[k]) >= 0.1) // move doesn't interact with the plane { continue; } // stop dead at a tripple plane interaction VectorCopy(origin, result); return 1; } } // if we have fixed all interactions, try another move VectorCopy(clipVelocity, velocity); VectorCopy(endClipVelocity, endVelocity); break; } } VectorCopy(origin, result); if (count == 0) { return 1; } return 0; }
/************************************************************************************************ * CRMBSPInstance::Spawn * spawns a bsp into the world using the previously aquired origin * * inputs: * none * * return: * none * ************************************************************************************************/ bool CRMBSPInstance::Spawn ( CRandomTerrain* terrain, qboolean IsServer) { #ifndef PRE_RELEASE_DEMO // TEntity* ent; float yaw; char temp[10000]; char *savePtr; vec3_t origin; vec3_t notmirrored; float water_level = terrain->GetLandScape()->GetWaterHeight(); const vec3_t& terxelSize = terrain->GetLandScape()->GetTerxelSize ( ); const vec3pair_t& bounds = terrain->GetLandScape()->GetBounds(); // If this entity somehow lost its collision flag then boot it if ( !GetArea().IsCollisionEnabled ( ) ) { return false; } // copy out the unmirrored version VectorCopy(GetOrigin(), notmirrored); // we want to mirror it before determining the Z value just in case the landscape isn't perfectly mirrored if (mMirror) { GetOrigin()[0] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][0] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][0] - GetOrigin()[0]; GetOrigin()[1] = TheRandomMissionManager->GetLandScape()->GetBounds()[0][1] + TheRandomMissionManager->GetLandScape()->GetBounds()[1][1] - GetOrigin()[1]; } // Align the instance to the center of a terxel GetOrigin ( )[0] = bounds[0][0] + (int)((GetOrigin ( )[0] - bounds[0][0] + terxelSize[0] / 2) / terxelSize[0]) * terxelSize[0]; GetOrigin ( )[1] = bounds[0][1] + (int)((GetOrigin ( )[1] - bounds[0][1] + terxelSize[1] / 2) / terxelSize[1]) * terxelSize[1]; // Make sure the bsp is resting on the ground, not below or above it // NOTE: This check is basically saying "is this instance not a bridge", because when instances are created they are all // placed above the world's Z boundary, EXCEPT FOR BRIDGES. So this call to GetWorldHeight will move all other instances down to // ground level except bridges if ( GetOrigin()[2] > terrain->GetBounds()[1][2] ) { if( GetFlattenRadius() ) { terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), false ); GetOrigin()[2] += 5; } else if (IsServer) { // if this instance does not flatten the ground around it, do a trace to more accurately determine its Z value trace_t tr; vec3_t end; vec3_t start; VectorCopy(GetOrigin(), end); VectorCopy(GetOrigin(), start); // start the trace below the top height of the landscape start[2] = TheRandomMissionManager->GetLandScape()->GetBounds()[1][2] - 1; // end the trace at the bottom of the world end[2] = MIN_WORLD_COORD; memset ( &tr, 0, sizeof ( tr ) ); SV_Trace( &tr, start, vec3_origin, vec3_origin, end, ENTITYNUM_NONE, CONTENTS_TERRAIN|CONTENTS_SOLID, G2_NOCOLLIDE, 0); //qfalse, 0, 10 ); if( !(tr.contents & CONTENTS_TERRAIN) || (tr.fraction == 1.0) ) { if ( 0 ) assert(0); // this should never happen // restore the unmirrored origin VectorCopy( notmirrored, GetOrigin() ); // don't spawn return false; } // assign the Z-value to wherever it hit the terrain GetOrigin()[2] = tr.endpos[2]; // lower it a little, otherwise the bottom of the instance might be exposed if on some weird sloped terrain GetOrigin()[2] -= 16; // FIXME: would it be better to use a number related to the instance itself like 1/5 it's height or something... } } else { terrain->GetLandScape()->GetWorldHeight ( GetOrigin(), GetBounds ( ), true ); } // save away the origin VectorCopy(GetOrigin(), origin); // make sure not to spawn if in water if (!HasObjective() && GetOrigin()[2] < water_level) return false; // restore the origin VectorCopy(origin, GetOrigin()); if (mMirror) { // change blue things to red for symmetric maps if (strlen(mFilter) > 0) { char * blue = strstr(mFilter,"blue"); if (blue) { blue[0] = (char) 0; strcat(mFilter, "red"); SetSide(SIDE_RED); } } if (strlen(mTeamFilter) > 0) { char * blue = strstr(mTeamFilter,"blue"); if (blue) { strcpy(mTeamFilter, "red"); SetSide(SIDE_RED); } } yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle) + 180; } else { yaw = RAD2DEG(mArea->GetAngle() + mBaseAngle); } /* if( TheRandomMissionManager->GetMission()->GetSymmetric() ) { vec3_t diagonal; vec3_t lineToPoint; vec3_t mins; vec3_t maxs; vec3_t point; vec3_t vProj; vec3_t vec; float distance; VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[1], maxs ); VectorCopy( TheRandomMissionManager->GetLandScape()->GetBounds()[0], mins ); VectorCopy( GetOrigin(), point ); mins[2] = maxs[2] = point[2] = 0; VectorSubtract( point, mins, lineToPoint ); VectorSubtract( maxs, mins, diagonal); VectorNormalize(diagonal); VectorMA( mins, DotProduct(lineToPoint, diagonal), diagonal, vProj); VectorSubtract(point, vProj, vec ); distance = VectorLength(vec); // if an instance is too close to the imaginary diagonal that cuts the world in half, don't spawn it // otherwise you can get overlapping instances if( distance < GetSpacingRadius() ) { #ifdef _DEBUG mAutomapSymbol = AUTOMAP_END; #endif if( !HasObjective() ) { return false; } } } */ // Spawn in the bsp model sprintf(temp, "{\n" "\"classname\" \"misc_bsp\"\n" "\"bspmodel\" \"%s\"\n" "\"origin\" \"%f %f %f\"\n" "\"angles\" \"0 %f 0\"\n" "\"filter\" \"%s\"\n" "\"teamfilter\" \"%s\"\n" "\"spacing\" \"%d\"\n" "\"flatten\" \"%d\"\n" "}\n", mBsp, GetOrigin()[0], GetOrigin()[1], GetOrigin()[2], AngleNormalize360(yaw), mFilter, mTeamFilter, (int)GetSpacingRadius(), (int)GetFlattenRadius() ); if (IsServer) { // only allow for true spawning on the server savePtr = sv.entityParsePoint; sv.entityParsePoint = temp; // VM_Call( cgvm, GAME_SPAWN_RMG_ENTITY ); // char *s; int bufferSize = 1024; char buffer[1024]; // s = COM_Parse( (const char **)&sv.entityParsePoint ); Q_strncpyz( buffer, sv.entityParsePoint, bufferSize ); if ( sv.entityParsePoint && sv.entityParsePoint[0] ) { ge->GameSpawnRMGEntity(sv.entityParsePoint); } sv.entityParsePoint = savePtr; } #ifndef DEDICATED DrawAutomapSymbol(); #endif Com_DPrintf( "RMG: Building '%s' spawned at (%f %f %f)\n", mBsp, GetOrigin()[0], GetOrigin()[1], GetOrigin()[2] ); // now restore the instances un-mirrored origin // NOTE: all this origin flipping, setting the side etc... should be done when mMirror is set // because right after this function is called, mMirror is set to 0 but all the instance data is STILL MIRRORED -- not good VectorCopy(notmirrored, GetOrigin()); #endif // PRE_RELEASE_DEMO return true; }
// hooked game trace function void SV_TraceHook(trace0_t &trace, const CVec3 &start, const CVec3 *mins, const CVec3 *maxs, const CVec3 &end, edict_t *passedict, int contentmask) { guard(SV_TraceHook); if (bspfile.suspendedItems && passedict) { // hack for game's droptofloor(): items, listed in bspfile.suspendedItems (for Q3A maps), // should 'fly' in the air; droptofloor always calls trace() with following params: // trace(ent->s.origin, ent->mins, ent->maxs, dest, ent, MASK_SOLID) // ent.solid == SOLID_TRIGGER (but: when item picked up, it still "thinking" with // droptofloor(), but with SOLID_NOT; see SetRespawn()) // dest = ent->s.origin + (0,0,-128) // Also, check for SV_PushEntity(): // trace(start, ent->mins, ent->maxs, end, ent, mask) // start == ent->s.origin // end == start+delta // mask == ent->clipmask or MASK_SOLID (clipmask used for clients/monsters only?) // game source reference: g_phys.c if (start == passedict->s.origin && // compare vectors, not pointers mins == &passedict->bounds.mins && maxs == &passedict->bounds.maxs && contentmask == MASK_SOLID && (passedict->solid == SOLID_TRIGGER || passedict->solid == SOLID_NOT) && // trigger entity end[0] == start[0] && end[1] == start[1] && end[2] < start[2]) // falling down { // check list ... for (const originInfo_t *info = bspfile.suspendedItems; info; info = info->next) #define CMP(n) (fabs(start[n] - info->origin[n]) < 0.5f) if (CMP(0) && CMP(1) && CMP(2)) { trace.startsolid = false; trace.endpos = start; trace.fraction = 1.0f; // disable SV_Impact() return; } #undef CMP } } trace_skipAlpha = true; //!! hack for kingpin CONTENTS_ALPHA if (mins || maxs) trace_skipAlpha = false; CBox bounds = nullBox; if (mins) bounds.mins = *mins; // required for game; may be NULL if (maxs) bounds.maxs = *maxs; // extended trace trace_t trace1; SV_Trace(trace1, start, end, bounds, passedict, contentmask); // copy base fields to trace0_t trace = trace1; trace_skipAlpha = false; //!! if (!sv_extProtocol->integer) return; if (mins || maxs) { shot_skip: shotLevel = 0; return; } static edict_t *ent; //?? // for details, look game/g_weapon.c :: fire_lead() if (contentmask == MASK_SHOT && ((unsigned)passedict) + 4 == (unsigned)&start) { if (ent == passedict && end == shotStart) goto shot_skip; // shotgun shot shotLevel = 1; shotStart = end; ent = passedict; } else if (shotLevel == 1) { if (passedict != ent || start != shotStart || (contentmask != (MASK_SHOT|MASK_WATER) && contentmask != MASK_SHOT)) goto shot_skip; shotEnd = end; shotLevel = 2; } else if (shotLevel > 1) { if (passedict != ent) goto shot_skip; } unguard; }