commit 5e959003e84a936b5c38124a1c337cee05f3cb92 Author: /dev/humancontroller Date: Fri Aug 5 22:39:14 2011 +0200 new AMP entities diff --git a/src/game/g_buildable.c b/src/game/g_buildable.c index ce38203..faadc4f 100644 --- a/src/game/g_buildable.c +++ b/src/game/g_buildable.c @@ -3379,8 +3379,12 @@ itemBuildError_t G_CanBuild( gentity_t *ent, buildable_t buildable, int distance if( !( normal[ 2 ] >= minNormal || ( invert && normal[ 2 ] <= -minNormal ) ) ) reason = IBE_NORMAL; - if( tr1.entityNum != ENTITYNUM_WORLD ) + if( !( tr1.entityNum == ENTITYNUM_WORLD || + !strcmp( g_entities[ tr1.entityNum ].classname, "func_destructable" ) || + !strcmp( g_entities[ tr1.entityNum ].classname, "func_spawn" ) ) ) + { reason = IBE_NORMAL; + } contents = trap_PointContents( entity_origin, -1 ); buildPoints = BG_Buildable( buildable )->buildPoints; diff --git a/src/game/g_combat.c b/src/game/g_combat.c index e51036c..53082d3 100644 --- a/src/game/g_combat.c +++ b/src/game/g_combat.c @@ -200,11 +200,14 @@ float G_RewardAttackers( gentity_t *self ) { 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; + if( !level.stagesLocked ) + { + // 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; diff --git a/src/game/g_local.h b/src/game/g_local.h index 585d4d4..cc12aaf 100644 --- a/src/game/g_local.h +++ b/src/game/g_local.h @@ -53,6 +53,13 @@ typedef struct gclient_s gclient_t; #define MAX_TARGETS 4 // note: if you increase these, then also #define MAX_TARGETNAMES 4 // change g_spawn.c to spawn extras +#define VALUE_MASK 0x0001FFFF +#define SIGN_BIT 0x00020000 +#define TEAM_BIT 0x00040000 +#define LOCKSTAGE_BIT 0x00080000 +#define RESET_AFTER_USE 0x40000000 +#define RESET_BIT 0x80000000 + // movers are things like doors, plats, buttons, etc typedef enum { @@ -245,6 +252,18 @@ struct gentity_s int resetValue; int buttonMask; + int targetGate; + int subType; + int gateState; + int finalGateState; + qboolean triggerOnlyOnRise; + short humanFunds; + short alienFunds; + int timeLimit; + int maxValue; + char *cvarName; + char *op; + int value; }; typedef enum @@ -674,6 +693,10 @@ typedef struct buildLog_t buildLog[ MAX_BUILDLOG ]; int buildId; int numBuildLogs; + + qboolean stagesLocked; + int internalAlienBPModCount; + int internalHumanBPModCount; } level_locals_t; #define CMD_CHEAT 0x0001 diff --git a/src/game/g_main.c b/src/game/g_main.c index 39a8482..7aef2a6 100644 --- a/src/game/g_main.c +++ b/src/game/g_main.c @@ -476,6 +476,17 @@ void G_UpdateCvars( void ) { trap_Cvar_Update( cv->vmCvar ); + if( !strcmp( cv->cvarName, "g_alienBuildPoints" ) ) + { + cv->modificationCount += level.internalAlienBPModCount; + level.internalAlienBPModCount = 0; + } + else if( !strcmp( cv->cvarName, "g_humanBuildPoints" ) ) + { + cv->modificationCount += level.internalHumanBPModCount; + level.internalHumanBPModCount = 0; + } + if( cv->modificationCount != cv->vmCvar->modificationCount ) { cv->modificationCount = cv->vmCvar->modificationCount; diff --git a/src/game/g_mover.c b/src/game/g_mover.c index 862402c..906d648 100644 --- a/src/game/g_mover.c +++ b/src/game/g_mover.c @@ -2576,3 +2576,152 @@ void SP_func_pendulum( gentity_t *ent ) ent->s.apos.trType = TR_SINE; ent->s.apos.trDelta[ 2 ] = speed; } + + +/* +==================== +G_KillBrushModel +==================== +*/ +void G_KillBrushModel( gentity_t *ent, gentity_t *activator ) +{ + gentity_t *e; + vec3_t mins, maxs; + trace_t tr; + + for( e = &g_entities[ 0 ]; e < &g_entities[ level.num_entities ]; ++e ) + { + if( !e->takedamage || !e->r.linked || !e->clipmask || ( e->client && e->client->noclip ) ) + continue; + + VectorAdd( e->r.currentOrigin, e->r.mins, mins ); + VectorAdd( e->r.currentOrigin, e->r.maxs, maxs ); + + if( !trap_EntityContact( mins, maxs, ent ) ) + continue; + + trap_Trace( &tr, e->r.currentOrigin, e->r.mins, e->r.maxs, + e->r.currentOrigin, e->s.number, e->clipmask ); + + if( tr.entityNum != ENTITYNUM_NONE ) + G_Damage( e, ent, activator, NULL, NULL, 100000, DAMAGE_NO_PROTECTION, MOD_CRUSH ); + } +} + + +/* +==================== +Use_func_spawn +==================== +*/ +void Use_func_spawn( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + if( ent->r.linked ) + trap_UnlinkEntity( ent ); + else + { + trap_LinkEntity( ent ); + if( !( ent->spawnflags & 2 ) ) + G_KillBrushModel( ent, activator ); + } +} + +/* +==================== +SP_func_spawn +==================== +*/ +void SP_func_spawn( gentity_t *ent ) +{ + ent->r.svFlags = SVF_USE_CURRENT_ORIGIN; + ent->s.eType = ET_MOVER; + ent->moverState = MOVER_POS1; + VectorCopy( ent->s.origin, ent->pos1 ); + + if( ent->model[ 0 ] == '*' ) + trap_SetBrushModel( ent, ent->model ); + else + { + ent->s.modelindex = G_ModelIndex( ent->model ); + VectorCopy( ent->s.angles, ent->s.apos.trBase ); + } + + ent->use = Use_func_spawn; + + if( ent->spawnflags & 1 ) + trap_LinkEntity( ent ); + else + trap_UnlinkEntity( ent ); +} + + +/* +==================== +Use_func_destructable +==================== +*/ +void Use_func_destructable( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + if( ent->r.linked ) + { + ent->takedamage = qfalse; + trap_UnlinkEntity( ent ); + if( ent->health <= 0 ) + { + G_RadiusDamage( ent->pos1, activator, ent->splashDamage, ent->splashRadius, ent, MOD_TRIGGER_HURT ); + G_UseTargets( ent, activator ); + } + } + else + { + trap_LinkEntity( ent ); + G_KillBrushModel( ent, activator ); + ent->health = ent->resetValue; + ent->takedamage = qtrue; + } +} + +/* +==================== +SP_func_destructable +==================== +*/ +void SP_func_destructable( gentity_t *ent ) +{ + char *s; + + G_SpawnString( "equipment", "", &s ); + BG_ParseCSVEquipmentList( s, ent->wTriggers, WP_NUM_WEAPONS, ent->uTriggers, UP_NUM_UPGRADES ); + G_SpawnString( "classes", "", &s ); + BG_ParseCSVClassList( s, ent->cTriggers, PCL_NUM_CLASSES ); + + G_SpawnInt( "damage", "0", &ent->splashDamage ); + G_SpawnInt( "radius", "0", &ent->splashRadius ); + G_SpawnInt( "health", "100", &ent->resetValue ); + if( ent->resetValue < 1 ) + ent->resetValue = 1; + + ent->r.svFlags = SVF_USE_CURRENT_ORIGIN; + ent->s.eType = ET_MOVER; + ent->moverState = MOVER_POS1; + VectorCopy( ent->s.origin, ent->pos1 ); + + if( ent->model[ 0 ] == '*' ) + trap_SetBrushModel( ent, ent->model ); + else + { + ent->s.modelindex = G_ModelIndex( ent->model ); + VectorCopy( ent->s.angles, ent->s.apos.trBase ); + } + + ent->use = Use_func_destructable; + + if( ent->spawnflags & 1 ) + trap_UnlinkEntity( ent ); + else + { + trap_LinkEntity( ent ); + ent->health = ent->resetValue; + ent->takedamage = qtrue; + } +} diff --git a/src/game/g_spawn.c b/src/game/g_spawn.c index 6dc67f8..bee667e 100644 --- a/src/game/g_spawn.c +++ b/src/game/g_spawn.c @@ -169,6 +169,9 @@ void SP_func_door_model( gentity_t *ent ); void SP_func_train( gentity_t *ent ); void SP_func_timer( gentity_t *self); +void SP_func_destructable( gentity_t *self ); +void SP_func_spawn( gentity_t *self ); + void SP_trigger_always( gentity_t *ent ); void SP_trigger_multiple( gentity_t *ent ); void SP_trigger_push( gentity_t *ent ); @@ -198,6 +201,16 @@ void SP_target_alien_win( gentity_t *ent ); void SP_target_human_win( gentity_t *ent ); void SP_target_hurt( gentity_t *ent ); +void SP_target_logic_gate( gentity_t *ent ); +void SP_target_count( gentity_t *ent ); +void SP_target_stgctrl( gentity_t *ent ); +void SP_target_if( gentity_t *ent ); +void SP_target_fund( gentity_t *ent ); +void SP_target_force_weapon( gentity_t *ent ); +void SP_target_force_class( gentity_t *ent ); +void SP_target_bpctrl( gentity_t *ent ); +void SP_target_equipment_class( gentity_t *ent ); + void SP_light( gentity_t *self ); void SP_info_null( gentity_t *self ); void SP_info_notnull( gentity_t *self ); @@ -216,6 +229,7 @@ spawn_t spawns[ ] = { { "func_bobbing", SP_func_bobbing }, { "func_button", SP_func_button }, + { "func_destructable", SP_func_destructable }, { "func_door", SP_func_door }, { "func_door_model", SP_func_door_model }, { "func_door_rotating", SP_func_door_rotating }, @@ -223,6 +237,7 @@ spawn_t spawns[ ] = { "func_pendulum", SP_func_pendulum }, { "func_plat", SP_func_plat }, { "func_rotating", SP_func_rotating }, + { "func_spawn", SP_func_spawn }, { "func_static", SP_func_static }, { "func_timer", SP_func_timer }, // rename trigger_timer? { "func_train", SP_func_train }, @@ -249,11 +264,21 @@ spawn_t spawns[ ] = // targets perform no action by themselves, but must be triggered // by another entity { "target_alien_win", SP_target_alien_win }, + { "target_and", SP_target_logic_gate }, + { "target_bpctrl", SP_target_bpctrl }, + { "target_class", SP_target_equipment_class }, + { "target_count", SP_target_count }, { "target_delay", SP_target_delay }, + { "target_equipment", SP_target_equipment_class }, + { "target_force_class", SP_target_force_class }, + { "target_force_weapon", SP_target_force_weapon }, + { "target_fund", SP_target_fund }, { "target_human_win", SP_target_human_win }, { "target_hurt", SP_target_hurt }, + { "target_if", SP_target_if }, { "target_kill", SP_target_kill }, { "target_location", SP_target_location }, + { "target_or", SP_target_logic_gate }, { "target_position", SP_target_position }, { "target_print", SP_target_print }, { "target_push", SP_target_push }, @@ -261,7 +286,9 @@ spawn_t spawns[ ] = { "target_rumble", SP_target_rumble }, { "target_score", SP_target_score }, { "target_speaker", SP_target_speaker }, + { "target_stgctrl", SP_target_stgctrl }, { "target_teleporter", SP_target_teleporter }, + { "target_xor", SP_target_logic_gate }, // Triggers are brush objects that cause an effect when contacted // by a living player, usually involving firing targets. @@ -484,6 +511,8 @@ void G_SpawnGEntityFromSpawnVars( void ) ent->targetnames[ j++ ] = ent->targetnames[ i ]; } + G_SpawnInt( "gate", "255", &ent->targetGate ); + // if we didn't get a classname, don't bother spawning anything if( !G_CallSpawn( ent ) ) G_FreeEntity( ent ); diff --git a/src/game/g_target.c b/src/game/g_target.c index 790a913..a3c05fa 100644 --- a/src/game/g_target.c +++ b/src/game/g_target.c @@ -478,3 +478,696 @@ void SP_target_hurt( gentity_t *self ) self->use = target_hurt_use; } + + +/* +==================== +Use_target_bpctrl +==================== +*/ +void Use_target_bpctrl( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + int bp; + + if( other->targetGate & RESET_BIT ) + bp = 0; + else if( other->targetGate & TEAM_BIT ) + bp = g_humanBuildPoints.integer; + else + bp = g_alienBuildPoints.integer; + + if( other->targetGate & SIGN_BIT ) + bp -= other->targetGate & VALUE_MASK; + else + bp += other->targetGate & VALUE_MASK; + + if( bp < 0 ) + bp = 0; + else if( bp > 99999 ) + bp = 99999; + + if( other->targetGate & TEAM_BIT ) + { + trap_Cvar_Set( "g_humanBuildPoints", va( "%i", bp ) ); + trap_Cvar_Update( &g_humanBuildPoints ); + ++level.internalHumanBPModCount; + } + else + { + trap_Cvar_Set( "g_alienBuildPoints", va( "%i", bp ) ); + trap_Cvar_Update( &g_alienBuildPoints ); + ++level.internalAlienBPModCount; + } +} + +/* +==================== +SP_target_bpctrl +==================== +*/ +void SP_target_bpctrl( gentity_t *ent ) +{ + ent->use = Use_target_bpctrl; +} + + +/* +==================== +Use_target_force_class + +this is roughly a copy of the appropriate code in Cmd_Class_f() +==================== +*/ +void Use_target_force_class( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + vec3_t infestOrigin; + vec3_t oldVel; + int oldBoostTime; + + if( activator && activator->client && + activator->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS && + activator->client->ps.stats[ STAT_CLASS ] != ent->cTriggers[ 0 ] && + G_RoomForClassChange( activator, ent->cTriggers[ 0 ], infestOrigin ) ) + { + activator->client->ps.stats[ STAT_STATE ] &= ~SS_WALLCLIMBING; + activator->s.apos.trBase[ ROLL ] = 0; + + activator->client->pers.evolveHealthFraction = (float)activator->client->ps.stats[ STAT_HEALTH ] / + (float)BG_Class( activator->client->pers.classSelection )->health; + + if( activator->client->pers.evolveHealthFraction < 0.0f ) + activator->client->pers.evolveHealthFraction = 0.0f; + else if( activator->client->pers.evolveHealthFraction > 1.0f ) + activator->client->pers.evolveHealthFraction = 1.0f; + + activator->client->pers.classSelection = ent->cTriggers[ 0 ]; + ClientUserinfoChanged( activator-g_entities, qfalse ); + VectorCopy( infestOrigin, activator->s.pos.trBase ); + + VectorCopy( activator->client->ps.velocity, oldVel ); + if( activator->client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) + oldBoostTime = activator->client->boostedTime; + else + oldBoostTime = -1; + + ClientSpawn( activator, activator, activator->s.pos.trBase, activator->s.apos.trBase ); + + VectorCopy( oldVel, activator->client->ps.velocity ); + if( oldBoostTime > 0 ) + { + activator->client->boostedTime = oldBoostTime; + activator->client->ps.stats[ STAT_STATE ] |= SS_BOOSTED; + } + + G_UseTargets( ent, activator ); + } +} + +/* +==================== +Use_target_force_class +==================== +*/ +void SP_target_force_class( gentity_t *ent ) +{ + char *s; + + G_SpawnString( "class", "", &s ); + ent->cTriggers[ 0 ] = BG_ClassByName( s )->number; + + if( ent->cTriggers[ 0 ] != PCL_NONE ) + ent->use = Use_target_force_class; +} + + +/* +==================== +Use_target_force_weapon +==================== +*/ + +static qboolean TryRemovingBattlesuit( gentity_t *player ) +{ + vec3_t newOrigin; + gclient_t *client; + + if( !G_RoomForClassChange( player, PCL_HUMAN, newOrigin ) ) + return qfalse; + + client = player->client; + VectorCopy( newOrigin, client->ps.origin ); + client->ps.stats[ STAT_CLASS ] = PCL_HUMAN; + client->pers.classSelection = PCL_HUMAN; + client->ps.eFlags ^= EF_TELEPORT_BIT; + BG_RemoveUpgradeFromInventory( UP_BATTLESUIT, client->ps.stats ); + + return qtrue; +} + +static void RemoveBatteryPack( gentity_t *player ) +{ + BG_RemoveUpgradeFromInventory( UP_BATTPACK, player->client->ps.stats ); + G_GiveClientMaxAmmo( player, qtrue ); +} + +static qboolean TryAddingBattlesuit( gentity_t *player ) +{ + vec3_t newOrigin; + gclient_t *client; + + if( !G_RoomForClassChange( player, PCL_HUMAN_BSUIT, newOrigin ) ) + return qfalse; + + client = player->client; + + VectorCopy( newOrigin, client->ps.origin ); + client->ps.stats[ STAT_CLASS ] = PCL_HUMAN_BSUIT; + client->pers.classSelection = PCL_HUMAN_BSUIT; + client->ps.eFlags ^= EF_TELEPORT_BIT; + BG_AddUpgradeToInventory( UP_BATTLESUIT, client->ps.stats ); + + BG_RemoveUpgradeFromInventory( UP_JETPACK, client->ps.stats ); + BG_RemoveUpgradeFromInventory( UP_HELMET, client->ps.stats ); + BG_RemoveUpgradeFromInventory( UP_LIGHTARMOUR, client->ps.stats ); + RemoveBatteryPack( player ); + + return qtrue; +} + +static qboolean TryRemovingUpgradeIfCarried( upgrade_t upgrade, gentity_t *player ) +{ + gclient_t *client = player->client; + + if( upgrade == UP_AMMO ) + { + if( client->ps.stats[ STAT_WEAPON ] != WP_NONE && + ( client->ps.ammo || client->ps.clips ) ) + { + client->ps.ammo = client->ps.clips = 0; + client->ps.stats[ STAT_MISC ] = 0; + G_ForceWeaponChange( player, BG_GetPlayerWeapon( &client->ps ) ); + return qtrue; + } + else + return qfalse; + } + + if( !BG_InventoryContainsUpgrade( upgrade, client->ps.stats ) ) + return qfalse; + + if( upgrade == UP_BATTPACK ) + { + RemoveBatteryPack( player ); + return qtrue; + } + else if( upgrade == UP_BATTLESUIT ) + { + return TryRemovingBattlesuit( player ); + } + else + { + BG_RemoveUpgradeFromInventory( upgrade, client->ps.stats ); + return qtrue; + } +} + +void Use_target_force_weapon( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + qboolean changed = qfalse; + gclient_t *client; + int i; + + if( !activator || !( client = activator->client ) || + client->ps.stats[ STAT_TEAM ] != TEAM_HUMANS ) + { + return; + } + + if( other->targetGate & RESET_BIT ) + { + if( client->ps.stats[ STAT_WEAPON ] != WP_NONE ) + { + changed = qtrue; + client->ps.stats[ STAT_WEAPON ] = WP_NONE; + client->ps.stats[ STAT_MISC ] = 0; + G_ForceWeaponChange( activator, WP_BLASTER ); + } + + for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; ++i ) + { + if( TryRemovingUpgradeIfCarried( i, activator ) ) + changed = qtrue; + } + } + + if( other->targetGate & SIGN_BIT ) + { + for( i = 0; ent->uTriggers[ i ] != UP_NONE; ++i ) + { + if( TryRemovingUpgradeIfCarried( ent->uTriggers[ i ], activator ) ) + changed = qtrue; + } + + for( i = 0; ent->wTriggers[ i ] != WP_NONE; ++i ) + { + if( client->ps.stats[ STAT_WEAPON ] == ent->wTriggers[ i ] ) + { + changed = qtrue; + client->ps.stats[ STAT_WEAPON ] = WP_NONE; + client->ps.stats[ STAT_MISC ] = 0; + G_ForceWeaponChange( activator, WP_BLASTER ); + } + } + } + else + { + for( i = 0; ent->uTriggers[ i ] != UP_NONE; ++i ) + { + if( ent->uTriggers[ i ] == UP_AMMO || ( ent->uTriggers[ i ] == UP_BATTPACK && + BG_InventoryContainsUpgrade( UP_BATTPACK, client->ps.stats ) ) ) + { + int ammo = client->ps.ammo; + int clips = client->ps.clips; + + G_GiveClientMaxAmmo( activator, BG_Weapon( client->ps.stats[ STAT_WEAPON ] )->usesEnergy ); + + if( ammo != client->ps.ammo || clips != client->ps.clips ) + changed = qtrue; + + continue; + } + + if( BG_InventoryContainsUpgrade( ent->uTriggers[ i ], client->ps.stats ) ) + continue; + + if( ent->uTriggers[ i ] == UP_MEDKIT || ent->uTriggers[ i ] == UP_GRENADE ) + { + changed = qtrue; + BG_AddUpgradeToInventory( ent->uTriggers[ i ], client->ps.stats ); + continue; + } + + if( ent->uTriggers[ i ] == UP_BATTLESUIT ) + { + if( TryAddingBattlesuit( activator ) ) + changed = qtrue; + continue; + } + + if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) && + !TryRemovingBattlesuit( activator ) ) + { + continue; + } + + changed = qtrue; + + if( ent->uTriggers[ i ] == UP_JETPACK ) + { + if( BG_InventoryContainsUpgrade( UP_BATTPACK, client->ps.stats ) ) + RemoveBatteryPack( activator ); + } + else if( ent->uTriggers[ i ] == UP_BATTPACK ) + BG_RemoveUpgradeFromInventory( UP_JETPACK, client->ps.stats ); + + BG_AddUpgradeToInventory( ent->uTriggers[ i ], client->ps.stats ); + + if( ent->uTriggers[ i ] == UP_BATTPACK ) + G_GiveClientMaxAmmo( activator, qtrue ); + } + + for( i = 0; ent->wTriggers[ i ] != WP_NONE; ++i ) + { + int ammo = client->ps.ammo; + int clips = client->ps.clips; + + if( client->ps.stats[ STAT_WEAPON ] == ent->wTriggers[ i ] ) + { + G_GiveClientMaxAmmo( activator, qfalse ); + G_GiveClientMaxAmmo( activator, qtrue ); + } + else + { + changed = qtrue; + client->ps.stats[ STAT_WEAPON ] = ent->wTriggers[ i ]; + client->ps.stats[ STAT_MISC ] = 0; + G_GiveClientMaxAmmo( activator, qfalse ); + G_GiveClientMaxAmmo( activator, qtrue ); + G_ForceWeaponChange( activator, WP_NONE ); + } + + if( ammo != client->ps.ammo || clips != client->ps.clips ) + changed = qtrue; + } + } + + if( changed ) + { + ClientUserinfoChanged( client->ps.clientNum, qfalse ); + G_UseTargets( ent, activator ); + } +} + +/* +==================== +SP_target_force_weapon +==================== +*/ +void SP_target_force_weapon( gentity_t *ent ) +{ + char *inventory; + + G_SpawnString( "inventory", "", &inventory ); + BG_ParseCSVEquipmentList( inventory, ent->wTriggers, WP_NUM_WEAPONS, ent->uTriggers, UP_NUM_UPGRADES ); + + ent->use = Use_target_force_weapon; +} + + +/* +==================== +Use_target_fund +==================== +*/ +void Use_target_fund( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + int cr; + + if( activator && activator->client ) + { + cr = activator->client->pers.credit; + + if( activator->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + G_AddCreditToClient( activator->client, ent->alienFunds, qtrue ); + else + G_AddCreditToClient( activator->client, ent->humanFunds, qtrue ); + + if( activator->client->pers.credit != cr ) + G_UseTargets( ent, activator ); + } +} + +/* +==================== +SP_target_fund +==================== +*/ +void SP_target_fund( gentity_t *ent ) +{ + float credits; + + G_SpawnFloat( "afund", "0", &credits ); + credits *= ALIEN_CREDITS_PER_KILL; + if( credits > 30000.0f ) + credits = 30000.0f; + else if( credits < -30000.0f ) + credits = -30000.0f; + ent->alienFunds = credits; + + G_SpawnFloat( "hfund", "0", &credits ); + if( credits > 30000.0f ) + credits = 30000.0f; + else if( credits < -30000.0f ) + credits = -30000.0f; + ent->humanFunds = credits; + + ent->use = Use_target_fund; +} + + +/* +==================== +Use_target_if +==================== +*/ +void Use_target_if( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + int i; + + i = trap_Cvar_VariableIntegerValue( ent->cvarName ); + + if( ( !strcmp( ent->op, "==" ) && i == ent->value ) || + ( !strcmp( ent->op, "!=" ) && i != ent->value ) || + ( !strcmp( ent->op, ">=" ) && i >= ent->value ) || + ( !strcmp( ent->op, ">" ) && i > ent->value ) || + ( !strcmp( ent->op, "<=" ) && i <= ent->value ) || + ( !strcmp( ent->op, "<" ) && i < ent->value ) ) + { + G_UseTargets( ent, activator ); + } +} + +/* +==================== +SP_target_if +==================== +*/ +void SP_target_if( gentity_t *ent ) +{ + G_SpawnString( "cvar", "", &ent->cvarName ); + G_SpawnString( "sign", "", &ent->op ); + G_SpawnInt( "value", "0", &ent->value ); + ent->use = Use_target_if; +} + +/* +==================== +Use_Target_stgctrl +==================== +*/ +void Use_target_stgctrl( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + int stage; + + if( other->targetGate & RESET_BIT ) + stage = 0; + else if( other->targetGate & TEAM_BIT ) + stage = g_humanStage.integer; + else + stage = g_alienStage.integer; + + if( other->targetGate & SIGN_BIT ) + stage -= other->targetGate & VALUE_MASK; + else + stage += other->targetGate & VALUE_MASK; + + if( stage < 0 ) + stage = 0; + + if( other->targetGate & TEAM_BIT ) + { + if( stage > g_humanMaxStage.integer ) + stage = g_humanMaxStage.integer; + trap_Cvar_Set( "g_humanStage", va( "%i", stage ) ); + trap_Cvar_Update( &g_humanStage ); + } + else + { + if( stage > g_alienMaxStage.integer ) + stage = g_alienMaxStage.integer; + trap_Cvar_Set( "g_alienStage", va( "%i", stage ) ); + trap_Cvar_Update( &g_alienStage ); + } + + level.stagesLocked = !!( other->targetGate & LOCKSTAGE_BIT ); +} + +/* +==================== +SP_target_stgctrl +==================== +*/ +void SP_target_stgctrl( gentity_t *ent ) +{ + ent->use = Use_target_stgctrl; +} + + +/* +==================== +Use_target_count +==================== +*/ +void Use_target_count( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + if( ( other->targetGate & RESET_BIT ) || + ( ent->timeLimit != 0 && level.time >= ent->timestamp ) ) + { + ent->count = 0; + ent->timestamp = level.time + ent->timeLimit; + } + + if( other->targetGate & SIGN_BIT ) + { + if( --ent->count < 0 ) + ent->count = 0; + } + else + { + if( ++ent->count >= ent->maxValue ) + { + ent->count = 0; + ent->timestamp = level.time + ent->timeLimit; + G_UseTargets( ent, activator ); + } + } +} + +/* +==================== +SP_target_count +==================== +*/ +void SP_target_count( gentity_t *ent ) +{ + G_SpawnInt( "reset", "0", &ent->timeLimit ); + if( ent->timeLimit > 0 ) + ent->timestamp = level.time + ent->timeLimit; + else + ent->timeLimit = 0; + + G_SpawnInt( "maxval", "1", &ent->maxValue ); + if( ent->maxValue < 1 ) + ent->maxValue = 1; + + ent->use = Use_target_count; +} + + +/* +==================== +target_logic_gate_boolean +==================== +*/ +static qboolean target_logic_gate_boolean( gentity_t *ent ) +{ + int x = ent->gateState ^ ent->finalGateState ^ VALUE_MASK; + if( ent->subType == 0 ) + return x == VALUE_MASK; + else if( ent->subType == 1 ) + return x != 0; + else + return x != 0 && ( x & ( x - 1 ) ) == 0; // return isPowerOf2( x ); +} + +/* +==================== +Use_target_logic_gate +==================== +*/ +void Use_target_logic_gate( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + int oldState; + qboolean wasTrue, isTrue; + + oldState = ent->gateState; + wasTrue = target_logic_gate_boolean( ent ); + + if( other->targetGate & RESET_BIT ) + ent->gateState = ent->resetValue; + else + ent->gateState ^= other->targetGate & VALUE_MASK; + + isTrue = target_logic_gate_boolean( ent ); + if( other->targetGate & RESET_AFTER_USE ) + ent->gateState = oldState; + + if( wasTrue != isTrue && ( !ent->triggerOnlyOnRise || isTrue ) ) + G_UseTargets( ent, activator ); +} + +/* +==================== +SP_target_logic_gate +==================== +*/ +void SP_target_logic_gate( gentity_t *ent ) +{ + int i; + + G_SpawnInt( "trigonlyrise", "0", &i ); + ent->triggerOnlyOnRise = !!i; + + if( !strcmp( ent->classname, "target_and" ) ) + { + ent->subType = 0; + G_SpawnInt( "resetval", "252", &ent->resetValue ); + G_SpawnInt( "finalval", "255", &ent->finalGateState ); + } + else if( !strcmp( ent->classname, "target_or" ) ) + { + ent->subType = 1; + G_SpawnInt( "resetval", "0", &ent->resetValue ); + G_SpawnInt( "finalval", "131071", &ent->finalGateState ); + } + else // "target_xor" + { + ent->subType = 2; + G_SpawnInt( "resetval", "254", &ent->resetValue ); + G_SpawnInt( "finalval", "131071", &ent->finalGateState ); + } + ent->resetValue &= VALUE_MASK; + ent->gateState = ent->resetValue; + ent->finalGateState &= VALUE_MASK; + + ent->use = Use_target_logic_gate; +} + + +/* +==================== +Use_target_equipment_class +==================== +*/ + +qboolean trigger_equipment_match( gentity_t *self, gentity_t *activator ); +qboolean trigger_class_match( gentity_t *self, gentity_t *activator ); + +void Use_target_equipment_class( gentity_t *ent, gentity_t *other, gentity_t *activator ) +{ + qboolean match; + + if( !activator || !activator->client ) + return; + + if( ent->subType == TEAM_HUMANS ) + { + if( activator->client->ps.stats[ STAT_TEAM ] != TEAM_HUMANS ) + return; + match = trigger_equipment_match( ent, activator ); + } + else + { + if( activator->client->ps.stats[ STAT_TEAM ] != TEAM_ALIENS ) + return; + match = trigger_class_match( ent, activator ); + } + + if( match == !( ent->spawnflags & 2 ) ) + G_UseTargets( ent, activator ); +} + +/* +==================== +SP_target_equipment +==================== +*/ +void SP_target_equipment_class( gentity_t *ent ) +{ + char *s; + + if( !strcmp( ent->classname, "target_equipment" ) ) + { + G_SpawnString( "equipment", "", &s ); + BG_ParseCSVEquipmentList( s, ent->wTriggers, WP_NUM_WEAPONS, ent->uTriggers, UP_NUM_UPGRADES ); + ent->subType = TEAM_HUMANS; + } + else + { + G_SpawnString( "classes", "", &s ); + BG_ParseCSVClassList( s, ent->cTriggers, PCL_NUM_CLASSES ); + ent->subType = TEAM_ALIENS; + } + + ent->use = Use_target_equipment_class; +}