diff --git a/common/const.h b/common/const.h index 2135f19c..476d6176 100644 --- a/common/const.h +++ b/common/const.h @@ -90,6 +90,7 @@ #define SOLID_SLIDEBOX 3 // touch on edge, but not an onground #define SOLID_BSP 4 // bsp clip, touch on edge, block #define SOLID_CUSTOM 5 // call external callbacks for tracing +#define SOLID_PORTAL 6 // borrowed from FTE // edict->deadflag values #define DEAD_NO 0 // alive diff --git a/engine/client/cl_demo.c b/engine/client/cl_demo.c index 881cb597..80ea87fe 100644 --- a/engine/client/cl_demo.c +++ b/engine/client/cl_demo.c @@ -613,6 +613,24 @@ void CL_ReadDemoSequence( qboolean discard ) cls.netchan.last_reliable_sequence = last_reliable_sequence; } +/* +================= +CL_DemoAborted +================= +*/ +void CL_DemoAborted( void ) +{ + if( cls.demofile ) + FS_Close( cls.demofile ); + cls.demoplayback = false; + cls.changedemo = false; + demo.framecount = 0; + cls.demofile = NULL; + cls.demonum = -1; + + Cvar_SetValue( "v_dark", 0.0f ); +} + /* ================= CL_DemoCompleted @@ -1186,8 +1204,7 @@ void CL_PlayDemo_f( void ) if( !FS_FileExists( filename, true )) { MsgDev( D_ERROR, "couldn't open %s\n", filename ); - Cvar_SetValue( "v_dark", 0.0f ); - cls.demonum = -1; // stop demo loop + CL_DemoAborted(); return; } @@ -1201,7 +1218,7 @@ void CL_PlayDemo_f( void ) if( demo.header.id != IDEMOHEADER ) { MsgDev( D_ERROR, "%s is not a demo file\n", filename ); - CL_DemoCompleted(); + CL_DemoAborted(); return; } @@ -1212,7 +1229,7 @@ void CL_PlayDemo_f( void ) if( demo.header.net_protocol != PROTOCOL_VERSION ) MsgDev( D_ERROR, "playdemo: net protocol outdated (%i should be %i)\n", demo.header.net_protocol, PROTOCOL_VERSION ); - CL_DemoCompleted(); + CL_DemoAborted(); return; } @@ -1223,7 +1240,7 @@ void CL_PlayDemo_f( void ) if( demo.directory.numentries < 1 || demo.directory.numentries > 1024 ) { MsgDev( D_ERROR, "demo had bogus # of directory entries: %i\n", demo.directory.numentries ); - CL_DemoCompleted(); + CL_DemoAborted(); return; } diff --git a/engine/client/cl_main.c b/engine/client/cl_main.c index da478dc2..f421170e 100644 --- a/engine/client/cl_main.c +++ b/engine/client/cl_main.c @@ -74,6 +74,11 @@ qboolean CL_Active( void ) return ( cls.state == ca_active ); } +qboolean CL_Initialized( void ) +{ + return cls.initialized; +} + //====================================================================== qboolean CL_IsInGame( void ) { diff --git a/engine/client/cl_scrn.c b/engine/client/cl_scrn.c index eb6438a6..423a325c 100644 --- a/engine/client/cl_scrn.c +++ b/engine/client/cl_scrn.c @@ -545,7 +545,13 @@ qboolean SCR_LoadVariableWidthFont( const char *fontname ) return true; } +/* +================ +SCR_LoadCreditsFont +FIXME: INTRESOURCE +================ +*/ void SCR_LoadCreditsFont( void ) { if( !SCR_LoadVariableWidthFont( "gfx.wad/creditsfont.fnt" )) @@ -555,6 +561,13 @@ void SCR_LoadCreditsFont( void ) } } +/* +================ +SCR_InstallParticlePalette + +FIXME: INTRESOURCE +================ +*/ void SCR_InstallParticlePalette( void ) { rgbdata_t *pic; @@ -589,6 +602,13 @@ void SCR_InstallParticlePalette( void ) } } +/* +================ +SCR_RegisterTextures + +FIXME: INTRESOURCE +================ +*/ void SCR_RegisterTextures( void ) { // register gfx.wad images diff --git a/engine/client/cl_view.c b/engine/client/cl_view.c index 14b159ac..988a397b 100644 --- a/engine/client/cl_view.c +++ b/engine/client/cl_view.c @@ -378,7 +378,6 @@ void V_PostRender( void ) CL_DrawDemoRecording(); CL_DrawHUD( CL_CHANGELEVEL ); R_ShowTextures(); - R_ShowTree(); Con_DrawConsole(); UI_UpdateMenu( host.realtime ); Con_DrawVersion(); diff --git a/engine/client/gl_backend.c b/engine/client/gl_backend.c index d4f80b15..c8d8a2de 100644 --- a/engine/client/gl_backend.c +++ b/engine/client/gl_backend.c @@ -723,100 +723,4 @@ rebuild_page: CL_DrawCenterPrint (); pglFinish(); -} - -#define POINT_SIZE 16.0f -#define NODE_INTERVAL_X(x) (x * 16.0f) -#define NODE_INTERVAL_Y(x) (x * 16.0f) - -static int recursion_level; -static int max_recursion; - -void R_DrawLeafNode( float x, float y, float scale ) -{ - float downScale = scale * 0.25f;// * POINT_SIZE; - - R_DrawStretchPic( x - downScale * 0.5f, y - downScale * 0.5f, downScale, downScale, 0, 0, 1, 1, tr.particleTexture ); -} - -void R_DrawNodeConnection( float x, float y, float x2, float y2 ) -{ - pglBegin( GL_LINES ); - pglVertex2f( x, y ); - pglVertex2f( x2, y2 ); - pglEnd(); -} - -void R_ShowTree_r( mnode_t *node, float x, float y, float scale, int shownodes ) -{ - float downScale = scale * 0.8f; - - downScale = Q_max( downScale, 1.0f ); - - if( !node ) return; - - recursion_level++; - - if( node->contents < 0 ) - { - mleaf_t *leaf = (mleaf_t *)node; - - if( recursion_level > max_recursion ) - max_recursion = recursion_level; - - if( shownodes == 1 ) - { - if( cl.worldmodel->leafs == leaf ) - pglColor4f( 1.0f, 1.0f, 1.0f, 1.0f ); - else if( RI.viewleaf && RI.viewleaf == leaf ) - pglColor4f( 1.0f, 0.0f, 0.0f, 1.0f ); - else pglColor4f( 0.0f, 1.0f, 0.0f, 1.0f ); - R_DrawLeafNode( x, y, scale ); - } - recursion_level--; - return; - } - - if( shownodes == 1 ) - { - pglColor4f( 0.0f, 0.0f, 1.0f, 1.0f ); - R_DrawLeafNode( x, y, scale ); - } - else if( shownodes == 2 ) - { - R_DrawNodeConnection( x, y, x - scale, y + scale ); - R_DrawNodeConnection( x, y, x + scale, y + scale ); - } - - R_ShowTree_r( node->children[1], x - scale, y + scale, downScale, shownodes ); - R_ShowTree_r( node->children[0], x + scale, y + scale, downScale, shownodes ); - - recursion_level--; -} - -void R_ShowTree( void ) -{ - float x = (float)((glState.width - (int)POINT_SIZE) >> 1); - float y = NODE_INTERVAL_Y(1.0); - - if( !cl.worldmodel ) - return; - - recursion_level = 0; -// max_recursion = 0; - - pglEnable( GL_BLEND ); - pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); - - pglLineWidth( 2.0f ); - pglColor3f( 1, 0.7f, 0 ); - pglDisable( GL_TEXTURE_2D ); - R_ShowTree_r( cl.worldmodel->nodes, x, y, max_recursion * 3.5f, 0 ); - pglEnable( GL_TEXTURE_2D ); - pglLineWidth( 1.0f ); - - R_ShowTree_r( cl.worldmodel->nodes, x, y, max_recursion * 3.5f, 0 ); - - Con_NPrintf( 2, "max recursion %d\n", max_recursion ); } \ No newline at end of file diff --git a/engine/client/gl_local.h b/engine/client/gl_local.h index 73d076fe..cfa93133 100644 --- a/engine/client/gl_local.h +++ b/engine/client/gl_local.h @@ -286,7 +286,6 @@ void GL_SetRenderMode( int mode ); void GL_TextureTarget( uint target ); void GL_Cull( GLenum cull ); void R_ShowTextures( void ); -void R_ShowTree( void ); // // gl_cull.c diff --git a/engine/client/gl_studio.c b/engine/client/gl_studio.c index ee59a1a8..c736dc98 100644 --- a/engine/client/gl_studio.c +++ b/engine/client/gl_studio.c @@ -3003,7 +3003,7 @@ void GL_StudioSetRenderMode( int rendermode ) break; case kRenderTransColor: pglBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); - pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_ALPHA ); + pglTexEnvi( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); pglEnable( GL_BLEND ); break; case kRenderTransAdd: diff --git a/engine/common/common.h b/engine/common/common.h index 1a95c836..48e0b512 100644 --- a/engine/common/common.h +++ b/engine/common/common.h @@ -801,6 +801,7 @@ qboolean CL_IsInMenu( void ); qboolean CL_IsInConsole( void ); qboolean CL_IsThirdPerson( void ); qboolean CL_IsIntermission( void ); +qboolean CL_Initialized( void ); char *CL_Userinfo( void ); float CL_GetServerTime( void ); float CL_GetLerpFrac( void ); diff --git a/engine/common/console.c b/engine/common/console.c index 266d2513..724a1a78 100644 --- a/engine/common/console.c +++ b/engine/common/console.c @@ -629,6 +629,8 @@ static qboolean Con_LoadVariableWidthFont( const char *fontname, cl_font_t *font /* ================ Con_LoadConsoleFont + +FIXME: INTRESOURCE ================ */ static void Con_LoadConsoleFont( int fontNumber, cl_font_t *font ) @@ -2222,8 +2224,8 @@ void Con_CharEvent( int key ) ========= Con_VidInit -reload background -resize console +reload backgrounds +FIXME: INTRESOURCE ========= */ void Con_VidInit( void ) diff --git a/engine/common/cvar.h b/engine/common/cvar.h index 381c24e4..5859bd5d 100644 --- a/engine/common/cvar.h +++ b/engine/common/cvar.h @@ -34,7 +34,6 @@ typedef struct convar_s // this part unique for convar_t char *desc; // variable descrition info char *def_string; // keep pointer to initial value - char *latched_string; // for CVAR_LATCH vars } convar_t; // cvar internal flags diff --git a/engine/common/filesystem.c b/engine/common/filesystem.c index ee53e71e..aaef315b 100644 --- a/engine/common/filesystem.c +++ b/engine/common/filesystem.c @@ -1338,10 +1338,14 @@ FS_CheckForGameDir */ static qboolean FS_CheckForGameDir( const char *gamedir ) { - // if directoy contain config.cfg it's 100% gamedir + // if directory contain config.cfg it's 100% gamedir if( FS_FileExists( va( "%s/config.cfg", gamedir ), false )) return true; + // if directory contain progs.dat it's 100% gamedir + if( FS_FileExists( va( "%s/progs.dat", gamedir ), false )) + return true; + // quake mods probably always archived but can missed config.cfg before first running if( FS_FileExists( va( "%s/pak0.pak", gamedir ), false )) return true; diff --git a/engine/common/model.c b/engine/common/model.c index f4348042..70f3a9b7 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -1704,22 +1704,13 @@ static void Mod_LoadSurfaces( const dlump_t *l ) prev_lightofs = lightofs; next_lightofs = 99999999; } -#if 0 - if( loadmodel->lightdata && lightofs != -1 ) - { - out->samples = loadmodel->lightdata + (lightofs / world.lightmap_samples); - // if deluxemap is present setup it too - if( world.deluxedata ) - out->info->deluxemap = world.deluxedata + (lightofs / 3); - } -#endif if( out->flags & SURF_DRAWTURB ) GL_SubdivideSurface( out ); // cut up polygon for warps } // now we have enough data to trying determine samplecount per lightmap pixel - if( test_lightsize > 0 && prev_lightofs != -1 && next_lightofs != -1 ) + if( test_lightsize > 0 && prev_lightofs != -1 && next_lightofs != -1 && next_lightofs != 99999999 ) { float samples = (float)(next_lightofs - prev_lightofs) / (float)test_lightsize; @@ -2769,7 +2760,7 @@ static void Mod_LoadBrushModel( model_t *mod, const void *buffer, qboolean *load mod->radius = RadiusFromBounds( mod->mins, mod->maxs ); mod->numleafs = bm->visleafs; - mod->flags = 0; +// mod->flags = 0; if( i != 0 ) { diff --git a/engine/common/net_ws.c b/engine/common/net_ws.c index 2fe854f0..3cd14d68 100644 --- a/engine/common/net_ws.c +++ b/engine/common/net_ws.c @@ -864,7 +864,8 @@ qboolean NET_GetLong( byte *pData, int size, int *outSize ) MsgDev( D_ERROR, "Malformed packet number (%i/%i)\n", packet_number + 1, packet_count ); return false; } - if (net.split.current_sequence == -1 || sequence_number != net.split.current_sequence ) + + if( net.split.current_sequence == -1 || sequence_number != net.split.current_sequence ) { net.split.current_sequence = pHeader->sequence_number; net.split.split_count = packet_count; diff --git a/engine/common/pm_trace.c b/engine/common/pm_trace.c index 96550950..001be13b 100644 --- a/engine/common/pm_trace.c +++ b/engine/common/pm_trace.c @@ -206,7 +206,7 @@ qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec float frac, midf; int side; vec3_t mid; - +loc0: // check for empty if( num < 0 ) { @@ -236,21 +236,20 @@ qboolean PM_RecursiveHullCheck( hull_t *hull, int num, float p1f, float p2f, vec node = hull->clipnodes + num; plane = hull->planes + node->planenum; - if( plane->type < 3 ) - { - t1 = p1[plane->type] - plane->dist; - t2 = p2[plane->type] - plane->dist; - } - else - { - t1 = DotProduct( plane->normal, p1 ) - plane->dist; - t2 = DotProduct( plane->normal, p2 ) - plane->dist; - } + t1 = PlaneDiff( p1, plane ); + t2 = PlaneDiff( p2, plane ); if( t1 >= 0.0f && t2 >= 0.0f ) - return PM_RecursiveHullCheck( hull, node->children[0], p1f, p2f, p1, p2, trace ); + { + num = node->children[0]; + goto loc0; + } + if( t1 < 0.0f && t2 < 0.0f ) - return PM_RecursiveHullCheck( hull, node->children[1], p1f, p2f, p1, p2, trace ); + { + num = node->children[1]; + goto loc0; + } // put the crosspoint DIST_EPSILON pixels on the near side side = (t1 < 0.0f); diff --git a/engine/common/world.c b/engine/common/world.c index 7ad13709..ad1ba869 100644 --- a/engine/common/world.c +++ b/engine/common/world.c @@ -166,6 +166,120 @@ void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t m } } +/* +================== +World_PortalCSG + +a portal is flush with a world surface behind it. this causes problems. namely that we can't pass through the portal plane +if the bsp behind it prevents out origin from getting through. so if the trace was clipped and ended infront of the portal, +continue the trace to the edges of the portal cutout instead. +================== +*/ +void World_PortalCSG( edict_t *portal, const vec3_t trace_mins, const vec3_t trace_maxs, const vec3_t start, const vec3_t end, trace_t *trace ) +{ + vec4_t planes[6]; //far, near, right, left, up, down + int plane, k; + vec3_t worldpos; + float bestfrac; + int hitplane; + model_t *model; + float portalradius; + + // only run this code if we impacted on the portal's parent. + if( trace->fraction == 1.0f && !trace->startsolid ) + return; + + // decide which clipping hull to use, based on the size + model = Mod_Handle( portal->v.modelindex ); + + if( !model || model->type != mod_brush ) + return; + + // make sure we use a sane valid position. + if( trace->startsolid ) VectorCopy( start, worldpos ); + else VectorCopy( trace->endpos, worldpos ); + + // determine the csg area. normals should be facing in + AngleVectors( portal->v.angles, planes[1], planes[3], planes[5] ); + VectorNegate(planes[1], planes[0]); + VectorNegate(planes[3], planes[2]); + VectorNegate(planes[5], planes[4]); + + portalradius = model->radius * 0.5f; + planes[0][3] = DotProduct( portal->v.origin, planes[0] ) - (4.0f / 32.0f); + planes[1][3] = DotProduct( portal->v.origin, planes[1] ) - (4.0f / 32.0f); //an epsilon beyond the portal + planes[2][3] = DotProduct( portal->v.origin, planes[2] ) - portalradius; + planes[3][3] = DotProduct( portal->v.origin, planes[3] ) - portalradius; + planes[4][3] = DotProduct( portal->v.origin, planes[4] ) - portalradius; + planes[5][3] = DotProduct( portal->v.origin, planes[5] ) - portalradius; + + // if we're actually inside the csg region + for( plane = 0; plane < 6; plane++ ) + { + float d = DotProduct( worldpos, planes[plane] ); + vec3_t nearest; + + for( k = 0; k < 3; k++ ) + nearest[k] = (planes[plane][k]>=0) ? trace_maxs[k] : trace_mins[k]; + + // front plane gets further away with side + if( !plane ) + { + planes[plane][3] -= DotProduct( nearest, planes[plane] ); + } + else if( plane > 1 ) + { + // side planes get nearer with size + planes[plane][3] += 24; // DotProduct( nearest, planes[plane] ); + } + + if( d - planes[plane][3] >= 0 ) + continue; // endpos is inside + else return; // end is already outside + } + + // yup, we're inside, the trace shouldn't end where it actually did + bestfrac = 1; + hitplane = -1; + + for( plane = 0; plane < 6; plane++ ) + { + float ds = DotProduct( start, planes[plane] ) - planes[plane][3]; + float de = DotProduct( end, planes[plane] ) - planes[plane][3]; + float frac; + + if( ds >= 0 && de < 0 ) + { + frac = (ds) / (ds - de); + if( frac < bestfrac ) + { + if( frac < 0 ) + frac = 0; + bestfrac = frac; + hitplane = plane; + } + } + } + + trace->startsolid = trace->allsolid = false; + + // if we cross the front of the portal, don't shorten the trace, + // that will artificially clip us + if( hitplane == 0 && trace->fraction > bestfrac ) + return; + + // okay, elongate to clip to the portal hole properly. + VectorLerp( start, bestfrac, end, trace->endpos ); + trace->fraction = bestfrac; + + if( hitplane >= 0 ) + { + VectorCopy( planes[hitplane], trace->plane.normal ); + trace->plane.dist = planes[hitplane][3]; + if( hitplane == 1 ) trace->ent = portal; + } +} + /* ================== RankForContents diff --git a/engine/common/world.h b/engine/common/world.h index 1eb86a20..86dec522 100644 --- a/engine/common/world.h +++ b/engine/common/world.h @@ -49,6 +49,7 @@ void ClearLink( link_t *l ); // trace common void World_MoveBounds( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, vec3_t boxmins, vec3_t boxmaxs ); void World_TransformAABB( matrix4x4 transform, const vec3_t mins, const vec3_t maxs, vec3_t outmins, vec3_t outmaxs ); +void World_PortalCSG( edict_t *portal, const vec3_t trace_mins, const vec3_t trace_maxs, const vec3_t start, const vec3_t end, trace_t *trace ); trace_t World_CombineTraces( trace_t *cliptrace, trace_t *trace, edict_t *touch ); int BoxOnPlaneSide( const vec3_t emins, const vec3_t emaxs, const mplane_t *p ); int RankForContents( int contents ); diff --git a/engine/engine.dsp b/engine/engine.dsp index 0a2105ac..f711bb54 100644 --- a/engine/engine.dsp +++ b/engine/engine.dsp @@ -63,7 +63,6 @@ SOURCE="$(InputPath)" BuildCmds= \ copy $(TargetDir)\xash.dll "D:\Xash3D\xash.dll" \ copy $(TargetDir)\xash.dll "D:\Paranoia2\xash.dll" \ - copy $(TargetDir)\xash.dll "D:\Area51\xash.dll" \ copy $(TargetDir)\xash.dll "D:\Quake\xash.dll" \ @@ -73,9 +72,6 @@ BuildCmds= \ "D:\Paranoia2\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) -"D:\Area51\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - $(BuildCmds) - "D:\Quake\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build @@ -115,7 +111,6 @@ SOURCE="$(InputPath)" BuildCmds= \ copy $(TargetDir)\xash.dll "D:\Xash3D\xash.dll" \ copy $(TargetDir)\xash.dll "D:\Paranoia2\xash.dll" \ - copy $(TargetDir)\xash.dll "D:\Area51\xash.dll" \ copy $(TargetDir)\xash.dll "D:\Quake\xash.dll" \ @@ -125,9 +120,6 @@ BuildCmds= \ "D:\Paranoia2\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) -"D:\Area51\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" - $(BuildCmds) - "D:\Quake\xash.dll" : $(SOURCE) "$(INTDIR)" "$(OUTDIR)" $(BuildCmds) # End Custom Build diff --git a/engine/physint.h b/engine/physint.h index 870ea67f..a3115766 100644 --- a/engine/physint.h +++ b/engine/physint.h @@ -55,6 +55,7 @@ typedef struct areanode_s struct areanode_s *children[2]; link_t trigger_edicts; link_t solid_edicts; + link_t portal_edicts; } areanode_t; typedef struct server_physics_api_s diff --git a/engine/server/sv_cmds.c b/engine/server/sv_cmds.c index 5746bf7e..51e43553 100644 --- a/engine/server/sv_cmds.c +++ b/engine/server/sv_cmds.c @@ -206,7 +206,7 @@ void SV_Map_f( void ) MsgDev( D_ERROR, "map %s is invalid or not supported\n", mapname ); return; } - + if( !FBitSet( flags, MAP_IS_EXIST )) { MsgDev( D_ERROR, "map %s doesn't exist\n", mapname ); diff --git a/engine/server/sv_save.c b/engine/server/sv_save.c index 100858f6..164fa51a 100644 --- a/engine/server/sv_save.c +++ b/engine/server/sv_save.c @@ -2114,6 +2114,7 @@ qboolean SV_LoadGame( const char *pPath ) file_t *pFile; qboolean validload = false; GAME_HEADER gameHeader; + int flags; string name; if( host.type == HOST_DEDICATED ) @@ -2151,6 +2152,21 @@ qboolean SV_LoadGame( const char *pPath ) validload = true; } FS_Close( pFile ); + + // now check for map problems + flags = SV_MapIsValid( gameHeader.mapName, GI->sp_entity, NULL ); + + if( FBitSet( flags, MAP_INVALID_VERSION )) + { + MsgDev( D_ERROR, "map %s is invalid or not supported\n", gameHeader.mapName ); + validload = false; + } + + if( !FBitSet( flags, MAP_IS_EXIST )) + { + MsgDev( D_ERROR, "map %s doesn't exist\n", gameHeader.mapName ); + validload = false; + } } else MsgDev( D_ERROR, "File not found or failed to open.\n" ); diff --git a/engine/server/sv_world.c b/engine/server/sv_world.c index a648e528..c5be5207 100644 --- a/engine/server/sv_world.c +++ b/engine/server/sv_world.c @@ -244,7 +244,7 @@ hull_t *SV_HullForBsp( edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_ if( world.sky_sphere || world.lightmap_samples == 1 ) { // alternate hull select for quake maps - if( size[0] < 3.0f ) + if( size[0] < 3.0f || ent->v.solid == SOLID_PORTAL ) hull = &model->hulls[0]; else if( size[0] <= 32.0f ) hull = &model->hulls[1]; @@ -254,7 +254,7 @@ hull_t *SV_HullForBsp( edict_t *ent, const vec3_t mins, const vec3_t maxs, vec3_ } else { - if( size[0] <= 8.0f ) + if( size[0] <= 8.0f || ent->v.solid == SOLID_PORTAL ) { hull = &model->hulls[0]; VectorCopy( hull->clip_mins, offset ); @@ -293,10 +293,13 @@ hull_t *SV_HullForEntity( edict_t *ent, vec3_t mins, vec3_t maxs, vec3_t offset hull_t *hull; vec3_t hullmins, hullmaxs; - if( ent->v.solid == SOLID_BSP ) + if( ent->v.solid == SOLID_BSP || ent->v.solid == SOLID_PORTAL ) { - if( ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP ) - Host_Error( "'%s' has SOLID_BSP without MOVETYPE_PUSH or MOVETYPE_PUSHSTEP\n", SV_ClassName( ent )); + if( ent->v.solid != SOLID_PORTAL ) + { + if( ent->v.movetype != MOVETYPE_PUSH && ent->v.movetype != MOVETYPE_PUSHSTEP ) + Host_Error( "'%s' has SOLID_BSP without MOVETYPE_PUSH or MOVETYPE_PUSHSTEP\n", SV_ClassName( ent )); + } hull = SV_HullForBsp( ent, mins, maxs, offset ); } else @@ -423,6 +426,7 @@ areanode_t *SV_CreateAreaNode( int depth, vec3_t mins, vec3_t maxs ) ClearLink( &anode->trigger_edicts ); ClearLink( &anode->solid_edicts ); + ClearLink( &anode->portal_edicts ); if( depth == AREA_DEPTH ) { @@ -677,6 +681,8 @@ void SV_LinkEdict( edict_t *ent, qboolean touch_triggers ) // link it in if( ent->v.solid == SOLID_TRIGGER ) InsertLinkBefore( &ent->area, &node->trigger_edicts ); + else if( ent->v.solid == SOLID_PORTAL ) + InsertLinkBefore( &ent->area, &node->portal_edicts ); else InsertLinkBefore( &ent->area, &node->solid_edicts ); if( touch_triggers && !iTouchLinkSemaphore ) @@ -876,7 +882,7 @@ void SV_ClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, vec3_t } // rotate start and end into the models frame of reference - if( ent->v.solid == SOLID_BSP && !VectorIsNull( ent->v.angles )) + if(( ent->v.solid == SOLID_BSP || ent->v.solid == SOLID_PORTAL ) && !VectorIsNull( ent->v.angles )) rotated = true; else rotated = false; @@ -1003,6 +1009,108 @@ void SV_CustomClipMoveToEntity( edict_t *ent, const vec3_t start, vec3_t mins, v } } +/* +==================== +SV_ClipToEntity + +generic clip function +==================== +*/ +static qboolean SV_ClipToEntity( edict_t *touch, moveclip_t *clip ) +{ + trace_t trace; + + if( touch->v.groupinfo != 0 && SV_IsValidEdict( clip->passedict ) && clip->passedict->v.groupinfo != 0 ) + { + if(( svs.groupop == 0 && ( touch->v.groupinfo & clip->passedict->v.groupinfo ) == 0) || + ( svs.groupop == 1 && (touch->v.groupinfo & clip->passedict->v.groupinfo ) != 0 )) + return true; + } + + if( touch == clip->passedict || touch->v.solid == SOLID_NOT ) + return true; + + if( touch->v.solid == SOLID_TRIGGER ) + Host_Error( "trigger in clipping list\n" ); + + // custom user filter + if( svgame.dllFuncs2.pfnShouldCollide ) + { + if( !svgame.dllFuncs2.pfnShouldCollide( touch, clip->passedict )) + return true; // originally this was 'return' but is completely wrong! + } + + // monsterclip filter (solid custom is a static or dynamic bodies) + if( touch->v.solid == SOLID_BSP || touch->v.solid == SOLID_CUSTOM ) + { + if( FBitSet( touch->v.flags, FL_MONSTERCLIP )) + { + // func_monsterclip works only with monsters that have same flag! + if( !FBitSet( clip->flags, FMOVE_MONSTERCLIP )) + return true; + } + } + else + { + // ignore all monsters but pushables + if( clip->type == MOVE_NOMONSTERS && touch->v.movetype != MOVETYPE_PUSHSTEP ) + return true; + } + + if( Mod_GetType( touch->v.modelindex ) == mod_brush && clip->flags & FMOVE_IGNORE_GLASS ) + { + // we ignore brushes with rendermode != kRenderNormal and without FL_WORLDBRUSH set + if( touch->v.rendermode != kRenderNormal && !FBitSet( touch->v.flags, FL_WORLDBRUSH )) + return true; + } + + if( !BoundsIntersect( clip->boxmins, clip->boxmaxs, touch->v.absmin, touch->v.absmax )) + return true; + + // aditional check to intersects clients with sphere + if( touch->v.solid != SOLID_SLIDEBOX && !SV_CheckSphereIntersection( touch, clip->start, clip->end )) + return true; + + // Xash3D extension + if( SV_IsValidEdict( clip->passedict ) && clip->passedict->v.solid == SOLID_TRIGGER ) + { + // never collide items and player (because call "give" always stuck item in player + // and total trace returns fail (old half-life bug) + // items touch should be done in SV_TouchLinks not here + if( FBitSet( touch->v.flags, FL_CLIENT|FL_FAKECLIENT )) + return true; + } + + // g-cont. make sure what size is really zero - check all the components + if( SV_IsValidEdict( clip->passedict ) && !VectorIsNull( clip->passedict->v.size ) && VectorIsNull( touch->v.size )) + return true; // points never interact + + // might intersect, so do an exact clip + if( clip->trace.allsolid ) return false; + + if( SV_IsValidEdict( clip->passedict )) + { + if( touch->v.owner == clip->passedict ) + return true; // don't clip against own missiles + if( clip->passedict->v.owner == touch ) + return true; // don't clip against owner + } + + // make sure we don't hit the world if we're inside the portal + if( touch->v.solid == SOLID_PORTAL ) + World_PortalCSG( touch, clip->mins, clip->maxs, clip->start, clip->end, &clip->trace ); + + if( touch->v.solid == SOLID_CUSTOM ) + SV_CustomClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); + else if( touch->v.flags & FL_MONSTER ) + SV_ClipMoveToEntity( touch, clip->start, clip->mins2, clip->maxs2, clip->end, &trace ); + else SV_ClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); + + clip->trace = World_CombineTraces( &clip->trace, &trace, touch ); + + return true; +} + /* ==================== SV_ClipToLinks @@ -1014,7 +1122,6 @@ static void SV_ClipToLinks( areanode_t *node, moveclip_t *clip ) { link_t *l, *next; edict_t *touch; - trace_t trace; // touch linked edicts for( l = node->solid_edicts.next; l != &node->solid_edicts; l = next ) @@ -1023,89 +1130,8 @@ static void SV_ClipToLinks( areanode_t *node, moveclip_t *clip ) touch = EDICT_FROM_AREA( l ); - if( touch->v.groupinfo != 0 && SV_IsValidEdict( clip->passedict ) && clip->passedict->v.groupinfo != 0 ) - { - if(( svs.groupop == 0 && ( touch->v.groupinfo & clip->passedict->v.groupinfo ) == 0) || - ( svs.groupop == 1 && (touch->v.groupinfo & clip->passedict->v.groupinfo ) != 0 )) - continue; - } - - if( touch == clip->passedict || touch->v.solid == SOLID_NOT ) - continue; - - if( touch->v.solid == SOLID_TRIGGER ) - Host_Error( "trigger in clipping list\n" ); - - // custom user filter - if( svgame.dllFuncs2.pfnShouldCollide ) - { - if( !svgame.dllFuncs2.pfnShouldCollide( touch, clip->passedict )) - continue; // originally this was 'return' but is completely wrong! - } - - // monsterclip filter (solid custom is a static or dynamic bodies) - if( touch->v.solid == SOLID_BSP || touch->v.solid == SOLID_CUSTOM ) - { - if( FBitSet( touch->v.flags, FL_MONSTERCLIP )) - { - // func_monsterclip works only with monsters that have same flag! - if( !FBitSet( clip->flags, FMOVE_MONSTERCLIP )) - continue; - } - } - else - { - // ignore all monsters but pushables - if( clip->type == MOVE_NOMONSTERS && touch->v.movetype != MOVETYPE_PUSHSTEP ) - continue; - } - - if( Mod_GetType( touch->v.modelindex ) == mod_brush && clip->flags & FMOVE_IGNORE_GLASS ) - { - // we ignore brushes with rendermode != kRenderNormal and without FL_WORLDBRUSH set - if( touch->v.rendermode != kRenderNormal && !FBitSet( touch->v.flags, FL_WORLDBRUSH )) - continue; - } - - if( !BoundsIntersect( clip->boxmins, clip->boxmaxs, touch->v.absmin, touch->v.absmax )) - continue; - - // aditional check to intersects clients with sphere - if( touch->v.solid != SOLID_SLIDEBOX && !SV_CheckSphereIntersection( touch, clip->start, clip->end )) - continue; - - // Xash3D extension - if( SV_IsValidEdict( clip->passedict ) && clip->passedict->v.solid == SOLID_TRIGGER ) - { - // never collide items and player (because call "give" always stuck item in player - // and total trace returns fail (old half-life bug) - // items touch should be done in SV_TouchLinks not here - if( touch->v.flags & ( FL_CLIENT|FL_FAKECLIENT )) - continue; - } - - // g-cont. make sure what size is really zero - check all the components - if( SV_IsValidEdict( clip->passedict ) && !VectorIsNull( clip->passedict->v.size ) && VectorIsNull( touch->v.size )) - continue; // points never interact - - // might intersect, so do an exact clip - if( clip->trace.allsolid ) return; - - if( SV_IsValidEdict( clip->passedict )) - { - if( touch->v.owner == clip->passedict ) - continue; // don't clip against own missiles - if( clip->passedict->v.owner == touch ) - continue; // don't clip against owner - } - - if( touch->v.solid == SOLID_CUSTOM ) - SV_CustomClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); - else if( touch->v.flags & FL_MONSTER ) - SV_ClipMoveToEntity( touch, clip->start, clip->mins2, clip->maxs2, clip->end, &trace ); - else SV_ClipMoveToEntity( touch, clip->start, clip->mins, clip->maxs, clip->end, &trace ); - - clip->trace = World_CombineTraces( &clip->trace, &trace, touch ); + if( !SV_ClipToEntity( touch, clip )) + return; // trace.allsoild } // recurse down both sides @@ -1117,6 +1143,38 @@ static void SV_ClipToLinks( areanode_t *node, moveclip_t *clip ) SV_ClipToLinks( node->children[1], clip ); } +/* +==================== +SV_ClipToPortals + +Mins and maxs enclose the entire area swept by the move +==================== +*/ +static void SV_ClipToPortals( areanode_t *node, moveclip_t *clip ) +{ + link_t *l, *next; + edict_t *touch; + + // touch linked edicts + for( l = node->portal_edicts.next; l != &node->portal_edicts; l = next ) + { + next = l->next; + + touch = EDICT_FROM_AREA( l ); + + if( !SV_ClipToEntity( touch, clip )) + return; // trace.allsoild + } + + // recurse down both sides + if( node->axis == -1 ) return; + + if( clip->boxmaxs[node->axis] > node->dist ) + SV_ClipToPortals( node->children[0], clip ); + if( clip->boxmins[node->axis] < node->dist ) + SV_ClipToPortals( node->children[1], clip ); +} + /* ==================== SV_ClipToWorldBrush @@ -1202,6 +1260,7 @@ trace_t SV_Move( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_t end, World_MoveBounds( start, clip.mins2, clip.maxs2, trace_endpos, clip.boxmins, clip.boxmaxs ); SV_ClipToLinks( sv_areanodes, &clip ); + SV_ClipToPortals( sv_areanodes, &clip ); clip.trace.fraction *= trace_fraction; svgame.globals->trace_ent = clip.trace.ent; @@ -1249,6 +1308,7 @@ trace_t SV_MoveNoEnts( const vec3_t start, vec3_t mins, vec3_t maxs, const vec3_ World_MoveBounds( start, clip.mins2, clip.maxs2, trace_endpos, clip.boxmins, clip.boxmaxs ); SV_ClipToWorldBrush( sv_areanodes, &clip ); + SV_ClipToPortals( sv_areanodes, &clip ); clip.trace.fraction *= trace_fraction; svgame.globals->trace_ent = clip.trace.ent;