Index: src/game/g_local.h =================================================================== --- src/game/g_local.h (revision 814) +++ src/game/g_local.h (working copy) @@ -345,6 +345,7 @@ char ip[ 16 ]; qboolean muted; int adminLevel; + qboolean designatedBuilder; } clientPersistant_t; // this structure is cleared on each ClientSpawn(), Index: src/game/g_buildable.c =================================================================== --- src/game/g_buildable.c (revision 814) +++ src/game/g_buildable.c (working copy) @@ -2775,9 +2784,13 @@ built->enemy = NULL; built->s.weapon = BG_FindProjTypeForBuildable( buildable ); - if( builder->client ) + if( builder->client ) { built->builtBy = builder->client->ps.clientNum; - else + + if( builder->client->pers.designatedBuilder ) { + built->s.eFlags |= EF_DBUILDER; // designated builder protection + } + } else built->builtBy = -1; G_SetOrigin( built, origin ); Index: src/game/bg_public.h =================================================================== --- src/game/bg_public.h (revision 814) +++ src/game/bg_public.h (working copy) @@ -295,6 +295,7 @@ #define EF_TEAMVOTED 0x00010000 // already cast a vote #define EF_BLOBLOCKED 0x00020000 // TA: caught by a trapper #define EF_REAL_LIGHT 0x00040000 // TA: light sprites according to ambient light +#define EF_DBUILDER 0x00080000 // designated builder protection typedef enum { Index: src/game/g_cmds.c =================================================================== --- src/game/g_cmds.c (revision 814) +++ src/game/g_cmds.c (working copy) @@ -1645,8 +1645,14 @@ trace_t tr; gentity_t *traceEnt; - if( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) + if( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) { + if( ( ent->client->hovel->s.eFlags & EF_DBUILDER ) && + !ent->client->pers.designatedBuilder ) { + trap_SendServerCommand( ent-g_entities, va( "print \"This structure is protected by designated builder\"" ) ); + return; // protected, leave alone + } G_Damage( ent->client->hovel, ent, ent, forward, ent->s.origin, 10000, 0, MOD_SUICIDE ); + } if( !( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) ) { @@ -1662,6 +1668,12 @@ ( ( ent->client->ps.weapon >= WP_ABUILD ) && ( ent->client->ps.weapon <= WP_HBUILD ) ) ) { + if( ( traceEnt->s.eFlags & EF_DBUILDER ) && + !ent->client->pers.designatedBuilder ) { + trap_SendServerCommand( ent-g_entities, va( "print \"This structure is protected by designated builder\n\"" ) ); + return; // protected, leave alone + } + // Don't allow destruction of hovel with granger inside if( traceEnt->s.modelindex == BA_A_HOVEL && traceEnt->active ) return; @@ -2265,7 +2264,31 @@ */ void Cmd_Reload_f( gentity_t *ent ) { - if( ent->client->ps.weaponstate != WEAPON_RELOADING ) + if( ent->client->pers.designatedBuilder && + ( ent->client->ps.weapon >= WP_ABUILD ) && + ( ent->client->ps.weapon <= WP_HBUILD ) ) { + vec3_t forward, end; + trace_t tr; + gentity_t *traceEnt; + + AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL ); + VectorMA( ent->client->ps.origin, 100, forward, end ); + + trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID ); + traceEnt = &g_entities[ tr.entityNum ]; + + if( tr.fraction < 1.0f && ( traceEnt->s.eType == ET_BUILDABLE ) && + ( traceEnt->biteam == ent->client->pers.teamSelection ) ) { + if( traceEnt->s.eFlags & EF_DBUILDER ) { + trap_SendServerCommand( ent-g_entities, va( "print \"Structure protection removed\n\"" ) ); + traceEnt->s.eFlags &= ~EF_DBUILDER; + } else { + trap_SendServerCommand( ent-g_entities, va( "print \"Structure protection applied\n\"" ) ); + traceEnt->s.eFlags |= EF_DBUILDER; + } + } + + } else if( ent->client->ps.weaponstate != WEAPON_RELOADING ) ent->client->ps.pm_flags |= PMF_WEAPON_RELOAD; } Index: src/game/g_admin.h =================================================================== --- src/game/g_admin.h (revision 814) +++ src/game/g_admin.h (working copy) @@ -161,6 +161,7 @@ qboolean G_admin_restart( gentity_t *ent, int skiparg ); qboolean G_admin_nextmap( gentity_t *ent, int skiparg ); qboolean G_admin_namelog( gentity_t *ent, int skiparg ); +qboolean G_admin_designate( gentity_t *ent, int skiparg ); void G_admin_print( gentity_t *ent, char *m ); void G_admin_buffer_print( gentity_t *ent, char *m ); Index: src/game/g_admin.c =================================================================== --- src/game/g_admin.c (revision 814) +++ src/game/g_admin.c (working copy) @@ -60,6 +60,11 @@ "" }, + {"designate", G_admin_designate, "d", + "give the player designated builder privileges", + "[^3name|slot#^7]" + }, + {"help", G_admin_help, "h", "display commands available to you or help on a specific command", "(^5command^7)" @@ -143,6 +148,11 @@ "[^3ban slot#^7]" }, + {"undesignate", G_admin_designate, "d", + "revoke designated builder privileges", + "[^3name|slot#^7]" + }, + {"unmute", G_admin_mute, "m", "unmute a muted player", "[^3name|slot#^7]" @@ -2600,6 +2610,66 @@ return qtrue; } +qboolean G_admin_designate( gentity_t *ent, int skiparg ) +{ + int pids[ MAX_CLIENTS ]; + char name[ MAX_NAME_LENGTH ], err[ MAX_STRING_CHARS ]; + char command[ MAX_ADMIN_CMD_LEN ], *cmd; + gentity_t *vic; + + if( G_SayArgc() < 2 + skiparg ) + { + ADMP( "^3!designate: ^7usage: designate [name|slot#]\n" ); + return qfalse; + } + G_SayArgv( skiparg, command, sizeof( command ) ); + cmd = command; + if( cmd && *cmd == '!' ) + cmd++; + G_SayArgv( 1 + skiparg, name, sizeof( name ) ); + if( G_ClientNumbersFromString( name, pids ) != 1 ) + { + G_MatchOnePlayer( pids, err, sizeof( err ) ); + ADMP( va( "^3!designate: ^7%s\n", err ) ); + return qfalse; + } + if( !admin_higher( ent, &g_entities[ pids[ 0 ] ] ) && !Q_stricmp( cmd, "undesignate" ) ) + { + ADMP( "^3!mute: ^7sorry, but your intended victim has a higher admin" + " level than you\n" ); + return qfalse; + } + vic = &g_entities[ pids[ 0 ] ]; + if( vic->client->pers.designatedBuilder == qtrue ) + { + if( !Q_stricmp( cmd, "designate" ) ) + { + ADMP( "^3!designate: ^7player is already designated builder\n" ); + return qtrue; + } + vic->client->pers.designatedBuilder = qfalse; + CPx( pids[ 0 ], "cp \"^1Your designation has been revoked\"" ); + AP( va( "print \"^3!designate: ^7%s^7's designation has been revoked by %s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + else + { + if( !Q_stricmp( cmd, "undesignate" ) ) + { + ADMP( "^3!undesignate: ^7player is not currently designated builder\n" ); + return qtrue; + } + vic->client->pers.designatedBuilder = qtrue; + CPx( pids[ 0 ], "cp \"^1You've been designated\"" ); + AP( va( "print \"^3!designate: ^7%s^7 has been designated by ^7%s\n\"", + vic->client->pers.netname, + ( ent ) ? ent->client->pers.netname : "console" ) ); + } + ClientUserinfoChanged( pids[ 0 ] ); + return qtrue; +} + /* * This function facilitates the TP define. ADMP() is similar to CP except that * it prints the message to the server console if ent is not defined.