static void SV_AddEntitiesVisibleFromPoint( vec3_t origin, clientSnapshot_t *frame, snapshotEntityNumbers_t *eNums, qboolean portal ) { int e, i; sharedEntity_t *ent; svEntity_t *svEnt; int l; int clientarea, clientcluster; int leafnum; int c_fullsend; byte *clientpvs; byte *bitvector; vec3_t difference; float length, radius; // during an error shutdown message we may need to transmit // the shutdown message after the server has shutdown, so // specfically check for it if ( !sv.state ) { return; } leafnum = CM_PointLeafnum (origin); clientarea = CM_LeafArea (leafnum); clientcluster = CM_LeafCluster (leafnum); // calculate the visible areas frame->areabytes = CM_WriteAreaBits( frame->areabits, clientarea ); clientpvs = CM_ClusterPVS (clientcluster); c_fullsend = 0; for ( e = 0 ; e < sv.num_entities ; e++ ) { ent = SV_GentityNum(e); // never send entities that aren't linked in if ( !ent->r.linked ) { continue; } if (ent->s.eFlags & EF_PERMANENT) { // he's permanent, so don't send him down! continue; } if (ent->s.number != e) { Com_DPrintf ("FIXING ENT->S.NUMBER!!!\n"); ent->s.number = e; } // entities can be flagged to explicitly not be sent to the client if ( ent->r.svFlags & SVF_NOCLIENT ) { continue; } // entities can be flagged to be sent to only one client if ( ent->r.svFlags & SVF_SINGLECLIENT ) { if ( ent->r.singleClient != frame->ps.clientNum ) { continue; } } // entities can be flagged to be sent to everyone but one client if ( ent->r.svFlags & SVF_NOTSINGLECLIENT ) { if ( ent->r.singleClient == frame->ps.clientNum ) { continue; } } svEnt = SV_SvEntityForGentity( ent ); // don't double add an entity through portals if ( svEnt->snapshotCounter == sv.snapshotCounter ) { continue; } // broadcast entities are always sent, and so is the main player so we don't see noclip weirdness if ( ent->r.svFlags & SVF_BROADCAST || (e == frame->ps.clientNum) || (ent->r.broadcastClients[frame->ps.clientNum/32] & (1<<(frame->ps.clientNum%32)))) { SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (ent->s.isPortalEnt) { //rww - portal entities are always sent as well SV_AddEntToSnapshot( svEnt, ent, eNums ); continue; } if (com_RMG && com_RMG->integer) { VectorAdd(ent->r.absmax, ent->r.absmin, difference); VectorScale(difference, 0.5f, difference); VectorSubtract(origin, difference, difference); length = VectorLength(difference); // calculate the diameter VectorSubtract(ent->r.absmax, ent->r.absmin, difference); radius = VectorLength(difference); if (length-radius < /*sv_RMGDistanceCull->integer*/5000.0f) { // more of a diameter check SV_AddEntToSnapshot( svEnt, ent, eNums ); } } else { // ignore if not touching a PV leaf // check area if ( !CM_AreasConnected( clientarea, svEnt->areanum ) ) { // doors can legally straddle two areas, so // we may need to check another one if ( !CM_AreasConnected( clientarea, svEnt->areanum2 ) ) { continue; // blocked by a door } } bitvector = clientpvs; // check individual leafs if ( !svEnt->numClusters ) { continue; } l = 0; for ( i=0 ; i < svEnt->numClusters ; i++ ) { l = svEnt->clusternums[i]; if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } // if we haven't found it to be visible, // check overflow clusters that coudln't be stored if ( i == svEnt->numClusters ) { if ( svEnt->lastCluster ) { for ( ; l <= svEnt->lastCluster ; l++ ) { if ( bitvector[l >> 3] & (1 << (l&7) ) ) { break; } } if ( l == svEnt->lastCluster ) { continue; // not visible } } else { continue; } } if (g_svCullDist != -1.0f) { //do a distance cull check VectorAdd(ent->r.absmax, ent->r.absmin, difference); VectorScale(difference, 0.5f, difference); VectorSubtract(origin, difference, difference); length = VectorLength(difference); // calculate the diameter VectorSubtract(ent->r.absmax, ent->r.absmin, difference); radius = VectorLength(difference); if (length-radius >= g_svCullDist) { //then don't add it continue; } } // add it SV_AddEntToSnapshot( svEnt, ent, eNums ); // if its a portal entity, add everything visible from its camera position if ( ent->r.svFlags & SVF_PORTAL ) { if ( ent->s.generic1 ) { vec3_t dir; VectorSubtract(ent->s.origin, origin, dir); if ( VectorLengthSquared(dir) > (float) ent->s.generic1 * ent->s.generic1 ) { continue; } } SV_AddEntitiesVisibleFromPoint( ent->s.origin2, frame, eNums, qtrue ); } } }
/** * @brief Perform an approximate trace to find a taggable entity. * @param team Team the caller belongs to. * @param refreshTagged Refresh all already tagged entities's tags and exclude these entities from further consideration. */ gentity_t *TagTrace( const vec3_t begin, const vec3_t end, int skip, int mask, team_t team, qboolean refreshTagged ) { tagtrace_ent_t list[ MAX_GENTITIES ]; int i, count = 0; gentity_t *ent, *reticleEnt = NULL; vec3_t seg, delta; float dot; VectorSubtract( end, begin, seg ); // Do a trace for bounding boxes under the reticle first, they are prefered { trace_t tr; trap_Trace( &tr, begin, NULL, NULL, end, skip, mask, 0 ); if ( EntityTaggable( tr.entityNum, team ) ) { reticleEnt = g_entities + tr.entityNum; if ( !refreshTagged || !CheckRefreshTag( reticleEnt, team ) ) return reticleEnt; } } for( i = 0; i < level.num_entities; i++ ) { ent = g_entities + i; if( ent == reticleEnt ) continue; if( !ent->inuse ) continue; if( !EntityTaggable( i, team ) ) continue; VectorSubtract( ent->r.currentOrigin, begin, delta ); dot = DotProduct( seg, delta ) / VectorLength( seg ) / VectorLength( delta ); if( dot < 0.9 ) continue; if( !trap_InPVS( ent->r.currentOrigin, begin ) ) continue; // LOS { trace_t tr; trap_Trace( &tr, begin, NULL, NULL, ent->r.currentOrigin, skip, mask, 0 ); if( tr.entityNum != i ) continue; } if( refreshTagged && CheckRefreshTag( ent, team ) ) continue; list[ count ].ent = ent; list[ count++ ].dot = dot; } if( !count ) return NULL; qsort( list, count, sizeof( tagtrace_ent_t ), TagTrace_EntCmp ); return list[ 0 ].ent; }
//MP RULE - ALL PROCESSORIENTCOMMANDS FUNCTIONS MUST BE BG-COMPATIBLE!!! //If you really need to violate this rule for SP, then use ifdefs. //By BG-compatible, I mean no use of game-specific data - ONLY use //stuff available in the MP bgEntity (in SP, the bgEntity is #defined //as a gentity, but the MP-compatible access restrictions are based //on the bgEntity structure in the MP codebase) -rww // ProcessOrientCommands the Vehicle. static void ProcessOrientCommands( Vehicle_t *pVeh ) { /********************************************************************************/ /* BEGIN Here is where make sure the vehicle is properly oriented. BEGIN */ /********************************************************************************/ float speed; bgEntity_t *parent = pVeh->m_pParentEntity; playerState_t *parentPS, *riderPS; bgEntity_t *rider = NULL; if (parent->s.owner != ENTITYNUM_NONE) { rider = PM_BGEntForNum(parent->s.owner); //&g_entities[parent->r.ownerNum]; } if ( !rider ) { rider = parent; } parentPS = parent->playerState; riderPS = rider->playerState; speed = VectorLength( &parentPS->velocity ); // If the player is the rider... if ( rider->s.number < MAX_CLIENTS ) {//FIXME: use the vehicle's turning stat in this calc WalkerYawAdjust(pVeh, riderPS, parentPS); //FighterPitchAdjust(pVeh, riderPS, parentPS); pVeh->m_vOrientation->pitch = riderPS->viewangles.pitch; } else { float turnSpeed = pVeh->m_pVehicleInfo->turningSpeed; if ( !pVeh->m_pVehicleInfo->turnWhenStopped && !parentPS->speed )//FIXME: or !pVeh->m_ucmd.forwardmove? {//can't turn when not moving //FIXME: or ramp up to max turnSpeed? turnSpeed = 0.0f; } if (rider->s.eType == ET_NPC) {//help NPCs out some turnSpeed *= 2.0f; if (parentPS->speed > 200.0f) { turnSpeed += turnSpeed * parentPS->speed/200.0f*0.05f; } } turnSpeed *= pVeh->m_fTimeModifier; //default control scheme: strafing turns, mouselook aims if ( pVeh->m_ucmd.rightmove < 0 ) { pVeh->m_vOrientation->yaw += turnSpeed; } else if ( pVeh->m_ucmd.rightmove > 0 ) { pVeh->m_vOrientation->yaw -= turnSpeed; } if ( pVeh->m_pVehicleInfo->malfunctionArmorLevel && pVeh->m_iArmor <= pVeh->m_pVehicleInfo->malfunctionArmorLevel ) {//damaged badly } } /********************************************************************************/ /* END Here is where make sure the vehicle is properly oriented. END */ /********************************************************************************/ }
/* ================= R_SubdividePatchToGrid ================= */ srfGridMesh_t *R_SubdividePatchToGrid( int width, int height, drawVert_t points[MAX_PATCH_SIZE*MAX_PATCH_SIZE] ) { int i, j, k, l; drawVert_t prev, next, mid; float len, maxLen; int dir; int t; MAC_STATIC drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE]; float errorTable[2][MAX_GRID_SIZE]; srfGridMesh_t *grid; drawVert_t *vert; vec3_t tmpVec; for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { ctrl[j][i] = points[j*width+i]; } } for ( dir = 0 ; dir < 2 ; dir++ ) { for ( j = 0 ; j < MAX_GRID_SIZE ; j++ ) { errorTable[dir][j] = 0; } // horizontal subdivisions for ( j = 0 ; j + 2 < width ; j += 2 ) { // check subdivided midpoints against control points maxLen = 0; for ( i = 0 ; i < height ; i++ ) { vec3_t midxyz; vec3_t dir; vec3_t projected; float d; // calculate the point on the curve for ( l = 0 ; l < 3 ; l++ ) { midxyz[l] = (ctrl[i][j].xyz[l] + ctrl[i][j+1].xyz[l] * 2 + ctrl[i][j+2].xyz[l] ) * 0.25; } // see how far off the line it is // using dist-from-line will not account for internal // texture warping, but it gives a lot less polygons than // dist-from-midpoint VectorSubtract( midxyz, ctrl[i][j].xyz, midxyz ); VectorSubtract( ctrl[i][j+2].xyz, ctrl[i][j].xyz, dir ); VectorNormalize( dir ); d = DotProduct( midxyz, dir ); VectorScale( dir, d, projected ); VectorSubtract( midxyz, projected, midxyz); len = VectorLength( midxyz ); if ( len > maxLen ) { maxLen = len; } } // if all the points are on the lines, remove the entire columns if ( maxLen < 0.1 ) { errorTable[dir][j+1] = 999; continue; } // see if we want to insert subdivided columns if ( width + 2 > MAX_GRID_SIZE ) { errorTable[dir][j+1] = 1.0/maxLen; continue; // can't subdivide any more } if ( maxLen <= r_subdivisions->value ) { errorTable[dir][j+1] = 1.0/maxLen; continue; // didn't need subdivision } errorTable[dir][j+2] = 1.0/maxLen; // insert two columns and replace the peak width += 2; for ( i = 0 ; i < height ; i++ ) { LerpDrawVert( &ctrl[i][j], &ctrl[i][j+1], &prev ); LerpDrawVert( &ctrl[i][j+1], &ctrl[i][j+2], &next ); LerpDrawVert( &prev, &next, &mid ); for ( k = width - 1 ; k > j + 3 ; k-- ) { ctrl[i][k] = ctrl[i][k-2]; } ctrl[i][j + 1] = prev; ctrl[i][j + 2] = mid; ctrl[i][j + 3] = next; } // back up and recheck this set again, it may need more subdivision j -= 2; } Transpose( width, height, ctrl ); t = width; width = height; height = t; } // put all the aproximating points on the curve PutPointsOnCurve( ctrl, width, height ); // cull out any rows or columns that are colinear for ( i = 1 ; i < width-1 ; i++ ) { if ( errorTable[0][i] != 999 ) { continue; } for ( j = i+1 ; j < width ; j++ ) { for ( k = 0 ; k < height ; k++ ) { ctrl[k][j-1] = ctrl[k][j]; } errorTable[0][j-1] = errorTable[0][j]; } width--; } for ( i = 1 ; i < height-1 ; i++ ) { if ( errorTable[1][i] != 999 ) { continue; } for ( j = i+1 ; j < height ; j++ ) { for ( k = 0 ; k < width ; k++ ) { ctrl[j-1][k] = ctrl[j][k]; } errorTable[1][j-1] = errorTable[1][j]; } height--; } #if 1 // flip for longest tristrips as an optimization // the results should be visually identical with or // without this step if ( height > width ) { Transpose( width, height, ctrl ); InvertErrorTable( errorTable, width, height ); t = width; width = height; height = t; InvertCtrl( width, height, ctrl ); } #endif // calculate normals MakeMeshNormals( width, height, ctrl ); // copy the results out to a grid grid = (struct srfGridMesh_s *) ri.Hunk_Alloc( (width * height - 1) * sizeof( drawVert_t ) + sizeof( *grid ), qtrue ); grid->widthLodError = (float *) ri.Hunk_Alloc( width * 4, qfalse ); memcpy( grid->widthLodError, errorTable[0], width * 4 ); grid->heightLodError = (float *) ri.Hunk_Alloc( height * 4, qfalse ); memcpy( grid->heightLodError, errorTable[1], height * 4 ); grid->width = width; grid->height = height; grid->surfaceType = SF_GRID; ClearBounds( grid->meshBounds[0], grid->meshBounds[1] ); for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { vert = &grid->verts[j*width+i]; *vert = ctrl[j][i]; AddPointToBounds( vert->xyz, grid->meshBounds[0], grid->meshBounds[1] ); } } // compute local origin and bounds VectorAdd( grid->meshBounds[0], grid->meshBounds[1], grid->localOrigin ); VectorScale( grid->localOrigin, 0.5f, grid->localOrigin ); VectorSubtract( grid->meshBounds[0], grid->localOrigin, tmpVec ); grid->meshRadius = VectorLength( tmpVec ); VectorCopy( grid->localOrigin, grid->lodOrigin ); grid->lodRadius = grid->meshRadius; return grid; }
rc_t execute_tbl_join( KDirectory * dir, const char * accession_path, const char * accession_short, join_stats * stats, const char * tbl_name, const struct temp_dir * temp_dir, struct temp_registry * registry, size_t cur_cache, size_t buf_size, uint32_t num_threads, bool show_progress, format_t fmt, const join_options * join_options ) { rc_t rc = 0; if ( show_progress ) rc = KOutMsg( "join :" ); if ( rc == 0 ) { uint64_t row_count = 0; rc = extract_sra_row_count( dir, accession_path, tbl_name, cur_cache, &row_count ); /* above */ if ( rc == 0 && row_count > 0 ) { bool name_column_present; if ( tbl_name == NULL ) rc = cmn_check_tbl_column( dir, accession_path, "NAME", &name_column_present ); else rc = cmn_check_db_column( dir, accession_path, tbl_name, "NAME", &name_column_present ); if ( rc == 0 ) { Vector threads; int64_t row = 1; uint32_t thread_id; uint64_t rows_per_thread; struct bg_progress * progress = NULL; struct join_options corrected_join_options; /* helper.h */ VectorInit( &threads, 0, num_threads ); corrected_join_options . rowid_as_name = name_column_present ? join_options -> rowid_as_name : true; corrected_join_options . skip_tech = join_options -> skip_tech; corrected_join_options . print_read_nr = join_options -> print_read_nr; corrected_join_options . print_name = name_column_present; corrected_join_options . min_read_len = join_options -> min_read_len; corrected_join_options . filter_bases = join_options -> filter_bases; corrected_join_options . terminate_on_invalid = join_options -> terminate_on_invalid; if ( row_count < ( num_threads * 100 ) ) { num_threads = 1; rows_per_thread = row_count; } else { rows_per_thread = ( row_count / num_threads ) + 1; } if ( show_progress ) rc = bg_progress_make( &progress, row_count, 0, 0 ); /* progress_thread.c */ for ( thread_id = 0; rc == 0 && thread_id < num_threads; ++thread_id ) { join_thread_data * jtd = calloc( 1, sizeof * jtd ); if ( jtd != NULL ) { jtd -> dir = dir; jtd -> accession_path = accession_path; jtd -> accession_short = accession_short; jtd -> tbl_name = tbl_name; jtd -> first_row = row; jtd -> row_count = rows_per_thread; jtd -> cur_cache = cur_cache; jtd -> buf_size = buf_size; jtd -> progress = progress; jtd -> registry = registry; jtd -> fmt = fmt; jtd -> join_options = &corrected_join_options; rc = make_joined_filename( temp_dir, jtd -> part_file, sizeof jtd -> part_file, accession_short, thread_id ); /* temp_dir.c */ if ( rc == 0 ) { rc = KThreadMake( &jtd -> thread, cmn_thread_func, jtd ); if ( rc != 0 ) ErrMsg( "KThreadMake( fastq/special #%d ) -> %R", thread_id, rc ); else { rc = VectorAppend( &threads, NULL, jtd ); if ( rc != 0 ) ErrMsg( "VectorAppend( sort-thread #%d ) -> %R", thread_id, rc ); } row += rows_per_thread; } } } { /* collect the threads, and add the join_stats */ uint32_t i, n = VectorLength( &threads ); for ( i = VectorStart( &threads ); i < n; ++i ) { join_thread_data * jtd = VectorGet( &threads, i ); if ( jtd != NULL ) { rc_t rc_thread; KThreadWait( jtd -> thread, &rc_thread ); if ( rc_thread != 0 ) rc = rc_thread; KThreadRelease( jtd -> thread ); add_join_stats( stats, &jtd -> stats ); free( jtd ); } } VectorWhack ( &threads, NULL, NULL ); } bg_progress_release( progress ); /* progress_thread.c ( ignores NULL )*/ } } } return rc; }
/** * @brief Fills in texorg, worldtotex. and textoworld */ static void CalcLightinfoVectors (lightinfo_t* l) { const dBspTexinfo_t* tex; int i; vec3_t texnormal; vec_t distscale, dist; tex = &curTile->texinfo[l->face->texinfo]; for (i = 0; i < 2; i++) VectorCopy(tex->vecs[i], l->worldtotex[i]); /* calculate a normal to the texture axis. points can be moved along this * without changing their S/T */ texnormal[0] = tex->vecs[1][1] * tex->vecs[0][2] - tex->vecs[1][2] * tex->vecs[0][1]; texnormal[1] = tex->vecs[1][2] * tex->vecs[0][0] - tex->vecs[1][0] * tex->vecs[0][2]; texnormal[2] = tex->vecs[1][0] * tex->vecs[0][1] - tex->vecs[1][1] * tex->vecs[0][0]; VectorNormalize(texnormal); /* flip it towards plane normal */ distscale = DotProduct(texnormal, l->facenormal); if (!distscale) { Verb_Printf(VERB_EXTRA, "WARNING: Texture axis perpendicular to face\n"); distscale = 1.0; } if (distscale < 0.0) { distscale = -distscale; VectorSubtract(vec3_origin, texnormal, texnormal); } /* distscale is the ratio of the distance along the texture normal to * the distance along the plane normal */ distscale = 1.0 / distscale; for (i = 0; i < 2; i++) { const vec_t len = VectorLength(l->worldtotex[i]); const vec_t distance = DotProduct(l->worldtotex[i], l->facenormal) * distscale; VectorMA(l->worldtotex[i], -distance, texnormal, l->textoworld[i]); VectorScale(l->textoworld[i], (1.0f / len) * (1.0f / len), l->textoworld[i]); } /* calculate texorg on the texture plane */ for (i = 0; i < 3; i++) l->texorg[i] = -tex->vecs[0][3] * l->textoworld[0][i] - tex->vecs[1][3] * l->textoworld[1][i]; /* project back to the face plane */ dist = DotProduct(l->texorg, l->facenormal) - l->facedist - 1; dist *= distscale; VectorMA(l->texorg, -dist, texnormal, l->texorg); /* compensate for org'd bmodels */ VectorAdd(l->texorg, l->modelorg, l->texorg); /* total sample count */ l->numsurfpt = l->texsize[0] * l->texsize[1]; l->surfpt = Mem_AllocTypeN(vec3_t, l->numsurfpt); if (!l->surfpt) Sys_Error("Surface too large to light (" UFO_SIZE_T ")", l->numsurfpt * sizeof(*l->surfpt)); /* distance between samples */ l->step = 1 << config.lightquant; }
/************************************************************************************************ * CRMAreaManager::MoveArea * Moves an area within the area manager thus shifting any other areas as needed * * inputs: * area - area to be moved * origin - new origin to attempt to move to * * return: * none * ************************************************************************************************/ void CRMAreaManager::MoveArea ( CRMArea* movedArea, vec3_t origin) { int index; int size; // Increment the addcount (this is for infinite protection) movedArea->AddMoveCount (); // Infinite recursion prevention if ( movedArea->GetMoveCount() > 250 ) { // assert ( 0 ); movedArea->EnableCollision ( false ); return; } // First set the area's origin, This may cause it to be in collision with // another area but that will get fixed later movedArea->SetOrigin ( origin ); // when symmetric we want to ensure that no instances end up on the "other" side of the imaginary diaganol that cuts the map in two // mSymmetric tells us which side of the map is legal if ( movedArea->GetSymmetric ( ) ) { const vec3pair_t& bounds = TheRandomMissionManager->GetLandScape()->GetBounds(); vec3_t point; vec3_t dir; vec3_t tang; bool push; float len; VectorSubtract( movedArea->GetOrigin(), bounds[0], point ); VectorSubtract( bounds[1], bounds[0], dir ); VectorNormalize(dir); dir[2] = 0; point[2] = 0; VectorMA( bounds[0], DotProduct(point, dir), dir, tang ); VectorSubtract ( movedArea->GetOrigin(), tang, dir ); dir[2] = 0; push = false; len = VectorNormalize(dir); if ( len < movedArea->GetRadius ( ) ) { if ( movedArea->GetLockOrigin ( ) ) { movedArea->EnableCollision ( false ); return; } VectorMA ( point, (movedArea->GetSpacingRadius() - len) + TheRandomMissionManager->GetLandScape()->irand(10,movedArea->GetSpacingRadius()), dir, point ); origin[0] = point[0] + bounds[0][0]; origin[1] = point[1] + bounds[0][1]; movedArea->SetOrigin ( origin ); } switch ( movedArea->GetSymmetric ( ) ) { case SYMMETRY_TOPLEFT: if ( origin[1] > origin[0] ) { movedArea->Mirror ( ); } break; case SYMMETRY_BOTTOMRIGHT: if ( origin[1] < origin[0] ) { movedArea->Mirror ( ); } break; default: // unknown symmetry type assert ( 0 ); break; } } // Confine to area unless we are being pushed back by the same guy who pushed us last time (infinite loop) if ( movedArea->GetConfineRadius() ) { if ( movedArea->GetMoveCount() < 25 ) { vec3_t cdiff; float cdist; VectorSubtract ( movedArea->GetOrigin(), movedArea->GetConfineOrigin(), cdiff ); cdiff[2] = 0; cdist = VectorLength ( cdiff ); if ( cdist + movedArea->GetSpacingRadius() > movedArea->GetConfineRadius() ) { cdist = movedArea->GetConfineRadius() - movedArea->GetSpacingRadius(); VectorNormalize ( cdiff ); VectorMA ( movedArea->GetConfineOrigin(), cdist, cdiff, movedArea->GetOrigin()); } } else { index = 0; } } // See if it fell off the world in the x direction if ( movedArea->GetOrigin()[0] + movedArea->GetSpacingRadius() > mMaxs[0] ) movedArea->GetOrigin()[0] = mMaxs[0] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200)); else if ( movedArea->GetOrigin()[0] - movedArea->GetSpacingRadius() < mMins[0] ) movedArea->GetOrigin()[0] = mMins[0] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200)); // See if it fell off the world in the y direction if ( movedArea->GetOrigin()[1] + movedArea->GetSpacingRadius() > mMaxs[1] ) movedArea->GetOrigin()[1] = mMaxs[1] - movedArea->GetSpacingRadius() - (TheRandomMissionManager->GetLandScape()->irand(10,200)); else if ( movedArea->GetOrigin()[1] - movedArea->GetSpacingRadius() < mMins[1] ) movedArea->GetOrigin()[1] = mMins[1] + movedArea->GetSpacingRadius() + (TheRandomMissionManager->GetLandScape()->irand(10,200)); // Look at what we need to look at movedArea->LookAt ( movedArea->GetLookAtOrigin() ); // Dont collide against things that have no collision // if ( !movedArea->IsCollisionEnabled ( ) ) // { // return; // } // See if its colliding for(index = 0, size = mAreas.size(); index < size; index ++ ) { CRMArea *area = mAreas[index]; vec3_t diff; vec3_t newOrigin; float dist; float targetdist; // Skip the one that was moved in the first place if ( area == movedArea ) { continue; } if ( area->GetLockOrigin ( ) && movedArea->GetLockOrigin( ) ) { continue; } // Dont collide against things that have no collision if ( !area->IsCollisionEnabled ( ) ) { continue; } // Grab the distance between the two // only want the horizontal distance -- dmv //dist = Distance ( movedArea->GetOrigin ( ), area->GetOrigin ( )); vec3_t maOrigin; vec3_t aOrigin; VectorCopy(movedArea->GetOrigin(), maOrigin); VectorCopy(area->GetOrigin(), aOrigin); maOrigin[2] = aOrigin[2] = 0; dist = Distance ( maOrigin, aOrigin ); targetdist = movedArea->GetSpacingRadius() + area->GetSpacingRadius() + maximum(movedArea->GetPaddingSize(),area->GetPaddingSize()); if ( dist == 0 ) { area->GetOrigin()[0] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f); area->GetOrigin()[1] += (50 * (float)(TheRandomMissionManager->GetLandScape()->irand(0,99))/100.0f); VectorCopy(area->GetOrigin(), aOrigin); aOrigin[2] = 0; dist = Distance ( maOrigin, aOrigin ); } // Are they are enough apart? if ( dist >= targetdist ) { continue; } // Dont move a step if locked if ( area->GetLockOrigin ( ) ) { MoveArea ( area, area->GetOrigin ( ) ); continue; } // we got a collision, move the guy we hit VectorSubtract ( area->GetOrigin(), movedArea->GetOrigin(), diff ); diff[2] = 0; VectorNormalize ( diff ); // Push by the difference in the distance and no-collide radius VectorMA ( area->GetOrigin(), targetdist - dist + 1 , diff, newOrigin ); // Move the area now MoveArea ( area, newOrigin ); } }
/* ================ RemoveLinearMeshColumsRows ================ */ mesh_t *RemoveLinearMeshColumnsRows( mesh_t *in ) { int i, j, k; float len, maxLength; vec3_t proj, dir; mesh_t out; /* ydnar: static for os x */ MAC_STATIC bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; out.width = in->width; out.height = in->height; for ( i = 0 ; i < in->width ; i++ ) { for ( j = 0 ; j < in->height ; j++ ) { expand[j][i] = in->verts[j*in->width+i]; } } for ( j = 1 ; j < out.width - 1; j++ ) { maxLength = 0; for ( i = 0 ; i < out.height ; i++ ) { ProjectPointOntoVector(expand[i][j].xyz, expand[i][j-1].xyz, expand[i][j+1].xyz, proj); VectorSubtract(expand[i][j].xyz, proj, dir); len = VectorLength(dir); if (len > maxLength) { maxLength = len; } } if (maxLength < 0.1) { out.width--; for ( i = 0 ; i < out.height ; i++ ) { for (k = j; k < out.width; k++) { expand[i][k] = expand[i][k+1]; } } j--; } } for ( j = 1 ; j < out.height - 1; j++ ) { maxLength = 0; for ( i = 0 ; i < out.width ; i++ ) { ProjectPointOntoVector(expand[j][i].xyz, expand[j-1][i].xyz, expand[j+1][i].xyz, proj); VectorSubtract(expand[j][i].xyz, proj, dir); len = VectorLength(dir); if (len > maxLength) { maxLength = len; } } if (maxLength < 0.1) { out.height--; for ( i = 0 ; i < out.width ; i++ ) { for (k = j; k < out.height; k++) { expand[k][i] = expand[k+1][i]; } } j--; } } // collapse the verts out.verts = &expand[0][0]; for ( i = 1 ; i < out.height ; i++ ) { memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) ); } return CopyMesh(&out); }
/* ================= SubdivideMeshQuads ================= */ mesh_t *SubdivideMeshQuads( mesh_t *in, float minLength, int maxsize, int *widthtable, int *heighttable ) { int i, j, k, w, h, maxsubdivisions, subdivisions; vec3_t dir; float length, maxLength, amount; mesh_t out; bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; out.width = in->width; out.height = in->height; for ( i = 0 ; i < in->width ; i++ ) { for ( j = 0 ; j < in->height ; j++ ) { expand[j][i] = in->verts[j*in->width+i]; } } if (maxsize > MAX_EXPANDED_AXIS) Error("SubdivideMeshQuads: maxsize > MAX_EXPANDED_AXIS"); // horizontal subdivisions maxsubdivisions = (maxsize - in->width) / (in->width - 1); for ( w = 0, j = 0 ; w < in->width - 1; w++, j += subdivisions + 1) { maxLength = 0; for ( i = 0 ; i < out.height ; i++ ) { VectorSubtract(expand[i][j+1].xyz, expand[i][j].xyz, dir); length = VectorLength( dir ); if (length > maxLength) { maxLength = length; } } subdivisions = (int) (maxLength / minLength); if (subdivisions > maxsubdivisions) subdivisions = maxsubdivisions; widthtable[w] = subdivisions + 1; if (subdivisions <= 0) continue; out.width += subdivisions; for ( i = 0 ; i < out.height ; i++ ) { for ( k = out.width - 1 ; k > j + subdivisions; k-- ) { expand[i][k] = expand[i][k-subdivisions]; } for (k = 1; k <= subdivisions; k++) { amount = (float) k / (subdivisions + 1); LerpDrawVertAmount(&expand[i][j], &expand[i][j+subdivisions+1], amount, &expand[i][j+k]); } } } maxsubdivisions = (maxsize - in->height) / (in->height - 1); for ( h = 0, j = 0 ; h < in->height - 1; h++, j += subdivisions + 1) { maxLength = 0; for ( i = 0 ; i < out.width ; i++ ) { VectorSubtract(expand[j+1][i].xyz, expand[j][i].xyz, dir); length = VectorLength( dir ); if (length > maxLength) { maxLength = length; } } subdivisions = (int) (maxLength / minLength); if (subdivisions > maxsubdivisions) subdivisions = maxsubdivisions; heighttable[h] = subdivisions + 1; if (subdivisions <= 0) continue; out.height += subdivisions; for ( i = 0 ; i < out.width ; i++ ) { for ( k = out.height - 1 ; k > j + subdivisions; k-- ) { expand[k][i] = expand[k-subdivisions][i]; } for (k = 1; k <= subdivisions; k++) { amount = (float) k / (subdivisions + 1); LerpDrawVertAmount(&expand[j][i], &expand[j+subdivisions+1][i], amount, &expand[j+k][i]); } } } // collapse the verts out.verts = &expand[0][0]; for ( i = 1 ; i < out.height ; i++ ) { memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) ); } return CopyMesh(&out); }
/* ================= MakeMeshNormals ================= */ void MakeMeshNormals( mesh_t in ) { int i, j, k, dist; vec3_t normal; vec3_t sum; int count; vec3_t base; vec3_t delta; int x, y; bspDrawVert_t *dv; vec3_t around[8], temp; qboolean good[8]; qboolean wrapWidth, wrapHeight; float len; int neighbors[8][2] = { {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} }; wrapWidth = qfalse; for ( i = 0 ; i < in.height ; i++ ) { VectorSubtract( in.verts[i*in.width].xyz, in.verts[i*in.width+in.width-1].xyz, delta ); len = VectorLength( delta ); if ( len > 1.0 ) { break; } } if ( i == in.height ) { wrapWidth = qtrue; } wrapHeight = qfalse; for ( i = 0 ; i < in.width ; i++ ) { VectorSubtract( in.verts[i].xyz, in.verts[i + (in.height-1)*in.width].xyz, delta ); len = VectorLength( delta ); if ( len > 1.0 ) { break; } } if ( i == in.width) { wrapHeight = qtrue; } for ( i = 0 ; i < in.width ; i++ ) { for ( j = 0 ; j < in.height ; j++ ) { count = 0; dv = &in.verts[j*in.width+i]; VectorCopy( dv->xyz, base ); for ( k = 0 ; k < 8 ; k++ ) { VectorClear( around[k] ); good[k] = qfalse; for ( dist = 1 ; dist <= 3 ; dist++ ) { x = i + neighbors[k][0] * dist; y = j + neighbors[k][1] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = in.width - 1 + x; } else if ( x >= in.width ) { x = 1 + x - in.width; } } if ( wrapHeight ) { if ( y < 0 ) { y = in.height - 1 + y; } else if ( y >= in.height ) { y = 1 + y - in.height; } } if ( x < 0 || x >= in.width || y < 0 || y >= in.height ) { break; // edge of patch } VectorSubtract( in.verts[y*in.width+x].xyz, base, temp ); if ( VectorNormalize( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[k] = qtrue; VectorCopy( temp, around[k] ); break; // good edge } } } VectorClear( sum ); for ( k = 0 ; k < 8 ; k++ ) { if ( !good[k] || !good[(k+1)&7] ) { continue; // didn't get two points } CrossProduct( around[(k+1)&7], around[k], normal ); if ( VectorNormalize( normal, normal ) == 0 ) { continue; } VectorAdd( normal, sum, sum ); count++; } if ( count == 0 ) { //Sys_Printf("bad normal\n"); count = 1; } VectorNormalize( sum, dv->normal ); } } }
/* ================= SubdivideMesh ================= */ mesh_t *SubdivideMesh( mesh_t in, float maxError, float minLength ) { int i, j, k, l; bspDrawVert_t prev, next, mid; vec3_t prevxyz, nextxyz, midxyz; vec3_t delta; float len; mesh_t out; /* ydnar: static for os x */ MAC_STATIC bspDrawVert_t expand[MAX_EXPANDED_AXIS][MAX_EXPANDED_AXIS]; out.width = in.width; out.height = in.height; for ( i = 0 ; i < in.width ; i++ ) { for ( j = 0 ; j < in.height ; j++ ) { expand[j][i] = in.verts[j*in.width+i]; } } // horizontal subdivisions for ( j = 0 ; j + 2 < out.width ; j += 2 ) { // check subdivided midpoints against control points for ( i = 0 ; i < out.height ; i++ ) { for ( l = 0 ; l < 3 ; l++ ) { prevxyz[l] = expand[i][j+1].xyz[l] - expand[i][j].xyz[l]; nextxyz[l] = expand[i][j+2].xyz[l] - expand[i][j+1].xyz[l]; midxyz[l] = (expand[i][j].xyz[l] + expand[i][j+1].xyz[l] * 2 + expand[i][j+2].xyz[l] ) * 0.25; } // if the span length is too long, force a subdivision if ( VectorLength( prevxyz ) > minLength || VectorLength( nextxyz ) > minLength ) { break; } // see if this midpoint is off far enough to subdivide VectorSubtract( expand[i][j+1].xyz, midxyz, delta ); len = VectorLength( delta ); if ( len > maxError ) { break; } } if ( out.width + 2 >= MAX_EXPANDED_AXIS ) { break; // can't subdivide any more } if ( i == out.height ) { continue; // didn't need subdivision } // insert two columns and replace the peak out.width += 2; for ( i = 0 ; i < out.height ; i++ ) { LerpDrawVert( &expand[i][j], &expand[i][j+1], &prev ); LerpDrawVert( &expand[i][j+1], &expand[i][j+2], &next ); LerpDrawVert( &prev, &next, &mid ); for ( k = out.width - 1 ; k > j + 3 ; k-- ) { expand[i][k] = expand[i][k-2]; } expand[i][j + 1] = prev; expand[i][j + 2] = mid; expand[i][j + 3] = next; } // back up and recheck this set again, it may need more subdivision j -= 2; } // vertical subdivisions for ( j = 0 ; j + 2 < out.height ; j += 2 ) { // check subdivided midpoints against control points for ( i = 0 ; i < out.width ; i++ ) { for ( l = 0 ; l < 3 ; l++ ) { prevxyz[l] = expand[j+1][i].xyz[l] - expand[j][i].xyz[l]; nextxyz[l] = expand[j+2][i].xyz[l] - expand[j+1][i].xyz[l]; midxyz[l] = (expand[j][i].xyz[l] + expand[j+1][i].xyz[l] * 2 + expand[j+2][i].xyz[l] ) * 0.25; } // if the span length is too long, force a subdivision if ( VectorLength( prevxyz ) > minLength || VectorLength( nextxyz ) > minLength ) { break; } // see if this midpoint is off far enough to subdivide VectorSubtract( expand[j+1][i].xyz, midxyz, delta ); len = VectorLength( delta ); if ( len > maxError ) { break; } } if ( out.height + 2 >= MAX_EXPANDED_AXIS ) { break; // can't subdivide any more } if ( i == out.width ) { continue; // didn't need subdivision } // insert two columns and replace the peak out.height += 2; for ( i = 0 ; i < out.width ; i++ ) { LerpDrawVert( &expand[j][i], &expand[j+1][i], &prev ); LerpDrawVert( &expand[j+1][i], &expand[j+2][i], &next ); LerpDrawVert( &prev, &next, &mid ); for ( k = out.height - 1 ; k > j + 3 ; k-- ) { expand[k][i] = expand[k-2][i]; } expand[j+1][i] = prev; expand[j+2][i] = mid; expand[j+3][i] = next; } // back up and recheck this set again, it may need more subdivision j -= 2; } // collapse the verts out.verts = &expand[0][0]; for ( i = 1 ; i < out.height ; i++ ) { memmove( &out.verts[i*out.width], expand[i], out.width * sizeof(bspDrawVert_t) ); } return CopyMesh(&out); }
/* ================ InitMover "pos1", "pos2", and "speed" should be set before calling, so the movement delta can be calculated ================ */ void InitMover(gentity_t* ent) { vec3_t move; float distance; float light; vec3_t color; qboolean lightSet, colorSet; char* sound; // if the "model2" key is set, use a seperate model // for drawing, but clip against the brushes if (ent->model2) { ent->s.modelindex2 = G_ModelIndex(ent->model2); } // if the "loopsound" key is set, use a constant looping sound when moving if (G_SpawnString("noise", "100", &sound)) { ent->s.loopSound = G_SoundIndex(sound); } // if the "color" or "light" keys are set, setup constantLight lightSet = G_SpawnFloat("light", "100", &light); colorSet = G_SpawnVector("color", "1 1 1", color); if (lightSet || colorSet) { int r, g, b, i; r = color[0] * 255; if (r > 255) { r = 255; } g = color[1] * 255; if (g > 255) { g = 255; } b = color[2] * 255; if (b > 255) { b = 255; } i = light / 4; if (i > 255) { i = 255; } ent->s.constantLight = r | (g << 8) | (b << 16) | (i << 24); } ent->use = Use_BinaryMover; ent->reached = Reached_BinaryMover; ent->moverState = MOVER_POS1; ent->r.svFlags = SVF_USE_CURRENT_ORIGIN; ent->s.eType = ET_MOVER; VectorCopy(ent->pos1, ent->r.currentOrigin); trap_LinkEntity(ent); ent->s.pos.trType = TR_STATIONARY; VectorCopy(ent->pos1, ent->s.pos.trBase); // calculate time to reach second position from speed VectorSubtract(ent->pos2, ent->pos1, move); distance = VectorLength(move); if (! ent->speed) { ent->speed = 100; } VectorScale(move, ent->speed, ent->s.pos.trDelta); ent->s.pos.trDuration = distance * 1000 / ent->speed; if (ent->s.pos.trDuration <= 0) { ent->s.pos.trDuration = 1; } }
/* =============== Reached_Train =============== */ void Reached_Train(gentity_t* ent) { gentity_t* next; float speed; vec3_t move; float length; // copy the apropriate values next = ent->nextTrain; if (!next || !next->nextTrain) { return; // just stop } // fire all other targets G_UseTargets(next, NULL); // set the new trajectory ent->nextTrain = next->nextTrain; VectorCopy(next->s.origin, ent->pos1); VectorCopy(next->nextTrain->s.origin, ent->pos2); // if the path_corner has a speed, use that if (next->speed) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if (speed < 1) { speed = 1; } // calculate duration VectorSubtract(ent->pos2, ent->pos1, move); length = VectorLength(move); ent->s.pos.trDuration = length * 1000 / speed; // Tequila comment: Be sure to send to clients after any fast move case ent->r.svFlags &= ~SVF_NOCLIENT; // Tequila comment: Fast move case if (ent->s.pos.trDuration < 1) { // Tequila comment: As trDuration is used later in a division, we need to avoid that case now // With null trDuration, // the calculated rocks bounding box becomes infinite and the engine think for a short time // any entity is riding that mover but not the world entity... In rare case, I found it // can also stuck every map entities after func_door are used. // The desired effect with very very big speed is to have instant move, so any not null duration // lower than a frame duration should be sufficient. // Afaik, the negative case don't have to be supported. ent->s.pos.trDuration = 1; // Tequila comment: Don't send entity to clients so it becomes really invisible ent->r.svFlags |= SVF_NOCLIENT; } // looping sound ent->s.loopSound = next->soundLoop; // start it going SetMoverState(ent, MOVER_1TO2, level.time); // if there is a "wait" value on the target, don't start moving yet if (next->wait) { ent->nextthink = level.time + next->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } }
/* =========================================================================== CreatePathAStar This function uses the A* pathfinding algorithm to determine the shortest path between any two nodes. It's fairly complex, so I'm not really going to explain it much. Look up A* and binary heaps for more info. pathlist stores the ideal path between the nodes, in reverse order, and the return value is the number of nodes in that path =========================================================================== */ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist) { //all the data we have to hold...since we can't do dynamic allocation, has to be MAX_NODES //we can probably lower this later - eg, the open list should never have more than at most a few dozen items on it short int openlist[MAX_NODES+1]; //add 1 because it's a binary heap, and they don't use 0 - 1 is the first used index float gcost[MAX_NODES]; int fcost[MAX_NODES]; char list[MAX_NODES]; //0 is neither, 1 is open, 2 is closed - char because it's the smallest data type short int parent[MAX_NODES]; short int numOpen = 0; short int atNode, temp, newnode=-1; qboolean found = qfalse; int count = -1; float gc; int i, u, v, m; vec3_t vec; //clear out all the arrays memset(openlist, 0, sizeof(short int)*(MAX_NODES+1)); memset(fcost, 0, sizeof(int)*MAX_NODES); memset(list, 0, sizeof(char)*MAX_NODES); memset(parent, 0, sizeof(short int)*MAX_NODES); memset(gcost, -1, sizeof(float)*MAX_NODES); //make sure we have valid data before calculating everything if ((from == NODE_INVALID) || (to == NODE_INVALID) || (from >= MAX_NODES) || (to >= MAX_NODES) || (from == to)) return -1; openlist[1] = from; //add the starting node to the open list ++numOpen; gcost[from] = 0; //its f and g costs are obviously 0 fcost[from] = 0; while (1) { if (numOpen != 0) //if there are still items in the open list { //pop the top item off of the list atNode = openlist[1]; list[atNode] = 2; //put the node on the closed list so we don't check it again --numOpen; openlist[1] = openlist[numOpen+1]; //move the last item in the list to the top position v = 1; //this while loop reorders the list so that the new lowest fcost is at the top again while (1) { u = v; if ((2*u+1) < numOpen) //if both children exist { if (fcost[openlist[u]] >= fcost[openlist[2*u]]) v = 2*u; if (fcost[openlist[v]] >= fcost[openlist[2*u+1]]) v = 2*u+1; } else { if ((2*u) < numOpen) //if only one child exists { if (fcost[openlist[u]] >= fcost[openlist[2*u]]) v = 2*u; } } if (u != v) //if they're out of order, swap this item with its parent { temp = openlist[u]; openlist[u] = openlist[v]; openlist[v] = temp; } else break; } for (i = 0; i < nodes[atNode].enodenum; i++) //loop through all the links for this node { newnode = nodes[atNode].links[i].targetNode; //if this path is blocked, skip it if (nodes[atNode].links[i].flags & PATH_BLOCKED) continue; //if this path is blocked, skip it if (bot->client && (bot->client->ps.eFlags & EF_TANK) && nodes[atNode].links[i].flags & PATH_NOTANKS) continue; //skip any unreachable nodes if (bot->client && (nodes[newnode].type & NODE_ALLY_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_ALLIES)) continue; if (bot->client && (nodes[newnode].type & NODE_AXIS_UNREACHABLE) && (bot->client->sess.sessionTeam == TEAM_AXIS)) continue; if (list[newnode] == 2) //if this node is on the closed list, skip it continue; if (list[newnode] != 1) //if this node is not already on the open list { openlist[++numOpen] = newnode; //add the new node to the open list list[newnode] = 1; parent[newnode] = atNode; //record the node's parent if (newnode == to) //if we've found the goal, don't keep computing paths! break; //this will break the 'for' and go all the way to 'if (list[to] == 1)' //store it's f cost value fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost); //this loop re-orders the heap so that the lowest fcost is at the top m = numOpen; while (m != 1) //while this item isn't at the top of the heap already { //if it has a lower fcost than its parent if (fcost[openlist[m]] <= fcost[openlist[m/2]]) { temp = openlist[m/2]; openlist[m/2] = openlist[m]; openlist[m] = temp; //swap them m /= 2; } else break; } } else //if this node is already on the open list { gc = gcost[atNode]; VectorSubtract(nodes[newnode].origin, nodes[atNode].origin, vec); gc += VectorLength(vec); //calculate what the gcost would be if we reached this node along the current path if (gc < gcost[newnode]) //if the new gcost is less (ie, this path is shorter than what we had before) { parent[newnode] = atNode; //set the new parent for this node gcost[newnode] = gc; //and the new g cost for (i = 1; i < numOpen; i++) //loop through all the items on the open list { if (openlist[i] == newnode) //find this node in the list { //calculate the new fcost and store it fcost[newnode] = GetFCost(to, newnode, parent[newnode], gcost); //reorder the list again, with the lowest fcost item on top m = i; while (m != 1) { //if the item has a lower fcost than it's parent if (fcost[openlist[m]] < fcost[openlist[m/2]]) { temp = openlist[m/2]; openlist[m/2] = openlist[m]; openlist[m] = temp; //swap them m /= 2; } else break; } break; //exit the 'for' loop because we already changed this node } //if } //for } //if (gc < gcost[newnode]) } //if (list[newnode] != 1) --> else } //for (loop through links) } //if (numOpen != 0) else { found = qfalse; //there is no path between these nodes break; } if (list[to] == 1) //if the destination node is on the open list, we're done { found = qtrue; break; } } //while (1) if (found == qtrue) //if we found a path { //G_Printf("%s - path found!n", bot->client->pers.netname); count = 0; temp = to; //start at the end point while (temp != from) //travel along the path (backwards) until we reach the starting point { pathlist[count++] = temp; //add the node to the pathlist and increment the count temp = parent[temp]; //move to the parent of this node to continue the path } pathlist[count++] = from; //add the beginning node to the end of the pathlist #ifdef __BOT_SHORTEN_ROUTING__ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1 #endif //__BOT_SHORTEN_ROUTING__ } else { //G_Printf("^1*** ^4BOT DEBUG^5: (CreatePathAStar) There is no route between node ^7%i^5 and node ^7%i^5.n", from, to); count = CreateDumbRoute(from, to, pathlist); if (count > 0) { #ifdef __BOT_SHORTEN_ROUTING__ count = ShortenASTARRoute(pathlist, count); // This isn't working... Dunno why.. Unique1 #endif //__BOT_SHORTEN_ROUTING__ return count; } } return count; //return the number of nodes in the path, -1 if not found }
qboolean FixBrokenSurface( mapDrawSurface_t *ds ) { bspDrawVert_t *dv1, *dv2, avg; int i, j, k; float dist; /* dummy check */ if( ds == NULL ) return qfalse; if( ds->type != SURFACE_FACE ) return qfalse; /* check all verts */ for( i = 0; i < ds->numVerts; i++ ) { /* get verts */ dv1 = &ds->verts[ i ]; dv2 = &ds->verts[ (i + 1) % ds->numVerts ]; /* degenerate edge? */ VectorSubtract( dv1->xyz, dv2->xyz, avg.xyz ); dist = VectorLength( avg.xyz ); if( dist < DEGENERATE_EPSILON ) { Sys_FPrintf( SYS_VRB, "WARNING: Degenerate T-junction edge found, fixing...\n" ); /* create an average drawvert */ /* ydnar 2002-01-26: added nearest-integer welding preference */ SnapWeldVector( dv1->xyz, dv2->xyz, avg.xyz ); VectorAdd( dv1->normal, dv2->normal, avg.normal ); VectorNormalize( avg.normal, avg.normal ); avg.st[ 0 ] = (dv1->st[ 0 ] + dv2->st[ 0 ]) * 0.5f; avg.st[ 1 ] = (dv1->st[ 1 ] + dv2->st[ 1 ]) * 0.5f; /* lightmap st/colors */ for( k = 0; k < MAX_LIGHTMAPS; k++ ) { avg.lightmap[ k ][ 0 ] = (dv1->lightmap[ k ][ 0 ] + dv2->lightmap[ k ][ 0 ]) * 0.5f; avg.lightmap[ k ][ 1 ] = (dv1->lightmap[ k ][ 1 ] + dv2->lightmap[ k ][ 1 ]) * 0.5f; for( j = 0; j < 4; j++ ) avg.color[ k ][ j ] = (int) (dv1->color[ k ][ j ] + dv2->color[ k ][ j ]) >> 1; } /* ydnar: der... */ memcpy( dv1, &avg, sizeof( avg ) ); /* move the remaining verts */ for( k = i + 2; k < ds->numVerts; k++ ) { /* get verts */ dv1 = &ds->verts[ k ]; dv2 = &ds->verts[ k - 1 ]; /* copy */ memcpy( dv2, dv1, sizeof( bspDrawVert_t ) ); } ds->numVerts--; /* after welding, we have to consider the same vertex again, as it now has a new neighbor dv2 */ --i; /* should ds->numVerts have become 0, then i is now -1. In the next iteration, the loop will abort. */ } }
void G_RunMissile( gentity_t *ent ) { vec3_t origin, oldOrg; trace_t tr; int trHitLoc=HL_NONE; VectorCopy( ent->currentOrigin, oldOrg ); // get current position if ( ent->s.pos.trType == TR_INTERPOLATE ) {//rolling missile? //FIXME: WTF?!! Sticks to stick missiles? //FIXME: they stick inside the player G_RollMissile( ent ); if ( ent->s.eType != ET_GENERAL ) {//didn't explode VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); gi.trace( &tr, oldOrg, ent->mins, ent->maxs, ent->currentOrigin, ent->s.number, ent->clipmask, G2_RETURNONHIT, 10 ); if ( VectorCompare( ent->s.pos.trDelta, vec3_origin ) ) { //VectorCopy( ent->currentAngles, ent->s.apos.trBase ); VectorClear( ent->s.apos.trDelta ); } else { vec3_t ang, fwdDir, rtDir; float speed; ent->s.apos.trType = TR_INTERPOLATE; VectorSet( ang, 0, ent->s.apos.trBase[1], 0 ); AngleVectors( ang, fwdDir, rtDir, NULL ); speed = VectorLength( ent->s.pos.trDelta )*4; //HMM, this works along an axis-aligned dir, but not along diagonals //This is because when roll gets to 90, pitch becomes yaw, and vice-versa //Maybe need to just set the angles directly? ent->s.apos.trDelta[0] = DotProduct( fwdDir, ent->s.pos.trDelta ); ent->s.apos.trDelta[1] = 0;//never spin! ent->s.apos.trDelta[2] = DotProduct( rtDir, ent->s.pos.trDelta ); VectorNormalize( ent->s.apos.trDelta ); VectorScale( ent->s.apos.trDelta, speed, ent->s.apos.trDelta ); ent->s.apos.trTime = level.previousTime; } } } else { EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner /* gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_RETURNONHIT, 10 ); */ gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ent->s.number, ent->clipmask, G2_COLLIDE, 10 ); /* if ( !VectorCompare( ent->mins, vec3_origin ) || !VectorCompare( ent->maxs, vec3_origin ) ) {//don't do ghoul trace if ent has size because g2 just ignores that anyway gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_NOCOLLIDE, 10 ); } else //Now we always do ghoul trace, regardless of bbox size of missile, this is presuming that non-point ghoul traces will be possible...? { gi.trace( &tr, ent->currentOrigin, vec3_origin, vec3_origin, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask, G2_RETURNONHIT, 10 ); } */ /* if ( tr.fraction == 0.0f && tr.plane.normal[2] == 1.0f && origin[2] < ent->currentOrigin[2] ) { if ( ent->s.pos.trType == TR_GRAVITY && !(ent->s.eFlags&EF_BOUNCE) && !(ent->s.eFlags&EF_BOUNCE_HALF) && ent->s.weapon == WP_THERMAL )//FIXME: EF_ROLLING { //FIXME: Needs to stop sometime! ent->s.pos.trType = TR_LINEAR; ent->s.pos.trDelta[2] = 0; EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position, // ignoring interactions with the missile owner gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ent->owner ? ent->owner->s.number : ENTITYNUM_NONE, ent->clipmask | CONTENTS_GHOUL2 ); if ( tr.fraction == 1.0f ) { VectorCopy( tr.endpos, ent->s.pos.trBase ); VectorScale( ent->s.pos.trDelta, 0.975f, ent->s.pos.trDelta ); ent->s.pos.trTime = level.time; } ent->s.pos.trType = TR_GRAVITY; } } */ if ( tr.entityNum != ENTITYNUM_NONE && &g_entities[tr.entityNum] != NULL ) { gentity_t *other = &g_entities[tr.entityNum]; // check for hitting a lightsaber if ( other->contents & CONTENTS_LIGHTSABER ) {//hit a lightsaber bbox if ( other->owner && other->owner->client && !other->owner->client->ps.saberInFlight && !InFront( ent->currentOrigin, other->owner->currentOrigin, other->owner->client->ps.viewangles, SABER_REFLECT_MISSILE_CONE ) )//other->owner->s.number == 0 && {//Jedi cannot block shots from behind! //re-trace from here, ignoring the lightsaber gi.trace( &tr, tr.endpos, ent->mins, ent->maxs, origin, tr.entityNum, ent->clipmask, G2_RETURNONHIT, 10 ); } } } VectorCopy( tr.endpos, ent->currentOrigin ); } // get current angles VectorMA( ent->s.apos.trBase, (level.time - ent->s.apos.trTime) * 0.001, ent->s.apos.trDelta, ent->s.apos.trBase ); //FIXME: Rolling things hitting G2 polys is weird /////////////////////////////////////////////////////// //? if ( tr.fraction != 1 ) { // did we hit or go near a Ghoul2 model? // qboolean hitModel = qfalse; for (int i=0; i < MAX_G2_COLLISIONS; i++) { if (tr.G2CollisionMap[i].mEntityNum == -1) { break; } CCollisionRecord &coll = tr.G2CollisionMap[i]; gentity_t *hitEnt = &g_entities[coll.mEntityNum]; /* Sorry...this was just getting in the way.... #if _DEBUG vec3_t delta; VectorSubtract(origin, coll.mCollisionPosition, delta); VectorNormalize(delta); VectorScale(delta, 30, delta); if (coll.mFlags & G2_BACKFACE) { VectorAdd(delta, coll.mCollisionPosition, delta); G_DebugLine(coll.mCollisionPosition, delta, 10000, 0x00ff0000, qtrue); } else { VectorSubtract(coll.mCollisionPosition, delta, delta); G_DebugLine(coll.mCollisionPosition, delta, 10000, 0x0000ff00, qtrue); } //loadsavecrash // VectorCopy(hitEnt->mins, hitEnt->s.mins); // VectorCopy(hitEnt->maxs, hitEnt->s.maxs); #endif */ // process collision records here... // make sure we only do this once, not for all the entrance wounds we might generate if ((coll.mFlags & G2_FRONTFACE)/* && !(hitModel)*/ && hitEnt->health) { // create a new surface using the details of what poly/surface/model we hit // int newSurface = gi.G2API_AddSurface(&hitEnt->ghoul2[coll.mModelIndex], coll.mSurfaceIndex, coll.mPolyIndex, coll.mBarycentricI, coll.mBarycentricJ, 10); // surfaceInfo_t *newSuf = &hitEnt->ghoul2[coll.mModelIndex].mSlist[newSurface]; // attach a bolt to this surface // int newBolt = gi.G2API_AddBoltSurfNum(&hitEnt->ghoul2[coll.mModelIndex], newSurface); // now attach an effect to this new bolt // Bolting on this effect just looks dumb and adds lots of unnecessary effects to the scene // // G_PlayEffect( G_EffectIndex( "blaster/smoke_bolton") , coll.mModelIndex, newBolt, hitEnt->s.number); // // // G_SetBoltSurfaceRemoval(coll.mEntityNum, coll.mModelIndex, newBolt, newSurface, 10000); // hitModel = qtrue; if (trHitLoc==HL_NONE) { G_GetHitLocFromSurfName( &g_entities[coll.mEntityNum], gi.G2API_GetSurfaceName( &g_entities[coll.mEntityNum].ghoul2[coll.mModelIndex], coll.mSurfaceIndex ), &trHitLoc, coll.mCollisionPosition, NULL, NULL, ent->methodOfDeath ); } break; // NOTE: the way this whole section was working, it would only get inside of this IF once anyway, might as well break out now } } } ///////////////////////////////////////////////////////// if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); if ( ent->s.pos.trType == TR_STATIONARY && (ent->s.eFlags&EF_MISSILE_STICK) ) {//stuck missiles should check some special stuff G_RunStuckMissile( ent ); return; } // check think function G_RunThink( ent ); if ( ent->s.eType != ET_MISSILE ) { return; // exploded } if ( ent->mass ) { G_MoverTouchPushTriggers( ent, oldOrg ); } /* if ( !(ent->s.eFlags & EF_TELEPORT_BIT) ) { G_MoverTouchTeleportTriggers( ent, oldOrg ); if ( ent->s.eFlags & EF_TELEPORT_BIT ) {//was teleported return; } } else { ent->s.eFlags &= ~EF_TELEPORT_BIT; } */ AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 );//wakes them up when see a shot passes in front of them if ( !Q_irand( 0, 10 ) ) {//not so often... if ( ent->splashDamage && ent->splashRadius ) {//I'm an exploder, let people around me know danger is coming if ( ent->s.weapon == WP_TRIP_MINE ) {//??? } else { if ( ent->s.weapon == WP_ROCKET_LAUNCHER && ent->e_ThinkFunc == thinkF_rocketThink ) {//homing rocket- run like hell! AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER_GREAT, 50 ); } else { AddSightEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER, 50 ); } AddSoundEvent( ent->owner, ent->currentOrigin, ent->splashRadius, AEL_DANGER ); } } else {//makes them run from near misses AddSightEvent( ent->owner, ent->currentOrigin, 48, AEL_DANGER, 50 ); } } if ( tr.fraction == 1 ) { return; } // never explode or bounce on sky if ( tr.surfaceFlags & SURF_NOIMPACT ) { G_FreeEntity( ent ); return; } G_MissileImpact( ent, &tr, trHitLoc ); }
void CBasePlayer::UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity ) { bool bWalking; float fvol; Vector knee; Vector feet; float height; float speed; float velrun; float velwalk; int fLadder; if ( m_flStepSoundTime > 0 ) { m_flStepSoundTime -= 1000.0f * gpGlobals->frametime; if ( m_flStepSoundTime < 0 ) { m_flStepSoundTime = 0; } } if ( m_flStepSoundTime > 0 ) return; if ( GetFlags() & (FL_FROZEN|FL_ATCONTROLS)) return; if ( GetMoveType() == MOVETYPE_NOCLIP || GetMoveType() == MOVETYPE_OBSERVER ) return; if ( !sv_footsteps.GetFloat() ) return; speed = VectorLength( vecVelocity ); float groundspeed = Vector2DLength( vecVelocity.AsVector2D() ); // determine if we are on a ladder fLadder = ( GetMoveType() == MOVETYPE_LADDER ); GetStepSoundVelocities( &velwalk, &velrun ); bool onground = ( GetFlags() & FL_ONGROUND ); bool movingalongground = ( groundspeed > 0.0001f ); bool moving_fast_enough = ( speed >= velwalk ); #ifdef PORTAL // In Portal we MUST play footstep sounds even when the player is moving very slowly // This is used to count the number of footsteps they take in the challenge mode // -Jeep moving_fast_enough = true; #endif // To hear step sounds you must be either on a ladder or moving along the ground AND // You must be moving fast enough if ( !moving_fast_enough || !(fLadder || ( onground && movingalongground )) ) return; // MoveHelper()->PlayerSetAnimation( PLAYER_WALK ); bWalking = speed < velrun; VectorCopy( vecOrigin, knee ); VectorCopy( vecOrigin, feet ); height = GetPlayerMaxs()[ 2 ] - GetPlayerMins()[ 2 ]; knee[2] = vecOrigin[2] + 0.2 * height; // find out what we're stepping in or on... if ( fLadder ) { psurface = GetLadderSurface(vecOrigin); fvol = 0.5; SetStepSoundTime( STEPSOUNDTIME_ON_LADDER, bWalking ); } #ifdef CSTRIKE_DLL else if ( enginetrace->GetPointContents( knee ) & MASK_WATER ) // we want to use the knee for Cstrike, not the waist #else else if ( GetWaterLevel() == WL_Waist ) #endif // CSTRIKE_DLL { static int iSkipStep = 0; if ( iSkipStep == 0 ) { iSkipStep++; return; } if ( iSkipStep++ == 3 ) { iSkipStep = 0; } psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "wade" ) ); fvol = 0.65; SetStepSoundTime( STEPSOUNDTIME_WATER_KNEE, bWalking ); } else if ( GetWaterLevel() == WL_Feet ) { psurface = physprops->GetSurfaceData( physprops->GetSurfaceIndex( "water" ) ); fvol = bWalking ? 0.2 : 0.5; SetStepSoundTime( STEPSOUNDTIME_WATER_FOOT, bWalking ); } else { if ( !psurface ) return; SetStepSoundTime( STEPSOUNDTIME_NORMAL, bWalking ); switch ( psurface->game.material ) { default: case CHAR_TEX_CONCRETE: fvol = bWalking ? 0.2 : 0.5; break; case CHAR_TEX_METAL: fvol = bWalking ? 0.2 : 0.5; break; case CHAR_TEX_DIRT: fvol = bWalking ? 0.25 : 0.55; break; case CHAR_TEX_VENT: fvol = bWalking ? 0.4 : 0.7; break; case CHAR_TEX_GRATE: fvol = bWalking ? 0.2 : 0.5; break; case CHAR_TEX_TILE: fvol = bWalking ? 0.2 : 0.5; break; case CHAR_TEX_SLOSH: fvol = bWalking ? 0.2 : 0.5; break; } } // play the sound // 65% volume if ducking if ( GetFlags() & FL_DUCKING ) { fvol *= 0.65; } PlayStepSound( feet, psurface, fvol, false ); }
void G_MissileImpacted( gentity_t *ent, gentity_t *other, vec3_t impactPos, vec3_t normal, int hitLoc=HL_NONE ) { // impact damage if ( other->takedamage ) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } int damage = ent->damage; if( other->client ) { class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.powerups[PW_SHOCKED] < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->s.powerups |= ( 1 << PW_SHOCKED ); other->client->ps.powerups[PW_SHOCKED] = level.time + 450; } //FIXME: throw some sparks off droids,too } } G_Damage( other, ent, ent->owner, velocity, impactPos, damage, ent->dflags, ent->methodOfDeath, hitLoc); } } // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? //G_FreeEntity(ent); if ( (other->takedamage && other->client ) || (ent->s.weapon == WP_FLECHETTE && other->contents&CONTENTS_LIGHTSABER) ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( normal ) ); ent->s.otherEntityNum = other->s.number; } else { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( normal ) ); ent->s.otherEntityNum = other->s.number; } VectorCopy( normal, ent->pos1 ); if ( ent->owner )//&& ent->owner->s.number == 0 ) { //Add the event AddSoundEvent( ent->owner, ent->currentOrigin, 256, AEL_SUSPICIOUS ); AddSightEvent( ent->owner, ent->currentOrigin, 512, AEL_DISCOVERED, 75 ); } ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; //SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth VectorCopy( impactPos, ent->s.pos.trBase ); G_SetOrigin( ent, impactPos ); // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { G_RadiusDamage( impactPos, ent->owner, ent->splashDamage, ent->splashRadius, other, ent->splashMethodOfDeath ); } gi.linkentity( ent ); }
/* ============== CG_DamageFeedback ============== */ void CG_DamageFeedback( int yawByte, int pitchByte, int damage ) { float left, front, up; float kick; int health; float scale; vec3_t dir; vec3_t angles; float dist; float yaw, pitch; int slot; viewDamage_t *vd; // show the attacking player's head and name in corner cg.attackerTime = cg.time; // the lower on health you are, the greater the view kick will be health = cg.snap->ps.stats[STAT_HEALTH]; if ( health < 40 ) { scale = 1; } else { scale = 40.0 / health; } kick = damage * scale; if ( kick < 5 ) { kick = 5; } if ( kick > 10 ) { kick = 10; } // find a free slot for ( slot = 0; slot < MAX_VIEWDAMAGE; slot++ ) { if ( cg.viewDamage[slot].damageTime + cg.viewDamage[slot].damageDuration < cg.time ) { break; } } if ( slot == MAX_VIEWDAMAGE ) { return; // no free slots, never override or splats will suddenly disappear } vd = &cg.viewDamage[slot]; // if yaw and pitch are both 255, make the damage always centered (falling, etc) if ( yawByte == 255 && pitchByte == 255 ) { vd->damageX = 0; vd->damageY = 0; cg.v_dmg_roll = 0; cg.v_dmg_pitch = -kick; } else { // positional pitch = pitchByte / 255.0 * 360; yaw = yawByte / 255.0 * 360; angles[PITCH] = pitch; angles[YAW] = yaw; angles[ROLL] = 0; AngleVectors( angles, dir, NULL, NULL ); VectorSubtract( vec3_origin, dir, dir ); front = DotProduct( dir, cg.refdef.viewaxis[0] ); left = DotProduct( dir, cg.refdef.viewaxis[1] ); up = DotProduct( dir, cg.refdef.viewaxis[2] ); dir[0] = front; dir[1] = left; dir[2] = 0; dist = VectorLength( dir ); if ( dist < 0.1 ) { dist = 0.1; } cg.v_dmg_roll = kick * left; cg.v_dmg_pitch = -kick * front; if ( front <= 0.1 ) { front = 0.1; } vd->damageX = crandom() * 0.3 + - left / front; vd->damageY = crandom() * 0.3 + up / dist; } // clamp the position if ( vd->damageX > 1.0 ) { vd->damageX = 1.0; } if ( vd->damageX < -1.0 ) { vd->damageX = -1.0; } if ( vd->damageY > 1.0 ) { vd->damageY = 1.0; } if ( vd->damageY < -1.0 ) { vd->damageY = -1.0; } // don't let the screen flashes vary as much if ( kick > 10 ) { kick = 10; } vd->damageValue = kick; cg.v_dmg_time = cg.time + DAMAGE_TIME; vd->damageTime = cg.snap->serverTime; vd->damageDuration = kick * 50 * ( 1 + 2 * ( !vd->damageX && !vd->damageY ) ); cg.damageTime = cg.snap->serverTime; cg.damageIndex = slot; }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int FindPlaneSeperatingWindings(winding_t *w1, winding_t *w2, vec3_t dir, vec3_t normal, float *dist) { int i, i2, j, j2, n; int sides1[3], sides2[3]; float dist1, dist2, dot, diff; vec3_t normal1, normal2; vec3_t v1, v2; for (i = 0; i < w1->numpoints; i++) { i2 = (i+1) % w1->numpoints; // VectorSubtract(w1->p[i2], w1->p[i], v1); if (VectorLength(v1) < 0.1) { //Log_Write("FindPlaneSeperatingWindings: winding1 with degenerate edge\r\n"); continue; } //end if CrossProduct(v1, dir, normal1); VectorNormalize(normal1); dist1 = DotProduct(normal1, w1->p[i]); // for (j = 0; j < w2->numpoints; j++) { j2 = (j+1) % w2->numpoints; // VectorSubtract(w2->p[j2], w2->p[j], v2); if (VectorLength(v2) < 0.1) { //Log_Write("FindPlaneSeperatingWindings: winding2 with degenerate edge\r\n"); continue; } //end if CrossProduct(v2, dir, normal2); VectorNormalize(normal2); dist2 = DotProduct(normal2, w2->p[j]); // diff = dist1 - dist2; if (diff < -0.1 || diff > 0.1) { dist2 = -dist2; VectorNegate(normal2, normal2); diff = dist1 - dist2; if (diff < -0.1 || diff > 0.1) continue; } //end if //check if the normal vectors are equal for (n = 0; n < 3; n++) { diff = normal1[n] - normal2[n]; if (diff < -0.0001 || diff > 0.0001) break; } //end for if (n != 3) continue; //check on which side of the seperating plane the points of //the first winding are sides1[0] = sides1[1] = sides1[2] = 0; for (n = 0; n < w1->numpoints; n++) { dot = DotProduct(w1->p[n], normal1) - dist1; if (dot > 0.1) sides1[0]++; else if (dot < -0.1) sides1[1]++; else sides1[2]++; } //end for //check on which side of the seperating plane the points of //the second winding are sides2[0] = sides2[1] = sides2[2] = 0; for (n = 0; n < w2->numpoints; n++) { //used normal1 and dist1 (they are equal to normal2 and dist2) dot = DotProduct(w2->p[n], normal1) - dist1; if (dot > 0.1) sides2[0]++; else if (dot < -0.1) sides2[1]++; else sides2[2]++; } //end for //if the first winding has points at both sides if (sides1[0] && sides1[1]) { Log_Write("FindPlaneSeperatingWindings: winding1 non-convex\r\n"); continue; } //end if //if the second winding has points at both sides if (sides2[0] && sides2[1]) { Log_Write("FindPlaneSeperatingWindings: winding2 non-convex\r\n"); continue; } //end if // if ((!sides1[0] && !sides1[1]) || (!sides2[0] && !sides2[1])) { //don't use one of the winding planes as the seperating plane continue; } //end if //the windings must be at different sides of the seperating plane if ((!sides1[0] && !sides2[1]) || (!sides1[1] && !sides2[0])) { VectorCopy(normal1, normal); *dist = dist1; return true; } //end if } //end for } //end for return false; } //end of the function FindPlaneSeperatingWindings
/* ================= MakeMeshNormals Handles all the complicated wrapping and degenerate cases ================= */ static void MakeMeshNormals( int width, int height, drawVert_t ctrl[MAX_GRID_SIZE][MAX_GRID_SIZE] ) { int i, j, k, dist; vec3_t normal; vec3_t sum; int count; vec3_t base; vec3_t delta; int x, y; drawVert_t *dv; vec3_t around[8], temp; qboolean good[8]; qboolean wrapWidth, wrapHeight; float len; static int neighbors[8][2] = { {0,1}, {1,1}, {1,0}, {1,-1}, {0,-1}, {-1,-1}, {-1,0}, {-1,1} }; wrapWidth = qfalse; for ( i = 0 ; i < height ; i++ ) { VectorSubtract( ctrl[i][0].xyz, ctrl[i][width-1].xyz, delta ); len = VectorLength( delta ); if ( len > 1.0 ) { break; } } if ( i == height ) { wrapWidth = qtrue; } wrapHeight = qfalse; for ( i = 0 ; i < width ; i++ ) { VectorSubtract( ctrl[0][i].xyz, ctrl[height-1][i].xyz, delta ); len = VectorLength( delta ); if ( len > 1.0 ) { break; } } if ( i == width) { wrapHeight = qtrue; } for ( i = 0 ; i < width ; i++ ) { for ( j = 0 ; j < height ; j++ ) { count = 0; dv = &ctrl[j][i]; VectorCopy( dv->xyz, base ); for ( k = 0 ; k < 8 ; k++ ) { VectorClear( around[k] ); good[k] = qfalse; for ( dist = 1 ; dist <= 3 ; dist++ ) { x = i + neighbors[k][0] * dist; y = j + neighbors[k][1] * dist; if ( wrapWidth ) { if ( x < 0 ) { x = width - 1 + x; } else if ( x >= width ) { x = 1 + x - width; } } if ( wrapHeight ) { if ( y < 0 ) { y = height - 1 + y; } else if ( y >= height ) { y = 1 + y - height; } } if ( x < 0 || x >= width || y < 0 || y >= height ) { break; // edge of patch } VectorSubtract( ctrl[y][x].xyz, base, temp ); if ( VectorNormalize2( temp, temp ) == 0 ) { continue; // degenerate edge, get more dist } else { good[k] = qtrue; VectorCopy( temp, around[k] ); break; // good edge } } } VectorClear( sum ); for ( k = 0 ; k < 8 ; k++ ) { if ( !good[k] || !good[(k+1)&7] ) { continue; // didn't get two points } CrossProduct( around[(k+1)&7], around[k], normal ); if ( VectorNormalize2( normal, normal ) == 0 ) { continue; } VectorAdd( normal, sum, sum ); count++; } if ( count == 0 ) { //printf("bad normal\n"); count = 1; } VectorNormalize2( sum, dv->normal ); } } }
//#ifdef ME //=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== winding_t *MergeWindings(winding_t *w1, winding_t *w2, vec3_t planenormal) { winding_t *neww; float dist; int i, j, n, found, insertafter; int sides[MAX_POINTS_ON_WINDING+4]; vec3_t newp[MAX_POINTS_ON_WINDING+4]; int numpoints; vec3_t edgevec, sepnormal, v; RemoveEqualPoints(w1, 0.2); numpoints = w1->numpoints; memcpy(newp, w1->p, w1->numpoints * sizeof(vec3_t)); // for (i = 0; i < w2->numpoints; i++) { VectorCopy(w2->p[i], v); for (j = 0; j < numpoints; j++) { VectorSubtract(newp[(j+1)%numpoints], newp[(j)%numpoints], edgevec); CrossProduct(edgevec, planenormal, sepnormal); VectorNormalize(sepnormal); if (VectorLength(sepnormal) < 0.9) { //remove the point from the new winding for (n = j; n < numpoints-1; n++) { VectorCopy(newp[n+1], newp[n]); sides[n] = sides[n+1]; } //end for numpoints--; j--; Log_Print("MergeWindings: degenerate edge on winding %f %f %f\n", sepnormal[0], sepnormal[1], sepnormal[2]); continue; } //end if dist = DotProduct(newp[(j)%numpoints], sepnormal); if (DotProduct(v, sepnormal) - dist < -0.1) sides[j] = SIDE_BACK; else sides[j] = SIDE_FRONT; } //end for //remove all unnecesary points for (j = 0; j < numpoints;) { if (sides[j] == SIDE_BACK && sides[(j+1)%numpoints] == SIDE_BACK) { //remove the point from the new winding for (n = (j+1)%numpoints; n < numpoints-1; n++) { VectorCopy(newp[n+1], newp[n]); sides[n] = sides[n+1]; } //end for numpoints--; } //end if else { j++; } //end else } //end for // found = false; for (j = 0; j < numpoints; j++) { if (sides[j] == SIDE_FRONT && sides[(j+1)%numpoints] == SIDE_BACK) { if (found) Log_Print("Warning: MergeWindings: front to back found twice\n"); found = true; } //end if } //end for // for (j = 0; j < numpoints; j++) { if (sides[j] == SIDE_FRONT && sides[(j+1)%numpoints] == SIDE_BACK) { insertafter = (j+1)%numpoints; //insert the new point after j+1 for (n = numpoints-1; n > insertafter; n--) { VectorCopy(newp[n], newp[n+1]); } //end for numpoints++; VectorCopy(v, newp[(insertafter+1)%numpoints]); break; } //end if } //end for } //end for neww = AllocWinding(numpoints); neww->numpoints = numpoints; memcpy(neww->p, newp, numpoints * sizeof(vec3_t)); RemoveColinearPoints(neww); return neww; } //end of the function MergeWindings
/* ================= R_SetupEntityLighting Calculates all the lighting values that will be used by the Calc_* functions ================= */ void R_SetupEntityLighting( const trRefdef_t *refdef, trRefEntity_t *ent ) { int i; dlight_t *dl; float power; vec3_t dir; float d; vec3_t lightDir; vec3_t lightOrigin; // lighting calculations if ( ent->lightingCalculated ) { return; } ent->lightingCalculated = qtrue; // // trace a sample point down to find ambient light // if ( ent->e.renderfx & RF_LIGHTING_ORIGIN ) { // seperate lightOrigins are needed so an object that is // sinking into the ground can still be lit, and so // multi-part models can be lit identically VectorCopy( ent->e.lightingOrigin, lightOrigin ); } else { VectorCopy( ent->e.origin, lightOrigin ); } // if NOWORLDMODEL, only use dynamic lights (menu system, etc) if ( !(refdef->rdflags & RDF_NOWORLDMODEL ) && tr.world->lightGridData ) { R_SetupEntityLightingGrid( ent ); } else { ent->ambientLight[0] = ent->ambientLight[1] = ent->ambientLight[2] = tr.identityLight * 150; ent->directedLight[0] = ent->directedLight[1] = ent->directedLight[2] = tr.identityLight * 150; VectorCopy( tr.sunDirection, ent->lightDir ); } // bonus items and view weapons have a fixed minimum add if ( 1 /* ent->e.renderfx & RF_MINLIGHT */ ) { // give everything a minimum light add ent->ambientLight[0] += tr.identityLight * 32; ent->ambientLight[1] += tr.identityLight * 32; ent->ambientLight[2] += tr.identityLight * 32; } // // modify the light by dynamic lights // d = VectorLength( ent->directedLight ); VectorScale( ent->lightDir, d, lightDir ); for ( i = 0 ; i < refdef->num_dlights ; i++ ) { dl = &refdef->dlights[i]; VectorSubtract( dl->origin, lightOrigin, dir ); d = VectorNormalize( dir ); power = DLIGHT_AT_RADIUS * ( dl->radius * dl->radius ); if ( d < DLIGHT_MINIMUM_RADIUS ) { d = DLIGHT_MINIMUM_RADIUS; } d = power / ( d * d ); VectorMA( ent->directedLight, d, dl->color, ent->directedLight ); VectorMA( lightDir, d, dir, lightDir ); } // clamp ambient for ( i = 0 ; i < 3 ; i++ ) { if ( ent->ambientLight[i] > tr.identityLightByte ) { ent->ambientLight[i] = tr.identityLightByte; } } if ( r_debugLight->integer ) { LogLight( ent ); } // save out the byte packet version ((byte *)&ent->ambientLightInt)[0] = myftol( ent->ambientLight[0] ); ((byte *)&ent->ambientLightInt)[1] = myftol( ent->ambientLight[1] ); ((byte *)&ent->ambientLightInt)[2] = myftol( ent->ambientLight[2] ); ((byte *)&ent->ambientLightInt)[3] = 0xff; // transform the direction to local space VectorNormalize( lightDir ); ent->lightDir[0] = DotProduct( lightDir, ent->e.axis[0] ); ent->lightDir[1] = DotProduct( lightDir, ent->e.axis[1] ); ent->lightDir[2] = DotProduct( lightDir, ent->e.axis[2] ); }
//=========================================================================== // // Parameter: - // Returns: - // Changes Globals: - //=========================================================================== int WindingError(winding_t *w) { int i, j; vec_t *p1, *p2; vec_t d, edgedist; vec3_t dir, edgenormal, facenormal; vec_t area; vec_t facedist; if (w->numpoints < 3) { sprintf(windingerror, "winding %i points", w->numpoints); return WE_NOTENOUGHPOINTS; } //end if area = WindingArea(w); if (area < 1) { sprintf(windingerror, "winding %f area", area); return WE_SMALLAREA; } //end if WindingPlane (w, facenormal, &facedist); for (i=0 ; i<w->numpoints ; i++) { p1 = w->p[i]; for (j=0 ; j<3 ; j++) { if (p1[j] > BOGUS_RANGE || p1[j] < -BOGUS_RANGE) { sprintf(windingerror, "winding point %d BUGUS_RANGE \'%f %f %f\'", j, p1[0], p1[1], p1[2]); return WE_POINTBOGUSRANGE; } //end if } //end for j = i+1 == w->numpoints ? 0 : i+1; // check the point is on the face plane d = DotProduct (p1, facenormal) - facedist; if (d < -ON_EPSILON || d > ON_EPSILON) { sprintf(windingerror, "winding point %d off plane", i); return WE_POINTOFFPLANE; } //end if // check the edge isnt degenerate p2 = w->p[j]; VectorSubtract (p2, p1, dir); if (VectorLength (dir) < ON_EPSILON) { sprintf(windingerror, "winding degenerate edge %d-%d", i, j); return WE_DEGENERATEEDGE; } //end if CrossProduct (facenormal, dir, edgenormal); VectorNormalize (edgenormal); edgedist = DotProduct (p1, edgenormal); edgedist += ON_EPSILON; // all other points must be on front side for (j=0 ; j<w->numpoints ; j++) { if (j == i) continue; d = DotProduct (w->p[j], edgenormal); if (d > edgedist) { sprintf(windingerror, "winding non-convex"); return WE_NONCONVEX; } //end if } //end for } //end for return WE_NONE; } //end of the function WindingError
void G_MissileImpact( gentity_t *ent, trace_t *trace ) { gentity_t *other; qboolean hitClient = qfalse; qboolean isKnockedSaber = qfalse; other = &g_entities[trace->entityNum]; // check for bounce if ( !other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags & ( FL_BOUNCE | FL_BOUNCE_HALF ) ) ) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } else if (ent->neverFree && ent->s.weapon == WP_SABER && (ent->flags & FL_BOUNCE_HALF)) { //this is a knocked-away saber if (ent->bounceCount > 0 || ent->bounceCount == -5) { G_BounceMissile( ent, trace ); G_AddEvent( ent, EV_GRENADE_BOUNCE, 0 ); return; } isKnockedSaber = qtrue; } // I would glom onto the FL_BOUNCE code section above, but don't feel like risking breaking something else if ( (!other->takedamage && (ent->bounceCount > 0 || ent->bounceCount == -5) && ( ent->flags&(FL_BOUNCE_SHRAPNEL) ) ) || ((trace->surfaceFlags&SURF_FORCEFIELD)&&!ent->splashDamage&&!ent->splashRadius&&(ent->bounceCount > 0 || ent->bounceCount == -5)) ) { G_BounceMissile( ent, trace ); if ( ent->bounceCount < 1 ) { ent->flags &= ~FL_BOUNCE_SHRAPNEL; } return; } /* if ( !other->takedamage && ent->s.weapon == WP_THERMAL && !ent->alt_fire ) {//rolling thermal det - FIXME: make this an eFlag like bounce & stick!!! //G_BounceRollMissile( ent, trace ); if ( ent->owner && ent->owner->s.number == 0 ) { G_MissileAddAlerts( ent ); } //trap->linkentity( ent ); return; } */ if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && otherOwner->client->ps.duelInProgress && otherOwner->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } else if (!isKnockedSaber) { if (other->takedamage && other->client && other->client->ps.duelInProgress && other->client->ps.duelIndex != ent->r.ownerNum) { goto killProj; } } if (other->flags & FL_DMG_BY_HEAVY_WEAP_ONLY) { if (ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_ROCKET && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_ROCKET_HOMING && ent->methodOfDeath != MOD_THERMAL && ent->methodOfDeath != MOD_THERMAL_SPLASH && ent->methodOfDeath != MOD_TRIP_MINE_SPLASH && ent->methodOfDeath != MOD_TIMED_MINE_SPLASH && ent->methodOfDeath != MOD_DET_PACK_SPLASH && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && ent->methodOfDeath != MOD_SABER && ent->methodOfDeath != MOD_TURBLAST) { vec3_t fwd; if (trace) { VectorCopy(trace->plane.normal, fwd); } else { //oh well AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } } if ((other->flags & FL_SHIELDED) && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->s.weapon != WP_EMPLACED_GUN && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_TURBLAST && ent->methodOfDeath != MOD_VEHICLE && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && !(ent->dflags&DAMAGE_HEAVY_WEAP_CLASS) ) { vec3_t fwd; if (other->client) { AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); } else { AngleVectors(other->r.currentAngles, fwd, NULL, NULL); } G_DeflectMissile(other, ent, fwd); G_MissileBounceEffect(ent, ent->r.currentOrigin, fwd); return; } if (other->takedamage && other->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT && other->client->ps.saberBlockTime < level.time && !isKnockedSaber && WP_SaberCanBlock(other, ent->r.currentOrigin, 0, 0, qtrue, 0)) { //only block one projectile per 200ms (to prevent giant swarms of projectiles being blocked) vec3_t fwd; gentity_t *te; int otherDefLevel = other->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove || other->client->pers.cmd.rightmove) */ if (other->client->ps.velocity[2] > 0 || other->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(other->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(other, ent, fwd); } else { G_ReflectMissile(other, ent, fwd); } other->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100)); //200; //For jedi AI other->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { other->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } else if ((other->r.contents & CONTENTS_LIGHTSABER) && !isKnockedSaber) { //hit this person's saber, so.. gentity_t *otherOwner = &g_entities[other->r.ownerNum]; if (otherOwner->takedamage && otherOwner->client && ent->s.weapon != WP_ROCKET_LAUNCHER && ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_TRIP_MINE && ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_DEMP2 && ent->methodOfDeath != MOD_REPEATER_ALT && ent->methodOfDeath != MOD_FLECHETTE_ALT_SPLASH && ent->methodOfDeath != MOD_CONC && ent->methodOfDeath != MOD_CONC_ALT /*&& otherOwner->client->ps.saberBlockTime < level.time*/) { //for now still deflect even if saberBlockTime >= level.time because it hit the actual saber vec3_t fwd; gentity_t *te; int otherDefLevel = otherOwner->client->ps.fd.forcePowerLevel[FP_SABER_DEFENSE]; //in this case, deflect it even if we can't actually block it because it hit our saber //WP_SaberCanBlock(otherOwner, ent->r.currentOrigin, 0, 0, qtrue, 0); if (otherOwner->client && otherOwner->client->ps.weaponTime <= 0) { WP_SaberBlockNonRandom(otherOwner, ent->r.currentOrigin, qtrue); } te = G_TempEntity( ent->r.currentOrigin, EV_SABER_BLOCK ); VectorCopy(ent->r.currentOrigin, te->s.origin); VectorCopy(trace->plane.normal, te->s.angles); te->s.eventParm = 0; te->s.weapon = 0;//saberNum te->s.legsAnim = 0;//bladeNum /*if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove || otherOwner->client->pers.cmd.rightmove)*/ if (otherOwner->client->ps.velocity[2] > 0 || otherOwner->client->pers.cmd.forwardmove < 0) //now we only do it if jumping or running backward. Should be able to full-on charge. { otherDefLevel -= 1; if (otherDefLevel < 0) { otherDefLevel = 0; } } AngleVectors(otherOwner->client->ps.viewangles, fwd, NULL, NULL); if (otherDefLevel == FORCE_LEVEL_1) { //if def is only level 1, instead of deflecting the shot it should just die here } else if (otherDefLevel == FORCE_LEVEL_2) { G_DeflectMissile(otherOwner, ent, fwd); } else { G_ReflectMissile(otherOwner, ent, fwd); } otherOwner->client->ps.saberBlockTime = level.time + (350 - (otherDefLevel*100));//200; //For jedi AI otherOwner->client->ps.saberEventFlags |= SEF_DEFLECTED; if (otherDefLevel == FORCE_LEVEL_3) { otherOwner->client->ps.saberBlockTime = 0; //^_^ } if (otherDefLevel == FORCE_LEVEL_1) { goto killProj; } return; } } // check for sticking if ( !other->takedamage && ( ent->s.eFlags & EF_MISSILE_STICK ) ) { laserTrapStick( ent, trace->endpos, trace->plane.normal ); G_AddEvent( ent, EV_MISSILE_STICK, 0 ); return; } // impact damage if (other->takedamage && !isKnockedSaber) { // FIXME: wrong damage direction? if ( ent->damage ) { vec3_t velocity; qboolean didDmg = qfalse; if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue; } BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); if ( VectorLength( velocity ) == 0 ) { velocity[2] = 1; // stepped on a grenade } if (ent->s.weapon == WP_BOWCASTER || ent->s.weapon == WP_FLECHETTE || ent->s.weapon == WP_ROCKET_LAUNCHER) { if (ent->s.weapon == WP_FLECHETTE && (ent->s.eFlags & EF_ALT_FIRING)) { /* fix: there are rare situations where flechette did explode by timeout AND by impact in the very same frame, then here ent->think was set to G_FreeEntity, so the folowing think did invalidate this entity, BUT it would be reused later in this function for explosion event. This, then, would set ent->freeAfterEvent to qtrue, so event later, when reusing this entity by using G_InitEntity(), it would have this freeAfterEvent set AND this would in case of dropped item erase it from game immeadiately. THIS for example caused very rare flag dissappearing bug. */ if (ent->think == WP_flechette_alt_blow) ent->think(ent); } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, DAMAGE_HALF_ABSORB, ent->methodOfDeath); didDmg = qtrue; } } else { G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, /*ent->s.origin*/ent->r.currentOrigin, ent->damage, 0, ent->methodOfDeath); didDmg = qtrue; } if (didDmg && other && other->client) { //What I'm wondering is why this isn't in the NPC pain funcs. But this is what SP does, so whatever. class_t npc_class = other->client->NPC_class; // If we are a robot and we aren't currently doing the full body electricity... if ( npc_class == CLASS_SEEKER || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE || npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 || npc_class == CLASS_REMOTE || npc_class == CLASS_MARK1 || npc_class == CLASS_MARK2 || //npc_class == CLASS_PROTOCOL ||//no protocol, looks odd npc_class == CLASS_INTERROGATOR || npc_class == CLASS_ATST || npc_class == CLASS_SENTRY ) { // special droid only behaviors if ( other->client->ps.electrifyTime < level.time + 100 ) { // ... do the effect for a split second for some more feedback other->client->ps.electrifyTime = level.time + 450; } //FIXME: throw some sparks off droids,too } } } if ( ent->s.weapon == WP_DEMP2 ) {//a hit with demp2 decloaks people, disables ships if ( other && other->client && other->client->NPC_class == CLASS_VEHICLE ) {//hit a vehicle if ( other->m_pVehicle //valid vehicle ent && other->m_pVehicle->m_pVehicleInfo//valid stats && (other->m_pVehicle->m_pVehicleInfo->type == VH_SPEEDER//always affect speeders ||(other->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER && ent->classname && Q_stricmp("vehicle_proj", ent->classname ) == 0) )//only vehicle ion weapons affect a fighter in this manner && !FighterIsLanded( other->m_pVehicle , &other->client->ps )//not landed && !(other->spawnflags&2) )//and not suspended {//vehicles hit by "ion cannons" lose control if ( other->client->ps.electrifyTime > level.time ) {//add onto it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime += Q_irand(200,500); if ( other->client->ps.electrifyTime > level.time + 4000 ) {//cap it other->client->ps.electrifyTime = level.time + 4000; } } else {//start it //FIXME: extern the length of the "out of control" time? other->client->ps.electrifyTime = level.time + Q_irand(200,500); } } } else if ( other && other->client && other->client->ps.powerups[PW_CLOAKED] ) { Jedi_Decloak( other ); if ( ent->methodOfDeath == MOD_DEMP2_ALT ) {//direct hit with alt disables cloak forever //permanently disable the saboteur's cloak other->client->cloakToggleTime = Q3_INFINITE; } else {//temp disable other->client->cloakToggleTime = level.time + Q_irand( 3000, 10000 ); } } } } killProj: // is it cheaper in bandwidth to just remove this ent and create a new // one, rather than changing the missile into the explosion? if ( other->takedamage && other->client && !isKnockedSaber ) { G_AddEvent( ent, EV_MISSILE_HIT, DirToByte( trace->plane.normal ) ); ent->s.otherEntityNum = other->s.number; } else if( trace->surfaceFlags & SURF_METALSTEPS ) { G_AddEvent( ent, EV_MISSILE_MISS_METAL, DirToByte( trace->plane.normal ) ); } else if (ent->s.weapon != G2_MODEL_PART && !isKnockedSaber) { G_AddEvent( ent, EV_MISSILE_MISS, DirToByte( trace->plane.normal ) ); } if (!isKnockedSaber) { ent->freeAfterEvent = qtrue; // change over to a normal entity right at the point of impact ent->s.eType = ET_GENERAL; } SnapVectorTowards( trace->endpos, ent->s.pos.trBase ); // save net bandwidth G_SetOrigin( ent, trace->endpos ); ent->takedamage = qfalse; // splash damage (doesn't apply to person directly hit) if ( ent->splashDamage ) { if( G_RadiusDamage( trace->endpos, ent->parent, ent->splashDamage, ent->splashRadius, other, ent, ent->splashMethodOfDeath ) ) { if( !hitClient && g_entities[ent->r.ownerNum].client ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; } } } if (ent->s.weapon == G2_MODEL_PART) { ent->freeAfterEvent = qfalse; //it will free itself } trap->LinkEntity( (sharedEntity_t *)ent ); }
void PM_VehicleImpact(bgEntity_t *pEnt, trace_t *trace) { // See if the vehicle has crashed into the ground. Vehicle_t *pSelfVeh = pEnt->m_pVehicle; float magnitude = VectorLength( pm->ps->velocity ) * pSelfVeh->m_pVehicleInfo->mass / 50.0f; qboolean forceSurfDestruction = qfalse; #ifdef QAGAME gentity_t *hitEnt = trace!=NULL?&g_entities[trace->entityNum]:NULL; if (!hitEnt || (pSelfVeh && pSelfVeh->m_pPilot && hitEnt && hitEnt->s.eType == ET_MISSILE && hitEnt->inuse && hitEnt->r.ownerNum == pSelfVeh->m_pPilot->s.number) ) { return; } if ( pSelfVeh//I have a vehicle struct && pSelfVeh->m_iRemovedSurfaces )//vehicle has bits removed {//spiralling to our deaths, explode on any solid impact if ( hitEnt->s.NPC_class == CLASS_VEHICLE ) {//hit another vehicle, explode! //Give credit to whoever got me into this death spiral state gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } //FIXME: damage hitEnt, some, too? Our explosion should hurt them some, but... G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT return; } else if ( !VectorCompare( trace->plane.normal, vec3_origin ) && (trace->entityNum == ENTITYNUM_WORLD || hitEnt->r.bmodel ) ) {//have a valid hit plane and we hit a solid brush vec3_t moveDir; float impactDot; VectorCopy( pm->ps->velocity, moveDir ); VectorNormalize( moveDir ); impactDot = DotProduct( moveDir, trace->plane.normal ); if ( impactDot <= -0.7f )//hit rather head-on and hard {// Just DIE now //Give credit to whoever got me into this death spiral state gentity_t *parent = (gentity_t *)pSelfVeh->m_pParentEntity; gentity_t *killer = NULL; if (parent->client->ps.otherKiller < ENTITYNUM_WORLD && parent->client->ps.otherKillerTime > level.time) { gentity_t *potentialKiller = &g_entities[parent->client->ps.otherKiller]; if (potentialKiller->inuse && potentialKiller->client) { //he's valid I guess killer = potentialKiller; } } G_Damage( (gentity_t *)pEnt, killer, killer, NULL, pm->ps->origin, 999999, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT return; } } } if ( trace->entityNum < ENTITYNUM_WORLD && hitEnt->s.eType == ET_MOVER && hitEnt->s.apos.trType != TR_STATIONARY//rotating && (hitEnt->spawnflags&16) //IMPACT && Q_stricmp( "func_rotating", hitEnt->classname ) == 0 ) {//hit a func_rotating that is supposed to destroy anything it touches! //guarantee the hit will happen, thereby taking off a piece of the ship forceSurfDestruction = qtrue; } else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #else if ( (fabs(pm->ps->velocity[0])+fabs(pm->ps->velocity[1])) < 100.0f && pm->ps->velocity[2] > -100.0f ) #endif /* if ( (pSelfVeh->m_ulFlags&VEH_GEARSOPEN) && trace->plane.normal[2] > 0.7f && fabs(pSelfVeh->m_vOrientation[PITCH]) < 0.2f && fabs(pSelfVeh->m_vOrientation[ROLL]) < 0.2f )*/ {//we're landing, we're cool //this was annoying me -rww //FIXME: this shouldn't even be getting called when the vehicle is at rest! #ifdef QAGAME if (hitEnt && (hitEnt->s.eType == ET_PLAYER || hitEnt->s.eType == ET_NPC) && pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //always smack players } else #endif { return; } } if ( pSelfVeh && (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) && //this is kind of weird on tauntauns and atst's.. (magnitude >= 100||forceSurfDestruction) ) { if ( pEnt->m_pVehicle->m_iHitDebounce < pm->cmd.serverTime || forceSurfDestruction ) {//a bit of a hack, may conflict with getting shot, but... //FIXME: impact sound and effect should be gotten from g_vehicleInfo...? //FIXME: should pass in trace.endpos and trace.plane.normal vec3_t vehUp; #ifndef QAGAME bgEntity_t *hitEnt; #endif if ( trace && !pSelfVeh->m_iRemovedSurfaces && !forceSurfDestruction ) { qboolean turnFromImpact = qfalse, turnHitEnt = qfalse; float l = pm->ps->speed*0.5f; vec3_t bounceDir; #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( (trace->entityNum == ENTITYNUM_WORLD || hitEnt->s.solid == SOLID_BMODEL)//bounce off any brush && !VectorCompare(trace->plane.normal, vec3_origin) )//have a valid plane to bounce off of { //bounce off in the opposite direction of the impact if (pSelfVeh->m_pVehicleInfo->type == VH_SPEEDER) { pm->ps->speed *= pml.frametime; VectorCopy(trace->plane.normal, bounceDir); } else if ( trace->plane.normal[2] >= MIN_LANDING_SLOPE//flat enough to land on && pSelfVeh->m_LandTrace.fraction < 1.0f //ground present && pm->ps->speed <= MIN_LANDING_SPEED ) {//could land here, don't bounce off, in fact, return altogether! return; } else { if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { turnFromImpact = qtrue; } VectorCopy(trace->plane.normal, bounceDir); } } else if ( pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER ) {//check for impact with another fighter #ifndef QAGAME bgEntity_t *hitEnt = PM_BGEntForNum(trace->entityNum); #endif if ( hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle && hitEnt->m_pVehicle->m_pVehicleInfo && hitEnt->m_pVehicle->m_pVehicleInfo->type == VH_FIGHTER ) {//two vehicles hit each other, turn away from the impact turnFromImpact = qtrue; turnHitEnt = qtrue; #ifndef QAGAME VectorSubtract( pm->ps->origin, hitEnt->s.origin, bounceDir ); #else VectorSubtract( pm->ps->origin, hitEnt->r.currentOrigin, bounceDir ); #endif VectorNormalize( bounceDir ); } } if ( turnFromImpact ) {//bounce off impact surf and turn away vec3_t pushDir={0}, turnAwayAngles, turnDelta; float turnStrength, pitchTurnStrength, yawTurnStrength; vec3_t moveDir; float bounceDot, turnDivider; //bounce if ( !turnHitEnt ) {//hit wall VectorScale(bounceDir, (pm->ps->speed*0.25f/pSelfVeh->m_pVehicleInfo->mass), pushDir); } else {//hit another fighter #ifndef QAGAME VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, bounceDir ); #else if ( hitEnt->client ) { VectorScale( bounceDir, (pm->ps->speed+hitEnt->client->ps.speed)*0.5f, pushDir ); } else { VectorScale( bounceDir, (pm->ps->speed+hitEnt->s.speed)*0.5f, pushDir ); } #endif VectorScale(pushDir, (l/pSelfVeh->m_pVehicleInfo->mass), pushDir); VectorScale(pushDir, 0.1f, pushDir); } VectorNormalize2( pm->ps->velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(pm->ps->velocity, pushDir, pm->ps->velocity); //turn turnDivider = (pSelfVeh->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } turnStrength = (magnitude/2000.0f); if ( turnStrength < 0.1f ) { turnStrength = 0.1f; } else if ( turnStrength > 2.0f ) { turnStrength = 2.0f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, pSelfVeh->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } pSelfVeh->m_vFullAngleVelocity[PITCH] = AngleNormalize180(pSelfVeh->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } pSelfVeh->m_vFullAngleVelocity[ROLL] = AngleNormalize180(pSelfVeh->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } #ifdef QAGAME//server-side, turn the guy we hit away from us, too if ( turnHitEnt//make the other guy turn and get pushed && hitEnt->client //must be a valid client && !FighterIsLanded( hitEnt->m_pVehicle, &hitEnt->client->ps )//but not if landed && !(hitEnt->spawnflags&2) )//and not if suspended { l = hitEnt->client->ps.speed; //now bounce *them* away and turn them //flip the bounceDir VectorScale( bounceDir, -1, bounceDir ); //do bounce VectorScale( bounceDir, (pm->ps->speed+l)*0.5f, pushDir ); VectorScale(pushDir, (l*0.5f/hitEnt->m_pVehicle->m_pVehicleInfo->mass), pushDir); VectorNormalize2( hitEnt->client->ps.velocity, moveDir ); bounceDot = DotProduct( moveDir, bounceDir )*-1; if ( bounceDot < 0.1f ) { bounceDot = 0.1f; } VectorScale( pushDir, bounceDot, pushDir ); VectorAdd(hitEnt->client->ps.velocity, pushDir, hitEnt->client->ps.velocity); //turn turnDivider = (hitEnt->m_pVehicle->m_pVehicleInfo->mass/400.0f); if ( turnHitEnt ) {//don't turn as much when hit another ship turnDivider *= 4.0f; } if ( turnDivider < 0.5f ) { turnDivider = 0.5f; } //get the angles we are going to turn towards vectoangles( bounceDir, turnAwayAngles ); //get the delta from our current angles to those new angles AnglesSubtract( turnAwayAngles, hitEnt->m_pVehicle->m_vOrientation, turnDelta ); //now do pitch if ( !bounceDir[2] ) {//shouldn't be any pitch } else { pitchTurnStrength = turnStrength*turnDelta[PITCH]; if ( pitchTurnStrength > MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( pitchTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { pitchTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[PITCH] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[PITCH]+pitchTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //now do yaw if ( !bounceDir[0] && !bounceDir[1] ) {//shouldn't be any yaw } else { yawTurnStrength = turnStrength*turnDelta[YAW]; if ( yawTurnStrength > MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = MAX_IMPACT_TURN_ANGLE; } else if ( yawTurnStrength < -MAX_IMPACT_TURN_ANGLE ) { yawTurnStrength = -MAX_IMPACT_TURN_ANGLE; } hitEnt->m_pVehicle->m_vFullAngleVelocity[ROLL] = AngleNormalize180(hitEnt->m_pVehicle->m_vOrientation[ROLL]-yawTurnStrength/turnDivider*pSelfVeh->m_fTimeModifier); } //NOTE: will these angle changes stick or will they be stomped // when the vehicle goes through its own update and re-grabs // its angles from its pilot...? Should we do a // SetClientViewAngles on the pilot? } #endif } } #ifdef QAGAME if (!hitEnt) { return; } AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); if ( pSelfVeh->m_pVehicleInfo->iImpactFX ) { //tempent use bad! G_AddEvent((gentity_t *)pEnt, EV_PLAY_EFFECT_ID, pSelfVeh->m_pVehicleInfo->iImpactFX); } pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; magnitude /= pSelfVeh->m_pVehicleInfo->toughness * 50.0f; if (hitEnt && (hitEnt->s.eType != ET_TERRAIN || !(hitEnt->spawnflags & 1) || pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER)) { //don't damage the vehicle from terrain that doesn't want to damage vehicles if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //increase the damage... float mult = (pSelfVeh->m_vOrientation[PITCH]*0.1f); if (mult < 1.0f) { mult = 1.0f; } if (hitEnt->inuse && hitEnt->takedamage) { //if the other guy takes damage, don't hurt us a lot for ramming him //unless it's a vehicle, then we get 1.5 times damage if (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class == CLASS_VEHICLE && hitEnt->m_pVehicle) { mult = 1.5f; } else { mult = 0.5f; } } magnitude *= mult; } pSelfVeh->m_iLastImpactDmg = magnitude; //FIXME: what about proper death credit to the guy who shot you down? //FIXME: actually damage part of the ship that impacted? G_Damage( (gentity_t *)pEnt, NULL, NULL, NULL, pm->ps->origin, magnitude*5, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT if (pSelfVeh->m_pVehicleInfo->surfDestruction) { G_FlyVehicleSurfaceDestruction((gentity_t *)pEnt, trace, magnitude, forceSurfDestruction ); } pSelfVeh->m_ulFlags |= VEH_CRASHING; } if (hitEnt && hitEnt->inuse && hitEnt->takedamage) { //damage this guy because we hit him float pmult = 1.0f; int finalD; gentity_t *attackEnt; if ( (hitEnt->s.eType == ET_PLAYER && hitEnt->s.number < MAX_CLIENTS) || (hitEnt->s.eType == ET_NPC && hitEnt->s.NPC_class != CLASS_VEHICLE) ) { //probably a humanoid, or something if (pSelfVeh->m_pVehicleInfo->type == VH_FIGHTER) { //player die good.. if me fighter pmult = 2000.0f; } else { pmult = 40.0f; } if (hitEnt->client && BG_KnockDownable(&hitEnt->client->ps) && G_CanBeEnemy((gentity_t *)pEnt, hitEnt)) { //smash! if (hitEnt->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN) { hitEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; hitEnt->client->ps.forceHandExtendTime = pm->cmd.serverTime + 1100; hitEnt->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim } hitEnt->client->ps.otherKiller = pEnt->s.number; hitEnt->client->ps.otherKillerTime = pm->cmd.serverTime + 5000; hitEnt->client->ps.otherKillerDebounceTime = pm->cmd.serverTime + 100; //add my velocity into his to force him along in the correct direction from impact VectorAdd(hitEnt->client->ps.velocity, pm->ps->velocity, hitEnt->client->ps.velocity); //upward thrust hitEnt->client->ps.velocity[2] += 200.0f; } } if (pSelfVeh->m_pPilot) { attackEnt = (gentity_t *)pSelfVeh->m_pPilot; } else { attackEnt = (gentity_t *)pEnt; } finalD = magnitude*pmult; if (finalD < 1) { finalD = 1; } G_Damage( hitEnt, attackEnt, attackEnt, NULL, pm->ps->origin, finalD, 0, MOD_MELEE );//FIXME: MOD_IMPACT } #else //this is gonna result in "double effects" for the client doing the prediction. //it doesn't look bad though. could just use predicted events, but I'm too lazy. hitEnt = PM_BGEntForNum(trace->entityNum); if (!hitEnt || hitEnt->s.owner != pEnt->s.number) { //don't hit your own missiles! AngleVectors( pSelfVeh->m_vOrientation, NULL, NULL, vehUp ); pEnt->m_pVehicle->m_iHitDebounce = pm->cmd.serverTime + 200; trap_FX_PlayEffectID( pSelfVeh->m_pVehicleInfo->iImpactFX, pm->ps->origin, vehUp, -1, -1 ); pSelfVeh->m_ulFlags |= VEH_CRASHING; } #endif } } }
/*** Moves an entity like a func_train entity. Targets have to be path_corner entities. @function AsTrain @param mover Entity to move. @param target path_corner entity to move to. @param speed Speed to move with to the first path_corner. @return Success or failure. */ static int Mover_AsTrain(lua_State * L) { lent_t *lent, *tlent; gentity_t *ent = NULL; gentity_t *targ = NULL; vec3_t move; float length; int id = 0, tid = 0; float speed = (float)luaL_checknumber(L, 3); if(lua_isnumber(L, 1)) { id = luaL_checkint(L, 1); if(id < 0 || id > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } ent = &g_entities[id]; if(ent == NULL) { lua_pushboolean(L, qfalse); return 1; } } else { lent = Lua_GetEntity(L, 1); if(lent == NULL || lent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } ent = lent->e; } if(luaL_checkint(L, 2)) { tid = luaL_checkint(L, 2); if(tid < 0 || tid > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } targ = &g_entities[tid]; if(targ == NULL) { lua_pushboolean(L, qfalse); return 1; } } else { tlent = Lua_GetEntity(L, 2); if(!tlent || tlent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } targ = tlent->e; } LUA_DEBUG("Mover_AsTrain - start: ent=%d target=%d speed=%f", ent->s.number, targ->s.number, speed); if(ent == NULL || targ == NULL) { LUA_DEBUG("Mover_AsTrain - return: ent or/and target missing"); lua_pushboolean(L, qfalse); return 1; } if(speed < 1) { LUA_DEBUG("Mover_AsTrain - moving: speed less than 1 fixed"); speed = 1; } if(ent->nextTrain) { LUA_DEBUG("Mover_AsTrain - pathing: NextTrain=%d ", ent->nextTrain->s.number); } ent->speed = speed; ent->nextTrain = targ; ent->reached = Reached_Train; ent->target = G_NewString(targ->targetname); Think_SetupTrainTargets(ent); BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->r.currentOrigin); VectorCopy(ent->r.currentOrigin, ent->s.origin); VectorCopy(ent->s.origin, ent->pos1); VectorCopy(ent->nextTrain->s.origin, ent->pos2); VectorSubtract(ent->pos2, ent->pos1, move); length = VectorLength(move); if(length <= 0.05) { G_SetOrigin(ent, ent->pos2); LUA_DEBUG("Mover_AsTrain - return: snapped to target, length too small length=%f", length); lua_pushboolean(L, qtrue); return 1; } ent->s.pos.trDuration = length * 1000 / speed; ent->s.loopSound = ent->nextTrain->soundLoop; SetMoverState(ent, MOVER_1TO2, level.time); LUA_DEBUG("Mover_AsTrain - return: moving to target, length=%f duration=%d", length, ent->s.pos.trDuration); lua_pushboolean(L, qtrue); return 1; }
// use this to change between particle and trail code //#define BLOOD_PARTICLE_TRAIL void CG_BloodTrail(localEntity_t *le) { int t; int t2; int step; vec3_t newOrigin; float vl; //bani #ifndef BLOOD_PARTICLE_TRAIL static vec3_t col = { 1, 1, 1 }; #endif if (!cg_blood.integer) { return; } // step = 150; #ifdef BLOOD_PARTICLE_TRAIL step = 10; #else // time it takes to move 3 units vl = VectorLength(le->pos.trDelta); // rain - need to check FLT_EPSILON because floating-point math sucks <3 if (vl < FLT_EPSILON) { return; } step = (1000 * 3) / vl; // avoid another div by 0 // check against <= 0 instead of == 0, because it can still wrap; (3000 / (FLT_EPSILON*11.7f)) < 0 if (step <= 0) { return; } #endif t = step * ((cg.time - cg.frametime + step) / step); t2 = step * (cg.time / step); for ( ; t <= t2; t += step) { BG_EvaluateTrajectory(&le->pos, t, newOrigin, qfalse, -1); #ifdef BLOOD_PARTICLE_TRAIL CG_Particle_Bleed(cgs.media.smokePuffShader, newOrigin, vec3_origin, 0, 500 + rand() % 200); #else // blood trail using trail code (should be faster since we don't have to spawn as many) le->headJuncIndex = CG_AddTrailJunc(le->headJuncIndex, le, // rain - zinx's trail fix cgs.media.bloodTrailShader, t, STYPE_STRETCH, newOrigin, 180, 1.0, // start alpha 0.0, // end alpha 12.0, 12.0, TJFL_NOCULL, col, col, 0, 0); #endif } }
/* =========== FindTarget Self is currently not attacking anything, so try to find a target Returns TRUE if an enemy was sighted When a player fires a missile, the point of impact becomes a fakeplayer so that monsters that see the impact will respond as if they had seen the player. To avoid spending too much time, only a single client (or fakeclient) is checked each frame. This means multi player games will have slightly slower noticing monsters. ============ */ bool FindTarget (edict_t *self) { edict_t *client; bool heardit; bool noise; int r; if (self->monsterinfo.aiflags & AI_GOOD_GUY) { if (self->goalentity && self->goalentity->r.inuse && self->goalentity->classname) { if (strcmp(self->goalentity->classname, "target_actor") == 0) return false; } //FIXME look for monsters? return false; } // if we're going to a combat point, just proceed if (self->monsterinfo.aiflags & AI_COMBAT_POINT) return false; // if the first spawnflag bit is set, the monster will only wake up on // really seeing the player, not another monster getting angry or hearing // something // revised behavior so they will wake up if they "see" a player make a noise // but not weapon impact/explosion noises heardit = false; if ((level.sight_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) ) { client = level.sight_entity; if (client->enemy == self->enemy) { return false; } } else if (level.sound_entity_framenum+1 >= level.framenum) { client = level.sound_entity; heardit = true; } else if (!(self->enemy) && (level.sound2_entity_framenum+1 >= level.framenum) && !(self->spawnflags & 1) ) { client = level.sound2_entity; heardit = true; } else { client = level.sight_client; if (!client) return false; // no clients to get mad at } // if the entity went away, forget it if (!client->r.inuse) return false; if (client == self->enemy) return true; // JDC false; noise = strcmp(client->classname, "player_noise") == 0; if (!noise && (client->r.svflags & SVF_NOCLIENT)) return false; if (client->r.client) { if (client->flags & FL_NOTARGET) return false; } else if (client->r.svflags & SVF_MONSTER) { if (!client->enemy) return false; if (client->enemy->flags & FL_NOTARGET) return false; } else if (heardit) { if (client->r.owner->flags & FL_NOTARGET) return false; } else return false; if (!heardit) { r = range (self, client); if (r == RANGE_FAR) return false; // this is where we would check invisibility // is client in an spot too dark to be seen? // if (client->light_level <= 5) // return false; if (!G_Visible (self, client)) { return false; } if (r == RANGE_NEAR) { if (client->show_hostile < level.time && !G_InFront (self, client)) { return false; } } else if (r == RANGE_MID) { if (!G_InFront (self, client)) { return false; } } self->enemy = client; if (!noise) { self->monsterinfo.aiflags &= ~AI_SOUND_TARGET; if (!self->enemy->r.client) { self->enemy = self->enemy->enemy; if (!self->enemy->r.client) { self->enemy = NULL; return false; } } } } else // heardit { vec3_t temp; if (self->spawnflags & 1) { if (!G_Visible (self, client)) return false; } VectorSubtract (client->s.origin, self->s.origin, temp); if (VectorLength(temp) > 1000) // too far to hear { return false; } // check area portals - if they are different and not connected then we can't hear it if (client->r.areanum != self->r.areanum) if (!trap_CM_AreasConnected(self->r.areanum, client->r.areanum)) return false; self->ideal_yaw = vectoyaw(temp); M_ChangeYaw (self); // hunt the sound for a bit; hopefully find the real player self->monsterinfo.aiflags |= AI_SOUND_TARGET; self->enemy = client; } // // got one // FoundTarget (self); if (!(self->monsterinfo.aiflags & AI_SOUND_TARGET) && (self->monsterinfo.sight)) self->monsterinfo.sight (self, self->enemy); return true; }
void CG_AddScorePlum( localEntity_t *le ) { refEntity_t *re; vec3_t origin, delta, dir, vec, up = {0, 0, 1}; float c, len; int i, score, digits[10], numdigits, negative; re = &le->refEntity; c = ( le->endTime - cg.time ) * le->lifeRate; score = le->radius; if (score < 0) { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0x11; re->shaderRGBA[2] = 0x11; } else { re->shaderRGBA[0] = 0xff; re->shaderRGBA[1] = 0xff; re->shaderRGBA[2] = 0xff; if (score >= 50) { re->shaderRGBA[1] = 0; } else if (score >= 20) { re->shaderRGBA[0] = re->shaderRGBA[1] = 0; } else if (score >= 10) { re->shaderRGBA[2] = 0; } else if (score >= 2) { re->shaderRGBA[0] = re->shaderRGBA[2] = 0; } } if (c < 0.25) re->shaderRGBA[3] = 0xff * 4 * c; else re->shaderRGBA[3] = 0xff; re->radius = NUMBER_SIZE / 2; VectorCopy(le->pos.trBase, origin); origin[2] += 110 - c * 100; VectorSubtract(cg.refdef.vieworg, origin, dir); CrossProduct(dir, up, vec); VectorNormalize(vec); VectorMA(origin, -10 + 20 * sin(c * 2 * M_PI), vec, origin); // if the view would be "inside" the sprite, kill the sprite // so it doesn't add too much overdraw VectorSubtract( origin, cg.refdef.vieworg, delta ); len = VectorLength( delta ); if ( len < 20 ) { CG_FreeLocalEntity( le ); return; } negative = qfalse; if (score < 0) { negative = qtrue; score = -score; } for (numdigits = 0; !(numdigits && !score); numdigits++) { digits[numdigits] = score % 10; score = score / 10; } if (negative) { digits[numdigits] = 10; numdigits++; } for (i = 0; i < numdigits; i++) { VectorMA(origin, (float) (((float) numdigits / 2) - i) * NUMBER_SIZE, vec, re->origin); re->customShader = cgs.media.numberShaders[digits[numdigits-1-i]]; trap_R_AddRefEntityToScene( re ); } }