void Rancor_Smash( void ) { int radiusEntNums[128]; int numEnts; const float radius = 128; const float halfRadSquared = ((radius/2)*(radius/2)); const float radiusSquared = (radius*radius); float distSq; int i; vec3_t boltOrg; AddSoundEvent( NPCS.NPC, NPCS.NPC->r.currentOrigin, 512, AEL_DANGER, qfalse );//, qtrue ); numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, NPCS.NPC->client->renderInfo.handLBolt, boltOrg ); for ( i = 0; i < numEnts; i++ ) { gentity_t *radiusEnt = &g_entities[radiusEntNums[i]]; if ( !radiusEnt->inuse ) { continue; } if ( radiusEnt == NPCS.NPC ) {//Skip the rancor ent continue; } if ( radiusEnt->client == NULL ) {//must be a client continue; } if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) ) {//can't be one being held continue; } distSq = DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ); if ( distSq <= radiusSquared ) { G_Sound( radiusEnt, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) ); if ( distSq < halfRadSquared ) {//close enough to do damage, too G_Damage( radiusEnt, NPCS.NPC, NPCS.NPC, vec3_origin, radiusEnt->r.currentOrigin, Q_irand( 10, 25 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); } if ( radiusEnt->health > 0 && radiusEnt->client && radiusEnt->client->NPC_class != CLASS_RANCOR && radiusEnt->client->NPC_class != CLASS_ATST ) { if ( distSq < halfRadSquared || radiusEnt->client->ps.groundEntityNum != ENTITYNUM_NONE ) {//within range of my fist or withing ground-shaking range and not in the air G_Knockdown( radiusEnt );//, NPC, vec3_origin, 100, qtrue ); } } } } }
//extern void G_Knockdown( gentity_t *victim ); //[/KnockdownSys] static void Howler_TryDamage( int damage, qboolean tongue, qboolean knockdown ) { vec3_t start, end, dir; trace_t tr; float dist; if ( tongue ) { G_GetBoltPosition( NPC, NPC->NPC->genericBolt1, start, 0 ); G_GetBoltPosition( NPC, NPC->NPC->genericBolt2, end, 0 ); VectorSubtract( end, start, dir ); dist = VectorNormalize( dir ); VectorMA( start, dist+16, dir, end ); } else { VectorCopy( NPC->r.currentOrigin, start ); AngleVectors( NPC->r.currentAngles, dir, NULL, NULL ); VectorMA( start, MIN_DISTANCE*2, dir, end ); } /* RACC - don't need this for now. #ifndef FINAL_BUILD if ( d_saberCombat.integer > 1 ) { G_DebugLine(start, end, 1000, 0x000000ff, qtrue); } #endif */ // Should probably trace from the mouth, but, ah well. trap_Trace( &tr, start, vec3_origin, vec3_origin, end, NPC->s.number, MASK_SHOT ); if ( tr.entityNum < ENTITYNUM_WORLD ) {//hit *something* gentity_t *victim = &g_entities[tr.entityNum]; if ( !victim->client || victim->client->NPC_class != CLASS_HOWLER ) {//not another howler if ( knockdown && victim->client ) {//only do damage if victim isn't knocked down. If he isn't, knock him down if ( PM_InKnockDown( &victim->client->ps ) ) { return; } } //FIXME: some sort of damage effect (claws and tongue are cutting you... blood?) G_Damage( victim, NPC, NPC, dir, tr.endpos, damage, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); if ( knockdown && victim->health > 0 ) {//victim still alive //[KnockdownSys] G_Knockdown( victim, NPC, NPC->client->ps.velocity, 500, qfalse ); //G_Knockdown(victim); //[/KnockdownSys] } } } }
void npc_push(gentity_t *self, gentity_t *other, trace_t *trace ) { if(!other) return; if(!other->client) return; if(other->client->pushEffectTime > level.time || other->client->ps.fd.forceGripBeingGripped > level.time) {//Other player was pushed! float speed = (vec_t)sqrt (other->client->ps.velocity[0]* other->client->ps.velocity[0] + other->client->ps.velocity[1]* other->client->ps.velocity[1])/2; if(speed > 50) { int damage = (speed >= 100 ? 35 : 10); gentity_t *gripper = NULL; int i=0; G_Knockdown(self, other, other->client->ps.velocity, 100, qfalse); self->client->ps.velocity[1] = other->client->ps.velocity[1]*5.5f; self->client->ps.velocity[0] = other->client->ps.velocity[0]*5.5f; for(i = 0;i < 1024;i++) { gripper = &g_entities[i]; if(gripper && gripper->client) { if(gripper->client->ps.fd.forceGripEntityNum == other->client->ps.clientNum) break; } } if(gripper == NULL) return; G_Printf("Damage: %i\n",damage); G_Damage(other, gripper, gripper, NULL, NULL, damage, DAMAGE_NO_ARMOR, MOD_FORCE_DARK); G_Damage(self, other, other, NULL, NULL, damage, DAMAGE_NO_ARMOR, 0); } } }
void Rancor_Swing( qboolean tryGrab ) { int radiusEntNums[128]; int numEnts; const float radius = 88; const float radiusSquared = (radius*radius); int i; vec3_t boltOrg; numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, NPCS.NPC->client->renderInfo.handRBolt, boltOrg ); for ( i = 0; i < numEnts; i++ ) { gentity_t *radiusEnt = &g_entities[radiusEntNums[i]]; if ( !radiusEnt->inuse ) { continue; } if ( radiusEnt == NPCS.NPC ) {//Skip the rancor ent continue; } if ( radiusEnt->client == NULL ) {//must be a client continue; } if ( (radiusEnt->client->ps.eFlags2&EF2_HELD_BY_MONSTER) ) {//can't be one already being held continue; } if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared ) { if ( tryGrab && NPCS.NPC->count != 1 //don't have one in hand or in mouth already - FIXME: allow one in hand and any number in mouth! && radiusEnt->client->NPC_class != CLASS_RANCOR && radiusEnt->client->NPC_class != CLASS_GALAKMECH && radiusEnt->client->NPC_class != CLASS_ATST && radiusEnt->client->NPC_class != CLASS_GONK && radiusEnt->client->NPC_class != CLASS_R2D2 && radiusEnt->client->NPC_class != CLASS_R5D2 && radiusEnt->client->NPC_class != CLASS_MARK1 && radiusEnt->client->NPC_class != CLASS_MARK2 && radiusEnt->client->NPC_class != CLASS_MOUSE && radiusEnt->client->NPC_class != CLASS_PROBE && radiusEnt->client->NPC_class != CLASS_SEEKER && radiusEnt->client->NPC_class != CLASS_REMOTE && radiusEnt->client->NPC_class != CLASS_SENTRY && radiusEnt->client->NPC_class != CLASS_INTERROGATOR && radiusEnt->client->NPC_class != CLASS_VEHICLE ) {//grab if ( NPCS.NPC->count == 2 ) {//have one in my mouth, remove him TIMER_Remove( NPCS.NPC, "clearGrabbed" ); Rancor_DropVictim( NPCS.NPC ); } NPCS.NPC->enemy = radiusEnt;//make him my new best friend radiusEnt->client->ps.eFlags2 |= EF2_HELD_BY_MONSTER; //FIXME: this makes it so that the victim can't hit us with shots! Just use activator or something radiusEnt->client->ps.hasLookTarget = qtrue; radiusEnt->client->ps.lookTarget = NPCS.NPC->s.number; NPCS.NPC->activator = radiusEnt;//remember him NPCS.NPC->count = 1;//in my hand //wait to attack TIMER_Set( NPCS.NPC, "attacking", NPCS.NPC->client->ps.legsTimer + Q_irand(500, 2500) ); if ( radiusEnt->health > 0 && radiusEnt->pain ) {//do pain on enemy radiusEnt->pain( radiusEnt, NPCS.NPC, 100 ); //GEntity_PainFunc( radiusEnt, NPC, NPC, radiusEnt->r.currentOrigin, 0, MOD_CRUSH ); } else if ( radiusEnt->client ) { radiusEnt->client->ps.forceHandExtend = HANDEXTEND_NONE; radiusEnt->client->ps.forceHandExtendTime = 0; NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } } else {//smack vec3_t pushDir; vec3_t angs; G_Sound( radiusEnt, CHAN_AUTO, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) ); //actually push the enemy /* //VectorSubtract( radiusEnt->r.currentOrigin, boltOrg, pushDir ); VectorSubtract( radiusEnt->r.currentOrigin, NPC->r.currentOrigin, pushDir ); pushDir[2] = Q_flrand( 100, 200 ); VectorNormalize( pushDir ); */ VectorCopy( NPCS.NPC->client->ps.viewangles, angs ); angs[YAW] += flrand( 25, 50 ); angs[PITCH] = flrand( -25, -15 ); AngleVectors( angs, pushDir, NULL, NULL ); if ( radiusEnt->client->NPC_class != CLASS_RANCOR && radiusEnt->client->NPC_class != CLASS_ATST ) { G_Damage( radiusEnt, NPCS.NPC, NPCS.NPC, vec3_origin, radiusEnt->r.currentOrigin, Q_irand( 25, 40 ), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_MELEE ); G_Throw( radiusEnt, pushDir, 250 ); if ( radiusEnt->health > 0 ) {//do pain on enemy G_Knockdown( radiusEnt );//, NPC, pushDir, 100, qtrue ); } } } } } }
/* ================= ConsoleCommand // these are added in cg_main, CG_Init so they tab-complete ================= */ qboolean ConsoleCommand( void ) { char *cmd; cmd = gi.argv(0); if ( Q_stricmp (cmd, "entitylist") == 0 ) { Svcmd_EntityList_f(); return qtrue; } if (Q_stricmp (cmd, "game_memory") == 0) { Svcmd_GameMem_f(); return qtrue; } // if (Q_stricmp (cmd, "addbot") == 0) { // Svcmd_AddBot_f(); // return qtrue; // } if (Q_stricmp (cmd, "nav") == 0) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_Nav_f (); return qtrue; } if (Q_stricmp (cmd, "npc") == 0) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_NPC_f (); return qtrue; } if (Q_stricmp (cmd, "use") == 0) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_Use_f (); return qtrue; } if ( Q_stricmp( cmd, "ICARUS" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Quake3Game()->Svcmd(); return qtrue; } if ( Q_stricmp( cmd, "saberColor" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_SaberColor_f(); return qtrue; } if ( Q_stricmp( cmd, "saber" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_Saber_f(); return qtrue; } if ( Q_stricmp( cmd, "saberblade" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } Svcmd_SaberBlade_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceJump" ) == 0 ) { Svcmd_ForceJump_f(); return qtrue; } if ( Q_stricmp( cmd, "setSaberThrow" ) == 0 ) { Svcmd_SaberThrow_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceHeal" ) == 0 ) { Svcmd_ForceHeal_f(); return qtrue; } if ( Q_stricmp( cmd, "setForcePush" ) == 0 ) { Svcmd_ForcePush_f(); return qtrue; } if ( Q_stricmp( cmd, "setForcePull" ) == 0 ) { Svcmd_ForcePull_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceSpeed" ) == 0 ) { Svcmd_ForceSpeed_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceGrip" ) == 0 ) { Svcmd_ForceGrip_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceLightning" ) == 0 ) { Svcmd_ForceLightning_f(); return qtrue; } if ( Q_stricmp( cmd, "setMindTrick" ) == 0 ) { Svcmd_MindTrick_f(); return qtrue; } if ( Q_stricmp( cmd, "setSaberDefense" ) == 0 ) { Svcmd_SaberDefense_f(); return qtrue; } if ( Q_stricmp( cmd, "setSaberOffense" ) == 0 ) { Svcmd_SaberOffense_f(); return qtrue; } if ( Q_stricmp( cmd, "setForceRage" ) == 0 ) { Svcmd_ForceSetLevel_f( FP_RAGE ); return qtrue; } if ( Q_stricmp( cmd, "setForceDrain" ) == 0 ) { Svcmd_ForceSetLevel_f( FP_DRAIN ); return qtrue; } if ( Q_stricmp( cmd, "setForceProtect" ) == 0 ) { Svcmd_ForceSetLevel_f( FP_PROTECT ); return qtrue; } if ( Q_stricmp( cmd, "setForceAbsorb" ) == 0 ) { Svcmd_ForceSetLevel_f( FP_ABSORB ); return qtrue; } if ( Q_stricmp( cmd, "setForceSight" ) == 0 ) { Svcmd_ForceSetLevel_f( FP_SEE ); return qtrue; } if ( Q_stricmp( cmd, "setForceAll" ) == 0 ) { Svcmd_ForceJump_f(); Svcmd_SaberThrow_f(); Svcmd_ForceHeal_f(); Svcmd_ForcePush_f(); Svcmd_ForcePull_f(); Svcmd_ForceSpeed_f(); Svcmd_ForceGrip_f(); Svcmd_ForceLightning_f(); Svcmd_MindTrick_f(); Svcmd_SaberDefense_f(); Svcmd_SaberOffense_f(); Svcmd_ForceSetLevel_f( FP_RAGE ); Svcmd_ForceSetLevel_f( FP_DRAIN ); Svcmd_ForceSetLevel_f( FP_PROTECT ); Svcmd_ForceSetLevel_f( FP_ABSORB ); Svcmd_ForceSetLevel_f( FP_SEE ); for ( int i = SS_NONE+1; i < SS_NUM_SABER_STYLES; i++ ) { g_entities[0].client->ps.saberStylesKnown |= (1<<i); } return qtrue; } if ( Q_stricmp( cmd, "saberAttackCycle" ) == 0 ) { Svcmd_SaberAttackCycle_f(); return qtrue; } if ( Q_stricmp( cmd, "runscript" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } char *cmd2 = gi.argv(1); if ( cmd2 && cmd2[0] ) { char *cmd3 = gi.argv(2); if ( cmd3 && cmd3[0] ) { gentity_t *found = NULL; if ( (found = G_Find(NULL, FOFS(targetname), cmd2 ) ) != NULL ) { Quake3Game()->RunScript( found, cmd3 ); } else { //can't find cmd2 gi.Printf( S_COLOR_RED"runscript: can't find targetname %s\n", cmd2 ); } } else { Quake3Game()->RunScript( &g_entities[0], cmd2 ); } } else { gi.Printf( S_COLOR_RED"usage: runscript <ent targetname> scriptname\n" ); } //FIXME: else warning return qtrue; } if ( Q_stricmp( cmd, "playerteam" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } char *cmd2 = gi.argv(1); int n; if ( !*cmd2 || !cmd2[0] ) { gi.Printf( S_COLOR_RED"'playerteam' - change player team, requires a team name!\n" ); gi.Printf( S_COLOR_RED"Valid team names are:\n"); for ( n = (TEAM_FREE + 1); n < TEAM_NUM_TEAMS; n++ ) { gi.Printf( S_COLOR_RED"%s\n", GetStringForID( TeamTable, n ) ); } } else { team_t team; team = (team_t)GetIDForString( TeamTable, cmd2 ); if ( team == -1 ) { gi.Printf( S_COLOR_RED"'playerteam' unrecognized team name %s!\n", cmd2 ); gi.Printf( S_COLOR_RED"Valid team names are:\n"); for ( n = TEAM_FREE; n < TEAM_NUM_TEAMS; n++ ) { gi.Printf( S_COLOR_RED"%s\n", GetStringForID( TeamTable, n ) ); } } else { g_entities[0].client->playerTeam = team; //FIXME: convert Imperial, Malon, Hirogen and Klingon to Scavenger? } } return qtrue; } if ( Q_stricmp( cmd, "control" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } char *cmd2 = gi.argv(1); if ( !*cmd2 || !cmd2[0] ) { if ( !G_ClearViewEntity( &g_entities[0] ) ) { gi.Printf( S_COLOR_RED"control <NPC_targetname>\n", cmd2 ); } } else { Q3_SetViewEntity( 0, cmd2 ); } return qtrue; } if ( Q_stricmp( cmd, "grab" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } char *cmd2 = gi.argv(1); if ( !*cmd2 || !cmd2[0] ) { if ( !G_ReleaseEntity( &g_entities[0] ) ) { gi.Printf( S_COLOR_RED"grab <NPC_targetname>\n", cmd2 ); } } else { G_GrabEntity( &g_entities[0], cmd2 ); } return qtrue; } if ( Q_stricmp( cmd, "knockdown" ) == 0 ) { if ( !g_cheats->integer ) { gi.SendServerCommand( 0, "print \"Cheats are not enabled on this server.\n\""); return qfalse; } G_Knockdown( &g_entities[0], &g_entities[0], vec3_origin, 300, qtrue ); return qtrue; } if ( Q_stricmp( cmd, "playerModel" ) == 0 ) { if ( gi.argc() == 1 ) { gi.Printf( S_COLOR_RED"USAGE: playerModel <NPC Name>\n playerModel <g2model> <skinhead> <skintorso> <skinlower>\n playerModel player (builds player from customized menu settings)\n" ); gi.Printf( "playerModel = %s ", va("%s %s %s %s\n", g_char_model->string, g_char_skin_head->string, g_char_skin_torso->string, g_char_skin_legs->string ) ); } else if ( gi.argc() == 2 ) { G_ChangePlayerModel( &g_entities[0], gi.argv(1) ); } else if ( gi.argc() == 5 ) { //instead of setting it directly via a command, we now store it in cvars //G_ChangePlayerModel( &g_entities[0], va("%s|%s|%s|%s", gi.argv(1), gi.argv(2), gi.argv(3), gi.argv(4)) ); gi.cvar_set("g_char_model", gi.argv(1) ); gi.cvar_set("g_char_skin_head", gi.argv(2) ); gi.cvar_set("g_char_skin_torso", gi.argv(3) ); gi.cvar_set("g_char_skin_legs", gi.argv(4) ); G_InitPlayerFromCvars( &g_entities[0] ); } return qtrue; } if ( Q_stricmp( cmd, "playerTint" ) == 0 ) { if ( gi.argc() == 4 ) { g_entities[0].client->renderInfo.customRGBA[0] = atoi(gi.argv(1)); g_entities[0].client->renderInfo.customRGBA[1] = atoi(gi.argv(2)); g_entities[0].client->renderInfo.customRGBA[2] = atoi(gi.argv(3)); gi.cvar_set("g_char_color_red", gi.argv(1) ); gi.cvar_set("g_char_color_green", gi.argv(2) ); gi.cvar_set("g_char_color_blue", gi.argv(3) ); } else { gi.Printf( S_COLOR_RED"USAGE: playerTint <red 0 - 255> <green 0 - 255> <blue 0 - 255>\n" ); gi.Printf( "playerTint = %s\n", va("%d %d %d", g_char_color_red->integer, g_char_color_green->integer, g_char_color_blue->integer ) ); } return qtrue; } if ( Q_stricmp( cmd, "nexttestaxes" ) == 0 ) { G_NextTestAxes(); } if ( Q_stricmp( cmd, "exitview" ) == 0 ) { Svcmd_ExitView_f(); } if (Q_stricmp (cmd, "iknowkungfu") == 0) { gi.cvar_set( "g_debugMelee", "1" ); G_SetWeapon( &g_entities[0], WP_MELEE ); for ( int i = FP_FIRST; i < NUM_FORCE_POWERS; i++ ) { g_entities[0].client->ps.forcePowersKnown |= ( 1 << i ); if ( i == FP_TELEPATHY ) { g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_4; } else { g_entities[0].client->ps.forcePowerLevel[i] = FORCE_LEVEL_3; } } } return qfalse; }
void SandCreature_Attack( qboolean miss ) { //FIXME: make it able to grab a thermal detonator, take it down, // then have it explode inside them, killing them // (or, do damage, making them stick half out of the ground and // screech for a bit, giving you a chance to run for it!) //FIXME: effect and sound //FIXME: shootable during this anim? if ( !NPC->enemy->client ) { NPC_SetAnim( NPC, SETANIM_LEGS, BOTH_ATTACK1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); } else { NPC_SetAnim( NPC, SETANIM_LEGS, Q_irand( BOTH_ATTACK1, BOTH_ATTACK2 ), SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD|SETANIM_FLAG_RESTART ); } //don't do anything else while in this anim TIMER_Set( NPC, "attacking", NPC->client->ps.legsAnimTimer ); float playerDist = Distance( player->currentOrigin, NPC->currentOrigin ); if ( playerDist < 256 ) { //FIXME: tone this down CGCam_Shake( 0.75f*playerDist/128.0f, NPC->client->ps.legsAnimTimer ); } if ( miss ) {//purposely missed him, chance of knocking him down //FIXME: if, during the attack anim, I do end up catching him close to my mouth, then snatch him anyway... if ( NPC->enemy && NPC->enemy->client ) { vec3_t dir2Enemy; VectorSubtract( NPC->enemy->currentOrigin, NPC->currentOrigin, dir2Enemy ); if ( dir2Enemy[2] < 30 ) { dir2Enemy[2] = 30; } if ( g_spskill->integer > 0 ) { float enemyDist = VectorNormalize( dir2Enemy ); //FIXME: tone this down, smaller radius if ( enemyDist < 200 && NPC->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) { float throwStr = ((200-enemyDist)*0.4f)+20; if ( throwStr > 45 ) { throwStr = 45; } G_Throw( NPC->enemy, dir2Enemy, throwStr ); if ( g_spskill->integer > 1 ) {//knock them down, too if ( NPC->enemy->health > 0 && Q_flrand( 50, 150 ) > enemyDist ) {//knock them down G_Knockdown( NPC->enemy, NPC, dir2Enemy, 300, qtrue ); if ( NPC->enemy->s.number < MAX_CLIENTS ) {//make the player look up at me vec3_t vAng; vectoangles( dir2Enemy, vAng ); VectorSet( vAng, AngleNormalize180(vAng[PITCH])*-1, NPC->enemy->client->ps.viewangles[YAW], 0 ); SetClientViewAngle( NPC->enemy, vAng ); } } } } } } } else { NPC->enemy->activator = NPC; // kind of dumb, but when we are locked to the Rancor, we are owned by it. NPC->activator = NPC->enemy;//remember him //this guy isn't going anywhere anymore NPC->enemy->contents = 0; NPC->enemy->clipmask = 0; if ( NPC->activator->client ) { NPC->activator->client->ps.SaberDeactivate(); NPC->activator->client->ps.eFlags |= EF_HELD_BY_SAND_CREATURE; if ( NPC->activator->health > 0 && NPC->activator->client ) { G_AddEvent( NPC->activator, Q_irand(EV_DEATH1, EV_DEATH3), 0 ); NPC_SetAnim( NPC->activator, SETANIM_LEGS, BOTH_SWIM_IDLE1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); NPC_SetAnim( NPC->activator, SETANIM_TORSO, BOTH_FALLDEATH1, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); TossClientItems( NPC ); if ( NPC->activator->NPC ) {//no more thinking for you NPC->activator->NPC->nextBStateThink = Q3_INFINITE; } } /* if ( !NPC->activator->s.number ) { cg.overrides.active |= (CG_OVERRIDE_3RD_PERSON_CDP|CG_OVERRIDE_3RD_PERSON_RNG); cg.overrides.thirdPersonCameraDamp = 0; cg.overrides.thirdPersonRange = 120; } */ } else { NPC->activator->s.eFlags |= EF_HELD_BY_SAND_CREATURE; } } }
void Wampa_Slash( int boltIndex, qboolean backhand ) { int radiusEntNums[128]; int numEnts; const float radius = 88; const float radiusSquared = (radius*radius); int i; vec3_t boltOrg; int damage = (backhand)?Q_irand(10,15):Q_irand(20,30); numEnts = NPC_GetEntsNearBolt( radiusEntNums, radius, boltIndex, boltOrg ); for ( i = 0; i < numEnts; i++ ) { gentity_t *radiusEnt = &g_entities[radiusEntNums[i]]; if ( !radiusEnt->inuse ) { continue; } if ( radiusEnt == NPC ) {//Skip the wampa ent continue; } if ( radiusEnt->client == NULL ) {//must be a client continue; } if ( DistanceSquared( radiusEnt->r.currentOrigin, boltOrg ) <= radiusSquared ) { //smack G_Damage( radiusEnt, NPC, NPC, vec3_origin, radiusEnt->r.currentOrigin, damage, ((backhand)?DAMAGE_NO_ARMOR:(DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK)), MOD_MELEE ); if ( backhand ) { //actually push the enemy vec3_t pushDir; vec3_t angs; VectorCopy( NPC->client->ps.viewangles, angs ); angs[YAW] += flrand( 25, 50 ); angs[PITCH] = flrand( -25, -15 ); AngleVectors( angs, pushDir, NULL, NULL ); if ( radiusEnt->client->NPC_class != CLASS_WAMPA && radiusEnt->client->NPC_class != CLASS_RANCOR && radiusEnt->client->NPC_class != CLASS_ATST ) { G_Throw( radiusEnt, pushDir, 65 ); if ( BG_KnockDownable(&radiusEnt->client->ps) && radiusEnt->health > 0 && Q_irand( 0, 1 ) ) {//do pain on enemy radiusEnt->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN; radiusEnt->client->ps.forceDodgeAnim = 0; radiusEnt->client->ps.forceHandExtendTime = level.time + 1100; radiusEnt->client->ps.quickerGetup = qfalse; } } } else if ( radiusEnt->health <= 0 && radiusEnt->client ) {//killed them, chance of dismembering if ( !Q_irand( 0, 1 ) ) {//bite something off int hitLoc = Q_irand( G2_MODELPART_HEAD, G2_MODELPART_RLEG ); if ( hitLoc == G2_MODELPART_HEAD ) { NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATH17, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } else if ( hitLoc == G2_MODELPART_WAIST ) { NPC_SetAnim( radiusEnt, SETANIM_BOTH, BOTH_DEATHBACKWARD2, SETANIM_FLAG_OVERRIDE | SETANIM_FLAG_HOLD ); } G_Dismember( radiusEnt, NPC, radiusEnt->r.currentOrigin, hitLoc, 90, 0, radiusEnt->client->ps.torsoAnim, qtrue); } } else if ( !Q_irand( 0, 3 ) && radiusEnt->health > 0 ) {//one out of every 4 normal hits does a knockdown, too vec3_t pushDir; vec3_t angs; VectorCopy( NPC->client->ps.viewangles, angs ); angs[YAW] += flrand( 25, 50 ); angs[PITCH] = flrand( -25, -15 ); AngleVectors( angs, pushDir, NULL, NULL ); //[KnockdownSys] //ported multi-direction knockdowns from SP. G_Knockdown( radiusEnt, NPC, pushDir, 35, qtrue ); //G_Knockdown( radiusEnt ); //[/KnockdownSys] } G_Sound( radiusEnt, CHAN_WEAPON, G_SoundIndex( "sound/chars/rancor/swipehit.wav" ) ); } } }
//[/KnockdownSys] gentity_t *G_KickTrace( gentity_t *ent, vec3_t kickDir, float kickDist, vec3_t kickEnd, int kickDamage, float kickPush ) { vec3_t traceOrg, traceEnd, kickMins, kickMaxs; trace_t trace; gentity_t *hitEnt = NULL; VectorSet(kickMins, -2.0f, -2.0f, -2.0f); VectorSet(kickMaxs, 2.0f, 2.0f, 2.0f); //FIXME: variable kick height? if ( kickEnd && !VectorCompare( kickEnd, vec3_origin ) ) {//they passed us the end point of the trace, just use that //this makes the trace flat VectorSet( traceOrg, ent->r.currentOrigin[0], ent->r.currentOrigin[1], kickEnd[2] ); VectorCopy( kickEnd, traceEnd ); } else {//extrude VectorSet( traceOrg, ent->r.currentOrigin[0], ent->r.currentOrigin[1], ent->r.currentOrigin[2]+ent->r.maxs[2]*0.5f ); VectorMA( traceOrg, kickDist, kickDir, traceEnd ); } if (d_saberKickTweak.integer) { trap_G2Trace( &trace, traceOrg, kickMins, kickMaxs, traceEnd, ent->s.number, MASK_SHOT, G2TRFLAG_DOGHOULTRACE|G2TRFLAG_GETSURFINDEX|G2TRFLAG_THICK|G2TRFLAG_HITCORPSES, g_g2TraceLod.integer ); } else { trap_Trace( &trace, traceOrg, kickMins, kickMaxs, traceEnd, ent->s.number, MASK_SHOT ); } //G_TestLine(traceOrg, traceEnd, 0x0000ff, 5000); if ( trace.fraction < 1.0f && !trace.startsolid && !trace.allsolid ) { if (ent->client->jediKickTime > level.time) { if (trace.entityNum == ent->client->jediKickIndex) { //we are hitting the same ent we last hit in this same anim, don't hit it again return NULL; } } ent->client->jediKickIndex = trace.entityNum; ent->client->jediKickTime = level.time + ent->client->ps.legsTimer; hitEnt = &g_entities[trace.entityNum]; //FIXME: regardless of what we hit, do kick hit sound and impact effect //G_PlayEffect( "misc/kickHit", trace.endpos, trace.plane.normal ); if ( ent->client->ps.torsoAnim == BOTH_A7_HILT ) { G_Sound( ent, CHAN_AUTO, G_SoundIndex( "sound/movers/objects/saber_slam" ) ); } else { G_Sound( ent, CHAN_AUTO, G_SoundIndex( va( "sound/weapons/melee/punch%d", Q_irand( 1, 4 ) ) ) ); } if ( hitEnt->inuse ) {//we hit an entity //FIXME: don't hit same ent more than once per kick if ( hitEnt->takedamage ) {//hurt it if (hitEnt->client) { hitEnt->client->ps.otherKiller = ent->s.number; hitEnt->client->ps.otherKillerDebounceTime = level.time + 10000; hitEnt->client->ps.otherKillerTime = level.time + 10000; } if (d_saberKickTweak.integer) { G_Damage( hitEnt, ent, ent, kickDir, trace.endpos, kickDamage*0.2f, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); } else { G_Damage( hitEnt, ent, ent, kickDir, trace.endpos, kickDamage, DAMAGE_NO_KNOCKBACK, MOD_MELEE ); } } if ( hitEnt->client && !(hitEnt->client->ps.pm_flags&PMF_TIME_KNOCKBACK) //not already flying through air? Intended to stop multiple hits, but... && G_CanBeEnemy(ent, hitEnt) ) {//FIXME: this should not always work if ( hitEnt->health <= 0 ) {//we kicked a dead guy //throw harder - FIXME: no matter how hard I push them, they don't go anywhere... corpses use less physics??? // G_Throw( hitEnt, kickDir, kickPush*4 ); //see if we should play a better looking death on them // G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos ); //[KnockdownSys] //reenabled SP code since the knockdown code now based on the SP code again. G_Throw( hitEnt, kickDir, kickPush*4 ); //see if we should play a better looking death on them G_ThrownDeathAnimForDeathAnim( hitEnt, trace.endpos ); //G_TossTheMofo(hitEnt, kickDir, kickPush*4.0f); //[/KnockdownSys] } else { G_Throw( hitEnt, kickDir, kickPush ); if ( kickPush >= 75.0f && !Q_irand( 0, 2 ) ) { G_Knockdown( hitEnt, ent, kickDir, 300, qtrue ); } else { G_Knockdown( hitEnt, ent, kickDir, kickPush, qtrue ); } } } } } return (hitEnt); }
static void WP_FireConcussionAlt( gentity_t *ent ) {//a rail-gun-like beam int damage = weaponData[WP_CONCUSSION].altDamage, skip, traces = DISRUPTOR_ALT_TRACES; qboolean render_impact = qtrue; vec3_t start, end; vec3_t muzzle2, spot, dir; trace_t tr; gentity_t *traceEnt, *tent; float dist, shotDist, shotRange = 8192; qboolean hitDodged = qfalse; if (ent->s.number >= MAX_CLIENTS) { vec3_t angles; vectoangles(forwardVec, angles); angles[PITCH] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f angles[YAW] += ( crandom() * (CONC_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f));//was 0.5f AngleVectors(angles, forwardVec, vrightVec, up); } //Shove us backwards for half a second VectorMA( ent->client->ps.velocity, -200, forwardVec, ent->client->ps.velocity ); ent->client->ps.groundEntityNum = ENTITYNUM_NONE; if ( (ent->client->ps.pm_flags&PMF_DUCKED) ) {//hunkered down ent->client->ps.pm_time = 100; } else { ent->client->ps.pm_time = 250; } ent->client->ps.pm_flags |= PMF_TIME_KNOCKBACK|PMF_TIME_NOFRICTION; //FIXME: only if on ground? So no "rocket jump"? Or: (see next FIXME) //FIXME: instead, set a forced ucmd backmove instead of this sliding VectorCopy( muzzle, muzzle2 ); // making a backup copy // The trace start will originate at the eye so we can ensure that it hits the crosshair. if ( ent->NPC ) { switch ( g_spskill->integer ) { case 0: damage = CONC_ALT_NPC_DAMAGE_EASY; break; case 1: damage = CONC_ALT_NPC_DAMAGE_MEDIUM; break; case 2: default: damage = CONC_ALT_NPC_DAMAGE_HARD; break; } } VectorCopy( muzzle, start ); WP_TraceSetStart( ent, start, vec3_origin, vec3_origin ); skip = ent->s.number; // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) // { // // in overcharge mode, so doing double damage // damage *= 2; // } //Make it a little easier to hit guys at long range vec3_t shot_mins, shot_maxs; VectorSet( shot_mins, -1, -1, -1 ); VectorSet( shot_maxs, 1, 1, 1 ); for ( int i = 0; i < traces; i++ ) { VectorMA( start, shotRange, forwardVec, end ); //NOTE: if you want to be able to hit guys in emplaced guns, use "G2_COLLIDE, 10" instead of "G2_RETURNONHIT, 0" //alternately, if you end up hitting an emplaced_gun that has a sitter, just redo this one trace with the "G2_COLLIDE, 10" to see if we it the sitter //gi.trace( &tr, start, NULL, NULL, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 ); gi.trace( &tr, start, shot_mins, shot_maxs, end, skip, MASK_SHOT, G2_COLLIDE, 10 );//G2_RETURNONHIT, 0 ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { render_impact = qfalse; } if ( tr.entityNum == ent->s.number ) { // should never happen, but basically we don't want to consider a hit to ourselves? // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; #ifdef _DEBUG gi.Printf( "BAD! Concussion gun shot somehow traced back and hit the owner!\n" ); #endif continue; } // always render a shot beam, doing this the old way because I don't much feel like overriding the effect. //NOTE: let's just draw one beam at the end //tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT ); //tent->svFlags |= SVF_BROADCAST; //VectorCopy( muzzle2, tent->s.origin2 ); if ( tr.fraction >= 1.0f ) { // draw the beam but don't do anything else break; } traceEnt = &g_entities[tr.entityNum]; if ( traceEnt //&& traceEnt->NPC && ( traceEnt->s.weapon == WP_SABER || (traceEnt->client && (traceEnt->client->NPC_class == CLASS_BOBAFETT||traceEnt->client->NPC_class == CLASS_REBORN) ) ) ) {//FIXME: need a more reliable way to know we hit a jedi? hitDodged = Jedi_DodgeEvasion( traceEnt, ent, &tr, HL_NONE ); //acts like we didn't even hit him } if ( !hitDodged ) { if ( render_impact ) { if (( tr.entityNum < ENTITYNUM_WORLD && traceEnt->takedamage ) || !Q_stricmp( traceEnt->classname, "misc_model_breakable" ) || traceEnt->s.eType == ET_MOVER ) { // Create a simple impact type mark that doesn't last long in the world G_PlayEffect( G_EffectIndex( "concussion/alt_hit" ), tr.endpos, tr.plane.normal ); if ( traceEnt->client && LogAccuracyHit( traceEnt, ent )) {//NOTE: hitting multiple ents can still get you over 100% accuracy ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } int hitLoc = G_GetHitLocFromTrace( &tr, MOD_CONC_ALT ); qboolean noKnockBack = (traceEnt->flags&FL_NO_KNOCKBACK);//will be set if they die, I want to know if it was on *before* they died if ( traceEnt && traceEnt->client && traceEnt->client->NPC_class == CLASS_GALAKMECH ) {//hehe G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, 10, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT, hitLoc ); break; } G_Damage( traceEnt, ent, ent, forwardVec, tr.endpos, damage, DAMAGE_NO_KNOCKBACK|DAMAGE_NO_HIT_LOC, MOD_CONC_ALT, hitLoc ); //do knockback and knockdown manually if ( traceEnt->client ) {//only if we hit a client vec3_t pushDir; VectorCopy( forwardVec, pushDir ); if ( pushDir[2] < 0.2f ) { pushDir[2] = 0.2f; }//hmm, re-normalize? nah... //if ( traceEnt->NPC || Q_irand(0,g_spskill->integer+1) ) { if ( !noKnockBack ) {//knock-backable G_Throw( traceEnt, pushDir, 200 ); if ( traceEnt->client->NPC_class == CLASS_ROCKETTROOPER ) { traceEnt->client->ps.pm_time = Q_irand( 1500, 3000 ); } } if ( traceEnt->health > 0 ) {//alive if ( G_HasKnockdownAnims( traceEnt ) ) {//knock-downable G_Knockdown( traceEnt, ent, pushDir, 400, qtrue ); } } } } if ( traceEnt->s.eType == ET_MOVER ) {//stop the traces on any mover break; } } else { // we only make this mark on things that can't break or move tent = G_TempEntity( tr.endpos, EV_CONC_ALT_MISS ); tent->svFlags |= SVF_BROADCAST; VectorCopy( tr.plane.normal, tent->pos1 ); break; // hit solid, but doesn't take damage, so stop the shot...we _could_ allow it to shoot through walls, might be cool? } } else // not rendering impact, must be a skybox or other similar thing? { break; // don't try anymore traces } } // Get ready for an attempt to trace through another person VectorCopy( tr.endpos, muzzle2 ); VectorCopy( tr.endpos, start ); skip = tr.entityNum; hitDodged = qfalse; } //just draw one beam all the way to the end tent = G_TempEntity( tr.endpos, EV_CONC_ALT_SHOT ); tent->svFlags |= SVF_BROADCAST; VectorCopy( muzzle, tent->s.origin2 ); // now go along the trail and make sight events VectorSubtract( tr.endpos, muzzle, dir ); shotDist = VectorNormalize( dir ); //FIXME: if shoot *really* close to someone, the alert could be way out of their FOV for ( dist = 0; dist < shotDist; dist += 64 ) { //FIXME: on a really long shot, this could make a LOT of alerts in one frame... VectorMA( muzzle, dist, dir, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); //FIXME: creates *way* too many effects, make it one effect somehow? G_PlayEffect( G_EffectIndex( "concussion/alt_ring" ), spot, forwardVec ); } //FIXME: spawn a temp ent that continuously spawns sight alerts here? And 1 sound alert to draw their attention? VectorMA( start, shotDist-4, forwardVec, spot ); AddSightEvent( ent, spot, 256, AEL_DISCOVERED, 50 ); G_PlayEffect( G_EffectIndex( "concussion/altmuzzle_flash" ), muzzle, forwardVec ); }