/* =========================================================================== Copyright (C) 2000 - 2013, Raven Software, Inc. Copyright (C) 2001 - 2013, Activision, Inc. Copyright (C) 2013 - 2015, OpenJK contributors This file is part of the OpenJK source code. OpenJK is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License version 2 as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, see . =========================================================================== */ #include "g_local.h" #include "b_local.h" #include "g_functions.h" #include "wp_saber.h" #include "w_local.h" #include "bg_local.h" #include //------------------- // Wookiee Bowcaster //------------------- //--------------------------------------------------------- static void WP_BowcasterMainFire( gentity_t *ent ) //--------------------------------------------------------- { int damage = weaponData[WP_BOWCASTER].damage, count; float vel; vec3_t angs, forward, dir, start; gentity_t *missile; if ( BG_UseVRPosition(ent)) { BG_CalculateVRWeaponPosition(muzzle, angs); AngleVectors(angs, forward, NULL, NULL); } else { VectorCopy(forwardVec, forward); } VectorCopy( muzzle, start ); WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall // Do the damages if ( ent->s.number != 0 ) { if ( g_spskill->integer == 0 ) { damage = BOWCASTER_NPC_DAMAGE_EASY; } else if ( g_spskill->integer == 1 ) { damage = BOWCASTER_NPC_DAMAGE_NORMAL; } else { damage = BOWCASTER_NPC_DAMAGE_HARD; } } count = ( level.time - ent->client->ps.weaponChargeTime ) / BOWCASTER_CHARGE_UNIT; if ( count < 1 ) { count = 1; } else if ( count > 5 ) { count = 5; } if ( !(count & 1 )) { // if we aren't odd, knock us down a level count--; } // 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; // } WP_MissileTargetHint(ent, start, forwardVec); for ( int i = 0; i < count; i++ ) { // create a range of different velocities vel = BOWCASTER_VELOCITY * ( Q_flrand(-1.0f, 1.0f) * BOWCASTER_VEL_RANGE + 1.0f ); vectoangles( forward, angs ); if ( !(ent->client->ps.forcePowersActive&(1<client->ps.forcePowerLevel[FP_SEE] < FORCE_LEVEL_2 ) {//force sight 2+ gives perfect aim //FIXME: maybe force sight level 3 autoaims some? // add some slop to the fire direction angs[PITCH] += Q_flrand(-1.0f, 1.0f) * BOWCASTER_ALT_SPREAD * 0.2f; angs[YAW] += ((i+0.5f) * BOWCASTER_ALT_SPREAD - count * 0.5f * BOWCASTER_ALT_SPREAD ); if ( ent->NPC ) { angs[PITCH] += ( Q_flrand(-1.0f, 1.0f) * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) ); angs[YAW] += ( Q_flrand(-1.0f, 1.0f) * (BLASTER_NPC_SPREAD+(6-ent->NPC->currentAim)*0.25f) ); } } AngleVectors( angs, dir, NULL, NULL ); vec3_t rotatedDir; VectorRotateAroundAxis(dir, forward, vr->weaponangles[ANGLES_ADJUSTED][ROLL], rotatedDir); missile = CreateMissile( start, rotatedDir, vel, 10000, ent ); missile->classname = "bowcaster_proj"; missile->s.weapon = WP_BOWCASTER; VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); // if ( ent->client && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > 0 && ent->client->ps.powerups[PW_WEAPON_OVERCHARGE] > cg.time ) // { // missile->flags |= FL_OVERCHARGED; // } missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BOWCASTER; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; missile->splashDamage = weaponData[WP_BOWCASTER].splashDamage; missile->splashRadius = weaponData[WP_BOWCASTER].splashRadius; // we don't want it to bounce missile->bounceCount = 0; ent->client->sess.missionStats.shotsFired++; } } //--------------------------------------------------------- static void WP_BowcasterAltFire( gentity_t *ent ) //--------------------------------------------------------- { vec3_t start, forward, angs; int damage = weaponData[WP_BOWCASTER].altDamage; if ( BG_UseVRPosition(ent)) { BG_CalculateVRWeaponPosition(start, angs); AngleVectors(angs, forward, NULL, NULL); } else { VectorCopy( muzzle, start ); VectorCopy( forwardVec, forward ); } WP_TraceSetStart( ent, start, vec3_origin, vec3_origin );//make sure our start point isn't on the other side of a wall WP_MissileTargetHint(ent, start, forward); gentity_t *missile = CreateMissile( start, forward, BOWCASTER_VELOCITY, 10000, ent, qtrue ); missile->classname = "bowcaster_alt_proj"; missile->s.weapon = WP_BOWCASTER; // Do the damages if ( ent->s.number != 0 ) { if ( g_spskill->integer == 0 ) { damage = BOWCASTER_NPC_DAMAGE_EASY; } else if ( g_spskill->integer == 1 ) { damage = BOWCASTER_NPC_DAMAGE_NORMAL; } else { damage = BOWCASTER_NPC_DAMAGE_HARD; } } VectorSet( missile->maxs, BOWCASTER_SIZE, BOWCASTER_SIZE, BOWCASTER_SIZE ); VectorScale( missile->maxs, -1, missile->mins ); // 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 // missile->flags |= FL_OVERCHARGED; // damage *= 2; // } missile->s.eFlags |= EF_BOUNCE; missile->bounceCount = 3; missile->damage = damage; missile->dflags = DAMAGE_DEATH_KNOCKBACK; missile->methodOfDeath = MOD_BOWCASTER_ALT; missile->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER; missile->splashDamage = weaponData[WP_BOWCASTER].altSplashDamage; missile->splashRadius = weaponData[WP_BOWCASTER].altSplashRadius; } //--------------------------------------------------------- void WP_FireBowcaster( gentity_t *ent, qboolean alt_fire ) //--------------------------------------------------------- { if ( alt_fire ) { WP_BowcasterAltFire( ent ); } else { WP_BowcasterMainFire( ent ); } }