diff options
author | enneract <trem.redman@gmail.com> | 2014-02-25 13:03:43 +0100 |
---|---|---|
committer | enneract <trem.redman@gmail.com> | 2014-02-25 13:03:43 +0100 |
commit | dac3d3127fc94231bdde0c0822bb12de01e9e836 (patch) | |
tree | 17829bc1a1b0ddb2d49421c5ea0114b4c2eff436 /src | |
parent | cd9f8731a13a29d51a401f67ec2aa0b8962e01c8 (diff) |
0.1.7
Diffstat (limited to 'src')
-rw-r--r-- | src/cgame/cg_buildable.c | 682 | ||||
-rw-r--r-- | src/cgame/cg_draw.c | 81 | ||||
-rw-r--r-- | src/cgame/cg_ents.c | 3 | ||||
-rw-r--r-- | src/cgame/cg_event.c | 19 | ||||
-rw-r--r-- | src/cgame/cg_local.h | 121 | ||||
-rw-r--r-- | src/cgame/cg_main.c | 92 | ||||
-rw-r--r-- | src/cgame/cg_playerstate.c | 6 | ||||
-rw-r--r-- | src/cgame/cg_servercmds.c | 11 | ||||
-rw-r--r-- | src/cgame/cg_tutorial.c | 48 | ||||
-rw-r--r-- | src/cgame/cg_weapons.c | 292 | ||||
-rw-r--r-- | src/game/bg_misc.c | 244 | ||||
-rw-r--r-- | src/game/bg_public.h | 41 | ||||
-rw-r--r-- | src/game/g_active.c | 123 | ||||
-rw-r--r-- | src/game/g_buildable.c | 2285 | ||||
-rw-r--r-- | src/game/g_client.c | 8 | ||||
-rw-r--r-- | src/game/g_cmds.c | 108 | ||||
-rw-r--r-- | src/game/g_combat.c | 26 | ||||
-rw-r--r-- | src/game/g_local.h | 64 | ||||
-rw-r--r-- | src/game/g_main.c | 141 | ||||
-rw-r--r-- | src/game/g_weapon.c | 4 | ||||
-rw-r--r-- | src/game/tremulous.h | 89 | ||||
-rw-r--r-- | src/ui/ui_main.c | 35 |
22 files changed, 2525 insertions, 1998 deletions
diff --git a/src/cgame/cg_buildable.c b/src/cgame/cg_buildable.c index 0c851b0..5646f90 100644 --- a/src/cgame/cg_buildable.c +++ b/src/cgame/cg_buildable.c @@ -45,6 +45,77 @@ char *cg_buildableSoundNames[ MAX_BUILDABLE_ANIMATIONS ] = static sfxHandle_t defaultAlienSounds[ MAX_BUILDABLE_ANIMATIONS ]; static sfxHandle_t defaultHumanSounds[ MAX_BUILDABLE_ANIMATIONS ]; +/* +====================== +CG_RenderCuboid + +Render a cuboid with proper lighting and UV maps +====================== +*/ +static void CG_RenderCuboid_Face( vec3_t a, vec3_t b, vec3_t c, vec3_t d, + int da, int db, + vec4_t color, + float texscale, qhandle_t shader ) +{ + polyVert_t verts[ 4 ]; + + VectorCopy( d, verts[ 0 ].xyz ); + verts[ 0 ].st[ 0 ] = d[ da ] * texscale; + verts[ 0 ].st[ 1 ] = d[ db ] * texscale; + Vector4Copy( color, verts[ 0 ].modulate ); + VectorCopy( c, verts[ 1 ].xyz ); + verts[ 1 ].st[ 0 ] = c[ da ] * texscale; + verts[ 1 ].st[ 1 ] = c[ db ] * texscale; + Vector4Copy( color, verts[ 1 ].modulate ); + VectorCopy( b, verts[ 2 ].xyz ); + verts[ 2 ].st[ 0 ] = b[ da ] * texscale; + verts[ 2 ].st[ 1 ] = b[ db ] * texscale; + Vector4Copy( color, verts[ 2 ].modulate ); + VectorCopy( a, verts[ 3 ].xyz ); + verts[ 3 ].st[ 0 ] = a[ da ] * texscale; + verts[ 3 ].st[ 1 ] = a[ db ] * texscale; + Vector4Copy( color, verts[ 3 ].modulate ); + + trap_R_AddPolyToScene( shader, 4, verts ); +} + +static void CG_RenderCuboid( vec3_t mins, vec3_t maxs, float texscale, qhandle_t shader ) +{ + int i; + vec3_t midpoint, ambient, directed, idc; + vec4_t color = { 255.0f, 255.0f, 255.0f, 255.0f }; + vec3_t ppp, ppn, pnp, pnn, npp, npn, nnp, nnn; + + //lighting + VectorAdd( mins, maxs, midpoint ); + VectorScale( midpoint, 0.5f, midpoint ); + trap_R_LightForPoint( midpoint, ambient, directed, idc ); + VectorAdd( ambient, directed, color ); + for( i = 0; i < 3; i++ ) + if( color[ i ] > 255.0f ) + color[ i ] = 255.0f; + + //vertices + VectorSet( ppp, maxs[ 0 ], maxs[ 1 ], maxs[ 2 ] ); + VectorSet( ppn, maxs[ 0 ], maxs[ 1 ], mins[ 2 ] ); + VectorSet( pnp, maxs[ 0 ], mins[ 1 ], maxs[ 2 ] ); + VectorSet( pnn, maxs[ 0 ], mins[ 1 ], mins[ 2 ] ); + VectorSet( npp, mins[ 0 ], maxs[ 1 ], maxs[ 2 ] ); + VectorSet( npn, mins[ 0 ], maxs[ 1 ], mins[ 2 ] ); + VectorSet( nnp, mins[ 0 ], mins[ 1 ], maxs[ 2 ] ); + VectorSet( nnn, mins[ 0 ], mins[ 1 ], mins[ 2 ] ); + + //faces + //+-x + CG_RenderCuboid_Face( ppn, ppp, pnp, pnn, 1, 2, color, texscale, shader ); + CG_RenderCuboid_Face( nnn, nnp, npp, npn, 1, 2, color, texscale, shader ); + //+-y + CG_RenderCuboid_Face( ppp, ppn, npn, npp, 0, 2, color, texscale, shader ); + CG_RenderCuboid_Face( nnp, nnn, pnn, pnp, 0, 2, color, texscale, shader ); + //+-z + CG_RenderCuboid_Face( npp, nnp, pnp, ppp, 0, 1, color, texscale, shader ); + CG_RenderCuboid_Face( ppn, pnn, nnn, npn, 0, 1, color, texscale, shader ); +} /* ====================== @@ -1283,7 +1354,6 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t int health; float x, y; vec4_t color; - qboolean powered, marked; trace_t tr; float d; buildStat_t *bs; @@ -1378,8 +1448,8 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t } } - if(cuboid) - visible=qtrue; + if( cuboid ) + visible = qtrue; // hack to make the kit obscure view if( cg_drawGun.integer && visible && @@ -1429,13 +1499,13 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t else if( healthScale > 1.0f ) healthScale = 1.0f; - if(cuboid) + if( cuboid ) { - x=320; - y=240; - d=Distance(cg.refdef.vieworg,trac); - if(d<64.0f) - d=64.0f; + x = 320; + y = 240; + d = Distance( cg.refdef.vieworg, trac ); + if( d < 64.0f ) + d = 64.0f ; } else if( !CG_WorldToScreen( origin, &x, &y ) ) @@ -1454,9 +1524,6 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t // this is fudged to get the width/height in the cfg to be more realistic scale = ( picH / d ) * 3; - powered = es->eFlags & EF_B_POWERED; - marked = es->eFlags & EF_B_MARKED; - picH *= scale; picW *= scale; picX -= ( picW * 0.5f ); @@ -1527,20 +1594,21 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t } trap_R_SetColor( color ); - if( !powered ) + { float pX; - + pX = picX + ( subH * bs->horizontalMargin ); - CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); - } - - if( marked ) - { - float mX; - - mX = picX + picW - ( subH * bs->horizontalMargin ) - subH; - CG_DrawPic( mX, subY, subH, subH, bs->markedShader ); + + if( BG_Buildable( es->modelindex, NULL )->team == TEAM_HUMANS ) + { + float offs = 2000.0f / d; + CG_DrawPic( pX - offs, subY - offs, + subH + 2.0f * offs, subH + 2.0f * offs, + CG_BuildablePowerStatusIcon( es ) ); + } + else if( !( es->eFlags & EF_B_POWERED ) ) + CG_DrawPic( pX, subY, subH, subH, bs->noPowerShader ); } //NOTE: dont use CG_DrawField, too few digits @@ -1555,19 +1623,22 @@ static void CG_BuildableStatusDisplay( centity_t *cent, qboolean cuboid, vec3_t if( health > 0 && healthPoints < 1 ) healthPoints = 1; - Com_sprintf(buf,sizeof(buf),"%i",healthPoints); - bufl=strlen(buf); - cW=subH*cgDC.aspectScale; - cH=subH; - nX=picX+picW*0.5f-cW*bufl*0.5f; + Com_sprintf( buf, sizeof( buf ), "%i", healthPoints ); + bufl = strlen(buf); + cW = subH*cgDC.aspectScale; + cH = subH; + nX = picX + picW * 0.5f - cW * bufl * 0.5f; - for(i=0;i<bufl;i++) + for( i = 0; i < bufl; i++ ) { - if(buf[i]=='-') - frame=STAT_MINUS; - else - frame=buf[i]-'0'; - CG_DrawPic(nX+i*cW,y+bs->verticalMargin-subH*0.5f,cW,cH,cgs.media.numberShaders[frame]); + if( buf[ i ] == '-' ) + frame = STAT_MINUS; + else + frame = buf[ i ] - '0'; + CG_DrawPic( nX + i * cW, + y + bs->verticalMargin - subH * 0.5f, + cW, cH, + cgs.media.numberShaders[ frame ] ); } } @@ -1620,28 +1691,6 @@ static qboolean CG_PlayerIsBuilder( buildable_t buildable ) /* ================== -CG_BuildableRemovalPending -================== -*/ -static qboolean CG_BuildableRemovalPending( int entityNum ) -{ - int i; - playerState_t *ps = &cg.snap->ps; - - if( !( ps->stats[ STAT_BUILDABLE ] & SB_VALID_TOGGLEBIT ) ) - return qfalse; - - for( i = 0; i < MAX_MISC; i++ ) - { - if( ps->misc[ i ] == entityNum ) - return qtrue; - } - - return qfalse; -} - -/* -================== CG_DrawBuildableStatus ================== */ @@ -1656,7 +1705,7 @@ void CG_DrawBuildableStatus( void ) trace_t tr; qboolean cuboid; - if((cg.predictedPlayerState.stats[STAT_BUILDABLE]&~SB_VALID_TOGGLEBIT)>BA_NONE) + if( ( cg.predictedPlayerState.stats[STAT_BUILDABLE] & ~SB_VALID_TOGGLEBIT) > BA_NONE ) return; //hide buildstats if we're placing a buildable for( i = 0; i < cg.snap->numEntities; i++ ) @@ -1674,8 +1723,8 @@ void CG_DrawBuildableStatus( void ) qsort( buildableList, buildables, sizeof( int ), CG_SortDistance ); for( i = 0; i < buildables; i++ ) { - cuboid = BG_Buildable(cg_entities[buildableList[i]].currentState.modelindex,NULL)->cuboid; - if(cuboid && tr.entityNum!=buildableList[i] ) + cuboid = BG_IsCuboid( cg_entities[ buildableList[ i ] ].currentState.modelindex ); + if(cuboid && tr.entityNum != buildableList[ i ] ) continue; CG_BuildableStatusDisplay( &cg_entities[ buildableList[ i ] ], cuboid, tr.endpos ); } @@ -1713,11 +1762,11 @@ void CG_Buildable( centity_t *cent ) return; } - // cuboids use a bit different rendering code !@#CUBOID + // cuboids use a bit different rendering code if( BG_IsCuboid( es->modelindex ) ) { - qhandle_t texture=0,cracks=0; - vec3_t dims; + qhandle_t texture = 0,cracks = 0; + vec3_t dims, mins, maxs; const cuboidAttributes_t *cuboidAttr; const cuboidInfo_t *cuboidInfo; int i, health, sound; @@ -1733,12 +1782,30 @@ void CG_Buildable( centity_t *cent ) else if ( healthPct < 0.0f ) healthPct = 0.0f; - if( cuboidInfo->useCracks ) + if( ( es->eFlags & EF_B_SPAWNED ) && cuboidInfo->useCracks && healthPct < 0.95f ) { + float progress; + const float o = 1.02f; //md3 rendering is not exact, so render it bigger to compensate + if( cuboidInfo->textureCount ) texture = cuboidInfo->textures[ 0 ]; - if( healthPct < 0.75f ) - cracks = cgs.media.cuboidCracks[ (int)( CUBOID_CRACK_TEXTURES - 1 - floor( CUBOID_CRACK_TEXTURES * healthPct ) ) - 1 ]; + + memset( &ent, 0, sizeof( ent ) ); + ent.reType = RT_MODEL; + VectorCopy( cent->lerpOrigin, ent.origin ); + + ent.axis[0][0]=-dims[0]/2.0f*o;ent.axis[0][1]=0.0f; ent.axis[0][2]=0.0f; + ent.axis[1][0]=0.0f; ent.axis[1][1]=-dims[1]/2.0f*o;ent.axis[1][2]=0.0f; + ent.axis[2][0]=0.0f; ent.axis[2][1]=0.0f; ent.axis[2][2]=dims[2]/2.0f*o; + ent.nonNormalizedAxes = qtrue; + + progress = healthPct; + + ent.customShader = cgs.media.cuboidCracks; + ent.shaderTime = 0.001f * cg.time + progress; + + ent.hModel = cgs.media.cuboidModel; + trap_R_AddRefEntityToScene( &ent ); } else for( i = 0; i < cuboidInfo->textureCount; i++ ) @@ -1750,13 +1817,22 @@ void CG_Buildable( centity_t *cent ) if( !( es->eFlags & EF_B_SPAWNED ) ) { - sfxHandle_t prebuildSound=cgs.media.humanBuildablePrebuild; + sfxHandle_t prebuildSound; + if( team == TEAM_HUMANS ) { - texture = cgs.media.humanSpawningShader; - prebuildSound = cgs.media.humanBuildablePrebuild; + if( es->eFlags & EF_B_POWERED ) + { + texture = cgs.media.humanSpawningShader; + prebuildSound = cgs.media.humanBuildablePrebuild; + } + else + { + texture = cgs.media.humanUnpoweredSpawningShader; + prebuildSound = cgs.media.unpoweredSurgeLoop; + } } - else if(team==TEAM_ALIENS) + else if( team == TEAM_ALIENS ) { texture = cgs.media.cuboidAlienPrebuild; prebuildSound = cgs.media.alienBuildablePrebuild; @@ -1764,24 +1840,11 @@ void CG_Buildable( centity_t *cent ) cracks = 0; trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound ); } - - memset( &ent, 0, sizeof( ent ) ); - ent.reType = RT_MODEL; - VectorCopy( cent->lerpOrigin, ent.origin ); - VectorCopy( cent->lerpOrigin, ent.oldorigin ); - VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); - - //NOTE: don't use CG_PositionAndOrientateBuildable, it screws up everything - ent.axis[0][0]=-dims[0]/2.0f;ent.axis[0][1]=0.0f; ent.axis[0][2]=0.0f; - ent.axis[1][0]=0.0f; ent.axis[1][1]=-dims[1]/2.0f;ent.axis[1][2]=0.0f; - ent.axis[2][0]=0.0f; ent.axis[2][1]=0.0f; ent.axis[2][2]=dims[2]/2.0f; - ent.nonNormalizedAxes = qtrue; - - ent.customShader = texture; - ent.hModel = cgs.media.cuboidModel; - trap_R_AddRefEntityToScene( &ent ); - if( cracks ) - CG_DrawCuboid( ent.origin, dims, cracks, 1 ); + + BG_CuboidBBox( dims, mins, maxs ); + VectorAdd( mins, cent->lerpOrigin, mins ); + VectorAdd( maxs, cent->lerpOrigin, maxs ); + CG_RenderCuboid( mins, maxs, 0.01f, texture ); if( health < cent->lastBuildableHealth && ( es->eFlags & EF_B_SPAWNED ) ) { @@ -1795,247 +1858,253 @@ void CG_Buildable( centity_t *cent ) } } else - { - - memset ( &ent, 0, sizeof( ent ) ); + { + memset ( &ent, 0, sizeof( ent ) ); - VectorCopy( cent->lerpOrigin, ent.origin ); - VectorCopy( cent->lerpOrigin, ent.oldorigin ); - VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); + VectorCopy( cent->lerpOrigin, ent.origin ); + VectorCopy( cent->lerpOrigin, ent.oldorigin ); + VectorCopy( cent->lerpOrigin, ent.lightingOrigin ); - VectorCopy( es->origin2, surfNormal ); + VectorCopy( es->origin2, surfNormal ); - VectorCopy( es->angles, angles ); - BG_BuildableBoundingBox( es->modelindex, mins, maxs ); + VectorCopy( es->angles, angles ); + BG_BuildableBoundingBox( es->modelindex, mins, maxs ); - if( es->pos.trType == TR_STATIONARY ) - { - // Positioning a buildable involves potentially up to two traces, and - // seeing as buildables rarely move, we cache the results and recalculate - // only if the buildable moves or changes orientation - if( VectorCompare( cent->buildableCache.cachedOrigin, cent->lerpOrigin ) && - VectorCompare( cent->buildableCache.cachedNormal, surfNormal ) ) - { - VectorCopy( cent->buildableCache.axis[ 0 ], ent.axis[ 0 ] ); - VectorCopy( cent->buildableCache.axis[ 1 ], ent.axis[ 1 ] ); - VectorCopy( cent->buildableCache.axis[ 2 ], ent.axis[ 2 ] ); - VectorCopy( cent->buildableCache.origin, ent.origin ); - } - else + if( es->pos.trType == TR_STATIONARY ) { - CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, - es->number, mins, maxs, ent.axis, - ent.origin, qfalse ); - VectorCopy( ent.axis[ 0 ], cent->buildableCache.axis[ 0 ] ); - VectorCopy( ent.axis[ 1 ], cent->buildableCache.axis[ 1 ] ); - VectorCopy( ent.axis[ 2 ], cent->buildableCache.axis[ 2 ] ); - VectorCopy( ent.origin, cent->buildableCache.origin ); - VectorCopy( cent->lerpOrigin, cent->buildableCache.cachedOrigin ); - VectorCopy( surfNormal, cent->buildableCache.cachedNormal ); + // Positioning a buildable involves potentially up to two traces, and + // seeing as buildables rarely move, we cache the results and recalculate + // only if the buildable moves or changes orientation + if( VectorCompare( cent->buildableCache.cachedOrigin, cent->lerpOrigin ) && + VectorCompare( cent->buildableCache.cachedNormal, surfNormal ) ) + { + VectorCopy( cent->buildableCache.axis[ 0 ], ent.axis[ 0 ] ); + VectorCopy( cent->buildableCache.axis[ 1 ], ent.axis[ 1 ] ); + VectorCopy( cent->buildableCache.axis[ 2 ], ent.axis[ 2 ] ); + VectorCopy( cent->buildableCache.origin, ent.origin ); + } + else + { + CG_PositionAndOrientateBuildable( angles, ent.origin, surfNormal, + es->number, mins, maxs, ent.axis, + ent.origin, qfalse ); + VectorCopy( ent.axis[ 0 ], cent->buildableCache.axis[ 0 ] ); + VectorCopy( ent.axis[ 1 ], cent->buildableCache.axis[ 1 ] ); + VectorCopy( ent.axis[ 2 ], cent->buildableCache.axis[ 2 ] ); + VectorCopy( ent.origin, cent->buildableCache.origin ); + VectorCopy( cent->lerpOrigin, cent->buildableCache.cachedOrigin ); + VectorCopy( surfNormal, cent->buildableCache.cachedNormal ); + } } - } - - VectorMA( ent.origin, BG_BuildableConfig( es->modelindex )->zOffset, surfNormal, ent.origin ); - - VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all - VectorCopy( ent.origin, ent.lightingOrigin ); + VectorMA( ent.origin, BG_BuildableConfig( es->modelindex )->zOffset, surfNormal, ent.origin ); + - ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ]; + VectorCopy( ent.origin, ent.oldorigin ); // don't positionally lerp at all + VectorCopy( ent.origin, ent.lightingOrigin ); - if( !( es->eFlags & EF_B_SPAWNED ) ) - { - sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; + ent.hModel = cg_buildables[ es->modelindex ].models[ 0 ]; - if( team == TEAM_HUMANS ) + if( !( es->eFlags & EF_B_SPAWNED ) ) { - ent.customShader = cgs.media.humanSpawningShader; - prebuildSound = cgs.media.humanBuildablePrebuild; - } - else if( team == TEAM_ALIENS ) - prebuildSound = cgs.media.alienBuildablePrebuild; + sfxHandle_t prebuildSound = cgs.media.humanBuildablePrebuild; - trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound ); - } + if( team == TEAM_HUMANS ) + { + if( es->eFlags & EF_B_POWERED ) + { + ent.customShader = cgs.media.humanSpawningShader; + prebuildSound = cgs.media.humanBuildablePrebuild; + } + else + { + ent.customShader = cgs.media.humanUnpoweredSpawningShader; + prebuildSound = cgs.media.unpoweredSurgeLoop; + } + } + else if( team == TEAM_ALIENS ) + prebuildSound = cgs.media.alienBuildablePrebuild; + + trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, prebuildSound ); + } - CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp ); + CG_BuildableAnimation( cent, &ent.oldframe, &ent.frame, &ent.backlerp ); - //rescale the model - scale = BG_BuildableConfig( es->modelindex )->modelScale; + //rescale the model + scale = BG_BuildableConfig( es->modelindex )->modelScale; - if( scale != 1.0f ) - { - VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] ); - VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] ); - VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] ); + if( scale != 1.0f ) + { + VectorScale( ent.axis[ 0 ], scale, ent.axis[ 0 ] ); + VectorScale( ent.axis[ 1 ], scale, ent.axis[ 1 ] ); + VectorScale( ent.axis[ 2 ], scale, ent.axis[ 2 ] ); - ent.nonNormalizedAxes = qtrue; - } - else - ent.nonNormalizedAxes = qfalse; - + ent.nonNormalizedAxes = qtrue; + } + else + ent.nonNormalizedAxes = qfalse; - if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) - ent.customShader = cgs.media.redBuildShader; + //add to refresh list + trap_R_AddRefEntityToScene( &ent ); - //add to refresh list - trap_R_AddRefEntityToScene( &ent ); + CrossProduct( surfNormal, refNormal, xNormal ); + VectorNormalize( xNormal ); + rotAngle = RAD2DEG( acos( DotProduct( surfNormal, refNormal ) ) ); - CrossProduct( surfNormal, refNormal, xNormal ); - VectorNormalize( xNormal ); - rotAngle = RAD2DEG( acos( DotProduct( surfNormal, refNormal ) ) ); + //turret barrel bit + if( cg_buildables[ es->modelindex ].models[ 1 ] ) + { + refEntity_t turretBarrel; + vec3_t flatAxis[ 3 ]; - //turret barrel bit - if( cg_buildables[ es->modelindex ].models[ 1 ] ) - { - refEntity_t turretBarrel; - vec3_t flatAxis[ 3 ]; + memset( &turretBarrel, 0, sizeof( turretBarrel ) ); - memset( &turretBarrel, 0, sizeof( turretBarrel ) ); + turretBarrel.hModel = cg_buildables[ es->modelindex ].models[ 1 ]; - turretBarrel.hModel = cg_buildables[ es->modelindex ].models[ 1 ]; + CG_PositionEntityOnTag( &turretBarrel, &ent, ent.hModel, "tag_turret" ); + VectorCopy( cent->lerpOrigin, turretBarrel.lightingOrigin ); - CG_PositionEntityOnTag( &turretBarrel, &ent, ent.hModel, "tag_turret" ); - VectorCopy( cent->lerpOrigin, turretBarrel.lightingOrigin ); + { + vec3_t interpolated; + int i; - { - vec3_t interpolated; - int i; + for( i = 0; i < 3 ; i++ ) + interpolated[ i ] = LerpAngle( es->angles2[ i ], cent->nextState.angles2[ i ], cg.frameInterpolation ); - for( i = 0; i < 3 ; i++ ) - interpolated[ i ] = LerpAngle( es->angles2[ i ], cent->nextState.angles2[ i ], cg.frameInterpolation ); + AnglesToAxis( interpolated, flatAxis ); + } - AnglesToAxis( interpolated, flatAxis ); - } + RotatePointAroundVector( turretBarrel.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); + RotatePointAroundVector( turretBarrel.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); + RotatePointAroundVector( turretBarrel.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); - RotatePointAroundVector( turretBarrel.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); - RotatePointAroundVector( turretBarrel.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); - RotatePointAroundVector( turretBarrel.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); + turretBarrel.oldframe = ent.oldframe; + turretBarrel.frame = ent.frame; + turretBarrel.backlerp = ent.backlerp; - turretBarrel.oldframe = ent.oldframe; - turretBarrel.frame = ent.frame; - turretBarrel.backlerp = ent.backlerp; + turretBarrel.customShader = ent.customShader; - turretBarrel.customShader = ent.customShader; + if( scale != 1.0f ) + { + VectorScale( turretBarrel.axis[ 0 ], scale, turretBarrel.axis[ 0 ] ); + VectorScale( turretBarrel.axis[ 1 ], scale, turretBarrel.axis[ 1 ] ); + VectorScale( turretBarrel.axis[ 2 ], scale, turretBarrel.axis[ 2 ] ); - if( scale != 1.0f ) - { - VectorScale( turretBarrel.axis[ 0 ], scale, turretBarrel.axis[ 0 ] ); - VectorScale( turretBarrel.axis[ 1 ], scale, turretBarrel.axis[ 1 ] ); - VectorScale( turretBarrel.axis[ 2 ], scale, turretBarrel.axis[ 2 ] ); + turretBarrel.nonNormalizedAxes = qtrue; + } + else + turretBarrel.nonNormalizedAxes = qfalse; - turretBarrel.nonNormalizedAxes = qtrue; + trap_R_AddRefEntityToScene( &turretBarrel ); } - else - turretBarrel.nonNormalizedAxes = qfalse; - - if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) - turretBarrel.customShader = cgs.media.redBuildShader; - trap_R_AddRefEntityToScene( &turretBarrel ); - } + //turret barrel bit + if( cg_buildables[ es->modelindex ].models[ 2 ] ) + { + refEntity_t turretTop; + vec3_t flatAxis[ 3 ]; + vec3_t swivelAngles; - //turret barrel bit - if( cg_buildables[ es->modelindex ].models[ 2 ] ) - { - refEntity_t turretTop; - vec3_t flatAxis[ 3 ]; - vec3_t swivelAngles; + memset( &turretTop, 0, sizeof( turretTop ) ); - memset( &turretTop, 0, sizeof( turretTop ) ); + VectorCopy( es->angles2, swivelAngles ); + swivelAngles[ PITCH ] = 0.0f; - VectorCopy( es->angles2, swivelAngles ); - swivelAngles[ PITCH ] = 0.0f; + turretTop.hModel = cg_buildables[ es->modelindex ].models[ 2 ]; - turretTop.hModel = cg_buildables[ es->modelindex ].models[ 2 ]; + CG_PositionRotatedEntityOnTag( &turretTop, &ent, ent.hModel, "tag_turret" ); + VectorCopy( cent->lerpOrigin, turretTop.lightingOrigin ); + AnglesToAxis( swivelAngles, flatAxis ); - CG_PositionRotatedEntityOnTag( &turretTop, &ent, ent.hModel, "tag_turret" ); - VectorCopy( cent->lerpOrigin, turretTop.lightingOrigin ); - AnglesToAxis( swivelAngles, flatAxis ); + RotatePointAroundVector( turretTop.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); + RotatePointAroundVector( turretTop.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); + RotatePointAroundVector( turretTop.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); - RotatePointAroundVector( turretTop.axis[ 0 ], xNormal, flatAxis[ 0 ], -rotAngle ); - RotatePointAroundVector( turretTop.axis[ 1 ], xNormal, flatAxis[ 1 ], -rotAngle ); - RotatePointAroundVector( turretTop.axis[ 2 ], xNormal, flatAxis[ 2 ], -rotAngle ); + turretTop.oldframe = ent.oldframe; + turretTop.frame = ent.frame; + turretTop.backlerp = ent.backlerp; - turretTop.oldframe = ent.oldframe; - turretTop.frame = ent.frame; - turretTop.backlerp = ent.backlerp; + turretTop.customShader = ent.customShader; - turretTop.customShader = ent.customShader; + if( scale != 1.0f ) + { + VectorScale( turretTop.axis[ 0 ], scale, turretTop.axis[ 0 ] ); + VectorScale( turretTop.axis[ 1 ], scale, turretTop.axis[ 1 ] ); + VectorScale( turretTop.axis[ 2 ], scale, turretTop.axis[ 2 ] ); - if( scale != 1.0f ) - { - VectorScale( turretTop.axis[ 0 ], scale, turretTop.axis[ 0 ] ); - VectorScale( turretTop.axis[ 1 ], scale, turretTop.axis[ 1 ] ); - VectorScale( turretTop.axis[ 2 ], scale, turretTop.axis[ 2 ] ); + turretTop.nonNormalizedAxes = qtrue; + } + else + turretTop.nonNormalizedAxes = qfalse; - turretTop.nonNormalizedAxes = qtrue; + trap_R_AddRefEntityToScene( &turretTop ); } - else - turretTop.nonNormalizedAxes = qfalse; - - if( CG_PlayerIsBuilder( es->modelindex ) && CG_BuildableRemovalPending( es->number ) ) - turretTop.customShader = cgs.media.redBuildShader; - trap_R_AddRefEntityToScene( &turretTop ); - } - - //weapon effects for turrets - if( es->eFlags & EF_FIRING ) - { - weaponInfo_t *weapon = &cg_weapons[ es->weapon ]; - - if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME || - BG_Buildable( es->modelindex, NULL )->turretProjType == WP_TESLAGEN ) + //weapon effects for turrets + if( es->eFlags & EF_FIRING ) { - if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] || - weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] || - weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ) + weaponInfo_t *weapon = &cg_weapons[ es->weapon ]; + + if( cg.time - cent->muzzleFlashTime > MUZZLE_FLASH_TIME || + BG_Buildable( es->modelindex, NULL )->turretProjType == WP_TESLAGEN ) { - trap_R_AddLightToScene( cent->lerpOrigin, 300 + ( rand( ) & 31 ), - weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ], - weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ], - weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ); + if( weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ] || + weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ] || + weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ) + { + trap_R_AddLightToScene( cent->lerpOrigin, 300 + ( rand( ) & 31 ), + weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 0 ], + weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 1 ], + weapon->wim[ WPM_PRIMARY ].flashDlightColor[ 2 ] ); + } } - } - if( weapon->wim[ WPM_PRIMARY ].firingSound ) - { - trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, - weapon->wim[ WPM_PRIMARY ].firingSound ); + if( weapon->wim[ WPM_PRIMARY ].firingSound ) + { + trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, + weapon->wim[ WPM_PRIMARY ].firingSound ); + } + else if( weapon->readySound ) + trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound ); } - else if( weapon->readySound ) - trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, weapon->readySound ); - } - - //smoke etc for damaged buildables - CG_BuildableParticleEffects( cent ); - - + + //smoke etc for damaged buildables + CG_BuildableParticleEffects( cent ); + + - - health = es->generic1; + + health = es->generic1; - if( health < cent->lastBuildableHealth && - ( es->eFlags & EF_B_SPAWNED ) ) - { - if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) + if( health < cent->lastBuildableHealth && + ( es->eFlags & EF_B_SPAWNED ) ) { - if( team == TEAM_HUMANS ) + if( cent->lastBuildableDamageSoundTime + BUILDABLE_SOUND_PERIOD < cg.time ) { - int i = rand( ) % 4; - trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); - } - else if( team == TEAM_ALIENS ) - trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); + if( team == TEAM_HUMANS ) + { + int i = rand( ) % 4; + trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.humanBuildableDamage[ i ] ); + } + else if( team == TEAM_ALIENS ) + trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienBuildableDamage ); - cent->lastBuildableDamageSoundTime = cg.time; + cent->lastBuildableDamageSoundTime = cg.time; + } } - } - - cent->lastBuildableHealth = health; + cent->lastBuildableHealth = health; } //if (is a cuboid) + + + // play a loop if there's not enough power for it to build / activate + if( ( !( es->eFlags & EF_B_SPAWNED ) && + !( es->eFlags & EF_B_POWERED ) ) || + ( ( BG_Buildable( es->modelindex, NULL )->requiresPower || + es->modelindex != BA_H_REPEATER ) && + !( es->eFlags & EF_B_POWERED ) && + ( es->eFlags & EF_B_SURGE ) ) ) + trap_S_AddLoopingSound( es->number, cent->lerpOrigin, vec3_origin, cgs.media.unpoweredSurgeLoop ); } char cuboidInfo[128]; @@ -2049,17 +2118,21 @@ Draw the cuboid info string generated by CG_Cuboid_Info. */ void CG_Cuboid_DrawInfo(void) { - float x,y,w,h,s=0.5f; + float x,y,w,h,s=0.5f; + + // disabled by default (replaced by ckit's display) + if( !cg_drawCuboidInfo.integer ) + return; - if(!BG_Buildable(cg.predictedPlayerState.stats[STAT_BUILDABLE]&~SB_VALID_TOGGLEBIT,NULL)->cuboid) - return; + if( !BG_Buildable( cg.predictedPlayerState.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT, NULL )->cuboid ) + return; - w=UI_Text_Width(cuboidInfo,s); - h=UI_Text_Height(cuboidInfo,s); - x=320.0f-w/2.0f+cg_cuboidInfoX.value; - y=240.0f-h/2.0f+cg_cuboidInfoY.value; + w= UI_Text_Width( cuboidInfo, s ); + h= UI_Text_Height( cuboidInfo, s ); + x= 320.0f - w / 2.0f + cg_cuboidInfoX.value; + y= 240.0f - h / 2.0f + cg_cuboidInfoY.value; - UI_Text_Paint(x,y,s,colorWhite,cuboidInfo,0,0,ITEM_TEXTSTYLE_SHADOWEDMORE); + UI_Text_Paint( x, y, s, colorWhite, cuboidInfo, 0, 0, ITEM_TEXTSTYLE_SHADOWEDMORE ); } /* @@ -2076,6 +2149,9 @@ void CG_Cuboid_Info(void) const buildableAttributes_t *attr; int axis=cg_cuboidResizeAxis.integer; + if( !cg_drawCuboidInfo.integer ) + return; + attr=BG_Buildable(cg.predictedPlayerState.stats[STAT_BUILDABLE]&~SB_VALID_TOGGLEBIT,cg.cuboidSelection); Com_sprintf(cuboidInfo,sizeof(cuboidInfo), "^7[^3%c^7] | ^%c%.1f^7x^%c%.1f^7x^%c%.1f ^7| ^3%i^7HP | ^3%i^7ms | ^3%i^7BP", @@ -2342,3 +2418,53 @@ void CG_CuboidAttack_f(void) trap_SendClientCommand( va( "%s", CG_Argv(0) ) ); } +/* +====================== +CG_BuildablePowerStatusIcon + +Figures out the power status icon for a buildable +====================== +*/ +qhandle_t CG_BuildablePowerStatusIcon( entityState_t *es ) +{ + qboolean powered = ( es->eFlags & EF_B_POWERED ); + + if( !( es->eFlags & EF_B_SPAWNED ) ) + { + if( powered ) + return cgs.media.ckit_icon_surge; + else + return cgs.media.ckit_icon_nosurge; + } + + if( BG_Buildable( es->modelindex, NULL )->isPowerSource || + es->modelindex == BA_H_REPEATER ) + { + qboolean active = ( es->eFlags & EF_B_SURGE ); + + if( !active ) + return cgs.media.ckit_icon_off; + else if( powered ) + return cgs.media.ckit_icon_power; + else + return cgs.media.ckit_icon_nopower; + } + else + { + if( !( es->eFlags & EF_B_SURGE ) ) + { + if( powered ) + return cgs.media.ckit_icon_power; + else + return cgs.media.ckit_icon_nopower; + } + else + { + if( powered ) + return cgs.media.ckit_icon_surge; + else + return cgs.media.ckit_icon_nosurge; + } + } + return 0; +} diff --git a/src/cgame/cg_draw.c b/src/cgame/cg_draw.c index aa6df3d..e9cccf4 100644 --- a/src/cgame/cg_draw.c +++ b/src/cgame/cg_draw.c @@ -636,7 +636,6 @@ static void CG_DrawPlayerWallclimbing( rectDef_t *rect, vec4_t backColor, vec4_t static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color ) { int value; - int valueMarked = -1; qboolean bp = qfalse; switch( BG_PrimaryWeapon( cg.snap->ps.stats ) ) @@ -648,8 +647,7 @@ static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color ) case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: - value = cg.snap->ps.persistant[ PERS_BP ]; - valueMarked = cg.snap->ps.persistant[ PERS_MARKEDBP ]; + value = cg.snap->ps.persistant[ PERS_BUILDPOINTS ]; bp = qtrue; break; @@ -660,8 +658,6 @@ static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color ) if( value > 999 ) value = 999; - if( valueMarked > 999 ) - valueMarked = 999; if( value > -1 ) { @@ -678,10 +674,7 @@ static void CG_DrawPlayerAmmoValue( rectDef_t *rect, vec4_t color ) return; } - if( valueMarked > 0 ) - text = va( "%d+(%d)", value, valueMarked ); - else - text = va( "%d", value ); + text = va( "%d", value ); len = strlen( text ); @@ -2557,10 +2550,10 @@ CG_DrawCrosshair static void CG_DrawCrosshair( rectDef_t *rect, vec4_t color ) { float w, h; - qhandle_t hShader; float x, y; weaponInfo_t *wi; weapon_t weapon; + qboolean hit, ff; weapon = BG_GetPlayerWeapon( &cg.snap->ps ); @@ -2590,24 +2583,66 @@ static void CG_DrawCrosshair( rectDef_t *rect, vec4_t color ) x = rect->x + ( rect->w / 2 ) - ( w / 2 ); y = rect->y + ( rect->h / 2 ) - ( h / 2 ); - hShader = wi->crossHair; + hit = ( cg.time <= cg.lastHitTime + 75 ); + ff = ( cg.time == cg.crosshairClientTime || cg.crosshairBuildable >= 0 ); - //aiming at a friendly player/buildable, dim the crosshair - if( cg.time == cg.crosshairClientTime || cg.crosshairBuildable >= 0 ) - { - int i; - for( i = 0; i < 3; i++ ) - color[i] *= .5f; + color[ 3 ] = 0.6f; - } - - if( hShader != 0 ) + trap_R_SetColor( color ); + + switch( wi->crossHairType ) { + case CH_NONE: + break; + + case CH_DOT: + if( ff ) + CG_DrawPic( x, y, w, h, cgs.media.ch_friendly ); + else + CG_DrawPic( x, y, w, h, cgs.media.ch_dot ); - trap_R_SetColor( color ); - CG_DrawPic( x, y, w, h, hShader ); - trap_R_SetColor( NULL ); + if( hit ) + CG_DrawPic( x, y, w, h, cgs.media.ch_dothit ); + + break; + + case CH_CIRCLE: + if( ff ) + CG_DrawPic( x, y, w, h, cgs.media.ch_friendly ); + + if( hit ) + CG_DrawPic( x, y, w, h, cgs.media.ch_circlehit ); + else + CG_DrawPic( x, y, w, h, cgs.media.ch_circle ); + + break; + + case CH_CIRCLEDDOT: + if( ff ) + CG_DrawPic( x, y, w, h, cgs.media.ch_friendly ); + else + CG_DrawPic( x, y, w, h, cgs.media.ch_dot ); + + if( hit ) + CG_DrawPic( x, y, w, h, cgs.media.ch_circlehit ); + else + CG_DrawPic( x, y, w, h, cgs.media.ch_circle ); + + break; + + case CH_ALIEN: + if( ff ) + CG_DrawPic( x, y, w, h, cgs.media.ch_afriendly ); + else + CG_DrawPic( x, y, w, h, cgs.media.ch_adot ); + + if( hit ) + CG_DrawPic( x, y, w, h, cgs.media.ch_acircle ); + + break; } + + trap_R_SetColor( NULL ); } diff --git a/src/cgame/cg_ents.c b/src/cgame/cg_ents.c index 339cd8e..6b1523d 100644 --- a/src/cgame/cg_ents.c +++ b/src/cgame/cg_ents.c @@ -236,7 +236,8 @@ static void CG_EntityEffects( centity_t *cent ) // constant light glow - if ( cent->currentState.constantLight ) + if ( cent->currentState.constantLight && + cent->currentState.eType != ET_BUILDABLE ) { int cl; int i, r, g, b; diff --git a/src/cgame/cg_event.c b/src/cgame/cg_event.c index c8ca2e6..adc319c 100644 --- a/src/cgame/cg_event.c +++ b/src/cgame/cg_event.c @@ -808,6 +808,25 @@ void CG_EntityEvent( centity_t *cent, vec3_t position ) trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.repeaterUseSound ); break; + case EV_POWER_SWITCH: + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.powerSwitchSound ); + break; + + case EV_POWER_ZAP: + { + particleSystem_t *ps; + ps = CG_SpawnNewParticleSystem( cgs.media.humanPowerZapPS ); + + if( CG_IsParticleSystemValid( &ps ) ) + { + CG_SetAttachmentPoint( &ps->attachment, position ); + CG_SetAttachmentCent( &ps->attachment, cg_entities + es->number ); + CG_AttachToPoint( &ps->attachment ); + } + } + trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.powerZap[ rand() % 4 ] ); + break; + case EV_GRENADE_BOUNCE: if( rand( ) & 1 ) trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound1 ); diff --git a/src/cgame/cg_local.h b/src/cgame/cg_local.h index a81d1df..e4949b5 100644 --- a/src/cgame/cg_local.h +++ b/src/cgame/cg_local.h @@ -79,8 +79,6 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define TEAM_OVERLAY_MAXNAME_WIDTH 12 #define TEAM_OVERLAY_MAXLOCATION_WIDTH 16 -#define CUBOID_CRACK_TEXTURES 4 - typedef enum { FOOTSTEP_NORMAL, @@ -814,6 +812,15 @@ typedef struct weaponInfoMode_s sfxHandle_t impactFleshSound[ 4 ]; //random impact sound } weaponInfoMode_t; +enum +{ + CH_NONE, + CH_DOT, + CH_CIRCLE, + CH_CIRCLEDDOT, + CH_ALIEN +}; + // each WP_* weapon enum has an associated weaponInfo_t // that contains media references necessary to present the // weapon and its effects @@ -839,7 +846,7 @@ typedef struct weaponInfo_s qhandle_t weaponIcon; qhandle_t ammoIcon; - qhandle_t crossHair; + int crossHairType; int crossHairSize; sfxHandle_t readySound; @@ -932,6 +939,25 @@ typedef struct #define MAX_CONSOLE_TEXT 8192 #define MAX_CONSOLE_LINES 32 +#define MAX_CKIT_ROWS 6 +#define MAX_CKIT_COLUMNS 7 +#define MAX_CKIT_TEXT 64 //make it big so Com_sprintf won't spam errors + +typedef struct +{ + qhandle_t icon; + char text[ MAX_CKIT_TEXT ]; +} ckitDisplayLine_t; + +typedef struct +{ + qhandle_t background; + qhandle_t bigicona; + qhandle_t bigiconb; + ckitDisplayLine_t lines[ MAX_CKIT_ROWS ]; +} ckitDisplay_t; + + // all cg.stepTime, cg.duckTime, cg.landTime, etc are set to cg.time when the action // occurs, and they will have visible effects for #define STEP_TIME or whatever msec after @@ -1194,6 +1220,16 @@ typedef struct qhandle_t announcerStack[ MAX_ANNOUNCER_STACK ]; int announcerStackPos; int announcerStackLatest; + + // no space for voltage and current information so they're being sent + // over the network in commands + int bse_entityNum; + float bse_voltage; + float bse_current; + + ckitDisplay_t ckitDisp; + + int lastHitTime; } cg_t; @@ -1228,6 +1264,7 @@ typedef struct qhandle_t teamOverlayShader; qhandle_t numberShaders[ 11 ]; + qhandle_t digitalNumberShaders[ 12 ]; // note: 11 is comma qhandle_t shadowMarkShader; qhandle_t wakeMarkShader; @@ -1236,6 +1273,7 @@ typedef struct qhandle_t greenBuildShader; qhandle_t redBuildShader; qhandle_t humanSpawningShader; + qhandle_t humanUnpoweredSpawningShader; // disconnect qhandle_t disconnectPS; @@ -1337,13 +1375,17 @@ typedef struct qhandle_t humanBleedPS; qhandle_t alienBuildableBleedPS; qhandle_t humanBuildableBleedPS; - + + qhandle_t humanPowerZapPS; qhandle_t teslaZapTS; sfxHandle_t lCannonWarningSound; sfxHandle_t lCannonWarningSound2; + qhandle_t friendlyCrosshair; + qhandle_t hitCrosshair; + qhandle_t buildWeaponTimerPie[ 8 ]; qhandle_t upgradeClassIconShader; qhandle_t healthCross; @@ -1353,7 +1395,7 @@ typedef struct qhandle_t healthCrossPoisoned; qhandle_t healthCrossImplanted; - qhandle_t cuboidCracks[CUBOID_CRACK_TEXTURES-1]; + qhandle_t cuboidCracks; qhandle_t cuboidModel; qhandle_t cuboidRedBuildShader; qhandle_t cuboidYellowBuildShader; @@ -1378,6 +1420,50 @@ typedef struct qhandle_t basivisionShader; qhandle_t basivisionBlipShader; qhandle_t basivisionFlareShader; + + sfxHandle_t unpoweredSurgeLoop; + sfxHandle_t powerSwitchSound; + sfxHandle_t powerZap[ 4 ]; + + //ckit's hacky dynamic display + qhandle_t ckitBackgroundShader; //note: supposed to be dynamic but I've got nothing put there yet + qhandle_t ckitOverlayShader; + + qhandle_t ckit_background; + qhandle_t ckit_overlay; + qhandle_t ckit_bigicona; + qhandle_t ckit_bigiconb; + qhandle_t ckit_icon; + qhandle_t ckit_digit; + + qhandle_t ckit_icon_bp; + qhandle_t ckit_icon_current; + qhandle_t ckit_icon_depth; + qhandle_t ckit_icon_health; + qhandle_t ckit_icon_height; + qhandle_t ckit_icon_network; + qhandle_t ckit_icon_nopower; + qhandle_t ckit_icon_nosurge; + qhandle_t ckit_icon_off; + qhandle_t ckit_icon_power; + qhandle_t ckit_icon_storedbp; + qhandle_t ckit_icon_surge; + qhandle_t ckit_icon_time; + qhandle_t ckit_icon_voltage; + qhandle_t ckit_icon_width; + + qhandle_t ch_dot; + qhandle_t ch_dothit; + qhandle_t ch_circle; + qhandle_t ch_circlehit; + qhandle_t ch_friendly; + + qhandle_t ch_adot; + qhandle_t ch_acircle; + qhandle_t ch_afriendly; + qhandle_t ch_aheadshot; + + sfxHandle_t hitSound; } cgMedia_t; typedef struct @@ -1425,7 +1511,6 @@ typedef struct int timelimit; int maxclients; char mapname[ MAX_QPATH ]; - qboolean markDeconstruct; // Whether or not buildables are marked int voteTime[ NUM_TEAMS ]; int voteYes[ NUM_TEAMS ]; @@ -1623,8 +1708,10 @@ extern vmCvar_t cg_chatTeamPrefix; extern vmCvar_t cg_cuboidResizeAxis; extern vmCvar_t cg_cuboidResizeRate; extern vmCvar_t cg_cuboidPSQuality; + extern vmCvar_t cg_cuboidInfoX; extern vmCvar_t cg_cuboidInfoY; +extern vmCvar_t cg_drawCuboidInfo; extern vmCvar_t cg_fuelInfoX; extern vmCvar_t cg_fuelInfoY; @@ -1634,6 +1721,11 @@ extern vmCvar_t cg_announcer; extern vmCvar_t cg_cameraShakeMagnitude; +extern vmCvar_t cg_debug1; +extern vmCvar_t cg_debug2; +extern vmCvar_t cg_debug3; +extern vmCvar_t cg_debug4; + // // cg_main.c // @@ -1753,15 +1845,16 @@ void CG_DrawBuildableStatus( void ); void CG_InitBuildables( void ); void CG_HumanBuildableExplosion( vec3_t origin, vec3_t dir ); void CG_AlienBuildableExplosion( vec3_t origin, vec3_t dir ); -void CG_CuboidAxis_f(void); -void CG_CuboidRotate_f(void); -void CG_CuboidSize_f(void); -void CG_Cuboid_Send(void); -void CG_Cuboid_Response(void); +void CG_CuboidAxis_f( void ); +void CG_CuboidRotate_f( void ); +void CG_CuboidSize_f( void ); +void CG_Cuboid_Send( void ); +void CG_Cuboid_Response( void ); void CG_CuboidResize( qboolean enlarge ); -void CG_CuboidExplosion(buildable_t buildable, vec3_t origin, vec3_t cuboid); -void CG_DrawCuboidParticles(void); -void CG_CuboidAttack_f(void); +void CG_CuboidExplosion( buildable_t buildable, vec3_t origin, vec3_t cuboid ); +void CG_DrawCuboidParticles( void ); +void CG_CuboidAttack_f( void ); +qhandle_t CG_BuildablePowerStatusIcon( entityState_t *es ); // // cg_animation.c diff --git a/src/cgame/cg_main.c b/src/cgame/cg_main.c index ff3d567..ee37596 100644 --- a/src/cgame/cg_main.c +++ b/src/cgame/cg_main.c @@ -226,8 +226,10 @@ vmCvar_t cg_chatTeamPrefix; vmCvar_t cg_cuboidResizeAxis; vmCvar_t cg_cuboidResizeRate; vmCvar_t cg_cuboidPSQuality; + vmCvar_t cg_cuboidInfoX; vmCvar_t cg_cuboidInfoY; +vmCvar_t cg_drawCuboidInfo; vmCvar_t cg_fuelInfoX; vmCvar_t cg_fuelInfoY; @@ -237,6 +239,11 @@ vmCvar_t cg_announcer; vmCvar_t cg_cameraShakeMagnitude; +vmCvar_t cg_debug1; +vmCvar_t cg_debug2; +vmCvar_t cg_debug3; +vmCvar_t cg_debug4; + typedef struct { vmCvar_t *vmCvar; @@ -379,13 +386,19 @@ static cvarTable_t cvarTable[ ] = { &cg_cuboidInfoX, "cg_cuboidInfoX" ,"0", CVAR_ARCHIVE }, { &cg_cuboidInfoY, "cg_cuboidInfoY" ,"150", CVAR_ARCHIVE }, + { &cg_drawCuboidInfo, "cg_drawCuboidInfo" ,"0", CVAR_ARCHIVE }, { &cg_fuelInfoX, "cg_fuelInfoX" ,"0", CVAR_ARCHIVE }, { &cg_fuelInfoY, "cg_fuelInfoY" ,"150", CVAR_ARCHIVE }, { &cg_fuelInfoScale, "cg_fuelInfoScale" ,"0.5", CVAR_ARCHIVE }, { &cg_announcer, "cg_announcer", "1", CVAR_ARCHIVE }, - { &cg_cameraShakeMagnitude, "cg_cameraShakeMagnitude", "1", CVAR_ARCHIVE } + { &cg_cameraShakeMagnitude, "cg_cameraShakeMagnitude", "1", CVAR_ARCHIVE }, + + { &cg_debug1, "cg_debug1", "", CVAR_CHEAT }, + { &cg_debug2, "cg_debug2", "", CVAR_CHEAT }, + { &cg_debug3, "cg_debug3", "", CVAR_CHEAT }, + { &cg_debug4, "cg_debug4", "", CVAR_CHEAT } }; static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] ); @@ -745,6 +758,13 @@ static void CG_RegisterSounds( void ) cgs.media.cuboidResizeSoundB = trap_S_RegisterSound( "sound/cuboid/resizeb.wav", qfalse ); cgs.media.cuboidRotateSound = trap_S_RegisterSound( "sound/cuboid/rotate.wav", qfalse ); cgs.media.cuboidAxisChangeSound = trap_S_RegisterSound( "sound/cuboid/axischange.wav", qfalse ); + + cgs.media.unpoweredSurgeLoop = trap_S_RegisterSound( "sound/buildables/human/unpowered_surge.wav", qfalse ); + cgs.media.powerSwitchSound = trap_S_RegisterSound( "sound/buildables/human/switch.wav", qfalse ); + for( i = 0; i < 4; i++ ) + cgs.media.powerZap[ i ] = trap_S_RegisterSound( va( "sound/buildables/human/powerzap%i.wav", i + 1 ), qfalse ); + + cgs.media.hitSound = trap_S_RegisterSound( "sound/feedback/hit.wav", qfalse ); } @@ -776,6 +796,21 @@ static void CG_RegisterGraphics( void ) "gfx/2d/numbers/nine_32b", "gfx/2d/numbers/minus_32b", }; + static char *sb_digital[ 12 ] = + { + "gfx/2d/digital/0", + "gfx/2d/digital/1", + "gfx/2d/digital/2", + "gfx/2d/digital/3", + "gfx/2d/digital/4", + "gfx/2d/digital/5", + "gfx/2d/digital/6", + "gfx/2d/digital/7", + "gfx/2d/digital/8", + "gfx/2d/digital/9", + "gfx/2d/digital/minus", + "gfx/2d/digital/comma" + }; static char *buildWeaponTimerPieShaders[ 8 ] = { "ui/assets/neutral/1_5pie", @@ -797,6 +832,9 @@ static void CG_RegisterGraphics( void ) for( i = 0; i < 11; i++ ) cgs.media.numberShaders[ i ] = trap_R_RegisterShader( sb_nums[ i ] ); + + for( i = 0; i < 12; i++ ) + cgs.media.digitalNumberShaders[ i ] = trap_R_RegisterShader( sb_digital[ i ] ); cgs.media.viewBloodShader = trap_R_RegisterShader( "gfx/damage/fullscreen_painblend" ); @@ -813,14 +851,13 @@ static void CG_RegisterGraphics( void ) cgs.media.backTileShader = trap_R_RegisterShader( "console" ); - // building shaders cgs.media.greenBuildShader = trap_R_RegisterShader("gfx/misc/greenbuild" ); cgs.media.redBuildShader = trap_R_RegisterShader("gfx/misc/redbuild" ); cgs.media.humanSpawningShader = trap_R_RegisterShader("models/buildables/telenode/rep_cyl" ); + cgs.media.humanUnpoweredSpawningShader = trap_R_RegisterShader("gfx/misc/unpowered_prebuild" ); - for( i = 0; i < CUBOID_CRACK_TEXTURES - 1; i++ ) - cgs.media.cuboidCracks[ i ] = trap_R_RegisterShader( va( "models/cuboid/cracks_%i", i ) ); + cgs.media.cuboidCracks = trap_R_RegisterShader( "models/cuboid/cracks" ); cgs.media.cuboidModel = trap_R_RegisterModel( "models/cuboid/cuboid.md3" ); cgs.media.cuboidRedBuildShader = trap_R_RegisterShader( "gfx/cuboid/build_red" ); @@ -832,9 +869,30 @@ static void CG_RegisterGraphics( void ) cg.waitForCB = qfalse; cg.cuboidValid = qfalse; cg.latestCBNumber = 0; + VectorSet( cg.cuboidSelection, 30, 30, 30 ); + + // ckit... + cgs.media.ckitBackgroundShader = trap_R_RegisterShader( "gfx/ckit/background" ); + cgs.media.ckitOverlayShader = trap_R_RegisterShader( "gfx/ckit/overlay" ); + + cgs.media.ckit_icon_bp = trap_R_RegisterShader( "gfx/ckit/icon_bp" ); + cgs.media.ckit_icon_current = trap_R_RegisterShader( "gfx/ckit/icon_current" ); + cgs.media.ckit_icon_depth = trap_R_RegisterShader( "gfx/ckit/icon_depth" ); + cgs.media.ckit_icon_health = trap_R_RegisterShader( "gfx/ckit/icon_health" ); + cgs.media.ckit_icon_height = trap_R_RegisterShader( "gfx/ckit/icon_height" ); + cgs.media.ckit_icon_network = trap_R_RegisterShader( "gfx/ckit/icon_network" ); + cgs.media.ckit_icon_nopower = trap_R_RegisterShader( "gfx/ckit/icon_nopower" ); + cgs.media.ckit_icon_nosurge = trap_R_RegisterShader( "gfx/ckit/icon_nosurge" ); + cgs.media.ckit_icon_off = trap_R_RegisterShader( "gfx/ckit/icon_off" ); + cgs.media.ckit_icon_power = trap_R_RegisterShader( "gfx/ckit/icon_power" ); + cgs.media.ckit_icon_storedbp = trap_R_RegisterShader( "gfx/ckit/icon_storedbp" ); + cgs.media.ckit_icon_surge = trap_R_RegisterShader( "gfx/ckit/icon_surge" ); + cgs.media.ckit_icon_time = trap_R_RegisterShader( "gfx/ckit/icon_time" ); + cgs.media.ckit_icon_voltage = trap_R_RegisterShader( "gfx/ckit/icon_voltage" ); + cgs.media.ckit_icon_width = trap_R_RegisterShader( "gfx/ckit/icon_width" ); for( i = 0; i < 15; i++ ) - cgs.media.splashLogo[ i ] = trap_R_RegisterShader( va( "cuboid/logo_%i.tga", i ) ); + cgs.media.splashLogo[ i ] = trap_R_RegisterShader( va( "cuboid/logo_%i.tga", i ) ); cgs.media.splashLeft = trap_R_RegisterShader( "cuboid/logo_left.tga" ); cgs.media.splashRight = trap_R_RegisterShader( "cuboid/logo_right.tga" ); @@ -845,6 +903,21 @@ static void CG_RegisterGraphics( void ) for( i = 0; i < 8; i++ ) cgs.media.buildWeaponTimerPie[ i ] = trap_R_RegisterShader( buildWeaponTimerPieShaders[ i ] ); + cgs.media.friendlyCrosshair = trap_R_RegisterShader( "gfx/2d/crosshair_friendly.tga" ); + cgs.media.hitCrosshair = trap_R_RegisterShader( "gfx/2d/crosshair_hit.tga" ); + + cgs.media.ch_dot = trap_R_RegisterShader( "gfx/2d/ch_dot.tga" ); + cgs.media.ch_dothit = trap_R_RegisterShader( "gfx/2d/ch_dothit.tga" ); + cgs.media.ch_circle = trap_R_RegisterShader( "gfx/2d/ch_circle.tga" ); + cgs.media.ch_circlehit = trap_R_RegisterShader( "gfx/2d/ch_circlehit.tga" ); + cgs.media.ch_friendly = trap_R_RegisterShader( "gfx/2d/ch_friendly.tga" ); + + cgs.media.ch_adot = trap_R_RegisterShader( "gfx/2d/ch_adot.tga" ); + cgs.media.ch_acircle = trap_R_RegisterShader( "gfx/2d/ch_acircle.tga" ); + cgs.media.ch_afriendly = trap_R_RegisterShader( "gfx/2d/ch_afriendly.tga" ); + cgs.media.ch_aheadshot = trap_R_RegisterShader( "gfx/2d/ch_aheadshot.tga" ); + + // player health cross shaders cgs.media.healthCross = trap_R_RegisterShader( "ui/assets/neutral/cross.tga" ); cgs.media.healthCross2X = trap_R_RegisterShader( "ui/assets/neutral/cross2.tga" ); @@ -885,6 +958,8 @@ static void CG_RegisterGraphics( void ) cgs.media.humanBuildableBleedPS = CG_RegisterParticleSystem( "humanBuildableBleedPS"); cgs.media.alienBuildableBleedPS = CG_RegisterParticleSystem( "alienBuildableBleedPS" ); + cgs.media.humanPowerZapPS = CG_RegisterParticleSystem( "humanPowerZapPS" ); + cgs.media.alienBleedPS = CG_RegisterParticleSystem( "alienBleedPS" ); cgs.media.humanBleedPS = CG_RegisterParticleSystem( "humanBleedPS" ); @@ -1010,6 +1085,13 @@ static void CG_RegisterClients( void ) cgs.media.jetpackFlashModel = trap_R_RegisterModel( "models/players/human_base/jetpack_flash.md3" ); cgs.media.battpackModel = trap_R_RegisterModel( "models/players/human_base/battpack.md3" ); + cgs.media.ckit_background = trap_R_RegisterModel( "models/weapons/ckit/ckit_background.md3" ); + cgs.media.ckit_overlay = trap_R_RegisterModel( "models/weapons/ckit/ckit_overlay.md3" ); + cgs.media.ckit_bigicona = trap_R_RegisterModel( "models/weapons/ckit/ckit_bigicona.md3" ); + cgs.media.ckit_bigiconb = trap_R_RegisterModel( "models/weapons/ckit/ckit_bigiconb.md3" ); + cgs.media.ckit_icon = trap_R_RegisterModel( "models/weapons/ckit/ckit_icon.md3" ); + cgs.media.ckit_digit = trap_R_RegisterModel( "models/weapons/ckit/ckit_digit.md3" ); + cg.charModelFraction = 1.0f; trap_UpdateScreen( ); diff --git a/src/cgame/cg_playerstate.c b/src/cgame/cg_playerstate.c index 242a3bb..f7c8074 100644 --- a/src/cgame/cg_playerstate.c +++ b/src/cgame/cg_playerstate.c @@ -271,6 +271,12 @@ void CG_CheckLocalSounds( playerState_t *ps, playerState_t *ops ) last = cg.time; } } + + if( ps->persistant[ PERS_HITS ] != ops->persistant[ PERS_HITS ] ) + { + trap_S_StartSound( NULL, cg.predictedPlayerState.clientNum, CHAN_AUTO, cgs.media.hitSound ); + cg.lastHitTime = cg.time; + } // if we are going into the intermission, don't start any voices if( cg.intermissionStarted ) diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c index cf820c0..f9a173a 100644 --- a/src/cgame/cg_servercmds.c +++ b/src/cgame/cg_servercmds.c @@ -119,7 +119,6 @@ void CG_ParseServerinfo( void ) info = CG_ConfigString( CS_SERVERINFO ); cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); - cgs.markDeconstruct = atoi( Info_ValueForKey( info, "g_markDeconstruct" ) ); mapname = Info_ValueForKey( info, "mapname" ); Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); } @@ -661,13 +660,9 @@ void CG_Menu( int menu, int arg ) //=============================== case MN_H_NOBP: - if( cgs.markDeconstruct ) - longMsg = "There is no power remaining. Free up power by marking " - "existing buildable objects."; - else - longMsg = "There is no power remaining. Free up power by deconstructing " - "existing buildable objects."; - shortMsg = "There is no power remaining"; + longMsg = "You don't have enough build points. Acquire more of them from " + "a Factory."; + shortMsg = "Insufficient build points"; type = DT_BUILD; break; diff --git a/src/cgame/cg_tutorial.c b/src/cgame/cg_tutorial.c index 212db33..cb596b9 100644 --- a/src/cgame/cg_tutorial.c +++ b/src/cgame/cg_tutorial.c @@ -202,27 +202,9 @@ static void CG_AlienBuilderText( char *text, playerState_t *ps ) if( ( es = CG_BuildableInRange( ps, NULL ) ) ) { - if( cgs.markDeconstruct ) - { - if( es->eFlags & EF_B_MARKED ) - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to unmark this structure for replacement\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } - else - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to mark this structure for replacement\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } - } - else - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to destroy this structure\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } + Q_strcat( text, MAX_TUTORIAL_TEXT, + va( "Press %s to destroy this structure\n", + CG_KeyNameForCommand( "deconstruct" ) ) ); } if( ( ps->stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) == BA_NONE ) @@ -378,27 +360,9 @@ static void CG_HumanCkitText( char *text, playerState_t *ps ) if( ( es = CG_BuildableInRange( ps, NULL ) ) ) { - if( cgs.markDeconstruct ) - { - if( es->eFlags & EF_B_MARKED ) - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to unmark this structure\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } - else - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to mark this structure\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } - } - else - { - Q_strcat( text, MAX_TUTORIAL_TEXT, - va( "Press %s to destroy this structure\n", - CG_KeyNameForCommand( "deconstruct" ) ) ); - } + Q_strcat( text, MAX_TUTORIAL_TEXT, + va( "Press %s to destroy this structure\n", + CG_KeyNameForCommand( "deconstruct" ) ) ); } } diff --git a/src/cgame/cg_weapons.c b/src/cgame/cg_weapons.c index 7e22baf..b3dd4c7 100644 --- a/src/cgame/cg_weapons.c +++ b/src/cgame/cg_weapons.c @@ -605,15 +605,27 @@ static qboolean CG_ParseWeaponFile( const char *filename, weaponInfo_t *wi ) if( size < 0 ) size = 0; + wi->crossHairSize = size; + token = COM_Parse( &text_p ); if( !token ) break; - wi->crossHair = trap_R_RegisterShader( token ); - wi->crossHairSize = size; - - if( !wi->crossHair ) - CG_Printf( S_COLOR_RED "ERROR: weapon crosshair not found %s\n", token ); + if( !Q_stricmp( token, "none" ) ) + wi->crossHairType = CH_NONE; + else if( !Q_stricmp( token, "dot" ) ) + wi->crossHairType = CH_DOT; + else if( !Q_stricmp( token, "circle" ) ) + wi->crossHairType = CH_CIRCLE; + else if( !Q_stricmp( token, "circleddot" ) ) + wi->crossHairType = CH_CIRCLEDDOT; + else if( !Q_stricmp( token, "alien" ) ) + wi->crossHairType = CH_ALIEN; + else + { + Com_Printf( S_COLOR_YELLOW "WARNING: unknown cross hair type '%s'\n", token ); + wi->crossHairType = CH_NONE; + } continue; } @@ -869,6 +881,180 @@ static float CG_MachinegunSpinAngle( centity_t *cent, qboolean firing ) return angle; } +/* +============= +CG_GenerateCKitDisplay + +Figures out what should be displayed on the CKit's display +============= +*/ +static void CG_GenerateCKitDisplay( void ) +{ + int row = 0; + ckitDisplay_t *cd = &cg.ckitDisp; + entityState_t *es; + playerState_t *ps = &cg.predictedPlayerState; + qboolean target = qfalse; // true if looking at something + qboolean player = qfalse; // true if looking at a player (buildable otherwise) + int buildable; // buildable type + qboolean probing = qfalse; // measures voltages and currents + int powerNetwork; + int probeEntity; + qboolean building; // displays buildable's info + qboolean cuboid; // true if building cuboid + const buildableAttributes_t *battr; + int buildTimer; + + memset( &cg.ckitDisp, 0, sizeof( cg.ckitDisp ) ); + + /* + part 1: figure out what we're dealing with + */ + + powerNetwork = ( ps->misc[ MISC_INFOHEAD ] & 1023 ); + + probeEntity = ( ps->misc[ MISC_INFOHEAD ] >> 10 ) & 1023; + if( probeEntity != 1023 ) + { + target = qtrue; + if( probeEntity < MAX_CLIENTS ) + { + target = qtrue; + player = qtrue; + probing = ( cg.crosshairClientNum == probeEntity ); + } + else + probing = ( cg.crosshairBuildable == probeEntity ); + + es = &cg_entities[ probeEntity ].currentState; + } + + buildable = cg.predictedPlayerState.stats[ STAT_BUILDABLE ] &~ SB_VALID_TOGGLEBIT; + building = ( buildable != BA_NONE ); + if( building ) + { + battr = BG_Buildable( buildable, cg.cuboidSelection ); + cuboid = battr->cuboid; + } + + buildTimer = ps->stats[ STAT_MISC ]; + + /* + part 2: set up icons & text + */ + + cd->background = cgs.media.ckitBackgroundShader; + + // first big icon indicates if power is available + if( powerNetwork ) + cd->bigicona = cgs.media.ckit_icon_power; + else + cd->bigicona = cgs.media.ckit_icon_nopower; + + // second big icon always shows the building's state + if( target && !player ) + cd->bigiconb = CG_BuildablePowerStatusIcon( es ); + + if( building ) + { + if( cuboid ) + { + //width + cd->lines[ row ].icon = cgs.media.ckit_icon_width; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", cg.cuboidSelection[ 0 ] ); + + //height + cd->lines[ row ].icon = cgs.media.ckit_icon_depth; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", cg.cuboidSelection[ 1 ] ); + + //depth + cd->lines[ row ].icon = cgs.media.ckit_icon_height; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", cg.cuboidSelection[ 2 ] ); + } + + //build points + cd->lines[ row ].icon = cgs.media.ckit_icon_bp; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", battr->buildPoints ); + + //health + cd->lines[ row ].icon = cgs.media.ckit_icon_health; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", battr->health ); + + //build time + cd->lines[ row ].icon = cgs.media.ckit_icon_time; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", 0.001f * battr->buildTime ); + } + else + { + if( buildTimer ) + { + //build timer + cd->lines[ row ].icon = cgs.media.ckit_icon_time; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", 0.001f * buildTimer ); + } + + // power network detector + cd->lines[ row ].icon = cgs.media.ckit_icon_network; + if( powerNetwork ) + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", powerNetwork ); + else + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "--------------------" ); + + + if( target ) + { + battr = BG_Buildable( es->modelindex, es->angles ); + + if( probing ) + { + if( !player ) + { + // ammeter + cd->lines[ row ].icon = cgs.media.ckit_icon_current; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.2f", 0.001f * ps->misc[ MISC_INFO1 ] ); + + // voltmeter + cd->lines[ row ].icon = cgs.media.ckit_icon_voltage; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.2f", 0.001f * ps->misc[ MISC_INFO2 ] ); + + // stored BP + if( BG_Buildable( es->modelindex, NULL )->hasStorage ) + { + cd->lines[ row ].icon = cgs.media.ckit_icon_storedbp; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%.1f", 0.1f * ps->misc[ MISC_INFO3 ] ); + } + } + else + { + // health + cd->lines[ row ].icon = cgs.media.ckit_icon_health; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", ps->misc[ MISC_INFO1 ] ); + + // stored BP + if( ps->misc[ MISC_INFO3 ] != -1 ) + { + cd->lines[ row ].icon = cgs.media.ckit_icon_storedbp; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", ps->misc[ MISC_INFO2 ] ); + } + } + } + + if( !player ) + { + //health + cd->lines[ row ].icon = cgs.media.ckit_icon_health; + if( battr->cuboid ) + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", es->constantLight ); + else + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", es->generic1 ); + + //build points + cd->lines[ row ].icon = cgs.media.ckit_icon_bp; + Com_sprintf( cd->lines[ row++ ].text, MAX_CKIT_TEXT, "%i", battr->buildPoints ); + } + } + } +} /* ============= @@ -1033,6 +1219,100 @@ void CG_AddPlayerWeapon( refEntity_t *parent, playerState_t *ps, centity_t *cent CG_DestroyParticleSystem( ¢->muzzlePS ); } + // NOTE: this thing is full of magic numbers - they were determined by trial and error, do not modify + if( ps && weaponNum == WP_HBUILD ) + { + refEntity_t ent; + vec3_t origin; + float y; + int i, j, l; + + CG_GenerateCKitDisplay( ); + + memset( &ent, 0, sizeof( ent ) ); + ent.renderfx = parent->renderfx; + CG_PositionEntityOnTag( &ent, parent, parent->hModel, "tag_weapon" ); + + // offset it a bit + VectorMA( ent.origin, -0.1f, ent.axis[ 0 ], ent.origin ); + + // background + if( ( ent.customShader = cg.ckitDisp.background ) ) + { + ent.hModel = cgs.media.ckit_background; + trap_R_AddRefEntityToScene( &ent ); + } + + // overlay + ent.hModel = cgs.media.ckit_overlay; + ent.customShader = cgs.media.ckitOverlayShader; + trap_R_AddRefEntityToScene( &ent ); + + // big icons + if( ( ent.customShader = cg.ckitDisp.bigicona ) ) + { + ent.hModel = cgs.media.ckit_bigicona; + trap_R_AddRefEntityToScene( &ent ); + } + + if( ( ent.customShader = cg.ckitDisp.bigiconb ) ) + { + ent.hModel = cgs.media.ckit_bigiconb; + trap_R_AddRefEntityToScene( &ent ); + } + + // backup the origin + VectorCopy( ent.origin, origin ); + + // draw all 6 rows + for( y = 0.0f, i = 0; i < MAX_CKIT_ROWS; i++, y += -0.48f ) + { + VectorMA( origin, y * 0.42f, ent.axis[ 0 ], ent.origin ); + VectorMA( ent.origin, y, ent.axis[ 2 ], ent.origin ); + + if( ( ent.customShader = cg.ckitDisp.lines[ i ].icon ) ) + { + ent.hModel = cgs.media.ckit_icon; + trap_R_AddRefEntityToScene( &ent ); + } + + ent.hModel = cgs.media.ckit_digit; + l = strlen( cg.ckitDisp.lines[ i ].text ); + + if( l > MAX_CKIT_COLUMNS ) + l = MAX_CKIT_COLUMNS; + + VectorMA( ent.origin, -0.3f * ( MAX_CKIT_COLUMNS - l - 1 ), ent.axis[ 1 ], ent.origin ); + + for( j = 0; j < l && j < MAX_CKIT_COLUMNS; j++ ) + { + int index; + char ch; + + ch = cg.ckitDisp.lines[ i ].text[ j ]; + + if( ch >= '0' && ch <= '9' ) + index = ch - '0'; + else if( ch == '-' ) + index = 10; + else if( ch == '.' ) + index = 11; + else + index = 0; + + if( random() > 0.995f ) + { + index += (int)( random() * 11 ); + index %= 11; + } + + ent.customShader = cgs.media.digitalNumberShaders[ index ]; + VectorMA( ent.origin, -0.3f, ent.axis[ 1 ], ent.origin ); + trap_R_AddRefEntityToScene( &ent ); + } + } + } + // add the flash if( !weapon->wim[ weaponMode ].continuousFlash || !firing ) { @@ -1124,7 +1404,7 @@ void CG_AddViewWeapon( playerState_t *ps ) weaponInfo_t *wi; weapon_t weapon = ps->weapon; weaponMode_t weaponMode = ps->generic1; - vec3_t cuboidSize; + vec3_t cuboidSize; // no weapon carried - can't draw it if( weapon == WP_NONE ) diff --git a/src/game/bg_misc.c b/src/game/bg_misc.c index a416ecb..ca443f2 100644 --- a/src/game/bg_misc.c +++ b/src/game/bg_misc.c @@ -69,7 +69,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; ASPAWN_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_OVERMIND, //int buildNum; @@ -105,7 +106,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qtrue, //qboolean uniqueTest; OVERMIND_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_BARRICADE, //int buildNum; @@ -141,7 +143,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; BARRICADE_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_ACIDTUBE, //int buildNum; @@ -177,7 +180,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; ACIDTUBE_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_TRAPPER, //int buildNum; @@ -213,7 +217,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; TRAPPER_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_BOOSTER, //int buildNum; @@ -250,7 +255,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; BOOSTER_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_HIVE, //int buildNum; @@ -285,7 +291,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; HIVE_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_H_SPAWN, //int buildNum; @@ -321,7 +328,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; HSPAWN_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_H_MGTURRET, //int buildNum; @@ -357,7 +365,12 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; MGTURRET_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + MGTURRET_R_IDLE, //float resistance; + MGTURRET_R_ACTIVE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_TESLAGEN, //int buildNum; @@ -393,7 +406,12 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; TESLAGEN_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + TESLAGEN_R_IDLE, //float resistance; + TESLAGEN_R_ACTIVE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_ARMOURY, //int buildNum; @@ -429,7 +447,12 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; ARMOURY_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + ARMOURY_RESISTANCE, //float resistance; + ARMOURY_RESISTANCE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_DCC, //int buildNum; @@ -465,7 +488,12 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; DC_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + DC_R_IDLE, //float resistance; + DC_R_ACTIVE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_MEDISTAT, //int buildNum; @@ -502,20 +530,24 @@ static const buildableAttributes_t bg_buildableList[ ] = qtrue, //qboolean transparentTest; qfalse, //qboolean uniqueTest; MEDISTAT_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + MEDISTAT_R_IDLE, //float resistance; + MEDISTAT_R_ACTIVE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_REACTOR, //int buildNum; "reactor", //char *buildName; "Reactor", //char *humanName; - "All structures except the telenode rely on a reactor to operate. " - "The reactor provides power for all the human structures either " - "directly or via repeaters. Only one reactor can be built at a time.", - "team_human_reactor", //char *entityName; + "A large nuclear generator, able to power substantial amounts of " + "structures.", + "team_human_reactor_big", //char *entityName; TR_GRAVITY, //trType_t traj; 0.0, //float bounce; REACTOR_BP, //int buildPoints; - ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages + ( 1 << S3 ), //int stages REACTOR_HEALTH, //int health; 0, //int regenRate; REACTOR_SPLASHDAMAGE, //int splashDamage; @@ -536,9 +568,14 @@ static const buildableAttributes_t bg_buildableList[ ] = 0, //int creepSize; qfalse, //qboolean dccTest; qfalse, //qboolean transparentTest; - qtrue, //qboolean uniqueTest; + qfalse, //qboolean uniqueTest; REACTOR_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + qtrue, //qboolean isPowerSource; + qfalse, //qboolean requiresPower; + REACTOR_RESISTANCE, //float resistance; + REACTOR_RESISTANCE, //float surgeResistance; + qfalse //qboolean hasStorage; }, { BA_H_REPEATER, //int buildNum; @@ -574,7 +611,135 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; REPEATER_VALUE, //int value; - qfalse //qboolean cuboid; + qfalse, //qboolean cuboid; + DEFAULT_POWER_SETTINGS + }, + { + BA_H_CAPBANK, //int buildNum; + "capbank", //char *buildName; + "Capacitor Bank", //char *humanName; + "A bank of capacitors able to compensate for " + "fluctations in the power network or even power " + "the network in the absence of a Reactor.", + "team_human_capbank", //char *entityName; + TR_GRAVITY, //trType_t traj; + 0.0, //float bounce; + CAPBANK_BP, //int buildPoints; + ( 1 << S2 )|( 1 << S3 ), //int stages + CAPBANK_HEALTH, //int health; + 0, //int regenRate; + CAPBANK_SPLASHDAMAGE, //int splashDamage; + CAPBANK_SPLASHRADIUS, //int splashRadius; + MOD_HSPAWN, //int meansOfDeath; + TEAM_HUMANS, //int team; + ( 1 << WP_HBUILD ), //weapon_t buildWeapon; + BANIM_IDLE1, //int idleAnim; + 100, //int nextthink; + CAPBANK_BT, //int buildTime; + qtrue, //qboolean usable; + 0, //int turretRange; + 0, //int turretFireSpeed; + WP_NONE, //weapon_t turretProjType; + 0.95f, //float minNormal; + qfalse, //qboolean invertNormal; + qfalse, //qboolean creepTest; + 0, //int creepSize; + qfalse, //qboolean dccTest; + qfalse, //qboolean transparentTest; + qfalse, //qboolean uniqueTest; + CAPBANK_VALUE, //int value; + qfalse, //qboolean cuboid; + qtrue, //qboolean isPowerSource; + qfalse, //qboolean requiresPower; + CAPBANK_RESISTANCE, //float resistance; + CAPBANK_RESISTANCE, //float surgeResistance; + qfalse //qboolean hasStorage; + }, + { + BA_H_RTG, //int buildNum; + "rtg", //char *buildName; + "RTG Unit", //char *humanName; + "A portable all-in-one device, featuring a " + "radioisotope thermoelectric generator and a " + "miniaturized version of the Refinery. Does not " + "require power to be built. ^2First RTG Unit does " + "not require build points!", + "team_human_reactor", //char *entityName; + TR_GRAVITY, //trType_t traj; + 0.0, //float bounce; + RTG_BP, //int buildPoints; + ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages + RTG_HEALTH, //int health; + 0, //int regenRate; + RTG_SPLASHDAMAGE, //int splashDamage; + RTG_SPLASHRADIUS, //int splashRadius; + MOD_HSPAWN, //int meansOfDeath; + TEAM_HUMANS, //int team; + ( 1 << WP_HBUILD ), //weapon_t buildWeapon; + BANIM_IDLE1, //int idleAnim; + 500, //int nextthink; + RTG_BT, //int buildTime; + qtrue, //qboolean usable; + 0, //int turretRange; + 0, //int turretFireSpeed; + WP_NONE, //weapon_t turretProjType; + 0.95f, //float minNormal; + qfalse, //qboolean invertNormal; + qfalse, //qboolean creepTest; + 0, //int creepSize; + qfalse, //qboolean dccTest; + qfalse, //qboolean transparentTest; + qfalse, //qboolean uniqueTest; + RTG_VALUE, //int value; + qfalse, //qboolean cuboid; + qtrue, //qboolean isPowerSource; + qfalse, //qboolean requiresPower; + RTG_RESISTANCE, //float resistance; + RTG_RESISTANCE, //float surgeResistance; + qtrue //qboolean hasStorage; + }, + { + BA_H_REFINERY, //int buildNum; + "refinery", //char *buildName; + "Refinery", //char *humanName; + "A portable all-in-one device, featuring a " + "radioisotope thermoelectric generator and a " + "miniaturized version of the Refinery. Does not " + "require power to be built. ^1This is the first " + "buildable to be built.", + "team_human_refinery", //char *entityName; + TR_GRAVITY, //trType_t traj; + 0.0, //float bounce; + REFINERY_BP, //int buildPoints; + ( 1 << S2 )|( 1 << S3 ), //int stages + REFINERY_HEALTH, //int health; + 0, //int regenRate; + REFINERY_SPLASHDAMAGE, //int splashDamage; + REFINERY_SPLASHRADIUS, //int splashRadius; + MOD_HSPAWN, //int meansOfDeath; + TEAM_HUMANS, //int team; + ( 1 << WP_HBUILD ), //weapon_t buildWeapon; + BANIM_IDLE1, //int idleAnim; + 500, //int nextthink; + REFINERY_BT, //int buildTime; + qtrue, //qboolean usable; + 0, //int turretRange; + 0, //int turretFireSpeed; + WP_NONE, //weapon_t turretProjType; + 0.95f, //float minNormal; + qfalse, //qboolean invertNormal; + qfalse, //qboolean creepTest; + 0, //int creepSize; + qfalse, //qboolean dccTest; + qfalse, //qboolean transparentTest; + qfalse, //qboolean uniqueTest; + REFINERY_VALUE, //int value; + qfalse, //qboolean cuboid; + qfalse, //qboolean isPowerSource; + qtrue, //qboolean requiresPower; + REFINERY_R_IDLE, //float resistance; + REFINERY_R_ACTIVE, //float surgeResistance; + qtrue //qboolean hasStorage; }, { BA_H_CUBOID1, //int buildNum; @@ -610,7 +775,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; 0, //int value; - qtrue //qboolean cuboid; + qtrue, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_H_CUBOID2, //int buildNum; @@ -618,13 +784,13 @@ static const buildableAttributes_t bg_buildableList[ ] = "Glass", //char *humanName; "A cuboid made of a transparent chemical compound. " "Its durability is low compared to other glass-like " - "materials. However it has shown increased strenght " + "materials. However it has shown increased strength " "when formed into a thin pane shape.", "team_human_hcuboid2", //char *entityName; TR_GRAVITY, //trType_t traj; 0.0, //float bounce; 0, //int buildPoints; - ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages + ( 1 << S2 )|( 1 << S3 ), //int stages 0, //int health; 0, //int regenRate; 0, //int splashDamage; @@ -647,7 +813,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; 0, //int value; - qtrue //qboolean cuboid; + qtrue, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_H_CUBOID3, //int buildNum; @@ -661,7 +828,7 @@ static const buildableAttributes_t bg_buildableList[ ] = TR_GRAVITY, //trType_t traj; 0.0, //float bounce; 0, //int buildPoints; - ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages + ( 1 << S3 ), //int stages 0, //int health; 0, //int regenRate; 0, //int splashDamage; @@ -684,7 +851,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; 0, //int value; - qtrue //qboolean cuboid; + qtrue, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_CUBOID1, //int buildNum; @@ -720,7 +888,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; 0, //int value; - qtrue //qboolean cuboid; + qtrue, //qboolean cuboid; + DEFAULT_POWER_SETTINGS }, { BA_A_CUBOID2, //int buildNum; @@ -735,7 +904,7 @@ static const buildableAttributes_t bg_buildableList[ ] = TR_GRAVITY, //trType_t traj; 0.0, //float bounce; 0, //int buildPoints; - ( 1 << S1 )|( 1 << S2 )|( 1 << S3 ), //int stages + ( 1 << S2 )|( 1 << S3 ), //int stages 0, //int health; 0, //int regenRate; 0, //int splashDamage; @@ -758,7 +927,8 @@ static const buildableAttributes_t bg_buildableList[ ] = qfalse, //qboolean transparentTest; qfalse, //qboolean uniqueTest; 0, //int value; - qtrue //qboolean cuboid; + qtrue, //qboolean cuboid; + DEFAULT_POWER_SETTINGS } }; @@ -1283,7 +1453,7 @@ static const classAttributes_t bg_classList[ ] = 80, //int steptime; LEVEL2_SPEED, //float speed; 10.0f, //float acceleration; - 3.0f, //float airAcceleration; + 2.0f, //float airAcceleration; 6.0f, //float friction; 100.0f, //float stopSpeed; 380.0f, //float jumpMagnitude; @@ -1310,7 +1480,7 @@ static const classAttributes_t bg_classList[ ] = 80, //int steptime; LEVEL2_UPG_SPEED, //float speed; 10.0f, //float acceleration; - 3.0f, //float airAcceleration; + 2.0f, //float airAcceleration; 6.0f, //float friction; 100.0f, //float stopSpeed; 380.0f, //float jumpMagnitude; @@ -1419,9 +1589,9 @@ static const classAttributes_t bg_classList[ ] = 0.002f, //float bob; 1.0f, //float bobCycle; 100, //int steptime; - 1.0f, //float speed; + 1.1f, //float speed; 10.0f, //float acceleration; - 1.0f, //float airAcceleration; + 1.5f, //float airAcceleration; 6.0f, //float friction; 100.0f, //float stopSpeed; 220.0f, //float jumpMagnitude; @@ -4301,7 +4471,7 @@ const cuboidAttributes_t BG_CuboidTypes [] = 80, // float hppv CBHPT_PLAIN, // int hpt 3.0, // float bppv - 20, // int buildrate + 30, // int buildrate 5000, // int minbt qfalse, // qboolean regen; 0, // int regenspeed @@ -4325,7 +4495,7 @@ const cuboidAttributes_t BG_CuboidTypes [] = 52, // float hppv CBHPT_PANES, // int hpt 1.5, // float bppv - 15, // int buildrate + 25, // int buildrate 6500, // int minbt qfalse, // qboolean regen; 0, // int regenspeed @@ -4349,7 +4519,7 @@ const cuboidAttributes_t BG_CuboidTypes [] = 50, // float hppv CBHPT_PLAIN, // int hpt 4.0, // float bppv - 10, // int buildrate + 15, // int buildrate 8000, // int minbt qfalse, // qboolean regen; 0, // int regenspeed diff --git a/src/game/bg_public.h b/src/game/bg_public.h index e06015d..cbaf90a 100644 --- a/src/game/bg_public.h +++ b/src/game/bg_public.h @@ -280,12 +280,11 @@ typedef enum PERS_CREDIT, // human credit PERS_QUEUEPOS, // position in the spawn queue PERS_NEWWEAPON, // weapon to switch to - PERS_BP, - PERS_MARKEDBP, + PERS_BUILDPOINTS, //zdrytchx: no space in stats, use persistant. This meanas we risk doing a double jump upon spawning but death animations are 1700 msecs long, so technically it's impossible anyway PERS_JUMPTIME, PERS_SPAWNS_IMPLANTED - // netcode has space for 1 more + // netcode has space for 2 more } persEnum_t; #define PS_WALLCLIMBINGFOLLOW 0x00000001 @@ -293,6 +292,21 @@ typedef enum #define PS_NONSEGMODEL 0x00000004 #define PS_SPRINTTOGGLE 0x00000008 +// player_state->misc[] indexes +typedef enum +{ + MISC_INFOHEAD, + MISC_INFO1, + MISC_INFO2, + MISC_INFO3, + + MISC_CUBOID_X, + MISC_CUBOID_Y, + MISC_CUBOID_Z + + // netcode has space for 10 more +} miscEnum_t; + // entityState_t->eFlags // notice that some flags are overlapped, so their meaning depends on context #define EF_DEAD 0x0001 // don't draw a foe marker over players with EF_DEAD @@ -307,7 +321,7 @@ typedef enum // buildable flags: #define EF_B_SPAWNED 0x0008 #define EF_B_POWERED 0x0010 -#define EF_B_MARKED 0x0020 +#define EF_B_SURGE 0x0020 // note: enabled/disabled for Repeaters #define EF_WARN_CHARGE 0x0020 // Lucifer Cannon is about to overcharge #define EF_WALLCLIMB 0x0040 // wall walking @@ -438,6 +452,10 @@ typedef enum BA_H_REACTOR, BA_H_REPEATER, + BA_H_CAPBANK, + BA_H_RTG, + + BA_H_REFINERY, //cuboids must stay in a block #define CUBOID_FIRST BA_H_CUBOID1 @@ -581,7 +599,10 @@ typedef enum EV_ALIEN_HATCH_FAILURE, // when it doesns't work EV_JETPACK_DEACTIVATE, - EV_JETPACK_REFUEL + EV_JETPACK_REFUEL, + + EV_POWER_SWITCH, + EV_POWER_ZAP } entity_event_t; @@ -1068,9 +1089,17 @@ typedef struct qboolean transparentTest; qboolean uniqueTest; - int value; + int value; qboolean cuboid; + + //power grid features + qboolean isPowerSource; + qboolean requiresPower; + float resistance; + float surgeResistance; + + qboolean hasStorage; } buildableAttributes_t; typedef struct diff --git a/src/game/g_active.c b/src/game/g_active.c index 1fa0caa..95f00d6 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -682,20 +682,6 @@ void ClientTimerActions( gentity_t *ent, int msec ) client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT; else client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT; - - // Let the client know which buildables will be removed by building - for( i = 0; i < MAX_MISC; i++ ) - { - if( i < level.numBuildablesForRemoval ) - client->ps.misc[ i ] = level.markedBuildables[ i ]->s.number; - else - client->ps.misc[ i ] = 0; - } - } - else - { - for( i = 0; i < MAX_MISC; i++ ) - client->ps.misc[ i ] = 0; } break; @@ -780,6 +766,55 @@ void ClientTimerActions( gentity_t *ent, int msec ) ent->client->ps.stats[ STAT_SHAKE ] *= 0.77f; if( ent->client->ps.stats[ STAT_SHAKE ] < 0 ) ent->client->ps.stats[ STAT_SHAKE ] = 0; + + // update the currents and voltages + if( BG_GetPlayerWeapon( &client->ps ) == WP_HBUILD ) + { + trace_t tr; + vec3_t viewOrigin, forward, end; + gentity_t *traceEnt; + int head_network = 0, head_entity = 1023; + + BG_GetClientViewOrigin( &client->ps, viewOrigin ); + AngleVectors( client->ps.viewangles, forward, NULL, NULL ); + VectorMA( viewOrigin, 200, forward, end ); + + trap_Trace( &tr, viewOrigin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); + traceEnt = &g_entities[ tr.entityNum ]; + + head_network = ent->powerNetwork; + + if( tr.fraction < 0.99f ) + { + if( traceEnt->client ) + { + if( traceEnt->client->pers.teamSelection == TEAM_HUMANS && + traceEnt->client->ps.stats[ STAT_HEALTH ] >= 0 ) + { + head_entity = traceEnt->s.number; + client->ps.misc[ MISC_INFO1 ] = traceEnt->client->ps.stats[ STAT_HEALTH ]; + + if( BG_GetPlayerWeapon( &traceEnt->client->ps ) == WP_HBUILD ) + client->ps.misc[ MISC_INFO2 ] = traceEnt->client->ps.persistant[ PERS_BUILDPOINTS ]; + else + client->ps.misc[ MISC_INFO2 ] = -1; + } + } + else if( traceEnt->s.eType == ET_BUILDABLE && + traceEnt->health > 0 && traceEnt->buildableTeam == TEAM_HUMANS && + ( traceEnt->isPowerSource || traceEnt->requiresPower ) ) + { + head_network = traceEnt->powerNetwork; + head_entity = traceEnt->s.number; + client->ps.misc[ MISC_INFO1 ] = traceEnt->current * 1000; + client->ps.misc[ MISC_INFO2 ] = traceEnt->voltage * 1000; + client->ps.misc[ MISC_INFO3 ] = traceEnt->storedBP * 10; + } + } + + client->ps.misc[ MISC_INFOHEAD ] = ( head_network & 1023 ) | ( head_entity & 1023 ) << 10; + + } } while( client->time1000 >= 1000 ) @@ -1769,16 +1804,11 @@ void ClientThink_real( gentity_t *ent ) vec3_t eyes, view, point; gentity_t *traceEnt; -#define USE_OBJECT_RANGE 64 - - int entityList[ MAX_GENTITIES ]; - vec3_t range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE }; - vec3_t mins, maxs; - int i, num; +#define USE_OBJECT_RANGE 100 // look for object infront of player AngleVectors( client->ps.viewangles, view, NULL, NULL ); - BG_GetClientViewOrigin( &client->ps, eyes ); // !@#CUBOID + BG_GetClientViewOrigin( &client->ps, eyes ); VectorMA( eyes, USE_OBJECT_RANGE, view, point ); trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT ); @@ -1786,50 +1816,29 @@ void ClientThink_real( gentity_t *ent ) if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use ) traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context - else + else if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { - //no entity in front of player - do a small area search - - VectorAdd( client->ps.origin, range, maxs ); - VectorSubtract( client->ps.origin, range, mins ); - - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) + if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ], + client->pers.credit, + g_alienStage.integer ) ) { - traceEnt = &g_entities[ entityList[ i ] ]; - - if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use ) - { - traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context - break; - } + //no nearby objects and alien - show class menu + G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST ); } - - if( i == num && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + else { - if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ], - client->pers.credit, - g_alienStage.integer ) ) - { - //no nearby objects and alien - show class menu - G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST ); - } - else - { - //flash frags - G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 ); - } + //flash frags + G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 ); } } } - client->ps.persistant[ PERS_BP ] = G_GetBuildPoints( client->ps.origin, - client->ps.stats[ STAT_TEAM ] ); - client->ps.persistant[ PERS_MARKEDBP ] = G_GetMarkedBuildPoints( client->ps.origin, - client->ps.stats[ STAT_TEAM ] ); - - if( client->ps.persistant[ PERS_BP ] < 0 ) - client->ps.persistant[ PERS_BP ] = 0; + if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + { + client->ps.persistant[ PERS_BUILDPOINTS ] = G_GetBuildPoints( client->ps.origin, client->ps.stats[ STAT_TEAM ] ); + if( client->ps.persistant[ PERS_BUILDPOINTS ] < 0 ) + client->ps.persistant[ PERS_BUILDPOINTS ] = 0; + } // perform once-a-second actions ClientTimerActions( ent, msec ); diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index 0039ccd..8447e84 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -111,223 +111,6 @@ gentity_t *G_CheckSpawnPoint( int spawnNum, const vec3_t origin, return NULL; } -#define POWER_REFRESH_TIME 2000 - -/* -================ -G_FindPower - -attempt to find power for self, return qtrue if successful -================ -*/ -qboolean G_FindPower( gentity_t *self, qboolean searchUnspawned ) -{ - int i, j; - gentity_t *ent, *ent2; - gentity_t *closestPower = NULL; - int distance = 0; - int minDistance = REPEATER_BASESIZE + 1; - vec3_t temp_v; - - if( self->buildableTeam != TEAM_HUMANS ) - return qfalse; - - // Reactor is always powered - if( self->s.modelindex == BA_H_REACTOR ) - { - self->parentNode = self; - - return qtrue; - } - - // Handle repeaters - if( self->s.modelindex == BA_H_REPEATER ) - { - self->parentNode = G_Reactor( ); - - return self->parentNode != NULL; - } - - // Iterate through entities - for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - // If entity is a power item calculate the distance to it - if( ( ent->s.modelindex == BA_H_REACTOR || ent->s.modelindex == BA_H_REPEATER ) && - ( searchUnspawned || ent->spawned ) && ent->powered && ent->health > 0 ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); - - // Always prefer a reactor if there is one in range - if( ent->s.modelindex == BA_H_REACTOR && distance <= REACTOR_BASESIZE ) - { - // Only power as much BP as the reactor can hold - if( self->s.modelindex != BA_NONE ) - { - int buildPoints = g_humanBuildPoints.integer; - - // Scan the buildables in the reactor zone - for( j = MAX_CLIENTS, ent2 = g_entities + j; j < level.num_entities; j++, ent2++ ) - { - gentity_t *powerEntity; - - if( ent2->s.eType != ET_BUILDABLE ) - continue; - - if( ent2 == self ) - continue; - - powerEntity = ent2->parentNode; - - if( powerEntity && powerEntity->s.modelindex == BA_H_REACTOR && ( powerEntity == ent ) ) - { - buildPoints -= BG_Buildable( ent2->s.modelindex, ent2->cuboidSize )->buildPoints; - } - } - - buildPoints -= level.humanBuildPointQueue; - - buildPoints -= BG_Buildable( self->s.modelindex, self->cuboidSize )->buildPoints; - - if( buildPoints >= 0 ) - { - self->parentNode = ent; - return qtrue; - } - else - { - // a buildable can still be built if it shares BP from two zones - - // TODO: handle combined power zones here - } - } - - // Dummy buildables don't need to look for zones - else - { - self->parentNode = ent; - return qtrue; - } - } - else if( distance < minDistance ) - { - // It's a repeater, so check that enough BP will be available to power - // the buildable but only if self is a real buildable - - if( self->s.modelindex != BA_NONE ) - { - int buildPoints = g_humanRepeaterBuildPoints.integer; - - // Scan the buildables in the repeater zone - for( j = MAX_CLIENTS, ent2 = g_entities + j; j < level.num_entities; j++, ent2++ ) - { - gentity_t *powerEntity; - - if( ent2->s.eType != ET_BUILDABLE ) - continue; - - if( ent2 == self ) - continue; - - powerEntity = ent2->parentNode; - - if( powerEntity && powerEntity->s.modelindex == BA_H_REPEATER && ( powerEntity == ent ) ) - { - buildPoints -= BG_Buildable( ent2->s.modelindex, ent->cuboidSize )->buildPoints; - } - } - - if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active ) - buildPoints -= level.buildPointZones[ ent->buildPointZone ].queuedBuildPoints; - - buildPoints -= BG_Buildable( self->s.modelindex, self->cuboidSize )->buildPoints; - - if( buildPoints >= 0 ) - { - closestPower = ent; - minDistance = distance; - } - else - { - // a buildable can still be built if it shares BP from two zones - - // TODO: handle combined power zones here - } - } - else - { - // Dummy buildables don't need to look for zones - closestPower = ent; - minDistance = distance; - } - } - } - } - - self->parentNode = closestPower; - return self->parentNode != NULL; -} - -/* -================ -G_PowerEntityForPoint - -Simple wrapper to G_FindPower to find the entity providing -power for the specified point -================ -*/ -gentity_t *G_PowerEntityForPoint( const vec3_t origin ) -{ - gentity_t dummy; - - dummy.parentNode = NULL; - dummy.buildableTeam = TEAM_HUMANS; - dummy.s.modelindex = BA_NONE; - VectorCopy( origin, dummy.s.origin ); - - if( G_FindPower( &dummy, qfalse ) ) - return dummy.parentNode; - else - return NULL; -} - -/* -================ -G_PowerEntityForEntity - -Simple wrapper to G_FindPower to find the entity providing -power for the specified entity -================ -*/ -gentity_t *G_PowerEntityForEntity( gentity_t *ent ) -{ - if( G_FindPower( ent, qfalse ) ) - return ent->parentNode; - return NULL; -} - -/* -================ -G_IsPowered - -Check if a location has power, returning the entity type -that is providing it -================ -*/ -buildable_t G_IsPowered( vec3_t origin ) -{ - gentity_t *ent = G_PowerEntityForPoint( origin ); - - if( ent ) - return ent->s.modelindex; - else - return BA_NONE; -} - - /* ================== G_GetBuildPoints @@ -347,95 +130,36 @@ int G_GetBuildPoints( const vec3_t pos, team_t team ) } else if( team == TEAM_HUMANS ) { - gentity_t *powerPoint = G_PowerEntityForPoint( pos ); - - if( powerPoint && powerPoint->s.modelindex == BA_H_REACTOR ) - return level.humanBuildPoints; - - if( powerPoint && powerPoint->s.modelindex == BA_H_REPEATER && - powerPoint->usesBuildPointZone && level.buildPointZones[ powerPoint->buildPointZone ].active ) - { - return level.buildPointZones[ powerPoint->buildPointZone ].totalBuildPoints - - level.buildPointZones[ powerPoint->buildPointZone ].queuedBuildPoints; - } - - // Return the BP of the main zone by default - return level.humanBuildPoints; + return 0xDEADBEE; //humans use the material system } return 0; } /* -================== -G_GetMarkedBuildPoints +================ +G_IsDCCBuilt -Get the number of marked build points from a position -================== +See if any powered DCC exists +================ */ -int G_GetMarkedBuildPoints( const vec3_t pos, team_t team ) +qboolean G_IsDCCBuilt( void ) { - gentity_t *ent; int i; - int sum = 0; - - if( G_TimeTilSuddenDeath( ) <= 0 ) - return 0; - - if( !g_markDeconstruct.integer ) - return 0; - - for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( team == TEAM_HUMANS && - ent->s.modelindex != BA_H_REACTOR && - ent->s.modelindex != BA_H_REPEATER && - ent->parentNode != G_PowerEntityForPoint( pos ) ) - continue; - - if( !ent->inuse ) - continue; - - if( ent->health <= 0 ) - continue; - - if( ent->buildableTeam != team ) - continue; - - if( ent->deconstruct ) - sum += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints; - } - - return sum; -} - -/* -================== -G_InPowerZone - -See if a buildable is inside of another power zone. -Return pointer to provider if so. -It's different from G_FindPower because FindPower for -providers will find themselves. -(This doesn't check if power zones overlap) -================== -*/ -gentity_t *G_InPowerZone( gentity_t *self ) -{ - int i; - gentity_t *ent; - int distance; - vec3_t temp_v; + gentity_t *ent; + static gentity_t *cache = NULL; + + if( cache && cache->inuse && cache->s.eType == ET_BUILDABLE && + cache->s.modelindex == BA_H_DCC && cache->spawned && + cache->powered && cache->health >= 0 ) + return qtrue; for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { if( ent->s.eType != ET_BUILDABLE ) continue; - if( ent == self ) + if( ent->s.modelindex != BA_H_DCC ) continue; if( !ent->spawned ) @@ -443,81 +167,46 @@ gentity_t *G_InPowerZone( gentity_t *self ) if( ent->health <= 0 ) continue; + + if( !ent->powered ) + continue; - // if entity is a power item calculate the distance to it - if( ( ent->s.modelindex == BA_H_REACTOR || ent->s.modelindex == BA_H_REPEATER ) && - ent->spawned && ent->powered ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); + cache = ent; - if( ent->s.modelindex == BA_H_REACTOR && distance <= REACTOR_BASESIZE ) - return ent; - else if( ent->s.modelindex == BA_H_REPEATER && distance <= REPEATER_BASESIZE ) - return ent; - } + return qtrue; } - return NULL; + return qfalse; } -/* -================ -G_FindDCC - -attempt to find a controlling DCC for self, return number found -================ -*/ -int G_FindDCC( gentity_t *self ) -{ - int i; - gentity_t *ent; - int distance = 0; - vec3_t temp_v; - int foundDCC = 0; - - if( self->buildableTeam != TEAM_HUMANS ) - return 0; - - //iterate through entities - for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities && foundDCC < MAX_DCS_PER_BUILDABLE; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - //if entity is a dcc calculate the distance to it - if( ent->s.modelindex == BA_H_DCC && ent->spawned ) - { - VectorSubtract( self->s.origin, ent->s.origin, temp_v ); - distance = VectorLength( temp_v ); - if( distance < DC_RANGE && ent->powered ) - { - foundDCC++; - } - } - } - - return foundDCC; -} /* ================ -G_IsDCCBuilt +G_IsRTGBuilt -See if any powered DCC exists +See if any RTG exists ================ */ -qboolean G_IsDCCBuilt( void ) +qboolean G_IsRTGBuilt( void ) { int i; gentity_t *ent; + static gentity_t *cache = NULL; + + if( cache && cache->inuse && cache->s.eType == ET_BUILDABLE && + cache->s.modelindex == BA_H_DCC && cache->spawned && + cache->health >= 0 ) + return qtrue; for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { + if( !ent->r.linked ) + continue; + if( ent->s.eType != ET_BUILDABLE ) continue; - if( ent->s.modelindex != BA_H_DCC ) + if( ent->s.modelindex != BA_H_RTG ) continue; if( !ent->spawned ) @@ -526,6 +215,8 @@ qboolean G_IsDCCBuilt( void ) if( ent->health <= 0 ) continue; + cache = ent; + return qtrue; } @@ -534,33 +225,17 @@ qboolean G_IsDCCBuilt( void ) /* ================ -G_Reactor G_Overmind -Since there's only one of these and we quite often want to find them, cache the -results, but check them for validity each time +Since there's only one overmind and we quite often want to find it, cache the +results, but check it for validity each time -The code here will break if more than one reactor or overmind is allowed, even +The code here will break if more than one overmind is allowed, even if one of them is dead/unspawned ================ */ static gentity_t *G_FindBuildable( buildable_t buildable ); -gentity_t *G_Reactor( void ) -{ - static gentity_t *rc; - - // If cache becomes invalid renew it - if( !rc || rc->s.eType != ET_BUILDABLE || rc->s.modelindex != BA_H_REACTOR ) - rc = G_FindBuildable( BA_H_REACTOR ); - - // If we found it and it's alive, return it - if( rc && rc->spawned && rc->health > 0 ) - return rc; - - return NULL; -} - gentity_t *G_Overmind( void ) { static gentity_t *om; @@ -698,6 +373,457 @@ static void G_CreepSlow( gentity_t *self ) /* ================ +G_ScanPowerGrid + +Recursively finds all power entities reachable from the specified entity +================ +*/ +static struct +{ + int networkID; + + gentity_t *load[ MAX_GENTITIES ]; + int loadCount; + + gentity_t *sources[ MAX_GENTITIES ]; + int sourceCount; + + qboolean visited[ MAX_GENTITIES ]; +} grid; + +void G_ScanPowerGrid( gentity_t *this ) +{ + int i; + int nextList[ MAX_GENTITIES ], nextCount; + gentity_t *next; + vec3_t mins, maxs; + float range; + + switch( this->s.modelindex ) + { + case BA_H_REACTOR: + case BA_H_CAPBANK: + case BA_H_RTG: + range = REACTOR_BASESIZE; + break; + case BA_H_REPEATER: + range = REPEATER_BASESIZE; + break; + } + + for( i = 0; i < 3; i++ ) + { + mins[ i ] = this->s.origin[ i ] - range; + maxs[ i ] = this->s.origin[ i ] + range; + } + + nextCount = trap_EntitiesInBox( mins, maxs, nextList, MAX_GENTITIES ); + + for( i = 0; i < nextCount; i++ ) + { + if( grid.visited[ nextList[ i ] ] ) + continue; + grid.visited[ nextList[ i ] ] = qtrue; + + next = g_entities + nextList[ i ]; + + if( next->s.eType != ET_BUILDABLE ) + { + //let ckits know in which network's range they're in + if( next->client && + next->health >= 0 && + next->s.weapon == WP_HBUILD ) + next->powerNetwork = grid.networkID; + continue; + } + + if( next->health <= 0 ) + continue; + if( next->buildableTeam != TEAM_HUMANS ) + continue; + + //repeater just extends the power grid + //it does not provide or consume power + if( next->spawned && next->s.modelindex == BA_H_REPEATER ) + { + //switched off + if( !next->active ) + continue; + + next->powerNetwork = grid.networkID; + G_ScanPowerGrid( next ); + continue; + } + + if( !next->isPowerSource && !next->requiresPower ) + continue; + + next->powerNetwork = grid.networkID; + if( next->isPowerSource ) + { + //switched off + if( !next->active ) + continue; + grid.sources[ grid.sourceCount++ ] = next; + } + else + grid.load[ grid.loadCount++ ] = next; + } +} + + +/* +================ +G_CalculatePowerGrid + +This function takes the listed power sources (with voltages V and +internal resistances Rs) and load (with resistances R), constructs the +following electrical circuit and solves it: + + Legend: + -(-+)-- voltage source + -/\/\/- resistor + |- ground + + V1 Rs1 I1 R1 + +--(-+)--/\/\/-->-+ +--/\/\/--+ + | V2 Rs2 I2| I | R2 | + |--+--(-+)--/\/\/-->-+-->--+--/\/\/--+--| + | | | | + ... ... ... ... + | Vn Rsn In| | Rn | + +--(-+)--/\/\/-->-+ +--/\/\/--+ + +================ +*/ +void G_CalculatePowerGrid( void ) +{ + int i, j; + gentity_t *ent; + + float nGl, nGs, nG, nR; + float URs, U2, Is; + + // the net load conductance (resistors in parallel) + for( nGl = 0.0f, i = 0; i < grid.loadCount; i++ ) + nGl += 1.0f / grid.load[ i ]->resistance; + + // the net source conductance (resistors in parallel) + for( nGs = 0.0f, i = 0; i < grid.sourceCount; i++ ) + nGs += 1.0f / grid.sources[ i ]->resistance; + + // solve the circuit using the superposition theorem + for( i = 0; i < grid.sourceCount; i++ ) + { + ent = grid.sources[ i ]; + + // net load + other sources conductance + nG = nGl + nGs - 1.0f / ent->resistance; + + // net resistance + nR = 1.0f / nG + ent->resistance; + + // current flowing through the source + Is = ent->voltage / nR; + ent->current += ent->voltage / nR; + + // voltage drop on source's internal resistance + URs = Is * ent->resistance; + + // voltage drop on other sources or the entire load + U2 = ent->voltage - URs; + + // current flowing through other sources + for( j = 0; j < grid.sourceCount; j++ ) + if( i != j ) + grid.sources[ j ]->current -= U2 / grid.sources[ j ]->resistance; + + // current flowing through parts of the load + for( j = 0; j < grid.loadCount; j++ ) + grid.load[ j ]->current += U2 / grid.load[ j ]->resistance; + } +} + +/* +================ +G_UpdatePowerGrid + +Recalculate the entire power grid +================ +*/ +void G_UpdatePowerGrid( float dt ) +{ + int i; + gentity_t *ent; + + // reset all ckits + for( i = 0; i < MAX_CLIENTS; i++ ) + g_entities[ i ].powerNetwork = 0; + + // reset all power entities + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) + { + ent = g_entities + i; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( ent->buildableTeam != TEAM_HUMANS ) + continue; + + ent->powerNetwork = 0; + ent->current = 0.0f; + + if( !ent->spawned ) + { + ent->resistance = PREBUILD_RESISTANCE; + continue; + } + + if( ent->s.modelindex == BA_H_REACTOR || + ent->s.modelindex == BA_H_RTG ) + ent->voltage = POWER_VOLTAGE * g_voltageModifier.value; + + if( !ent->requiresPower || ent->isPowerSource ) + continue; + + if( ent->surge ) + ent->resistance = BG_Buildable( ent->s.modelindex, NULL )->surgeResistance; + else + ent->resistance = BG_Buildable( ent->s.modelindex, NULL )->resistance; + } + + // this table will be used throughout the following loop and its recursive calls + memset( grid.visited, 0, sizeof( grid.visited ) ); + + // find an unvisited power source and make a list of all power sources + // and receivers reachable from it for G_CalculatePowerGrid + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) + { + if( grid.visited[ i ] ) + continue; + + ent = g_entities + i; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( !ent->spawned ) + continue; + if( ent->health <= 0 ) + continue; + if( ent->buildableTeam != TEAM_HUMANS ) + continue; + if( !ent->isPowerSource ) + continue; + if( !ent->active ) + continue; + + // unique network id + grid.networkID = ent->s.number; + ent->powerNetwork = grid.networkID; + + // traverse the world and find all reachable power entities + grid.loadCount = 0; + grid.sourceCount = 0; + + // add this source to the list + grid.visited[ i ] = qtrue; + grid.sources[ grid.sourceCount++ ] = ent; + + // scan recursively + G_ScanPowerGrid( ent ); + + // calculate the power grid + G_CalculatePowerGrid( ); + } + + // calculate voltages, power levels, etc. + for( i = MAX_CLIENTS; i < level.num_entities; i++ ) + { + ent = g_entities + i; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( ent->buildableTeam != TEAM_HUMANS ) + continue; + + if( ent->isPowerSource ) + { + if( ent->active && ent->s.modelindex == BA_H_CAPBANK ) + ent->voltage -= ent->current * dt / CAPBANK_CAPACITY; + + //zapping effect + #define MIN_ZAP_CURRENT 2.0f + #define ZAP_CHANCE_FACTOR 0.01f + if( ent->current > MIN_ZAP_CURRENT ) + { + float chance; + + chance = ( ent->current - MIN_ZAP_CURRENT ) * ZAP_CHANCE_FACTOR; + if( chance > 0.2f ) + chance = 0.2f; + + chance = 1.0f - chance; + if( random() > chance ) + G_AddEvent( ent, EV_POWER_ZAP, 0 ); + } + } + else + ent->voltage = ent->current * ent->resistance; + } +} + +/* +================ +G_SetupPowerEntity + +Called when a Human buildable finishes spawning and needs power grid +related variables to be set accordingly +================ +*/ +void G_SetupPowerEntity( gentity_t *built ) +{ + built->requiresPower = BG_Buildable( built->s.modelindex, NULL )->requiresPower; + built->isPowerSource = BG_Buildable( built->s.modelindex, NULL )->isPowerSource; + built->resistance = BG_Buildable( built->s.modelindex, NULL )->resistance; + + if( built->isPowerSource ) + { + switch( built->s.modelindex ) + { + case BA_H_REACTOR: + built->resistance = REACTOR_RESISTANCE; + break; + case BA_H_CAPBANK: + built->voltage = 0.0f; //spawn discharged + built->resistance = CAPBANK_RESISTANCE; + case BA_H_RTG: + built->resistance = RTG_RESISTANCE; + break; + } + } +} + +/* +================ +G_PowerForPoint + +Returns to which network ID this point belongs +================ +*/ + +int G_PowerForPoint( vec3_t point ) +{ + int i; + int list[ MAX_GENTITIES ], count; + gentity_t *ent; + vec3_t mins, maxs; + float range; +/* + switch( this->s.modelindex ) + { + + }*/ + range = REACTOR_BASESIZE; + + for( i = 0; i < 3; i++ ) + { + mins[ i ] = point[ i ] - range; + maxs[ i ] = point[ i ] + range; + } + + count = trap_EntitiesInBox( mins, maxs, list, MAX_GENTITIES ); + + for( i = 0; i < count; i++ ) + { + ent = g_entities + list[ i ]; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( ent->buildableTeam != TEAM_HUMANS ) + continue; + if( ent->health <= 0 ) + continue; + if( !ent->spawned ) + continue; + if( !ent->powerNetwork ) + continue; + + switch( ent->s.modelindex ) + { + case BA_H_REACTOR: + case BA_H_CAPBANK: + case BA_H_RTG: + range = REACTOR_BASESIZE; + break; + case BA_H_REPEATER: + range = REPEATER_BASESIZE; + break; + default: + continue; + } + + if( Distance( ent->s.origin, point ) > range ) + continue; + + return ent->powerNetwork; + } + return 0; +} + +/* +================ +NOTES TO G_CheckPower AND G_Surge + +Make sure that a buildable's resistance NEVER depends on its power +state. Failure to ensure that will result in feedback loops in the +power grid and general weirdness. +================ +*/ + +/* +================ +G_CheckPower + +(Helper for Human buildable think functions) +Checks if there's enough power for the buildable to idle +================ +*/ +qboolean G_CheckPower( gentity_t *self, float min_current ) +{ + self->surge = qfalse; + self->surgePowered = qfalse; + self->powered = qtrue; + + if( self->current < min_current ) + { + self->powered = qfalse; + return qfalse; + } + + return qtrue; +} + +/* +================ +G_Surge + +(Helper for Human buildable think functions) +Checks if there's enough power for the buildable to perform its action +================ +*/ +qboolean G_Surge( gentity_t *self, float surge_current ) +{ + self->surge = qtrue; + + if( self->current < surge_current ) + return qfalse; + + self->surgePowered = qtrue; + return qtrue; +} + +/* +================ nullDieFunction hack to prevent compilers complaining about function pointer -> NULL conversion @@ -1536,42 +1662,6 @@ void ATrapper_Think( gentity_t *self ) //================================================================================== - - - -/* -================ -G_SuicideIfNoPower - -Destroy human structures that have been unpowered too long -================ -*/ -static qboolean G_SuicideIfNoPower( gentity_t *self ) -{ - if( self->buildableTeam != TEAM_HUMANS ) - return qfalse; - - if( !self->powered ) - { - // if the power hasn't reached this buildable for some time, then destroy the buildable - if( self->count == 0 ) - self->count = level.time; - else if( ( level.time - self->count ) >= HUMAN_BUILDABLE_INACTIVE_TIME ) - { - if( self->parentNode ) - G_Damage( self, NULL, g_entities + self->parentNode->killedBy, - NULL, NULL, self->health, 0, MOD_NOCREEP ); - else - G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_NOCREEP ); - return qtrue; - } - } - else - self->count = 0; - - return qfalse; -} - /* ================ G_IdlePowerState @@ -1594,13 +1684,9 @@ static void G_IdlePowerState( gentity_t *self ) } - - //================================================================================== - - /* ================ HSpawn_Disappear @@ -1666,7 +1752,6 @@ void HSpawn_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int self->die = nullDieFunction; self->killedBy = attacker - g_entities; - self->powered = qfalse; //free up power self->s.eFlags &= ~EF_FIRING; //prevent any firing effects if( self->spawned ) @@ -1694,36 +1779,30 @@ void HSpawn_Think( gentity_t *self ) { gentity_t *ent; - // set parentNode - self->powered = G_FindPower( self, qfalse ); - - if( G_SuicideIfNoPower( self ) ) + if( !self->spawned || self->health <= 0 ) return; - - if( self->spawned ) + + //only suicide if at rest + if( self->s.groundEntityNum ) { - //only suicide if at rest - if( self->s.groundEntityNum ) + if( ( ent = G_CheckSpawnPoint( self->s.number, self->s.origin, + self->s.origin2, BA_H_SPAWN, NULL ) ) != NULL ) { - if( ( ent = G_CheckSpawnPoint( self->s.number, self->s.origin, - self->s.origin2, BA_H_SPAWN, NULL ) ) != NULL ) + // If the thing blocking the spawn is a buildable, kill it. + // If it's part of the map, kill self. + if( ent->s.eType == ET_BUILDABLE ) { - // If the thing blocking the spawn is a buildable, kill it. - // If it's part of the map, kill self. - if( ent->s.eType == ET_BUILDABLE ) - { - G_Damage( ent, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); - G_SetBuildableAnim( self, BANIM_SPAWN1, qtrue ); - } - else if( ent->s.number == ENTITYNUM_WORLD || ent->s.eType == ET_MOVER ) - { - G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); - return; - } - - if( ent->s.eType == ET_CORPSE ) - G_FreeEntity( ent ); //quietly remove + G_Damage( ent, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); + G_SetBuildableAnim( self, BANIM_SPAWN1, qtrue ); + } + else if( ent->s.number == ENTITYNUM_WORLD || ent->s.eType == ET_MOVER ) + { + G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); + return; } + + if( ent->s.eType == ET_CORPSE ) + G_FreeEntity( ent ); //quietly remove } } @@ -1735,7 +1814,25 @@ void HSpawn_Think( gentity_t *self ) //================================================================================== +/* +================ +HRepeater_Think + +Think function for Human Repeater +================ +*/ +void HRepeater_Think( gentity_t *self ) +{ + self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; + + if( !self->spawned || self->health <= 0 ) + return; + + self->powered = self->active && ( self->powerNetwork != 0 ); + + G_IdlePowerState( self ); +} /* @@ -1752,7 +1849,6 @@ static void HRepeater_Die( gentity_t *self, gentity_t *inflictor, gentity_t *att self->die = nullDieFunction; self->killedBy = attacker - g_entities; - self->powered = qfalse; //free up power self->s.eFlags &= ~EF_FIRING; //prevent any firing effects if( self->spawned ) @@ -1767,83 +1863,34 @@ static void HRepeater_Die( gentity_t *self, gentity_t *inflictor, gentity_t *att } G_LogDestruction( self, attacker, mod ); - - if( self->usesBuildPointZone ) - { - buildPointZone_t *zone = &level.buildPointZones[self->buildPointZone]; - - zone->active = qfalse; - self->usesBuildPointZone = qfalse; - } } /* ================ -HRepeater_Think +HSwitchable_Use -Think for human power repeater +Use for switchable buildings ================ */ -void HRepeater_Think( gentity_t *self ) +void HSwitchable_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) { - int i; - gentity_t *powerEnt; - buildPointZone_t *zone; - - self->powered = G_FindPower( self, qfalse ); - - powerEnt = G_InPowerZone( self ); - if( powerEnt != NULL ) - { - // If the repeater is inside of another power zone then suicide - // Attribute death to whoever built the reactor if that's a human, - // which will ensure that it does not queue the BP - if( powerEnt->builtBy >= 0 ) - G_Damage( self, NULL, g_entities + powerEnt->builtBy, NULL, NULL, self->health, 0, MOD_SUICIDE ); - else - G_Damage( self, NULL, NULL, NULL, NULL, self->health, 0, MOD_SUICIDE ); + if( !self->spawned || self->health <= 0 ) return; - } - - G_IdlePowerState( self ); - // Initialise the zone once the repeater has spawned - if( self->spawned && ( !self->usesBuildPointZone || !level.buildPointZones[ self->buildPointZone ].active ) ) + if( !other || !other->client ) + return; + + // ckits and blasters switch the building + if( other->s.weapon == WP_HBUILD || + other->s.weapon == WP_BLASTER ) { - // See if a free zone exists - for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ ) - { - zone = &level.buildPointZones[ i ]; - - if( !zone->active ) - { - // Initialise the BP queue with no BP queued - zone->queuedBuildPoints = 0; - zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer; - zone->nextQueueTime = level.time; - zone->active = qtrue; - - self->buildPointZone = zone - level.buildPointZones; - self->usesBuildPointZone = qtrue; - - break; - } - } + self->active ^= 1; + G_AddEvent( self, EV_POWER_SWITCH, 0 ); + return; } - self->nextthink = level.time + POWER_REFRESH_TIME; -} - -/* -================ -HRepeater_Use - -Use for human power repeater -================ -*/ -void HRepeater_Use( gentity_t *self, gentity_t *other, gentity_t *activator ) -{ - if( self->health <= 0 || !self->spawned ) + // not powered + if( !self->powerNetwork ) return; if( other && other->client ) @@ -1869,8 +1916,16 @@ void HReactor_Think( gentity_t *self ) vec3_t mins, maxs; int i, num; gentity_t *enemy, *tent; + qboolean fired = qfalse; + + if( !self->spawned || self->health <= 0 ) + return; - if( self->dcc ) + self->powered = self->active; + + G_IdlePowerState( self ); + + if( G_IsDCCBuilt( ) ) { VectorAdd( self->s.origin, dccrange, maxs ); VectorSubtract( self->s.origin, dccrange, mins ); @@ -1881,54 +1936,62 @@ void HReactor_Think( gentity_t *self ) VectorSubtract( self->s.origin, range, mins ); } - if( self->spawned && ( self->health > 0 ) ) + // Creates a tesla trail for every target + num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + for( i = 0; i < num; i++ ) { - qboolean fired = qfalse; - - // Creates a tesla trail for every target - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - enemy = &g_entities[ entityList[ i ] ]; - if( !enemy->client || - enemy->client->ps.stats[ STAT_TEAM ] != TEAM_ALIENS ) - continue; - if( enemy->flags & FL_NOTARGET ) - continue; + enemy = &g_entities[ entityList[ i ] ]; + if( !enemy->client || + enemy->client->ps.stats[ STAT_TEAM ] != TEAM_ALIENS ) + continue; + if( enemy->flags & FL_NOTARGET ) + continue; - tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL ); - tent->s.generic1 = self->s.number; //src - tent->s.clientNum = enemy->s.number; //dest - VectorCopy( self->s.pos.trBase, tent->s.origin2 ); - fired = qtrue; - } + tent = G_TempEntity( enemy->s.pos.trBase, EV_TESLATRAIL ); + tent->s.generic1 = self->s.number; //src + tent->s.clientNum = enemy->s.number; //dest + VectorCopy( self->s.pos.trBase, tent->s.origin2 ); + fired = qtrue; + } - // Actual damage is done by radius - if( fired ) - { - self->timestamp = level.time; - if( self->dcc ) - G_SelectiveRadiusDamage( self->s.pos.trBase, self, - REACTOR_ATTACK_DCC_DAMAGE, - REACTOR_ATTACK_DCC_RANGE, self, - MOD_REACTOR, TEAM_HUMANS ); - else - G_SelectiveRadiusDamage( self->s.pos.trBase, self, - REACTOR_ATTACK_DAMAGE, - REACTOR_ATTACK_RANGE, self, - MOD_REACTOR, TEAM_HUMANS ); - } + // Actual damage is done by radius + if( fired ) + { + self->timestamp = level.time; + if( G_IsDCCBuilt( ) ) + G_SelectiveRadiusDamage( self->s.pos.trBase, self, + REACTOR_ATTACK_DCC_DAMAGE, + REACTOR_ATTACK_DCC_RANGE, self, + MOD_REACTOR, TEAM_HUMANS ); + else + G_SelectiveRadiusDamage( self->s.pos.trBase, self, + REACTOR_ATTACK_DAMAGE, + REACTOR_ATTACK_RANGE, self, + MOD_REACTOR, TEAM_HUMANS ); } - if( self->dcc ) - self->nextthink = level.time + REACTOR_ATTACK_DCC_REPEAT; - else - self->nextthink = level.time + REACTOR_ATTACK_REPEAT; + if( G_IsDCCBuilt( ) ) + self->nextthink -= REACTOR_ATTACK_DCC_REPEAT - REACTOR_ATTACK_REPEAT; } //================================================================================== +/* +================ +HArmoury_Think +* +Think function for Human Armoury +================ +*/ +void HArmoury_Think( gentity_t *self ) +{ + self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; + + if( !self->spawned ) + return; + G_CheckPower( self, ARMOURY_CURRENT ); +} /* ================ @@ -1953,57 +2016,9 @@ void HArmoury_Activate( gentity_t *self, gentity_t *other, gentity_t *activator } } -/* -================ -HArmoury_Think - -Think for armoury -================ -*/ -void HArmoury_Think( gentity_t *self ) -{ - //make sure we have power - self->nextthink = level.time + POWER_REFRESH_TIME; - - self->powered = G_FindPower( self, qfalse ); - - G_SuicideIfNoPower( self ); -} - - - - -//================================================================================== - - - - - -/* -================ -HDCC_Think - -Think for dcc -================ -*/ -void HDCC_Think( gentity_t *self ) -{ - //make sure we have power - self->nextthink = level.time + POWER_REFRESH_TIME; - - self->powered = G_FindPower( self, qfalse ); - - G_SuicideIfNoPower( self ); -} - - - - //================================================================================== - - /* ================ HMedistat_Die @@ -2037,10 +2052,12 @@ void HMedistat_Think( gentity_t *self ) qboolean occupied = qfalse; self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; - - self->powered = G_FindPower( self, qfalse ); - if( G_SuicideIfNoPower( self ) ) + + if( !self->spawned || self->health <= 0 ) return; + + G_CheckPower( self, MEDISTAT_I_IDLE ); + G_IdlePowerState( self ); //clear target's healing flag @@ -2055,105 +2072,105 @@ void HMedistat_Think( gentity_t *self ) self->active = qfalse; self->enemy = NULL; } - - self->nextthink = level.time + POWER_REFRESH_TIME; - return; } - if( self->spawned ) + VectorAdd( self->s.origin, self->r.maxs, maxs ); + VectorAdd( self->s.origin, self->r.mins, mins ); + + mins[ 2 ] += fabs( self->r.mins[ 2 ] ) + self->r.maxs[ 2 ]; + maxs[ 2 ] += 60; //player height + + //if active use the healing idle + if( self->active ) + G_SetIdleBuildableAnim( self, BANIM_IDLE2 ); + + //check if a previous occupier is still here + num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + for( i = 0; i < num; i++ ) { - VectorAdd( self->s.origin, self->r.maxs, maxs ); - VectorAdd( self->s.origin, self->r.mins, mins ); + player = &g_entities[ entityList[ i ] ]; - mins[ 2 ] += fabs( self->r.mins[ 2 ] ) + self->r.maxs[ 2 ]; - maxs[ 2 ] += 60; //player height + if( player->flags & FL_NOTARGET ) + continue; // notarget cancels even beneficial effects? + + //remove poison from everyone, not just the healed player + if( player->client && player->client->ps.stats[ STAT_STATE ] & SS_POISONED ) + player->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; + + if( self->enemy == player && player->client && + ( ( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) && + player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] && + PM_Live( player->client->ps.pm_type ) ) ) + { + if( !G_Surge( self, MEDISTAT_I_ACTIVE ) ) + return; - //if active use the healing idle - if( self->active ) - G_SetIdleBuildableAnim( self, BANIM_IDLE2 ); - - //check if a previous occupier is still here - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + occupied = qtrue; + player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE; + } + } + + if( !occupied ) + { + self->enemy = NULL; + + //look for something to heal for( i = 0; i < num; i++ ) { player = &g_entities[ entityList[ i ] ]; if( player->flags & FL_NOTARGET ) continue; // notarget cancels even beneficial effects? - - //remove poison from everyone, not just the healed player - if( player->client && player->client->ps.stats[ STAT_STATE ] & SS_POISONED ) - player->client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; - - if( self->enemy == player && player->client && - ( (player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) && // DIFF NOTE: remove this change from diffs ASAP - player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] && - PM_Live( player->client->ps.pm_type ) ) ) - { - occupied = qtrue; - player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE; - } - } - - if( !occupied ) - { - self->enemy = NULL; - //look for something to heal - for( i = 0; i < num; i++ ) + if( player->client && player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { - player = &g_entities[ entityList[ i ] ]; - - if( player->flags & FL_NOTARGET ) - continue; // notarget cancels even beneficial effects? - - if( player->client && player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) + if( ( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] || + player->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) && + PM_Live( player->client->ps.pm_type ) ) { - if( ( player->health < player->client->ps.stats[ STAT_MAX_HEALTH ] || - player->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) && - PM_Live( player->client->ps.pm_type ) ) + if( !G_Surge( self, MEDISTAT_I_ACTIVE ) ) + return; + + self->enemy = player; + + //start the heal anim + if( !self->active && self->surge && self->surgePowered ) { - self->enemy = player; - - //start the heal anim - if( !self->active ) - { - G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse ); - self->active = qtrue; - player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE; - } + G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse ); + self->active = qtrue; + player->client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE; } - else if( !BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) ) - BG_AddUpgradeToInventory( UP_MEDKIT, player->client->ps.stats ); } + else if( !BG_InventoryContainsUpgrade( UP_MEDKIT, player->client->ps.stats ) ) + BG_AddUpgradeToInventory( UP_MEDKIT, player->client->ps.stats ); } } + } - //nothing left to heal so go back to idling - if( !self->enemy && self->active ) - { - G_SetBuildableAnim( self, BANIM_CONSTRUCT2, qtrue ); - G_SetIdleBuildableAnim( self, BANIM_IDLE1 ); + //nothing left to heal so go back to idling + if( !self->enemy && self->active ) + { + G_SetBuildableAnim( self, BANIM_CONSTRUCT2, qtrue ); + G_SetIdleBuildableAnim( self, BANIM_IDLE1 ); - self->active = qfalse; - } - else if( self->enemy && self->enemy->client ) //heal! - { - if( self->enemy->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) - self->enemy->client->ps.stats[ STAT_STAMINA ] += STAMINA_MEDISTAT_RESTORE; + self->active = qfalse; + } + else if( self->enemy && self->enemy->client ) //heal! + { + if( self->enemy->client->ps.stats[ STAT_STAMINA ] < STAMINA_MAX ) + self->enemy->client->ps.stats[ STAT_STAMINA ] += STAMINA_MEDISTAT_RESTORE; - if( self->enemy->client->ps.stats[ STAT_STAMINA ] > STAMINA_MAX ) - self->enemy->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX; + if( self->enemy->client->ps.stats[ STAT_STAMINA ] > STAMINA_MAX ) + self->enemy->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX; - self->enemy->health++; + self->enemy->health++; - //if they're completely healed, give them a medkit - if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] ) - { - self->enemy->health = self->enemy->client->ps.stats[ STAT_MAX_HEALTH ]; - if( !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) ) - BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats ); - } + //if they're completely healed, give them a medkit + if( self->enemy->health >= self->enemy->client->ps.stats[ STAT_MAX_HEALTH ] ) + { + self->enemy->health = self->enemy->client->ps.stats[ STAT_MAX_HEALTH ]; + if( !BG_InventoryContainsUpgrade( UP_MEDKIT, self->enemy->client->ps.stats ) ) + BG_AddUpgradeToInventory( UP_MEDKIT, self->enemy->client->ps.stats ); } } } @@ -2179,8 +2196,8 @@ qboolean HMGTurret_CheckTarget( gentity_t *self, gentity_t *target, trace_t tr; vec3_t dir, end; - if( !target || target->health <= 0 || !target->client || - target->client->pers.teamSelection != TEAM_ALIENS ) + if( !target || target->health <= 0 || !target->client /*|| + target->client->pers.teamSelection != TEAM_ALIENS */) //debug: target humans too for now return qfalse; if( target->flags & FL_NOTARGET ) @@ -2368,6 +2385,7 @@ HMGTurret_Think Think function for MG turret ================ */ + void HMGTurret_Think( gentity_t *self ) { self->nextthink = level.time + @@ -2375,10 +2393,12 @@ void HMGTurret_Think( gentity_t *self ) // Turn off client side muzzle flashes self->s.eFlags &= ~EF_FIRING; - - self->powered = G_FindPower( self, qfalse ); - if( G_SuicideIfNoPower( self ) ) + + if( !self->spawned || self->health <= 0 ) return; + + G_CheckPower( self, MGTURRET_I_IDLE ); + G_IdlePowerState( self ); // If not powered or spawned don't do anything @@ -2386,10 +2406,9 @@ void HMGTurret_Think( gentity_t *self ) { // if power loss drop turret if( self->spawned && - HMGTurret_State( self, MGT_STATE_INACTIVE ) ) + HMGTurret_State( self, MGT_STATE_INACTIVE ) ); return; - self->nextthink = level.time + POWER_REFRESH_TIME; return; } if( !self->spawned ) @@ -2428,6 +2447,9 @@ void HMGTurret_Think( gentity_t *self ) if( !self->active || self->turretSpinupTime > level.time ) return; + if( !G_Surge( self, MGTURRET_I_ACTIVE ) ) + return; + // Fire repeat delay if( self->timestamp > level.time ) return; @@ -2456,62 +2478,174 @@ Think function for Tesla Generator */ void HTeslaGen_Think( gentity_t *self ) { + vec3_t origin, range, mins, maxs; + int entityList[ MAX_GENTITIES ], i, num; + self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; - self->powered = G_FindPower( self, qfalse ); - if( G_SuicideIfNoPower( self ) ) + if( !self->spawned || self->health <= 0 ) return; + + G_CheckPower( self, TESLAGEN_I_IDLE ); + G_IdlePowerState( self ); //if not powered don't do anything and check again for power next think if( !self->powered ) { self->s.eFlags &= ~EF_FIRING; - self->nextthink = level.time + POWER_REFRESH_TIME; return; } - if( self->spawned && self->timestamp < level.time ) - { - vec3_t origin, range, mins, maxs; - int entityList[ MAX_GENTITIES ], i, num; + if( !self->spawned ) + return; - // Communicates firing state to client - self->s.eFlags &= ~EF_FIRING; + // Communicates firing state to client + self->s.eFlags &= ~EF_FIRING; - // Move the muzzle from the entity origin up a bit to fire over turrets - VectorMA( self->s.origin, self->r.maxs[ 2 ], self->s.origin2, origin ); + // Move the muzzle from the entity origin up a bit to fire over turrets + VectorMA( self->s.origin, self->r.maxs[ 2 ], self->s.origin2, origin ); - VectorSet( range, TESLAGEN_RANGE, TESLAGEN_RANGE, TESLAGEN_RANGE ); - VectorAdd( origin, range, maxs ); - VectorSubtract( origin, range, mins ); + VectorSet( range, TESLAGEN_RANGE, TESLAGEN_RANGE, TESLAGEN_RANGE ); + VectorAdd( origin, range, maxs ); + VectorSubtract( origin, range, mins ); - // Attack nearby Aliens - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) - { - self->enemy = &g_entities[ entityList[ i ] ]; + // Attack nearby Aliens + num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + for( i = 0; i < num; i++ ) + { + self->enemy = &g_entities[ entityList[ i ] ]; - if( self->enemy->flags & FL_NOTARGET ) - continue; + if( self->enemy->flags & FL_NOTARGET ) + continue; + + if( self->enemy->client && self->enemy->health > 0 && + /*self->enemy->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS &&*/ + Distance( origin, self->enemy->s.pos.trBase ) <= TESLAGEN_RANGE ) + { + if( !G_Surge( self, TESLAGEN_I_ACTIVE ) ) + break; - if( self->enemy->client && self->enemy->health > 0 && - self->enemy->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS && - Distance( origin, self->enemy->s.pos.trBase ) <= TESLAGEN_RANGE ) - FireWeapon( self ); + FireWeapon( self ); } - self->enemy = NULL; + } + self->enemy = NULL; - if( self->s.eFlags & EF_FIRING ) - { - G_AddEvent( self, EV_FIRE_WEAPON, 0 ); + if( self->s.eFlags & EF_FIRING ) + { + G_AddEvent( self, EV_FIRE_WEAPON, 0 ); - //doesn't really need an anim - //G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse ); + //doesn't really need an anim + //G_SetBuildableAnim( self, BANIM_ATTACK1, qfalse ); - self->timestamp = level.time + TESLAGEN_REPEAT; - } + self->timestamp = level.time + TESLAGEN_REPEAT; + } +} + +//================================================================================== + +void HDCC_Think( gentity_t *self ) +{ + int i, count, list[ MAX_GENTITIES ]; + gentity_t *ent; + vec3_t mins, maxs; + + self->nextthink = level.time + DC_HEALRATE; + + if( !self->spawned || self->health <= 0 ) + return; + + G_CheckPower( self, DC_I_IDLE ); + G_IdlePowerState( self ); + if( !self->powered ) + return; + + for( i = 0; i < 3; i++ ) + mins[ i ] = self->s.origin[ i ] - DC_RANGE, + maxs[ i ] = self->s.origin[ i ] + DC_RANGE; + + count = trap_EntitiesInBox( mins, maxs, list, MAX_GENTITIES ); + + for( i = 0; i < count; i++ ) + { + ent = g_entities + list[ i ]; + + if( ent->s.eType != ET_BUILDABLE ) + continue; + if( ent->buildableTeam != TEAM_HUMANS ) + continue; + if( ent->health >= BG_Buildable( ent->s.modelindex, ent->cuboidSize )->health ) + continue; + if( ent->lastDamageTime + HUMAN_REGEN_DAMAGE_TIME >= level.time ) + continue; + if( BG_Buildable( ent->s.modelindex, NULL )->cuboid && + !BG_CuboidAttributes( ent->s.modelindex )->repairable ) + continue; + + if( !G_Surge( self, DC_I_ACTIVE ) ) + return; + + ent->health++; + } +} + +//================================================================================== + +void HCapbank_Think( gentity_t *self) +{ + //nothing to do here +} + +//================================================================================== + +void HRTG_Think( gentity_t *self) +{ + self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; + + if( !self->spawned || self->health <= 0 ) + return; + + if( G_TimeTilSuddenDeath( ) <= 0 ) + { + self->storedBP = 0; + return; } + + self->storedBP += RTG_YIELD * g_massYieldModifier.value; + + if( self->storedBP > RTG_STORAGE ) + self->storedBP = RTG_STORAGE; +} + +//================================================================================== + +void HRefinery_Think( gentity_t *self) +{ + self->nextthink = level.time + BG_Buildable( self->s.modelindex, NULL )->nextthink; + + if( !self->spawned || self->health <= 0 ) + return; + + G_CheckPower( self, REFINERY_I_IDLE ); + G_IdlePowerState( self ); + + if( G_TimeTilSuddenDeath( ) <= 0 ) + { + self->storedBP = 0; + return; + } + + //nothing to do + if( self->storedBP >= REFINERY_STORAGE - 0.001f ) + return; + + if( !G_Surge( self, REFINERY_I_ACTIVE ) ) + return; + + self->storedBP += REFINERY_YIELD * g_massYieldModifier.value; + + if( self->storedBP > REFINERY_STORAGE ) + self->storedBP = REFINERY_STORAGE; } //================================================================================== @@ -2534,7 +2668,6 @@ void Cuboid_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int G_SetBuildableAnim( self, BANIM_DESTROY1, qtrue ); // just for sound self->die = nullDieFunction; self->killedBy = attacker - g_entities; - self->powered = qfalse; self->s.eFlags &= ~EF_FIRING; G_LogDestruction( self, attacker, mod ); dir[ 0 ] = @@ -2632,48 +2765,6 @@ void G_QueueBuildPoints( gentity_t *self ) level.alienBuildPointQueue += queuePoints; break; - - case TEAM_HUMANS: - powerEntity = G_PowerEntityForEntity( self ); - - if( powerEntity ) - { - int nqt; - switch( powerEntity->s.modelindex ) - { - case BA_H_REACTOR: - nqt = G_NextQueueTime( level.humanBuildPointQueue, - g_humanBuildPoints.integer, - g_humanBuildQueueTime.integer ); - if( !level.humanBuildPointQueue || - level.time + nqt < level.humanNextQueueTime ) - level.humanNextQueueTime = level.time + nqt; - - level.humanBuildPointQueue += queuePoints; - break; - - case BA_H_REPEATER: - if( powerEntity->usesBuildPointZone && - level.buildPointZones[ powerEntity->buildPointZone ].active ) - { - buildPointZone_t *zone = &level.buildPointZones[ powerEntity->buildPointZone ]; - - nqt = G_NextQueueTime( zone->queuedBuildPoints, - zone->totalBuildPoints, - g_humanRepeaterBuildQueueTime.integer ); - - if( !zone->queuedBuildPoints || - level.time + nqt < zone->nextQueueTime ) - zone->nextQueueTime = level.time + nqt; - - zone->queuedBuildPoints += queuePoints; - } - break; - - default: - break; - } - } } } @@ -2774,6 +2865,7 @@ void G_BuildableThink( gentity_t *ent, int msec ) { G_TeamCommand( TEAM_ALIENS, "cp \"The Overmind has awakened!\"" ); } + G_SetupPowerEntity( ent ); } // Timer actions @@ -2784,9 +2876,18 @@ void G_BuildableThink( gentity_t *ent, int msec ) if( !ent->spawned ) { - buildRate=(int)(ceil((float)maxHealth/(float)buildTime*1e3f)); - ent->health=MIN(ent->health+buildRate,maxHealth); - ent->healthLeft=MAX(ent->healthLeft-buildRate,0); + buildRate = (int)( ceil( (float)maxHealth / (float)buildTime * 1.0e3f ) ); + + if( ent->buildableTeam == TEAM_HUMANS && + ent->s.modelindex != BA_H_RTG ) + { + buildRate *= ent->current / PREBUILD_CURRENT; + if( buildRate < 0 ) + buildRate = 0; + ent->powered = ( buildRate > 0 ); + } + ent->health = MIN( ent->health + buildRate, maxHealth ); + ent->healthLeft = MAX( ent->healthLeft - buildRate, 0 ); } else if( ent->health > 0 && ent->health < maxHealth ) { @@ -2795,13 +2896,6 @@ void G_BuildableThink( gentity_t *ent, int msec ) { ent->health += regenRate; } - else if( ent->buildableTeam == TEAM_HUMANS && ent->dcc && - ( ent->lastDamageTime + HUMAN_REGEN_DAMAGE_TIME ) < level.time && - ( !BG_Buildable( ent->s.modelindex, NULL )->cuboid || - BG_CuboidAttributes( ent->s.modelindex )->repairable ) ) - { - ent->health += DC_HEALRATE * ent->dcc; - } } if( ent->health >= maxHealth ) @@ -2819,8 +2913,6 @@ void G_BuildableThink( gentity_t *ent, int msec ) if( ent->clientSpawnTime < 0 ) ent->clientSpawnTime = 0; - ent->dcc = ( ent->buildableTeam != TEAM_HUMANS ) ? 0 : G_FindDCC( ent ); - // Set health ent->s.generic1 = MIN( MAX( ent->health, 0 ), 999 ); @@ -2828,16 +2920,47 @@ void G_BuildableThink( gentity_t *ent, int msec ) BG_CuboidPackHealthSafe( ent->s.modelindex, &ent->s, ent->health ); // Set flags - ent->s.eFlags &= ~( EF_B_POWERED | EF_B_SPAWNED | EF_B_MARKED ); - if( ent->powered ) - ent->s.eFlags |= EF_B_POWERED; + ent->s.eFlags &= ~( EF_B_POWERED | EF_B_SPAWNED | EF_B_SURGE ); + + if( ent->buildableTeam == TEAM_HUMANS ) + { + if( ent->s.modelindex == BA_H_REPEATER ) + { + if( ent->powered ) + ent->s.eFlags |= EF_B_POWERED; + if( ent->active ) + ent->s.eFlags |= EF_B_SURGE; + } + else if( ent->isPowerSource ) + { + ent->s.eFlags |= EF_B_POWERED; + if( ent->active ) + ent->s.eFlags |= EF_B_SURGE; + } + else + { + if( ent->powered ) + { + if( ent->surge ) + { + ent->s.eFlags |= EF_B_SURGE; + if( ent->surgePowered ) + ent->s.eFlags |= EF_B_POWERED; + } + else + ent->s.eFlags |= EF_B_POWERED; + } + } + } + else + { + if( ent->powered ) + ent->s.eFlags |= EF_B_POWERED; + } if( ent->spawned ) ent->s.eFlags |= EF_B_SPAWNED; - if( ent->deconstruct ) - ent->s.eFlags |= EF_B_MARKED; - // Check if this buildable is touching any triggers G_BuildableTouchTriggers( ent ); @@ -2940,450 +3063,61 @@ static qboolean G_BuildablesIntersect( buildable_t a, vec3_t originA, vec3_t cub /* =============== -G_CompareBuildablesForRemoval - -qsort comparison function for a buildable removal list -=============== -*/ -static buildable_t cmpBuildable; -static vec3_t cmpOrigin; -static vec3_t cmpCuboid; -static int G_CompareBuildablesForRemoval( const void *a, const void *b ) -{ - int precedence[ ] = - { - BA_NONE, - - BA_A_BARRICADE, - BA_A_ACIDTUBE, - BA_A_TRAPPER, - BA_A_HIVE, - BA_A_BOOSTER, - BA_A_SPAWN, - BA_A_CUBOID1, - BA_A_OVERMIND, - - BA_H_MGTURRET, - BA_H_TESLAGEN, - BA_H_DCC, - BA_H_MEDISTAT, - BA_H_ARMOURY, - BA_H_SPAWN, - BA_H_REPEATER, - BA_H_CUBOID1, - BA_H_CUBOID2, - BA_H_REACTOR - }; - - gentity_t *buildableA, *buildableB; - int i; - int aPrecedence = 0, bPrecedence = 0; - qboolean aMatches = qfalse, bMatches = qfalse; - - buildableA = *(gentity_t **)a; - buildableB = *(gentity_t **)b; - - // Prefer the one that collides with the thing we're building - aMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, cmpCuboid, - buildableA->s.modelindex, buildableA->s.origin, buildableA->cuboidSize ); - bMatches = G_BuildablesIntersect( cmpBuildable, cmpOrigin, cmpCuboid, - buildableB->s.modelindex, buildableB->s.origin, buildableB->cuboidSize ); - if( aMatches && !bMatches ) - return -1; - else if( !aMatches && bMatches ) - return 1; - - // If the only spawn is marked, prefer it last - if( cmpBuildable == BA_A_SPAWN || cmpBuildable == BA_H_SPAWN ) - { - if( ( buildableA->s.modelindex == BA_A_SPAWN && level.numAlienSpawns == 1 ) || - ( buildableA->s.modelindex == BA_H_SPAWN && level.numHumanSpawns == 1 ) ) - return 1; - - if( ( buildableB->s.modelindex == BA_A_SPAWN && level.numAlienSpawns == 1 ) || - ( buildableB->s.modelindex == BA_H_SPAWN && level.numHumanSpawns == 1 ) ) - return -1; - } - - // If one matches the thing we're building, prefer it - aMatches = ( buildableA->s.modelindex == cmpBuildable ); - bMatches = ( buildableB->s.modelindex == cmpBuildable ); - if( aMatches && !bMatches ) - return -1; - else if( !aMatches && bMatches ) - return 1; - - // They're the same type - if( buildableA->s.modelindex == buildableB->s.modelindex ) - { - gentity_t *powerEntity = G_PowerEntityForPoint( cmpOrigin ); - - // Prefer the entity that is providing power for this point - aMatches = ( powerEntity == buildableA ); - bMatches = ( powerEntity == buildableB ); - if( aMatches && !bMatches ) - return -1; - else if( !aMatches && bMatches ) - return 1; - - // Pick the one marked earliest - return buildableA->deconstructTime - buildableB->deconstructTime; - } - - // Resort to preference list - for( i = 0; i < sizeof( precedence ) / sizeof( precedence[ 0 ] ); i++ ) - { - if( buildableA->s.modelindex == precedence[ i ] ) - aPrecedence = i; - - if( buildableB->s.modelindex == precedence[ i ] ) - bPrecedence = i; - } - - return aPrecedence - bPrecedence; -} - -/* -=============== -G_ClearDeconMarks - -Remove decon mark from all buildables -=============== -*/ -void G_ClearDeconMarks( void ) -{ - int i; - gentity_t *ent; - - for( i = MAX_CLIENTS, ent = g_entities + i ; i < level.num_entities ; i++, ent++ ) - { - if( !ent->inuse ) - continue; - - if( ent->s.eType != ET_BUILDABLE ) - continue; - - ent->deconstruct = qfalse; - } -} - -/* -=============== -G_FreeMarkedBuildables - -Free up build points for a team by deconstructing marked buildables -=============== -*/ -void G_FreeMarkedBuildables( gentity_t *deconner, char *readable, int rsize, - char *nums, int nsize ) -{ - int i; - int bNum; - int listItems = 0; - int totalListItems = 0; - gentity_t *ent; - int removalCounts[ BA_NUM_BUILDABLES ] = {0}; - - if( readable && rsize ) - readable[ 0 ] = '\0'; - if( nums && nsize ) - nums[ 0 ] = '\0'; - - if( !g_markDeconstruct.integer ) - return; // Not enabled, can't deconstruct anything - - for( i = 0; i < level.numBuildablesForRemoval; i++ ) - { - ent = level.markedBuildables[ i ]; - bNum = BG_Buildable( ent->s.modelindex, NULL )->number; - - if( removalCounts[ bNum ] == 0 ) - totalListItems++; - - G_Damage( ent, NULL, deconner, NULL, NULL, ent->health, 0, MOD_REPLACE ); - - removalCounts[ bNum ]++; - - if( nums ) - Q_strcat( nums, nsize, va( " %d", ent - g_entities ) ); - - G_FreeEntity( ent ); - } - - if( !readable ) - return; - - for( i = 0; i < BA_NUM_BUILDABLES; i++ ) - { - if( removalCounts[ i ] ) - { - if( listItems ) - { - if( listItems == ( totalListItems - 1 ) ) - Q_strcat( readable, rsize, va( "%s and ", - ( totalListItems > 2 ) ? "," : "" ) ); - else - Q_strcat( readable, rsize, ", " ); - } - Q_strcat( readable, rsize, va( "%s", BG_Buildable( i, NULL )->humanName ) ); - if( removalCounts[ i ] > 1 ) - Q_strcat( readable, rsize, va( " (%dx)", removalCounts[ i ] ) ); - listItems++; - } - } -} - -/* -=============== G_SufficientBPAvailable Determine if enough build points can be released for the buildable and list the buildables that must be destroyed if this is the case =============== */ -static itemBuildError_t G_SufficientBPAvailable( buildable_t buildable, +static itemBuildError_t G_SufficientBPAvailable( gclient_t *builder, + buildable_t buildable, vec3_t origin, - vec3_t cuboidSize ) + vec3_t cuboidSize ) { - int i; - int numBuildables = 0; - int numRequired = 0; - int pointsYielded = 0; - gentity_t *ent; - team_t team = BG_Buildable( buildable, NULL )->team; - int buildPoints = BG_Buildable( buildable, cuboidSize )->buildPoints; - int remainingBP, remainingSpawns; - qboolean collision = qfalse; - int collisionCount = 0; - qboolean repeaterInRange = qfalse; - int repeaterInRangeCount = 0; - itemBuildError_t bpError; - buildable_t spawn; - buildable_t core; - int spawnCount = 0; - qboolean changed = qtrue; - - level.numBuildablesForRemoval = 0; - - if( team == TEAM_ALIENS ) - { - remainingBP = G_GetBuildPoints( origin, team ); - remainingSpawns = level.numAlienSpawns; - bpError = IBE_NOALIENBP; - spawn = BA_A_SPAWN; - core = BA_A_OVERMIND; - } - else if( team == TEAM_HUMANS ) - { - if( buildable == BA_H_REACTOR || buildable == BA_H_REPEATER ) - remainingBP = level.humanBuildPoints; - else - remainingBP = G_GetBuildPoints( origin, team ); - - remainingSpawns = level.numHumanSpawns; - bpError = IBE_NOHUMANBP; - spawn = BA_H_SPAWN; - core = BA_H_REACTOR; - } - else - { - Com_Error( ERR_FATAL, "team is %d\n", team ); - return IBE_NONE; - } - - // Simple non-marking case - if( !g_markDeconstruct.integer ) + int required; + int available; + int error; + + required = BG_Buildable( buildable, cuboidSize )->buildPoints; + + switch( BG_Buildable( buildable, NULL )->team ) { - if( remainingBP - buildPoints < 0 ) - return bpError; - - // Check for buildable<->buildable collisions - for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) - { - if( ent->s.eType != ET_BUILDABLE ) - continue; - - if( G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize ) ) - return IBE_NOROOM; - - } - - return IBE_NONE; + case TEAM_ALIENS: + error = IBE_NOALIENBP; + available = G_GetBuildPoints( origin, TEAM_ALIENS ); + break; + + case TEAM_HUMANS: + error = IBE_NOHUMANBP; + if( !builder ) + available = required; //always enough + else + //first RTG is free + if( buildable == BA_H_RTG && !G_IsRTGBuilt() ) + available = INFINITE; + else + available = builder->ps.persistant[ PERS_BUILDPOINTS ]; + break; } + + if( required > available ) + return error; + + return IBE_NONE; - // Set buildPoints to the number extra that are required - buildPoints -= remainingBP; - // Build a list of buildable entities +#if 0 + // Check for buildable<->buildable collisions + // FIXME: is this check even needed? for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ ) { if( ent->s.eType != ET_BUILDABLE ) continue; - collision = G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize ); - - if( collision ) - { - // Don't allow replacements at all - if( g_markDeconstruct.integer == 1 ) - return IBE_NOROOM; - - // Only allow replacements of the same type - if( g_markDeconstruct.integer == 2 && ent->s.modelindex != buildable ) - return IBE_NOROOM; - - // Any other setting means anything goes - - collisionCount++; - } - - // Check if this is a repeater and it's in range - if( buildable == BA_H_REPEATER && - buildable == ent->s.modelindex && - Distance( ent->s.origin, origin ) < REPEATER_BASESIZE ) - { - repeaterInRange = qtrue; - repeaterInRangeCount++; - } - else - repeaterInRange = qfalse; - - // Don't allow marked buildables to be replaced in another zone, - // unless the marked buildable isn't in a zone (and thus unpowered) - if( team == TEAM_HUMANS && - buildable != BA_H_REACTOR && - buildable != BA_H_REPEATER && - ent->parentNode != G_PowerEntityForPoint( origin ) ) - continue; - - if( !ent->inuse ) - continue; - - if( ent->health <= 0 ) - continue; - - if( ent->buildableTeam != team ) - continue; - - // Explicitly disallow replacement of the core buildable with anything - // other than the core buildable - if( ent->s.modelindex == core && buildable != core ) - continue; - - // Don't allow a power source to be replaced by a dependant - if( team == TEAM_HUMANS && - G_PowerEntityForPoint( origin ) == ent && - buildable != BA_H_REPEATER && - buildable != core ) - continue; - - // Don't include unpowered buildables - if( !collision && !ent->powered ) - continue; - - if( ent->deconstruct ) - { - level.markedBuildables[ numBuildables++ ] = ent; - - // Buildables that are marked here will always end up at the front of the - // removal list, so just incrementing numBuildablesForRemoval is sufficient - if( collision || repeaterInRange ) - { - // Collided with something, so we definitely have to remove it or - // it's a repeater that intersects the new repeater's power area, - // so it must be removed - - if( collision ) - collisionCount--; - - if( repeaterInRange ) - repeaterInRangeCount--; - - if( ent->powered ) - pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints; - level.numBuildablesForRemoval++; - } - else if( BG_Buildable( ent->s.modelindex, NULL )->uniqueTest && - ent->s.modelindex == buildable ) - { - // If it's a unique buildable, it must be replaced by the same type - if( ent->powered ) - pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints; - level.numBuildablesForRemoval++; - } - } - } - - numRequired = level.numBuildablesForRemoval; - - // We still need build points, but have no candidates for removal - if( buildPoints > 0 && numBuildables == 0 ) - return bpError; - - // Collided with something we can't remove - if( collisionCount > 0 ) - return IBE_NOROOM; - - // There are one or more repeaters we can't remove - if( repeaterInRangeCount > 0 ) - return IBE_RPTPOWERHERE; - - // Sort the list - cmpBuildable = buildable; - VectorCopy( origin, cmpOrigin ); - VectorCopy( cuboidSize, cmpCuboid ); - qsort( level.markedBuildables, numBuildables, sizeof( level.markedBuildables[ 0 ] ), - G_CompareBuildablesForRemoval ); - - // Determine if there are enough markees to yield the required BP - for( ; pointsYielded < buildPoints && level.numBuildablesForRemoval < numBuildables; - level.numBuildablesForRemoval++ ) - { - ent = level.markedBuildables[ level.numBuildablesForRemoval ]; - if( ent->powered ) - pointsYielded += BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints; + if( G_BuildablesIntersect( buildable, origin, cuboidSize, ent->s.modelindex, ent->s.origin, ent->cuboidSize ) ) + return IBE_NOROOM; } - - // Do another pass to see if we can meet quota with fewer buildables - // than we have now due to mismatches between priority and BP amounts - // by repeatedly testing if we can chop off the first thing that isn't - // required by rules of collision/uniqueness, which are always at the head - while( changed && level.numBuildablesForRemoval > 1 && - level.numBuildablesForRemoval > numRequired ) - { - int pointsUnYielded = 0; - changed = qfalse; - ent = level.markedBuildables[ numRequired ]; - if( ent->powered ) - pointsUnYielded = BG_Buildable( ent->s.modelindex, ent->cuboidSize )->buildPoints; - - if( pointsYielded - pointsUnYielded >= buildPoints ) - { - pointsYielded -= pointsUnYielded; - memmove( &level.markedBuildables[ numRequired ], - &level.markedBuildables[ numRequired + 1 ], - ( level.numBuildablesForRemoval - numRequired ) - * sizeof( gentity_t * ) ); - level.numBuildablesForRemoval--; - changed = qtrue; - } - } - - for( i = 0; i < level.numBuildablesForRemoval; i++ ) - { - if( level.markedBuildables[ i ]->s.modelindex == spawn ) - spawnCount++; - } - - // Make sure we're not removing the last spawn - if( !g_cheats.integer && remainingSpawns > 0 && ( remainingSpawns - spawnCount ) < 1 ) - return IBE_LASTSPAWN; - - // Not enough points yielded - if( pointsYielded < buildPoints ) - return bpError; - else - return IBE_NONE; +#endif } /* @@ -3410,23 +3144,6 @@ static void G_SetBuildableLinkState( qboolean link ) } } -static void G_SetBuildableMarkedLinkState( qboolean link ) -{ - int i; - gentity_t *ent; - - for( i = 0; i < level.numBuildablesForRemoval; i++ ) - { - ent = level.markedBuildables[ i ]; - if( link ) - trap_LinkEntity( ent ); - else - trap_UnlinkEntity( ent ); - } -} - - - /* ================ G_CanBuild @@ -3482,7 +3199,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance contents = trap_PointContents( entity_origin, -1 ); - if( ( tempReason = G_SufficientBPAvailable( buildable, origin, cuboidSize ) ) != IBE_NONE ) + if( ( tempReason = G_SufficientBPAvailable( ent->client, buildable, origin, cuboidSize ) ) != IBE_NONE ) reason = tempReason; if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) @@ -3510,32 +3227,14 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance else if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { //human criteria - - // Check for power - if( G_IsPowered( entity_origin ) == BA_NONE ) - { - //tell player to build a repeater to provide power - if( buildable != BA_H_REACTOR && buildable != BA_H_REPEATER ) - reason = IBE_NOPOWERHERE; - } + if( !G_PowerForPoint( origin ) && + buildable != BA_H_RTG ) + reason = IBE_NOPOWERHERE; //this buildable requires a DCC if( BG_Buildable( buildable, NULL )->dccTest && !G_IsDCCBuilt( ) ) reason = IBE_NODCC; - //check that there is a parent reactor when building a repeater - if( buildable == BA_H_REPEATER ) - { - tempent = G_Reactor( ); - - if( tempent == NULL ) // No reactor - reason = IBE_RPTNOREAC; - else if( g_markDeconstruct.integer && G_IsPowered( entity_origin ) == BA_H_REACTOR ) - reason = IBE_RPTPOWERHERE; - else if( !g_markDeconstruct.integer && G_IsPowered( entity_origin ) ) - reason = IBE_RPTPOWERHERE; - } - // Check permission to build here if( tr1.surfaceFlags & SURF_NOHUMANBUILD || contents & CONTENTS_NOHUMANBUILD ) reason = IBE_PERMISSION; @@ -3549,7 +3248,7 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance if( BG_Buildable( buildable, NULL )->uniqueTest ) { tempent = G_FindBuildable( buildable ); - if( tempent && !tempent->deconstruct ) + if( tempent ) { switch( buildable ) { @@ -3570,18 +3269,12 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance //check there is enough room to spawn from (presuming this is a spawn) if( reason == IBE_NONE ) - { - G_SetBuildableMarkedLinkState( qfalse ); if( G_CheckSpawnPoint( ENTITYNUM_NONE, origin, normal, buildable, NULL ) != NULL ) reason = IBE_NORMAL; - G_SetBuildableMarkedLinkState( qtrue ); - } //this item does not fit here if( reason == IBE_NONE && ( tr2.startsolid || !COMPARE_FLOAT_EPSILON(tr3.fraction,1.0f) ) ) reason = IBE_NOROOM; - if( reason != IBE_NONE ) - level.numBuildablesForRemoval = 0; if( g_buildableDensityLimit.integer > 0 ) { @@ -3661,12 +3354,11 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, { gentity_t *built; vec3_t localOrigin; - char readable[ MAX_STRING_CHARS ]; char buildnums[ MAX_STRING_CHARS ]; buildLog_t *log; qboolean cuboid; - cuboid=BG_Buildable(buildable,NULL)->cuboid; + cuboid = BG_Buildable(buildable,NULL)->cuboid; VectorCopy( origin, localOrigin ); @@ -3675,10 +3367,6 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, else log = NULL; - // Free existing buildables - G_FreeMarkedBuildables( builder, readable, sizeof( readable ), - buildnums, sizeof( buildnums ) ); - // Spawn the buildable built = G_Spawn(); built->s.eType = ET_BUILDABLE; @@ -3692,13 +3380,13 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, if( !builder->client ) VectorMA( localOrigin, 1.0f, normal, localOrigin ); - if(cuboid) + if( cuboid ) { - BG_CuboidBBox(cuboidSize,built->r.mins,built->r.maxs); - VectorCopy(cuboidSize,&built->cuboidSize); + BG_CuboidBBox( cuboidSize, built->r.mins, built->r.maxs ); + VectorCopy( cuboidSize, &built->cuboidSize ); } else - BG_BuildableBoundingBox(buildable,built->r.mins,built->r.maxs); + BG_BuildableBoundingBox( buildable, built->r.mins, built->r.maxs ); built->health = 1; built->healthLeft = BG_Buildable( buildable, cuboidSize )->health-1; @@ -3713,8 +3401,7 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, built->spawned = qfalse; built->buildTime = built->s.time = level.time; - // build instantly in cheat mode - if( builder->client && (g_cheats.integer || g_instantBuild.integer) ) + if( builder->client && g_instantBuild.integer ) { built->health = BG_Buildable( buildable, cuboidSize )->health; built->buildTime = built->s.time = @@ -3804,16 +3491,34 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, case BA_H_REACTOR: built->think = HReactor_Think; built->die = HSpawn_Die; - built->use = HRepeater_Use; + built->use = HSwitchable_Use; built->powered = built->active = qtrue; break; case BA_H_REPEATER: built->think = HRepeater_Think; built->die = HRepeater_Die; - built->use = HRepeater_Use; + built->use = HSwitchable_Use; built->count = -1; break; + + case BA_H_CAPBANK: + built->think = HCapbank_Think; + built->die = HSpawn_Die; + built->use = HSwitchable_Use; + break; + + case BA_H_RTG: + built->think = HRTG_Think; + built->die = HSpawn_Die; + built->use = HSwitchable_Use; + break; + + case BA_H_REFINERY: + built->think = HRefinery_Think; + built->die = HSpawn_Die; + //built->use = HSwitchable_Use; + break; default: //erk @@ -3839,15 +3544,14 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, G_SetOrigin( built, localOrigin ); - // roughly nudge the buildable onto the surface D:< VectorScale( normal, -512.0f, built->s.pos.trDelta ); - if(BG_Buildable(buildable, NULL)->cuboid) - VectorCopy(cuboidSize,built->s.angles); + if( BG_Buildable( buildable, NULL )->cuboid ) + VectorCopy( cuboidSize,built->s.angles ); else { - VectorCopy( angles, built->s.angles ); - built->s.angles[ PITCH ] = 0.0f; + VectorCopy( angles, built->s.angles ); + built->s.angles[ PITCH ] = 0.0f; } built->s.pos.trType = BG_Buildable( buildable, NULL )->traj; @@ -3859,13 +3563,8 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, built->s.angles2[ YAW ] = angles[ YAW ]; built->s.angles2[ PITCH ] = MGTURRET_VERTICALCAP; - if( BG_Buildable( buildable, NULL )->team == TEAM_ALIENS ) - { - built->powered = qtrue; - built->s.eFlags |= EF_B_POWERED; - } - else if( ( built->powered = G_FindPower( built, qfalse ) ) ) - built->s.eFlags |= EF_B_POWERED; + built->powered = qtrue; + built->s.eFlags |= EF_B_POWERED; built->s.eFlags &= ~EF_B_SPAWNED; @@ -3878,32 +3577,47 @@ static gentity_t *G_Build( gentity_t *builder, buildable_t buildable, if( built->builtBy >= 0 ) G_SetBuildableAnim( built, BANIM_CONSTRUCT1, qtrue ); + if( BG_Buildable( buildable, NULL )->team == TEAM_HUMANS ) + { + //special case for the RTG unit that can be built w/o power + if( buildable == BA_H_RTG ) + built->requiresPower = qfalse; + else + built->requiresPower = qtrue; + + built->isPowerSource = qfalse; + built->resistance = PREBUILD_RESISTANCE; + if( BG_Buildable( buildable, NULL )->isPowerSource || + buildable == BA_H_REPEATER ) + built->active = qtrue; //spawn enabled + } + + // subtract build points + if( buildable != BA_H_RTG || G_IsRTGBuilt( ) ) //first RTG is free + if( builder && builder->client ) + builder->client->ps.persistant[ PERS_BUILDPOINTS ] -= BG_Buildable( buildable, cuboidSize )->buildPoints; + + BG_CuboidPackHealthSafe( built->s.modelindex, &built->s, built->health ); + trap_LinkEntity( built ); if( builder && builder->client ) { G_TeamCommand( builder->client->ps.stats[ STAT_TEAM ], - va( "print \"%s ^2built^7 by %s%s%s\n\"", - G_CuboidName(built->s.modelindex,cuboidSize,qfalse), - builder->client->pers.netname, - ( readable[ 0 ] ) ? "^7, ^3replacing^7 " : "", - readable ) ); - G_LogPrintf( "Construct: %d %d %s%s: %s" S_COLOR_WHITE " is building " - "%s%s%s\n", + va( "print \"%s ^2built^7 by %s\n\"", + G_CuboidName( built->s.modelindex, cuboidSize, qfalse ), + builder->client->pers.netname ) ); + G_LogPrintf( "Construct: %d %d %s%s: %s" S_COLOR_WHITE " is building %s\n", builder - g_entities, built - g_entities, BG_Buildable( built->s.modelindex, NULL )->name, buildnums, builder->client->pers.netname, - G_CuboidName(built->s.modelindex,built->cuboidSize,qtrue), - readable[ 0 ] ? ", replacing " : "", - readable ); + G_CuboidName( built->s.modelindex, built->cuboidSize, qtrue ) ); } if( log ) G_BuildLogSet( log, built ); - - BG_CuboidPackHealthSafe(built->s.modelindex,&built->s,built->health); return built; } @@ -4026,6 +3740,8 @@ static gentity_t *G_FinishSpawningBuildable( gentity_t *ent, qboolean force ) built->health = BG_Buildable( buildable, built->cuboidSize )->health; built->s.eFlags |= EF_B_SPAWNED; + G_SetupPowerEntity( built ); + // drop towards normal surface VectorScale( built->s.origin2, -4096.0f, dest ); VectorAdd( dest, built->s.origin, dest ); @@ -4407,8 +4123,6 @@ buildLog_t *G_BuildLogNew( gentity_t *actor, buildFate_t fate ) void G_BuildLogSet( buildLog_t *log, gentity_t *ent ) { log->modelindex = ent->s.modelindex; - log->deconstruct = log->deconstruct; - log->deconstructTime = ent->deconstructTime; VectorCopy( ent->s.pos.trBase, log->origin ); VectorCopy( ent->s.angles, log->angles ); VectorCopy( ent->s.origin2, log->origin2 ); @@ -4464,8 +4178,6 @@ void G_BuildLogRevertThink( gentity_t *ent ) } built = G_FinishSpawningBuildable( ent, qtrue ); - if( ( built->deconstruct = ent->deconstruct ) ) - built->deconstructTime = ent->deconstructTime; built->buildTime = built->s.time = 0; G_KillBox( built ); @@ -4482,8 +4194,6 @@ void G_BuildLogRevert( int id ) int i; vec3_t dist; - level.numBuildablesForRemoval = 0; - level.numBuildLogs -= level.buildId - id; while( level.buildId > id ) { @@ -4521,8 +4231,6 @@ void G_BuildLogRevert( int id ) VectorCopy( log->origin2, builder->s.origin2 ); VectorCopy( log->angles2, builder->s.angles2 ); builder->s.modelindex = log->modelindex; - builder->deconstruct = log->deconstruct; - builder->deconstructTime = log->deconstructTime; builder->think = G_BuildLogRevertThink; builder->nextthink = level.time + FRAMETIME; @@ -4539,27 +4247,6 @@ void G_BuildLogRevert( int id ) level.alienBuildPointQueue = MAX( 0, level.alienBuildPointQueue - value ); } - else - { - if( log->powerSource == BA_H_REACTOR ) - { - level.humanBuildPointQueue = - MAX( 0, level.humanBuildPointQueue - value ); - } - else if( log->powerSource == BA_H_REPEATER ) - { - gentity_t *source; - buildPointZone_t *zone; - - source = G_PowerEntityForPoint( log->origin ); - if( source && source->usesBuildPointZone ) - { - zone = &level.buildPointZones[ source->buildPointZone ]; - zone->queuedBuildPoints = - MAX( 0, zone->queuedBuildPoints - value ); - } - } - } } } } diff --git a/src/game/g_client.c b/src/game/g_client.c index 7596ea1..a3c7a67 100644 --- a/src/game/g_client.c +++ b/src/game/g_client.c @@ -1482,6 +1482,7 @@ void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles for( i = 0; i < MAX_PERSISTANT; i++ ) persistant[ i ] = client->ps.persistant[ i ]; + persistant[ PERS_BUILDPOINTS ] = 0; // clear buildpoints eventSequence = client->ps.eventSequence; memset( client, 0, sizeof( *client ) ); @@ -1795,11 +1796,12 @@ void ClientDisconnect( int clientNum ) G_RelayCuboidToSpectators Called everytime a player changes his cuboid size. -A server command is issued to everyone spectating him -so that their clients can know the cuboid size as well. ============ */ -void G_RelayCuboidToSpectators(gentity_t *self) +void G_RelayCuboidToSpectators( gentity_t *self ) { + self->client->ps.misc[ MISC_CUBOID_X ] = self->client->cuboidSelection[ 0 ] * 10; + self->client->ps.misc[ MISC_CUBOID_Y ] = self->client->cuboidSelection[ 1 ] * 10; + self->client->ps.misc[ MISC_CUBOID_Z ] = self->client->cuboidSelection[ 2 ] * 10; } diff --git a/src/game/g_cmds.c b/src/game/g_cmds.c index 2c15638..c1da4e0 100644 --- a/src/game/g_cmds.c +++ b/src/game/g_cmds.c @@ -1363,7 +1363,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } else if( !Q_stricmp( vote, "map_restart" ) ) { - if( level.time / 60000 >= g_restartVoteTimelimit.integer ) + if( g_restartVoteTimelimit.integer && ( level.time - level.startTime ) / 60000 >= g_restartVoteTimelimit.integer ) { trap_SendServerCommand( ent-g_entities, va( "print \"%s: It's not allowed to call a restart vote after %i minute%s.\n\"", @@ -1377,7 +1377,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } else if( !Q_stricmp( vote, "map" ) ) { - if( level.time / 60000 >= g_mapVoteTimelimit.integer ) + if( g_mapVoteTimelimit.integer && ( level.time - level.startTime ) / 60000 >= g_mapVoteTimelimit.integer ) { trap_SendServerCommand( ent-g_entities, va( "print \"%s: It's not allowed to call a map vote after %i minute%s. Call a ^1nextmap^7 vote instead\n\"", @@ -1434,7 +1434,7 @@ void Cmd_CallVote_f( gentity_t *ent ) } else if( !Q_stricmp( vote, "sudden_death" ) ) { - if(!g_suddenDeathVotePercent.integer) + if( !g_suddenDeathVotePercent.integer ) { trap_SendServerCommand( ent-g_entities, "print \"Sudden Death votes have been disabled\n\"" ); @@ -1918,6 +1918,7 @@ void Cmd_Destroy_f( gentity_t *ent ) char cmd[ 12 ]; qboolean deconstruct = qtrue; qboolean lastSpawn = qfalse; + int bp; if( ent->client->pers.namelog->denyBuild ) { @@ -1942,22 +1943,21 @@ void Cmd_Destroy_f( gentity_t *ent ) ( ( ent->client->ps.weapon >= WP_ABUILD ) && ( ent->client->ps.weapon <= WP_HBUILD ) ) ) { + //give some BP back: 20% for a dead buildable, 80% for a new one + bp = BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildPoints; + bp *= MAX( (float)traceEnt->health, 0.0f ) / + BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->health * 0.6f + 0.2f; + // Always let the builder prevent the explosion if( traceEnt->health <= 0 ) { + ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp; G_QueueBuildPoints( traceEnt ); G_RewardAttackers( traceEnt ); G_FreeEntity( traceEnt ); return; } - // Cancel deconstruction (unmark) - if( deconstruct && g_markDeconstruct.integer && traceEnt->deconstruct ) - { - traceEnt->deconstruct = qfalse; - return; - } - // Prevent destruction of the last spawn if( ent->client->pers.teamSelection == TEAM_ALIENS && traceEnt->s.modelindex == BA_A_SPAWN ) @@ -1972,8 +1972,7 @@ void Cmd_Destroy_f( gentity_t *ent ) lastSpawn = qtrue; } - if( lastSpawn && !g_cheats.integer && - !g_markDeconstruct.integer ) + if( lastSpawn && !g_cheats.integer ) { G_TriggerMenu( ent->client->ps.clientNum, MN_B_LASTSPAWN ); return; @@ -1986,9 +1985,7 @@ void Cmd_Destroy_f( gentity_t *ent ) return; } - if( !g_markDeconstruct.integer || - ( ent->client->pers.teamSelection == TEAM_HUMANS && - !G_FindPower( traceEnt, qtrue ) ) ) + if( ent->client->pers.teamSelection == TEAM_HUMANS ) { if( ent->client->buildTimer ) { @@ -2004,21 +2001,18 @@ void Cmd_Destroy_f( gentity_t *ent ) G_Damage( traceEnt, ent, ent, forward, tr.endpos, traceEnt->health, 0, MOD_SUICIDE ); } - else if( g_markDeconstruct.integer && - ( ent->client->pers.teamSelection != TEAM_HUMANS || - G_FindPower( traceEnt , qtrue ) || lastSpawn ) ) - { - traceEnt->deconstruct = qtrue; // Mark buildable for deconstruction - traceEnt->deconstructTime = level.time; - } else { if( !g_cheats.integer && !g_instantBuild.integer ) // add a bit to the build timer { - ent->client->buildTimer += - BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildTime / 4; - G_RecalcBuildTimer(ent->client); + ent->client->buildTimer += BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildTime / 4; + G_RecalcBuildTimer(ent->client); } + + ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp; + if( ent->client->ps.persistant[ PERS_BUILDPOINTS ] > CKIT_STORAGE ) + ent->client->ps.persistant[ PERS_BUILDPOINTS ] = CKIT_STORAGE; + G_Damage( traceEnt, ent, ent, forward, tr.endpos, traceEnt->health, 0, MOD_DECONSTRUCT ); G_FreeEntity( traceEnt ); @@ -2782,6 +2776,69 @@ void Cmd_Reload_f( gentity_t *ent ) playerState_t *ps = &ent->client->ps; int ammo; + // reload transfers mass in case of CKit + if( ps->weapon == WP_HBUILD ) + { + trace_t trace; + vec3_t eyes, view, point; + gentity_t *other; + + #define USE_OBJECT_RANGE 100 + + // look for object infront of player + AngleVectors( ent->client->ps.viewangles, view, NULL, NULL ); + BG_GetClientViewOrigin( &ent->client->ps, eyes ); + VectorMA( eyes, USE_OBJECT_RANGE, view, point ); + + trap_Trace( &trace, ent->client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT ); + other = &g_entities[ trace.entityNum ]; + + // transfer FROM buildable + if( other->s.eType == ET_BUILDABLE ) + { + if( other->buildableTeam == TEAM_HUMANS && + BG_Buildable( other->s.modelindex, NULL )->hasStorage && + other->spawned && other->health >= 0 && + other->storedBP > 0 ) + { + float bp; + + bp = floor( MIN( MIN( other->storedBP, 4 ), + CKIT_STORAGE - ent->client->ps.persistant[ PERS_BUILDPOINTS ] ) ); + + other->storedBP -= bp; + ent->client->ps.persistant[ PERS_BUILDPOINTS ] += bp; + + if( bp ) + G_AddEvent( ent, EV_RPTUSE_SOUND, 0 ); + else + G_AddEvent( ent, EV_BUILD_REPAIRED, 0 ); + } + } + //transfer TO another player + else if( other->client ) + { + if( BG_GetPlayerWeapon( &other->client->ps ) == WP_HBUILD && + other->client->ps.stats[ STAT_HEALTH ] >= 0 ) + { + int bp; + + bp = MIN( MIN( ent->client->ps.persistant[ PERS_BUILDPOINTS ], 4 ), + CKIT_STORAGE - other->client->ps.persistant[ PERS_BUILDPOINTS ] ); + + ent->client->ps.persistant[ PERS_BUILDPOINTS ] -= bp; + other->client->ps.persistant[ PERS_BUILDPOINTS ] += bp; + + if( bp ) + G_AddEvent( ent, EV_RPTUSE_SOUND, 0 ); + else + G_AddEvent( ent, EV_BUILD_REPAIRED, 0 ); + } + } + + return; + } + // weapon doesn't ever need reloading if( BG_Weapon( ps->weapon )->infiniteAmmo ) return; @@ -3305,7 +3362,6 @@ Cmd_Debug1_f */ void Cmd_Debug1_f( gentity_t *other ) { - other->client->ps.stats[ STAT_SHAKE ] += 70; } /* diff --git a/src/game/g_combat.c b/src/game/g_combat.c index fe5d607..6acf6b7 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -195,17 +195,18 @@ float G_RewardAttackers( gentity_t *self ) AddScore( player, stageValue ); - // killing buildables earns score, but not credits - if( self->s.eType != ET_BUILDABLE ) - { - G_AddCreditToClient( player->client, stageValue, qtrue ); + if( self->s.eType == ET_BUILDABLE ) + stageValue *= g_buildableValueModifier.value; + else + stageValue *= g_playerValueModifier.value; + + G_AddCreditToClient( player->client, stageValue, qtrue ); - // add to stage counters - if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) - alienCredits += stageValue; - else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) - humanCredits += stageValue; - } + // add to stage counters + if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + alienCredits += stageValue; + else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) + humanCredits += stageValue; } self->credits[ i ] = 0; } @@ -1069,7 +1070,7 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, } // base is under attack warning if DCC'd - if( targ->buildableTeam == TEAM_HUMANS && G_FindDCC( targ ) && + if( targ->buildableTeam == TEAM_HUMANS && G_IsDCCBuilt( ) && level.time > level.humanBaseAttackTimer ) { level.humanBaseAttackTimer = level.time + DC_ATTACK_PERIOD; @@ -1085,7 +1086,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, // add to the attacker's hit counter if( attacker->client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE - && targ->s.eType != ET_GENERAL ) + && targ->s.eType != ET_GENERAL + && mod != MOD_DECONSTRUCT ) { if( OnSameTeam( targ, attacker ) ) attacker->client->ps.persistant[ PERS_HITS ]--; diff --git a/src/game/g_local.h b/src/game/g_local.h index 001c7d9..944140f 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -198,16 +198,12 @@ struct gentity_s gentity_t *parentNode; // for creep and defence/spawn dependencies qboolean active; // for power repeater, but could be useful elsewhere qboolean locked; // used for turret tracking - qboolean powered; // for human buildables int builtBy; // clientNum of person that built this - int dcc; // number of controlling dccs qboolean spawned; // whether or not this buildable has finished spawning int shrunkTime; // time when a barricade shrunk or zero int buildTime; // when this buildable was built int animTime; // last animation change int time1000; // timer evaluated every second - qboolean deconstruct; // deconstruct if no BP left - int deconstructTime; // time at which structure marked int overmindAttackTimer; int overmindDyingTimer; int overmindSpawnsTimer; @@ -243,10 +239,24 @@ struct gentity_s qboolean pointAgainstWorld; // don't use the bbox for map collisions - int buildPointZone; // index for zone - int usesBuildPointZone; // does it use a zone? + vec3_t cuboidSize; - vec3_t cuboidSize; + //power grid + qboolean requiresPower; // false for telenodes, cuboids, etc. + qboolean isPowerSource; // true for all active elements (including the capbank) + + int powerNetwork; // which network is it in (0 is no network) + qboolean powered; // is this buildable powered? + qboolean surge; // true if the buildable requests high amount of power (resistance=surgeResistance) + // IMPORTANT NOTE: it should NOT depend on anything power-related to avoid + // unstable network states + qboolean surgePowered; // is surge powered? + float voltage; // voltage drop (load) or gain (source) + float current; // current flow + float resistance; // resistance (for sources it's their internal resistance) + + //mass + float storedBP; }; typedef enum @@ -485,16 +495,6 @@ void G_PrintSpawnQueue( spawnQueue_t *sq ); #define MAX_DAMAGE_REGION_TEXT 8192 #define MAX_DAMAGE_REGIONS 16 -// build point zone -typedef struct -{ - int active; - - int totalBuildPoints; - int queuedBuildPoints; - int nextQueueTime; -} buildPointZone_t; - // store locational damage regions typedef struct damageRegion_s { @@ -641,14 +641,6 @@ typedef struct int alienBuildPoints; int alienBuildPointQueue; int alienNextQueueTime; - int humanBuildPoints; - int humanBuildPointQueue; - int humanNextQueueTime; - - buildPointZone_t *buildPointZones; - - gentity_t *markedBuildables[ MAX_GENTITIES ]; - int numBuildablesForRemoval; int alienKills; int humanKills; @@ -801,8 +793,8 @@ gentity_t *G_CheckSpawnPoint( int spawnNum, const vec3_t origin, buildable_t G_IsPowered( vec3_t origin ); qboolean G_IsDCCBuilt( void ); -int G_FindDCC( gentity_t *self ); -gentity_t *G_Reactor( void ); +qboolean G_IsRTGBuilt( void ); +qboolean G_FindDCC( gentity_t *self ); gentity_t *G_Overmind( void ); qboolean G_FindCreep( gentity_t *self ); @@ -823,12 +815,7 @@ void G_BaseSelfDestruct( team_t team ); int G_NextQueueTime( int queuedBP, int totalBP, int queueBaseRate ); void G_QueueBuildPoints( gentity_t *self ); int G_GetBuildPoints( const vec3_t pos, team_t team ); -int G_GetMarkedBuildPoints( const vec3_t pos, team_t team ); qboolean G_FindPower( gentity_t *self, qboolean searchUnspawned ); -gentity_t *G_PowerEntityForPoint( const vec3_t origin ); -gentity_t *G_PowerEntityForEntity( gentity_t *ent ); -gentity_t *G_RepeaterEntityForPoint( vec3_t origin ); -gentity_t *G_InPowerZone( gentity_t *self ); buildLog_t *G_BuildLogNew( gentity_t *actor, buildFate_t fate ); void G_BuildLogSet( buildLog_t *log, gentity_t *ent ); void G_BuildLogAuto( gentity_t *actor, gentity_t *buildable, buildFate_t fate ); @@ -836,6 +823,7 @@ void G_BuildLogRevert( int id ); const char *G_CuboidName(buildable_t buildable, const vec3_t cuboidSize, qboolean verbose); void G_LayoutBuildItem( buildable_t buildable, vec3_t origin, vec3_t angles, vec3_t origin2, vec3_t angles2 ); void G_RemoveUnbuiltBuildables( gentity_t *self ); +void G_UpdatePowerGrid( float dt ); // // g_utils.c @@ -1158,11 +1146,6 @@ extern vmCvar_t pmove_msec; extern vmCvar_t g_alienBuildPoints; extern vmCvar_t g_alienBuildQueueTime; -extern vmCvar_t g_humanBuildPoints; -extern vmCvar_t g_humanBuildQueueTime; -extern vmCvar_t g_humanRepeaterBuildPoints; -extern vmCvar_t g_humanRepeaterBuildQueueTime; -extern vmCvar_t g_humanRepeaterMaxZones; extern vmCvar_t g_humanStage; extern vmCvar_t g_humanCredits; extern vmCvar_t g_humanMaxStage; @@ -1182,8 +1165,6 @@ extern vmCvar_t g_disabledEquipment; extern vmCvar_t g_disabledClasses; extern vmCvar_t g_disabledBuildables; -extern vmCvar_t g_markDeconstruct; - extern vmCvar_t g_debugMapRotation; extern vmCvar_t g_currentMapRotation; extern vmCvar_t g_mapRotationNodes; @@ -1227,6 +1208,11 @@ extern vmCvar_t g_cuboidHealthLimit; extern vmCvar_t g_buildableDensityLimit; extern vmCvar_t g_buildableDensityLimitRange; +extern vmCvar_t g_buildableValueModifier; +extern vmCvar_t g_playerValueModifier; +extern vmCvar_t g_massYieldModifier; +extern vmCvar_t g_voltageModifier; + void trap_Print( const char *fmt ); void trap_Error( const char *fmt ); diff --git a/src/game/g_main.c b/src/game/g_main.c index 0f9c31c..1cbf897 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -86,11 +86,6 @@ vmCvar_t g_maxNameChanges; vmCvar_t g_alienBuildPoints; vmCvar_t g_alienBuildQueueTime; -vmCvar_t g_humanBuildPoints; -vmCvar_t g_humanBuildQueueTime; -vmCvar_t g_humanRepeaterBuildPoints; -vmCvar_t g_humanRepeaterBuildQueueTime; -vmCvar_t g_humanRepeaterMaxZones; vmCvar_t g_humanStage; vmCvar_t g_humanCredits; vmCvar_t g_humanMaxStage; @@ -110,8 +105,6 @@ vmCvar_t g_disabledEquipment; vmCvar_t g_disabledClasses; vmCvar_t g_disabledBuildables; -vmCvar_t g_markDeconstruct; - vmCvar_t g_debugMapRotation; vmCvar_t g_currentMapRotation; vmCvar_t g_mapRotationNodes; @@ -158,6 +151,11 @@ vmCvar_t g_cuboidMode; vmCvar_t g_buildableDensityLimit; vmCvar_t g_buildableDensityLimitRange; +vmCvar_t g_buildableValueModifier; +vmCvar_t g_playerValueModifier; +vmCvar_t g_massYieldModifier; +vmCvar_t g_voltageModifier; + // copy cvars that can be set in worldspawn so they can be restored later static char cv_gravity[ MAX_CVAR_VALUE_STRING ]; static char cv_humanMaxStage[ MAX_CVAR_VALUE_STRING ]; @@ -232,11 +230,6 @@ static cvarTable_t gameCvarTable[ ] = { &g_alienBuildPoints, "g_alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, 0, 0, qfalse }, { &g_alienBuildQueueTime, "g_alienBuildQueueTime", DEFAULT_ALIEN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, - { &g_humanBuildPoints, "g_humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, 0, 0, qfalse }, - { &g_humanBuildQueueTime, "g_humanBuildQueueTime", DEFAULT_HUMAN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, - { &g_humanRepeaterBuildPoints, "g_humanRepeaterBuildPoints", DEFAULT_HUMAN_REPEATER_BUILDPOINTS, CVAR_ARCHIVE, 0, qfalse }, - { &g_humanRepeaterMaxZones, "g_humanRepeaterMaxZones", DEFAULT_HUMAN_REPEATER_MAX_ZONES, CVAR_ARCHIVE, 0, qfalse }, - { &g_humanRepeaterBuildQueueTime, "g_humanRepeaterBuildQueueTime", DEFAULT_HUMAN_REPEATER_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse }, { &g_humanStage, "g_humanStage", "0", 0, 0, qfalse }, { &g_humanCredits, "g_humanCredits", "0", 0, 0, qfalse }, { &g_humanMaxStage, "g_humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, 0, 0, qfalse, cv_humanMaxStage }, @@ -261,8 +254,6 @@ static cvarTable_t gameCvarTable[ ] = { &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse }, { &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse }, - { &g_markDeconstruct, "g_markDeconstruct", "3", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue }, - { &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse }, { &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING { &g_mapRotationNodes, "g_mapRotationNodes", "", CVAR_ROM, 0, qfalse }, @@ -301,7 +292,12 @@ static cvarTable_t gameCvarTable[ ] = { &g_cuboidMode, "g_cuboidMode", "0", CVAR_ARCHIVE, 0, qfalse }, { &g_buildableDensityLimit, "g_buildableDensityLimit", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }, - { &g_buildableDensityLimitRange, "g_buildableDensityLimitRange", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse } + { &g_buildableDensityLimitRange, "g_buildableDensityLimitRange", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }, + + { &g_playerValueModifier, "g_playerValueModifier", "0.5", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }, + { &g_buildableValueModifier, "g_buildableValueModifier", "0.16", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }, + { &g_massYieldModifier, "g_massYieldModifier", "1", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse }, + { &g_voltageModifier, "g_voltageModifier", "1.0", CVAR_ARCHIVE | CVAR_SERVERINFO } }; static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] ); @@ -1197,7 +1193,6 @@ void G_CalculateBuildPoints( void ) { int i; buildable_t buildable; - buildPointZone_t *zone; // BP queue updates while( level.alienBuildPointQueue > 0 && @@ -1209,15 +1204,6 @@ void G_CalculateBuildPoints( void ) g_alienBuildQueueTime.integer ); } - while( level.humanBuildPointQueue > 0 && - level.humanNextQueueTime < level.time ) - { - level.humanBuildPointQueue--; - level.humanNextQueueTime += G_NextQueueTime( level.humanBuildPointQueue, - g_humanBuildPoints.integer, - g_humanBuildQueueTime.integer ); - } - // Sudden Death checks if( G_TimeTilSuddenDeath( ) <= 0 && level.suddenDeathWarning < TW_PASSED ) { @@ -1226,7 +1212,6 @@ void G_CalculateBuildPoints( void ) trap_SendServerCommand( -1, "print \"Beginning Sudden Death.\n\"" ); trap_SendServerCommand( -1, "announce suddendeath" ); level.suddenDeathWarning = TW_PASSED; - G_ClearDeconMarks( ); // Clear blueprints, or else structs that cost 0 BP can still be built after SD for( i = 0; i < level.maxclients; i++ ) @@ -1246,101 +1231,26 @@ void G_CalculateBuildPoints( void ) level.suddenDeathWarning = TW_IMMINENT; } - level.humanBuildPoints = g_humanBuildPoints.integer - level.humanBuildPointQueue; level.alienBuildPoints = g_alienBuildPoints.integer - level.alienBuildPointQueue; - // Reset buildPointZones - for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ ) - { - buildPointZone_t *zone = &level.buildPointZones[ i ]; - - zone->active = qfalse; - zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer; - } - // Iterate through entities for( i = MAX_CLIENTS; i < level.num_entities; i++ ) { gentity_t *ent = &g_entities[ i ]; - buildPointZone_t *zone; buildable_t buildable; int cost; if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD ) continue; - // mark a zone as active - if( ent->usesBuildPointZone ) - { - assert( ent->buildPointZone >= 0 && ent->buildPointZone < g_humanRepeaterMaxZones.integer ); - - zone = &level.buildPointZones[ ent->buildPointZone ]; - zone->active = qtrue; - } - // Subtract the BP from the appropriate pool buildable = ent->s.modelindex; cost = BG_Buildable( buildable, ent->cuboidSize )->buildPoints; if( ent->buildableTeam == TEAM_ALIENS ) level.alienBuildPoints -= cost; - if( buildable == BA_H_REPEATER ) - level.humanBuildPoints -= cost; - else if( buildable != BA_H_REACTOR ) - { - gentity_t *power = G_PowerEntityForEntity( ent ); - - if( power ) - { - if( power->s.modelindex == BA_H_REACTOR ) - level.humanBuildPoints -= cost; - else if( power->s.modelindex == BA_H_REPEATER && power->usesBuildPointZone ) - level.buildPointZones[ power->buildPointZone ].totalBuildPoints -= cost; - } - } } - // Finally, update repeater zones and their queues - // note that this has to be done after the used BP is calculated - for( i = MAX_CLIENTS; i < level.num_entities; i++ ) - { - gentity_t *ent = &g_entities[ i ]; - - if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD || - ent->buildableTeam != TEAM_HUMANS ) - continue; - - buildable = ent->s.modelindex; - - if( buildable != BA_H_REPEATER ) - continue; - - if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active ) - { - zone = &level.buildPointZones[ ent->buildPointZone ]; - - if( G_TimeTilSuddenDeath( ) > 0 ) - { - // BP queue updates - while( zone->queuedBuildPoints > 0 && - zone->nextQueueTime < level.time ) - { - zone->queuedBuildPoints--; - zone->nextQueueTime += G_NextQueueTime( zone->queuedBuildPoints, - zone->totalBuildPoints, - g_humanRepeaterBuildQueueTime.integer ); - } - } - else - { - zone->totalBuildPoints = zone->queuedBuildPoints = 0; - } - } - } - - if( level.humanBuildPoints < 0 ) - level.humanBuildPoints = 0; - if( level.alienBuildPoints < 0 ) level.alienBuildPoints = 0; } @@ -2338,14 +2248,6 @@ void CheckCvars( void ) trap_Cvar_Set( "g_needpass", "0" ); } - // Unmark any structures for deconstruction when - // the server setting is changed - if( g_markDeconstruct.modificationCount != lastMarkDeconModCount ) - { - lastMarkDeconModCount = g_markDeconstruct.modificationCount; - G_ClearDeconMarks( ); - } - // If we change g_suddenDeathTime during a map, we need to update // when sd will begin if( g_suddenDeathTime.modificationCount != lastSDTimeModCount ) @@ -2354,24 +2256,6 @@ void CheckCvars( void ) level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000; } - // If the number of zones changes, we need a new array - if( g_humanRepeaterMaxZones.integer != lastNumZones ) - { - buildPointZone_t *newZones; - size_t newsize = g_humanRepeaterMaxZones.integer * sizeof( buildPointZone_t ); - size_t oldsize = lastNumZones * sizeof( buildPointZone_t ); - - newZones = BG_Alloc( newsize ); - if( level.buildPointZones ) - { - Com_Memcpy( newZones, level.buildPointZones, MIN( oldsize, newsize ) ); - BG_Free( level.buildPointZones ); - } - - level.buildPointZones = newZones; - lastNumZones = g_humanRepeaterMaxZones.integer; - } - level.frameMsec = trap_Milliseconds( ); } @@ -2586,6 +2470,9 @@ void G_RunFrame( int levelTime ) G_SpawnClients( TEAM_HUMANS ); G_CalculateAvgPlayers( ); G_UpdateZaps( msec ); + + // update the power grid + G_UpdatePowerGrid( 0.001f * msec ); // see if it is time to end the level CheckExitRules( ); diff --git a/src/game/g_weapon.c b/src/game/g_weapon.c index 4a28083..e4399a4 100644 --- a/src/game/g_weapon.c +++ b/src/game/g_weapon.c @@ -768,8 +768,8 @@ void CheckCkitRepair( gentity_t *ent ) if( tr.fraction < 1.0f && traceEnt->spawned && traceEnt->health > 0 && traceEnt->s.eType == ET_BUILDABLE && traceEnt->buildableTeam == TEAM_HUMANS ) { - if(BG_Buildable(traceEnt->s.modelindex,NULL)->cuboid) - if(!BG_CuboidAttributes(traceEnt->s.modelindex)->repairable) + if( BG_Buildable( traceEnt->s.modelindex, NULL )->cuboid ) + if( !BG_CuboidAttributes( traceEnt->s.modelindex )->repairable ) return; bHealth = BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->health; diff --git a/src/game/tremulous.h b/src/game/tremulous.h index bf60cd0..71d848e 100644 --- a/src/game/tremulous.h +++ b/src/game/tremulous.h @@ -600,10 +600,9 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define DC_SPLASHDAMAGE 50 #define DC_SPLASHRADIUS 100 #define DC_ATTACK_PERIOD 10000 // how often to spam "under attack" -#define DC_HEALRATE 4 +#define DC_HEALRATE 250 // +1 HP every this amount of time #define DC_RANGE 1000 #define DC_VALUE HBVM(DC_BP) -#define MAX_DCS_PER_BUILDABLE 2 #define ARMOURY_BP 10 #define ARMOURY_BT 15000 @@ -612,7 +611,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define ARMOURY_SPLASHRADIUS 100 #define ARMOURY_VALUE HBVM(ARMOURY_BP) -#define REACTOR_BP 0 +#define REACTOR_BP 36 #define REACTOR_BT 30000 #define REACTOR_HEALTH HBHM(930) #define REACTOR_SPLASHDAMAGE 200 @@ -623,7 +622,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define REACTOR_ATTACK_DCC_REPEAT 1000 #define REACTOR_ATTACK_DCC_RANGE 150.0f #define REACTOR_ATTACK_DCC_DAMAGE 40 -#define REACTOR_VALUE HBVM(30) +#define REACTOR_VALUE HBVM(REACTOR_BP) #define REPEATER_BP 4 #define REPEATER_BT 15000 @@ -632,6 +631,86 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define REPEATER_SPLASHRADIUS 100 #define REPEATER_VALUE HBVM(REPEATER_BP) +#define CAPBANK_BP 10 +#define CAPBANK_BT 20000 +#define CAPBANK_HEALTH HBHM(310) +#define CAPBANK_SPLASHDAMAGE 70 +#define CAPBANK_SPLASHRADIUS 140 +#define CAPBANK_VALUE HBVM(CAPBANK_BP) + +#define RTG_BP 14 +#define RTG_BT 20000 +#define RTG_HEALTH HBHM(460) +#define RTG_SPLASHDAMAGE 120 +#define RTG_SPLASHRADIUS 150 +#define RTG_VALUE HBVM(15) +#define RTG_YIELD 0.15 +#define RTG_STORAGE 15 + +#define REFINERY_BP 16 +#define REFINERY_BT 20000 +#define REFINERY_HEALTH HBHM(310) +#define REFINERY_SPLASHDAMAGE 100 +#define REFINERY_SPLASHRADIUS 150 +#define REFINERY_VALUE HBVM(REFINERY_BP) +#define REFINERY_YIELD 0.35 // at 2Hz +#define REFINERY_STORAGE 30 + +/* + * POWER GRID settings + * + * All units are SI: + * resistance (R) - ohms + * voltage (V) - volts + * current (I) - amperes + * capacity (C) - farads + */ + +//settings for buildables that are not a part of the power grid +#define DEFAULT_POWER_SETTINGS qfalse, qfalse, 0.0f, 0.0f, qfalse + +#define RESISTANCE(i,pc) (POWER_VOLTAGE/(i)*(pc)) + +#define POWER_VOLTAGE 100.0f + +#define RTG_RESISTANCE 5.0f + +#define REACTOR_RESISTANCE 1.0f + +#define CAPBANK_RESISTANCE 0.05f +#define CAPBANK_CAPACITY 2.0f + +#define PREBUILD_CURRENT 7.0f +#define PREBUILD_RESISTANCE RESISTANCE(PREBUILD_CURRENT,0.8f) + +#define MEDISTAT_I_IDLE 0.25f +#define MEDISTAT_R_IDLE RESISTANCE(MEDISTAT_I_IDLE,0.35f) +#define MEDISTAT_I_ACTIVE 1.0f +#define MEDISTAT_R_ACTIVE RESISTANCE(MEDISTAT_I_ACTIVE,0.75f) + +#define ARMOURY_CURRENT 0.1f +#define ARMOURY_RESISTANCE RESISTANCE(ARMOURY_CURRENT,0.25f) + +#define TESLAGEN_I_IDLE 0.6f +#define TESLAGEN_R_IDLE RESISTANCE(TESLAGEN_I_IDLE,0.52f) +#define TESLAGEN_I_ACTIVE 5.0f +#define TESLAGEN_R_ACTIVE RESISTANCE(TESLAGEN_I_ACTIVE,0.75f) + +#define MGTURRET_I_IDLE 0.3f +#define MGTURRET_R_IDLE RESISTANCE(MGTURRET_I_IDLE,0.6f) +#define MGTURRET_I_ACTIVE 2.0f +#define MGTURRET_R_ACTIVE RESISTANCE(MGTURRET_I_ACTIVE,0.75f) + +#define DC_I_IDLE 0.4f +#define DC_R_IDLE RESISTANCE(DC_I_IDLE,0.5f) +#define DC_I_ACTIVE 5.0f +#define DC_R_ACTIVE RESISTANCE(DC_I_ACTIVE,0.75f) + +#define REFINERY_I_IDLE 0.25f +#define REFINERY_R_IDLE RESISTANCE(REFINERY_I_IDLE,0.55f) +#define REFINERY_I_ACTIVE 4.0f +#define REFINERY_R_ACTIVE RESISTANCE(REFINERY_I_ACTIVE,0.75f) + /* * HUMAN misc */ @@ -664,6 +743,8 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA #define HUMAN_BUILDABLE_INACTIVE_TIME 90000 +#define CKIT_STORAGE 36 + /* * Misc */ diff --git a/src/ui/ui_main.c b/src/ui/ui_main.c index 6566d5e..ef53272 100644 --- a/src/ui/ui_main.c +++ b/src/ui/ui_main.c @@ -1690,7 +1690,7 @@ static void UI_DrawInfoPane( menuItem_t *item, rectDef_t *rect, float text_x, fl break; case TEAM_HUMANS: - string = "Power"; + string = "Mass"; break; default: @@ -2580,24 +2580,41 @@ static void UI_LoadHumanBuilds( void ) { int i, j = 0; stage_t stage; + static buildable_t list [ 14 ] = + { + BA_H_RTG, + BA_H_REACTOR, + BA_H_SPAWN, + BA_H_REFINERY, + BA_H_MGTURRET, + BA_H_ARMOURY, + BA_H_MEDISTAT, + BA_H_REPEATER, + BA_H_CAPBANK, + BA_H_DCC, + BA_H_TESLAGEN, + BA_H_CUBOID1, + BA_H_CUBOID2, + BA_H_CUBOID3 + }; UI_ParseCarriageList( ); stage = UI_GetCurrentHumanStage( ); uiInfo.humanBuildCount = 0; - for( i = BA_NONE + 1; i < BA_NUM_BUILDABLES; i++ ) + for( i = 0; i < 14; i++ ) { - if( BG_Buildable( i, NULL )->team == TEAM_HUMANS && - BG_Buildable( i, NULL )->buildWeapon & uiInfo.weapons && - BG_BuildableAllowedInStage( i, stage ) && - BG_BuildableIsAllowed( i ) ) + if( BG_Buildable( list[ i ], NULL )->team == TEAM_HUMANS && + BG_Buildable( list[ i ], NULL )->buildWeapon & uiInfo.weapons && + BG_BuildableAllowedInStage( list[ i ], stage ) && + BG_BuildableIsAllowed( list[ i ] ) ) { - uiInfo.humanBuildList[ j ].text = BG_Buildable( i, NULL )->humanName; + uiInfo.humanBuildList[ j ].text = BG_Buildable( list[ i ], NULL )->humanName; uiInfo.humanBuildList[ j ].cmd = - String_Alloc( va( "cmd build %s\n", BG_Buildable( i, NULL )->name ) ); + String_Alloc( va( "cmd build %s\n", BG_Buildable( list[ i ], NULL )->name ) ); uiInfo.humanBuildList[ j ].type = INFOTYPE_BUILDABLE; - uiInfo.humanBuildList[ j ].v.buildable = i; + uiInfo.humanBuildList[ j ].v.buildable = list[ i ]; j++; |