void cashspawn_think( edict_t *self ) { edict_t *cash; if ((num_cash_items > MAX_CASH_ITEMS) || (level.modeset == MATCHSETUP) || (level.modeset == MATCHCOUNT) || (level.modeset == PREGAME)) { self->nextthink = level.time + self->delay; return; } // spawn some money cash = G_Spawn(); VectorCopy( self->s.origin, cash->s.origin ); cash->movetype = MOVETYPE_BOUNCE; cash->solid = SOLID_TRIGGER; AngleVectors( self->s.angles, cash->velocity, NULL, NULL ); VectorScale( cash->velocity, self->speed, cash->velocity ); // randomize the velocity a bit VectorAdd( cash->velocity, tv( crandom()*self->speed*0.3, crandom()*self->speed*0.3, crandom()*self->speed*0.15 ), cash->velocity ); cash->s.renderfx2 |= RF2_NOSHADOW; // FIXME: doh this doesn't work, need to spawn actual item's, so the HUD is updated automatically when picking up if (!strcmp(self->type, "cashroll")) { // small dollar notes cash->s.modelindex = gi.modelindex( "models/pu_icon/cash/tris.md2" ); cash->gravity = 0.1 + random()*0.5; cash->think = cashroll_animate; cash->nextthink = level.time + 0.1; cash->s.angles[PITCH] = 10; VectorSet( cash->avelocity, 0, 10000 * cash->gravity, 0 ); VectorSet( cash->mins, -4, -4, -15 ); VectorSet( cash->maxs, 4, 4, -13 ); cash->item = FindItem("Cash"); cash->currentcash = CASH_ROLL; cash->touch = cash_touch; cash->timestamp = level.time + 60; cash->think = cashroll_animate; cash->nextthink = level.time + 0.1; } else { cash->s.modelindex = gi.modelindex( "models/pu_icon/money/money_sm.md2" ); cash->gravity = 1.0; VectorSet( cash->mins, -12, -12, -15 ); VectorSet( cash->maxs, 12, 12, 10 ); cash->item = FindItem("Small Cash Bag"); cash->currentcash = CASH_BAG; cash->touch = cash_touch; cash->think = cash_kill; cash->nextthink = level.time + 60; } num_cash_items++; self->nextthink = level.time + self->delay; }
/* ================ G_RunItem ================ */ void G_RunItem( gentity_t *ent ) { vec3_t origin; trace_t tr; int contents; int mask; // if groundentity has been set to -1, it may have been pushed off an edge if ( ent->s.groundEntityNum == ENTITYNUM_NONE ) { if ( ent->s.pos.trType != TR_GRAVITY ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; } } if ( ent->s.pos.trType == TR_STATIONARY ) { // check think function G_RunThink( ent ); if ( !g_gravity->value ) { ent->s.pos.trType = TR_GRAVITY; ent->s.pos.trTime = level.time; ent->s.pos.trDelta[0] += crandom() * 40.0f; // I dunno, just do this?? ent->s.pos.trDelta[1] += crandom() * 40.0f; ent->s.pos.trDelta[2] += random() * 20.0f; } return; } // get current position EvaluateTrajectory( &ent->s.pos, level.time, origin ); // trace a line from the previous position to the current position if ( ent->clipmask ) { mask = ent->clipmask; } else { mask = MASK_SOLID|CONTENTS_PLAYERCLIP;//shouldn't be able to get anywhere player can't } int ignore = ENTITYNUM_NONE; if ( ent->owner ) { ignore = ent->owner->s.number; } else if ( ent->activator ) { ignore = ent->activator->s.number; } gi.trace( &tr, ent->currentOrigin, ent->mins, ent->maxs, origin, ignore, mask, G2_NOCOLLIDE, 0 ); VectorCopy( tr.endpos, ent->currentOrigin ); if ( tr.startsolid ) { tr.fraction = 0; } gi.linkentity( ent ); // FIXME: avoid this for stationary? // check think function G_RunThink( ent ); if ( tr.fraction == 1 ) { if ( g_gravity->value <= 0 ) { if ( ent->s.apos.trType != TR_LINEAR ) { VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trDelta[1] = Q_flrand( -300, 300 ); ent->s.apos.trDelta[0] = Q_flrand( -10, 10 ); ent->s.apos.trDelta[2] = Q_flrand( -10, 10 ); ent->s.apos.trTime = level.time; } } //friction in zero-G if ( !g_gravity->value ) { float friction = 0.975f; /*friction -= ent->mass/1000.0f; if ( friction < 0.1 ) { friction = 0.1f; } */ VectorScale( ent->s.pos.trDelta, friction, ent->s.pos.trDelta ); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } return; } // if it is in a nodrop volume, remove it contents = gi.pointcontents( ent->currentOrigin, -1 ); if ( contents & CONTENTS_NODROP ) { G_FreeEntity( ent ); return; } if ( !tr.startsolid ) { G_BounceItem( ent, &tr ); } }
/* ============== CG_DamageFeedback ============== */ void CG_DamageFeedback(int yawByte, int pitchByte, int damage) { float left, front, up; float kick; int health; float scale; vec3_t dir; vec3_t angles; float dist; float yaw, pitch; int slot; viewDamage_t *vd; // show the attacking player's head and name in corner cg.attackerTime = cg.time; // the lower on health you are, the greater the view kick will be health = cg.snap->ps.stats[STAT_HEALTH]; if (health < 40) { scale = 1; } else { scale = 40.0 / health; } kick = damage * scale; if (kick < 5) { kick = 5; } if (kick > 10) { kick = 10; } // find a free slot for (slot = 0; slot < MAX_VIEWDAMAGE; slot++) { if (cg.viewDamage[slot].damageTime + cg.viewDamage[slot].damageDuration < cg.time) { break; } } if (slot == MAX_VIEWDAMAGE) { return; // no free slots, never override or splats will suddenly disappear } vd = &cg.viewDamage[slot]; // if yaw and pitch are both 255, make the damage always centered (falling, etc) if (yawByte == 255 && pitchByte == 255) { vd->damageX = 0; vd->damageY = 0; cg.v_dmg_roll = 0; cg.v_dmg_pitch = -kick; } else { // positional pitch = pitchByte / 255.0 * 360; yaw = yawByte / 255.0 * 360; angles[PITCH] = pitch; angles[YAW] = yaw; angles[ROLL] = 0; AngleVectors(angles, dir, NULL, NULL); VectorSubtract(vec3_origin, dir, dir); front = DotProduct(dir, cg.refdef.viewaxis[0]); left = DotProduct(dir, cg.refdef.viewaxis[1]); up = DotProduct(dir, cg.refdef.viewaxis[2]); dir[0] = front; dir[1] = left; dir[2] = 0; dist = VectorLength(dir); if (dist < 0.1) { dist = 0.1; } cg.v_dmg_roll = kick * left; cg.v_dmg_pitch = -kick * front; if (front <= 0.1) { front = 0.1; } vd->damageX = crandom() * 0.3 + -left / front; vd->damageY = crandom() * 0.3 + up / dist; } // clamp the position if (vd->damageX > 1.0) { vd->damageX = 1.0; } if (vd->damageX < -1.0) { vd->damageX = -1.0; } if (vd->damageY > 1.0) { vd->damageY = 1.0; } if (vd->damageY < -1.0) { vd->damageY = -1.0; } // don't let the screen flashes vary as much if (kick > 10) { kick = 10; } vd->damageValue = kick; cg.v_dmg_time = cg.time + DAMAGE_TIME; vd->damageTime = cg.snap->serverTime; vd->damageDuration = kick * 50 * (1 + 2 * (!vd->damageX && !vd->damageY)); cg.damageTime = cg.snap->serverTime; cg.damageIndex = slot; }
//------------------------------------ void Seeker_Strafe( void ) { int side; vec3_t end, right, dir; trace_t tr; if ( random() > 0.7f || !NPC->enemy || !NPC->enemy->client ) { // Do a regular style strafe AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL ); // Pick a random strafe direction, then check to see if doing a strafe would be // reasonably valid side = ( rand() & 1 ) ? -1 : 1; VectorMA( NPC->currentOrigin, SEEKER_STRAFE_DIS * side, right, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID, (EG2_Collision)0, 0 ); // Close enough if ( tr.fraction > 0.9f ) { float vel = SEEKER_STRAFE_VEL; float upPush = SEEKER_UPWARD_PUSH; if ( NPC->client->NPC_class != CLASS_BOBAFETT ) { G_Sound( NPC, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); } else { vel *= 3.0f; upPush *= 4.0f; } VectorMA( NPC->client->ps.velocity, vel*side, right, NPC->client->ps.velocity ); // Add a slight upward push NPC->client->ps.velocity[2] += upPush; NPCInfo->standTime = level.time + 1000 + random() * 500; } } else { // Do a strafe to try and keep on the side of their enemy AngleVectors( NPC->enemy->client->renderInfo.eyeAngles, dir, right, NULL ); // Pick a random side side = ( rand() & 1 ) ? -1 : 1; float stDis = SEEKER_STRAFE_DIS; if ( NPC->client->NPC_class == CLASS_BOBAFETT ) { stDis *= 2.0f; } VectorMA( NPC->enemy->currentOrigin, stDis * side, right, end ); // then add a very small bit of random in front of/behind the player action VectorMA( end, crandom() * 25, dir, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID, (EG2_Collision)0, 0 ); // Close enough if ( tr.fraction > 0.9f ) { VectorSubtract( tr.endpos, NPC->currentOrigin, dir ); dir[2] *= 0.25; // do less upward change float dis = VectorNormalize( dir ); // Try to move the desired enemy side VectorMA( NPC->client->ps.velocity, dis, dir, NPC->client->ps.velocity ); float upPush = SEEKER_UPWARD_PUSH; if ( NPC->client->NPC_class != CLASS_BOBAFETT ) { G_Sound( NPC, G_SoundIndex( "sound/chars/seeker/misc/hiss" )); } else { upPush *= 4.0f; } // Add a slight upward push NPC->client->ps.velocity[2] += upPush; NPCInfo->standTime = level.time + 2500 + random() * 500; } } }
void pendulum_die(edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { // Mostly copied from func_explosive_explode. We can't use that function because // the origin is a bit different. vec3_t origin; vec3_t forward, left, up; vec3_t chunkorigin; vec3_t size; int count; int mass; // Particles originate from business end of pendulum AngleVectors(self->s.angles, forward, left, up); VectorScale(forward, self->move_origin[0], forward); VectorScale(left, -self->move_origin[1], left); VectorScale(up, self->move_origin[2], up); VectorAdd(self->s.origin, forward, origin); VectorAdd(origin, left, origin); VectorAdd(origin, up, origin); self->mass *= 2; self->takedamage = DAMAGE_NO; VectorSubtract(origin, self->enemy->s.origin, self->velocity); VectorNormalize(self->velocity); VectorScale(self->velocity, 150, self->velocity); // start chunks towards the center VectorScale(size, 0.5, size); mass = self->mass; if (!mass) { mass = 75; } // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) { count = 8; } while (count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris(self, "models/objects/debris1/tris.md2", 1, chunkorigin, 0, 0); } } // small chunks count = mass / 25; if (count > 16) { count = 16; } while (count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris(self, "models/objects/debris2/tris.md2", 2, chunkorigin, 0, 0); } G_FreeEdict(self); }
/* ====================== CG_AddParticles ====================== */ void CG_ParticleSnowFlurry (qhandle_t pshader, centity_t *cent) { cparticle_t *p; qboolean turb = qtrue; if (!pshader) CG_Printf ("CG_ParticleSnowFlurry pshader == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->color = 0; p->alpha = 0.90f; p->alphavel = 0; p->start = cent->currentState.origin2[0]; p->end = cent->currentState.origin2[1]; p->endtime = cg.time + cent->currentState.time; p->startfade = cg.time + cent->currentState.time2; p->pshader = pshader; if (rand()%100 > 90) { p->height = 32; p->width = 32; p->alpha = 0.10f; } else { p->height = 1; p->width = 1; } p->vel[2] = -20; p->type = P_WEATHER_FLURRY; if (turb) p->vel[2] = -10; VectorCopy(cent->currentState.origin, p->org); p->org[0] = p->org[0]; p->org[1] = p->org[1]; p->org[2] = p->org[2]; p->vel[0] = p->vel[1] = 0; p->accel[0] = p->accel[1] = p->accel[2] = 0; p->vel[0] += cent->currentState.angles[0] * 32 + (crandom() * 16); p->vel[1] += cent->currentState.angles[1] * 32 + (crandom() * 16); p->vel[2] += cent->currentState.angles[2]; if (turb) { p->accel[0] = crandom () * 16; p->accel[1] = crandom () * 16; } }
/*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON This should be renamed trigger_timer... Repeatedly fires its targets. Can be turned on or off by using. "wait" base time between triggering all targets, default is 1 "random" wait variance, default is 0 so, the basic time between firing is a random time between (wait - random) and (wait + random) */ void func_timer_think( gentity_t *self ) { G_UseTargets (self, self->activator); // set time before next firing self->nextthink = level.time + 1000 * ( self->wait + crandom() * self->random ); }
//--------------------------------------------------------- void rocketThink( gentity_t *ent ) //--------------------------------------------------------- { vec3_t newdir, targetdir, up={0,0,1}, right; vec3_t org; float dot, dot2; if ( ent->disconnectDebounceTime && ent->disconnectDebounceTime < level.time ) {//time's up, we're done, remove us if ( ent->lockCount ) {//explode when die WP_ExplosiveDie( ent, ent->owner, ent->owner, 0, MOD_UNKNOWN, 0, HL_NONE ); } else {//just remove when die G_FreeEntity( ent ); } return; } if ( ent->enemy && ent->enemy->inuse ) { float vel = (ent->spawnflags&1)?ent->speed:ROCKET_VELOCITY; float newDirMult = ent->angle?ent->angle*2.0f:1.0f; float oldDirMult = ent->angle?(1.0f-ent->angle)*2.0f:1.0f; if ( (ent->spawnflags&1) ) {//vehicle rocket if ( ent->enemy->client && ent->enemy->client->NPC_class == CLASS_VEHICLE ) {//tracking another vehicle if ( ent->enemy->client->ps.speed+ent->speed > vel ) { vel = ent->enemy->client->ps.speed+ent->speed; } } } VectorCopy( ent->enemy->currentOrigin, org ); org[2] += (ent->enemy->mins[2] + ent->enemy->maxs[2]) * 0.5f; if ( ent->enemy->client ) { switch( ent->enemy->client->NPC_class ) { case CLASS_ATST: org[2] += 80; break; case CLASS_MARK1: org[2] += 40; break; case CLASS_PROBE: org[2] += 60; break; default: break; } if ( !TIMER_Done( ent->enemy, "flee" ) ) { TIMER_Set( ent->enemy, "rocketChasing", 500 ); } } VectorSubtract( org, ent->currentOrigin, targetdir ); VectorNormalize( targetdir ); // Now the rocket can't do a 180 in space, so we'll limit the turn to about 45 degrees. dot = DotProduct( targetdir, ent->movedir ); // a dot of 1.0 means right-on-target. if ( dot < 0.0f ) { // Go in the direction opposite, start a 180. CrossProduct( ent->movedir, up, right ); dot2 = DotProduct( targetdir, right ); if ( dot2 > 0 ) { // Turn 45 degrees right. VectorMA( ent->movedir, 0.3f*newDirMult, right, newdir ); } else { // Turn 45 degrees left. VectorMA(ent->movedir, -0.3f*newDirMult, right, newdir); } // Yeah we've adjusted horizontally, but let's split the difference vertically, so we kinda try to move towards it. newdir[2] = ( (targetdir[2]*newDirMult) + (ent->movedir[2]*oldDirMult) ) * 0.5; // slowing down coupled with fairly tight turns can lead us to orbit an enemy..looks bad so don't do it! // vel *= 0.5f; } else if ( dot < 0.70f ) { // Still a bit off, so we turn a bit softer VectorMA( ent->movedir, 0.5f*newDirMult, targetdir, newdir ); } else { // getting close, so turn a bit harder VectorMA( ent->movedir, 0.9f*newDirMult, targetdir, newdir ); } // add crazy drunkenness for ( int i = 0; i < 3; i++ ) { newdir[i] += crandom() * ent->random * 0.25f; } // decay the randomness ent->random *= 0.9f; if ( ent->enemy->client && ent->enemy->client->ps.groundEntityNum != ENTITYNUM_NONE ) {//tracking a client who's on the ground, aim at the floor...? // Try to crash into the ground if we get close enough to do splash damage float dis = Distance( ent->currentOrigin, org ); if ( dis < 128 ) { // the closer we get, the more we push the rocket down, heh heh. newdir[2] -= (1.0f - (dis / 128.0f)) * 0.6f; } } VectorNormalize( newdir ); VectorScale( newdir, vel * 0.5f, ent->s.pos.trDelta ); VectorCopy( newdir, ent->movedir ); SnapVector( ent->s.pos.trDelta ); // save net bandwidth VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; } ent->nextthink = level.time + ROCKET_ALT_THINK_TIME; // Nothing at all spectacular happened, continue. return; }
/*QUAKED func_explosive (0 .5 .8) ? Trigger_Spawn ANIMATED ANIMATED_FAST Any brush that you want to explode or break apart. If you want an ex0plosion, set dmg and it will do a radius explosion of that amount at the center of the bursh. If targeted it will not be shootable. health defaults to 100. mass defaults to 75. This determines how much debris is emitted when it explodes. You get one large chunk per 100 of mass (up to 8) and one small chunk per 25 of mass (up to 16). So 800 gives the most. */ void func_explosive_explode (edict_t *self, edict_t *inflictor, edict_t *attacker, int damage, vec3_t point) { vec3_t origin; vec3_t chunkorigin; vec3_t size; int count; int mass; // bmodel origins are (0 0 0), we need to adjust that here VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); VectorCopy (origin, self->s.origin); self->takedamage = DAMAGE_NO; if (self->dmg) T_RadiusDamage (self, attacker, self->dmg, NULL, self->dmg+40, MOD_EXPLOSIVE); VectorSubtract (self->s.origin, inflictor->s.origin, self->velocity); VectorNormalize (self->velocity); VectorScale (self->velocity, 150, self->velocity); // start chunks towards the center VectorScale (size, 0.5, size); mass = self->mass; if (!mass) mass = 75; // big chunks if (mass >= 100) { count = mass / 100; if (count > 8) count = 8; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", 1, chunkorigin); } } // small chunks count = mass / 25; if (count > 16) count = 16; while(count--) { chunkorigin[0] = origin[0] + crandom() * size[0]; chunkorigin[1] = origin[1] + crandom() * size[1]; chunkorigin[2] = origin[2] + crandom() * size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", 2, chunkorigin); } G_UseTargets (self, attacker); if (self->dmg) BecomeExplosion1 (self); else G_FreeEdict (self); }
void soldier_fire (edict_t *self, int flash_number) { vec3_t start; vec3_t forward, right, up; vec3_t aim; vec3_t dir; vec3_t end; float r, u; int flash_index; if ((self->s.skinnum % 6) < 2) flash_index = blaster_flash[flash_number]; else if ((self->s.skinnum % 6) < 4) flash_index = shotgun_flash[flash_number]; else flash_index = machinegun_flash[flash_number]; AngleVectors (self->s.angles, forward, right, NULL); G_ProjectSource (self->s.origin, monster_flash_offset[flash_index], forward, right, start); if (flash_number == 5 || flash_number == 6) { VectorCopy (forward, aim); } else { VectorCopy (self->enemy->s.origin, end); end[2] += self->enemy->viewheight; // Lazarus fog reduction of accuracy if(self->monsterinfo.visibility < FOG_CANSEEGOOD) { end[0] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[1] += crandom() * 640 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); end[2] += crandom() * 320 * (FOG_CANSEEGOOD - self->monsterinfo.visibility); } VectorSubtract (end, start, aim); // Lazarus: Accuracy is skill level dependent if(skill->value < 3) { vectoangles (aim, dir); AngleVectors (dir, forward, right, up); r = crandom()*(1000 - 333*skill->value); u = crandom()*(500 - 167*skill->value); VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); VectorSubtract (end, start, aim); } VectorNormalize (aim); } if ((self->s.skinnum % 6) <= 1) { // Lazarus: make bolt speed skill level dependent monster_fire_blaster (self, start, aim, 5, 600 + 100*skill->value, flash_index, EF_BLASTER, BLASTER_ORANGE); } else if ((self->s.skinnum % 6) <= 3) { monster_fire_shotgun (self, start, aim, 2, 1, DEFAULT_SHOTGUN_HSPREAD, DEFAULT_SHOTGUN_VSPREAD, DEFAULT_SHOTGUN_COUNT, flash_index); } else { if (!(self->monsterinfo.aiflags & AI_HOLD_FRAME)) self->monsterinfo.pausetime = level.time + (3 + rand() % 8) * FRAMETIME; monster_fire_bullet (self, start, aim, 2, 4, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, flash_index); if (level.time >= self->monsterinfo.pausetime) self->monsterinfo.aiflags &= ~AI_HOLD_FRAME; else self->monsterinfo.aiflags |= AI_HOLD_FRAME; } }
static qboolean CG_RainParticleGenerate( cg_atmosphericParticle_t *particle, vec3_t currvec, float currweight ) { // Attempt to 'spot' a raindrop somewhere below a sky texture. float angle, distance; float groundHeight, skyHeight; // int msec = trap_Milliseconds(); // n_generatetime++; angle = random() * 2*M_PI; distance = 20 + MAX_ATMOSPHERIC_DISTANCE * random(); particle->pos[0] = cg.refdef_current->vieworg[0] + sin(angle) * distance; particle->pos[1] = cg.refdef_current->vieworg[1] + cos(angle) * distance; // ydnar: choose a spawn point randomly between sky and ground skyHeight = BG_GetSkyHeightAtPoint( particle->pos ); if( skyHeight == MAX_ATMOSPHERIC_HEIGHT ) return qfalse; groundHeight = BG_GetSkyGroundHeightAtPoint( particle->pos ); if( groundHeight >= skyHeight ) return qfalse; particle->pos[2] = groundHeight + random() * (skyHeight - groundHeight); // make sure it doesn't fall from too far cause it then will go over our heads ('lower the ceiling') if( cg_atmFx.baseHeightOffset > 0 ) { if( particle->pos[2] - cg.refdef_current->vieworg[2] > cg_atmFx.baseHeightOffset ) { particle->pos[2] = cg.refdef_current->vieworg[2] + cg_atmFx.baseHeightOffset; if( particle->pos[2] < groundHeight ) { return qfalse; } } } // ydnar: rain goes in bursts { float maxActiveDrops; // every 10 seconds allow max raindrops maxActiveDrops = 0.50 * cg_atmFx.numDrops + 0.001 * cg_atmFx.numDrops * (10000 - (cg.time % 10000)); if( cg_atmFx.oldDropsActive > maxActiveDrops ) return qfalse; } CG_SetParticleActive( particle, ACT_FALLING ); particle->colour[0] = 0.6 + 0.2 * random() * 0xFF; particle->colour[1] = 0.6 + 0.2 * random() * 0xFF; particle->colour[2] = 0.6 + 0.2 * random() * 0xFF; VectorCopy( currvec, particle->delta ); particle->delta[2] += crandom() * 100; VectorCopy( particle->delta, particle->deltaNormalized ); VectorNormalizeFast( particle->deltaNormalized ); particle->height = ATMOSPHERIC_RAIN_HEIGHT + crandom() * 100; particle->weight = currweight; particle->effectshader = &cg_atmFx.effectshaders[0]; // particle->effectshader = &cg_atmFx.effectshaders[ (int) (random() * ( cg_atmFx.numEffectShaders - 1 )) ]; // generatetime += trap_Milliseconds() - msec; return( qtrue ); }
void FireTotem_think(edict_t *self, edict_t *caster) { int talentLevel; float chance; edict_t *target = NULL; //Totem should not work underwater (gee I wonder why). if(!self->waterlevel) { //Talent: Volcanic talentLevel = getTalentLevel(caster, TALENT_VOLCANIC); chance = 0.02 * talentLevel; //Find players in radius and attack them. while ((target = findclosestradius(target, self->s.origin, TOTEM_MAX_RANGE)) != NULL) { if (G_ValidTarget(self, target, true) && (self->s.origin[2]+64>target->s.origin[2])) { vec3_t forward, end; int damage = FIRETOTEM_DAMAGE_BASE + self->monsterinfo.level * FIRETOTEM_DAMAGE_MULT; int count = 10 + self->monsterinfo.level; int speed = 600; float val, dist; qboolean fireball=false; // copy target location G_EntMidPoint(target, end); // calculate distance to target dist = distance(end, self->s.origin); // move our target point based on projectile and enemy velocity VectorMA(end, (float)dist/speed, target->velocity, end); //Talent: Volcanic - chance to shoot a fireball if (talentLevel > 0 && chance > random()) fireball = true; // aim up if (fireball) val = ((dist/2048)*(dist/2048)*2048) + (end[2]-self->s.origin[2]);//4.4 else val = ((dist/512)*(dist/512)*512) + (end[2]-self->s.origin[2]); if (val < 0) val = 0; end[2] += val; // calculate direction vector to target VectorSubtract(end, self->s.origin, forward); VectorNormalize(forward); // don't fire in a perfectly straight line forward[1] += 0.05*crandom(); // spawn flames if (fireball) fire_fireball(self, self->s.origin, forward, 200, 125, 600, 5, 20);//4.4 else ThrowFlame(self, self->s.origin, forward, distance(self->s.origin, target->s.origin), speed, damage, GetRandom(2, 4)); self->lastsound = level.framenum; // refire delay self->delay = level.time + FRAMETIME; } } } }
void NPC_BSGM_Attack( void ) { //Don't do anything if we're hurt if ( NPC->painDebounceTime > level.time ) { NPC_UpdateAngles( qtrue, qtrue ); return; } #if 0 //FIXME: if killed enemy, use victory anim if ( NPC->enemy && NPC->enemy->health <= 0 && !NPC->enemy->s.number ) {//my enemy is dead if ( NPC->client->ps.torsoAnim == BOTH_STAND2TO1 ) { if ( NPC->client->ps.torsoTimer <= 500 ) { G_AddVoiceEvent( NPC, Q_irand( EV_VICTORY1, EV_VICTORY3 ), 3000 ); NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1START, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1START ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STARTGESTURE, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STARTGESTURE ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_TRIUMPHANT1STOP, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer += 500; NPC->client->ps.torsoTimer += 500; } } else if ( NPC->client->ps.torsoAnim == BOTH_TRIUMPHANT1STOP ) { if ( NPC->client->ps.torsoTimer <= 500 ) { NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_STAND1, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); NPC->client->ps.legsTimer = -1; NPC->client->ps.torsoTimer = -1; } } else if ( NPC->wait ) { if ( TIMER_Done( NPC, "gloatTime" ) ) { GM_StartGloat(); } else if ( DistanceHorizontalSquared( NPC->client->renderInfo.eyePoint, NPC->enemy->r.currentOrigin ) > 4096 && (NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) )//64 squared { NPCInfo->goalEntity = NPC->enemy; GM_Move(); } else {//got there GM_StartGloat(); } } NPC_FaceEnemy( qtrue ); NPC_UpdateAngles( qtrue, qtrue ); return; } #endif //If we don't have an enemy, just idle if ( NPC_CheckEnemyExt(qfalse) == qfalse || !NPC->enemy ) { NPC->enemy = NULL; NPC_BSGM_Patrol(); return; } enemyLOS4 = enemyCS4 = qfalse; move4 = qtrue; faceEnemy4 = qfalse; shoot4 = qfalse; hitAlly4 = qfalse; VectorClear( impactPos4 ); enemyDist4 = DistanceSquared( NPC->r.currentOrigin, NPC->enemy->r.currentOrigin ); //if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 || // NPC->client->ps.torsoAnim == BOTH_ATTACK5 ) if (0) { shoot4 = qfalse; if ( TIMER_Done( NPC, "smackTime" ) && !NPCInfo->blockedDebounceTime ) {//time to smack //recheck enemyDist4 and InFront if ( enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) ) { vec3_t smackDir; VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir ); smackDir[2] += 30; VectorNormalize( smackDir ); //hurt them G_Sound( NPC->enemy, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/skewerhit.wav" ) ); G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_ARMOR|DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); if ( NPC->client->ps.torsoAnim == BOTH_ATTACK4 ) {//smackdown int knockAnim = BOTH_KNOCKDOWN1; if ( BG_CrouchAnim( NPC->enemy->client->ps.legsAnim ) ) {//knockdown from crouch knockAnim = BOTH_KNOCKDOWN4; } //throw them smackDir[2] = 1; VectorNormalize( smackDir ); G_Throw( NPC->enemy, smackDir, 50 ); NPC_SetAnim( NPC->enemy, SETANIM_BOTH, knockAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } else {//uppercut //throw them G_Throw( NPC->enemy, smackDir, 100 ); //make them backflip NPC_SetAnim( NPC->enemy, SETANIM_BOTH, BOTH_KNOCKDOWN5, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } //done with the damage NPCInfo->blockedDebounceTime = 1; } } } else if ( NPC->lockCount ) //already shooting laser {//sometimes use the laser beam attack, but only after he's taken down our generator shoot4 = qfalse; if ( NPC->lockCount == 1 ) {//charging up if ( TIMER_Done( NPC, "beamDelay" ) ) {//time to start the beam int laserAnim; //if ( Q_irand( 0, 1 ) ) if (1) { laserAnim = BOTH_ATTACK2; } /* else { laserAnim = BOTH_ATTACK7; } */ NPC_SetAnim( NPC, SETANIM_BOTH, laserAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) ); //turn on beam effect NPC->lockCount = 2; G_PlayEffectID( G_EffectIndex("galak/trace_beam"), NPC->r.currentOrigin, vec3_origin ); NPC->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); if ( !NPCInfo->coverTarg ) {//for moving looping sound at end of trace NPCInfo->coverTarg = G_Spawn(); if ( NPCInfo->coverTarg ) { G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint ); NPCInfo->coverTarg->r.svFlags |= SVF_BROADCAST; NPCInfo->coverTarg->s.loopSound = G_SoundIndex( "sound/weapons/galak/lasercutting.wav" ); } } } } else {//in the actual attack now if ( NPC->client->ps.torsoTimer <= 0 ) {//attack done! NPC->lockCount = 0; G_FreeEntity( NPCInfo->coverTarg ); NPC->s.loopSound = 0; #if 0 NPC_SetAnim( NPC, SETANIM_TORSO, TORSO_DROPWEAP2, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); #endif TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer ); } else {//attack still going //do the trace and damage trace_t trace; vec3_t end, mins={-3,-3,-3}, maxs={3,3,3}; VectorMA( NPC->client->renderInfo.muzzlePoint, 1024, NPC->client->renderInfo.muzzleDir, end ); trap_Trace( &trace, NPC->client->renderInfo.muzzlePoint, mins, maxs, end, NPC->s.number, MASK_SHOT ); if ( trace.allsolid || trace.startsolid ) {//oops, in a wall if ( NPCInfo->coverTarg ) { G_SetOrigin( NPCInfo->coverTarg, NPC->client->renderInfo.muzzlePoint ); } } else {//clear if ( trace.fraction < 1.0f ) {//hit something gentity_t *traceEnt = &g_entities[trace.entityNum]; if ( traceEnt && traceEnt->takedamage ) {//damage it G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) ); G_Damage( traceEnt, NPC, NPC, NPC->client->renderInfo.muzzleDir, trace.endpos, 10, 0, MOD_UNKNOWN ); } } if ( NPCInfo->coverTarg ) { G_SetOrigin( NPCInfo->coverTarg, trace.endpos ); } if ( !Q_irand( 0, 5 ) ) { G_SoundAtLoc( trace.endpos, CHAN_AUTO, G_SoundIndex( "sound/weapons/galak/laserdamage.wav" ) ); } } } } } else {//Okay, we're not in a special attack, see if we should switch weapons or start a special attack /* if ( NPC->s.weapon == WP_REPEATER && !(NPCInfo->scriptFlags & SCF_ALT_FIRE)//using rapid-fire && NPC->enemy->s.weapon == WP_SABER //enemy using saber && NPC->client && (NPC->client->ps.saberEventFlags&SEF_DEFLECTED) && !Q_irand( 0, 50 ) ) {//he's deflecting my shots, switch to the laser or the lob fire for a while TIMER_Set( NPC, "noRapid", Q_irand( 2000, 6000 ) ); NPCInfo->scriptFlags |= SCF_ALT_FIRE; NPC->alt_fire = qtrue; if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && (Q_irand( 0, 1 )||enemyDist4 < MAX_LOB_DIST_SQUARED) ) {//shield down, use laser NPC_GM_StartLaser(); } } else*/ if (// !NPC->client->ps.powerups[PW_GALAK_SHIELD] 1 //rwwFIXMEFIXME: just act like the shield is down til the effects and stuff are done && enemyDist4 < MELEE_DIST_SQUARED && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) && NPC->enemy->localAnimIndex <= 1 )//within 80 and in front {//our shield is down, and enemy within 80, if very close, use melee attack to slap away if ( TIMER_Done( NPC, "attackDelay" ) ) { //animate me int swingAnim = BOTH_ATTACK1; #if 0 if ( NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH ) {//generator down, use random melee swingAnim = Q_irand( BOTH_ATTACK4, BOTH_ATTACK5 );//smackdown or uppercut } else {//always knock-away swingAnim = BOTH_ATTACK5;//uppercut } #endif //FIXME: swing sound NPC_SetAnim( NPC, SETANIM_BOTH, swingAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer + Q_irand( 1000, 3000 ) ); //delay the hurt until the proper point in the anim TIMER_Set( NPC, "smackTime", 600 ); NPCInfo->blockedDebounceTime = 0; //FIXME: say something? } } else if ( !NPC->lockCount && NPC->locationDamage[HL_GENERIC1] > GENERATOR_HEALTH && TIMER_Done( NPC, "attackDelay" ) && InFront( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, NPC->client->ps.viewangles, 0.3f ) && ((!Q_irand( 0, 10*(2-g_spskill.integer))&& enemyDist4 > MIN_LOB_DIST_SQUARED&& enemyDist4 < MAX_LOB_DIST_SQUARED) ||(!TIMER_Done( NPC, "noLob" )&&!TIMER_Done( NPC, "noRapid" ))) && NPC->enemy->s.weapon != WP_TURRET ) {//sometimes use the laser beam attack, but only after he's taken down our generator shoot4 = qfalse; NPC_GM_StartLaser(); } else if ( enemyDist4 < MIN_LOB_DIST_SQUARED && (NPC->enemy->s.weapon != WP_TURRET || Q_stricmp( "PAS", NPC->enemy->classname )) && TIMER_Done( NPC, "noRapid" ) )//256 {//enemy within 256 if ( (NPC->client->ps.weapon == WP_REPEATER) && (NPCInfo->scriptFlags & SCF_ALT_FIRE) ) {//shooting an explosive, but enemy too close, switch to primary fire NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; NPC->alt_fire = qfalse; //FIXME: use weap raise & lower anims NPC_ChangeWeapon( WP_REPEATER ); } } else if ( (enemyDist4 > MAX_LOB_DIST_SQUARED || (NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ))) && TIMER_Done( NPC, "noLob" ) )//448 {//enemy more than 448 away and we are ready to try lob fire again if ( (NPC->client->ps.weapon == WP_REPEATER) && !(NPCInfo->scriptFlags & SCF_ALT_FIRE) ) {//enemy far enough away to use lobby explosives NPCInfo->scriptFlags |= SCF_ALT_FIRE; NPC->alt_fire = qtrue; //FIXME: use weap raise & lower anims NPC_ChangeWeapon( WP_REPEATER ); } } } //can we see our target? if ( NPC_ClearLOS4( NPC->enemy ) ) { NPCInfo->enemyLastSeenTime = level.time;//used here for aim debouncing, not always a clear LOS enemyLOS4 = qtrue; if ( NPC->client->ps.weapon == WP_NONE ) { enemyCS4 = qfalse;//not true, but should stop us from firing NPC_AimAdjust( -1 );//adjust aim worse longer we have no weapon } else {//can we shoot our target? if ( ((NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE))) && enemyDist4 < MIN_LOB_DIST_SQUARED )//256 { enemyCS4 = qfalse;//not true, but should stop us from firing hitAlly4 = qtrue;//us! //FIXME: if too close, run away! } else { int hit = NPC_ShotEntity( NPC->enemy, impactPos4 ); gentity_t *hitEnt = &g_entities[hit]; if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) || ( hitEnt && hitEnt->takedamage ) ) {//can hit enemy or will hit glass or other breakable, so shoot anyway enemyCS4 = qtrue; NPC_AimAdjust( 2 );//adjust aim better longer we have clear shot at enemy VectorCopy( NPC->enemy->r.currentOrigin, NPCInfo->enemyLastSeenLocation ); } else {//Hmm, have to get around this bastard NPC_AimAdjust( 1 );//adjust aim better longer we can see enemy if ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->playerTeam ) {//would hit an ally, don't fire!!! hitAlly4 = qtrue; } else {//Check and see where our shot *would* hit... if it's not close to the enemy (within 256?), then don't fire } } } } } else if ( trap_InPVS( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin ) ) { int hit; gentity_t *hitEnt; if ( TIMER_Done( NPC, "talkDebounce" ) && !Q_irand( 0, 10 ) ) { if ( NPCInfo->enemyCheckDebounceTime < 8 ) { int speech = -1; switch( NPCInfo->enemyCheckDebounceTime ) { case 0: case 1: case 2: speech = EV_CHASE1 + NPCInfo->enemyCheckDebounceTime; break; case 3: case 4: case 5: speech = EV_COVER1 + NPCInfo->enemyCheckDebounceTime-3; break; case 6: case 7: speech = EV_ESCAPING1 + NPCInfo->enemyCheckDebounceTime-6; break; } NPCInfo->enemyCheckDebounceTime++; if ( speech != -1 ) { G_AddVoiceEvent( NPC, speech, Q_irand( 3000, 5000 ) ); TIMER_Set( NPC, "talkDebounce", Q_irand( 5000, 7000 ) ); } } } NPCInfo->enemyLastSeenTime = level.time; hit = NPC_ShotEntity( NPC->enemy, impactPos4 ); hitEnt = &g_entities[hit]; if ( hit == NPC->enemy->s.number || ( hitEnt && hitEnt->client && hitEnt->client->playerTeam == NPC->client->enemyTeam ) || ( hitEnt && hitEnt->takedamage ) ) {//can hit enemy or will hit glass or other breakable, so shoot anyway enemyCS4 = qtrue; } else { faceEnemy4 = qtrue; NPC_AimAdjust( -1 );//adjust aim worse longer we cannot see enemy } } if ( enemyLOS4 ) { faceEnemy4 = qtrue; } else { if ( !NPCInfo->goalEntity ) { NPCInfo->goalEntity = NPC->enemy; } if ( NPCInfo->goalEntity == NPC->enemy ) {//for now, always chase the enemy move4 = qtrue; } } if ( enemyCS4 ) { shoot4 = qtrue; //NPCInfo->enemyCheckDebounceTime = level.time;//actually used here as a last actual LOS } else { if ( !NPCInfo->goalEntity ) { NPCInfo->goalEntity = NPC->enemy; } if ( NPCInfo->goalEntity == NPC->enemy ) {//for now, always chase the enemy move4 = qtrue; } } //Check for movement to take care of GM_CheckMoveState(); //See if we should override shooting decision with any special considerations GM_CheckFireState(); if ( NPC->client->ps.weapon == WP_REPEATER && (NPCInfo->scriptFlags&SCF_ALT_FIRE) && shoot4 && TIMER_Done( NPC, "attackDelay" ) ) { vec3_t muzzle; vec3_t angles; vec3_t target; vec3_t velocity = {0,0,0}; vec3_t mins = {-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE,-REPEATER_ALT_SIZE}, maxs = {REPEATER_ALT_SIZE,REPEATER_ALT_SIZE,REPEATER_ALT_SIZE}; qboolean clearshot; CalcEntitySpot( NPC, SPOT_WEAPON, muzzle ); VectorCopy( NPC->enemy->r.currentOrigin, target ); target[0] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); target[1] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); target[2] += flrand( -5, 5 )+(crandom()*(6-NPCInfo->currentAim)*2); //Find the desired angles clearshot = WP_LobFire( NPC, muzzle, target, mins, maxs, MASK_SHOT|CONTENTS_LIGHTSABER, velocity, qtrue, NPC->s.number, NPC->enemy->s.number, 300, 1100, 1500, qtrue ); if ( VectorCompare( vec3_origin, velocity ) || (!clearshot&&enemyLOS4&&enemyCS4) ) {//no clear lob shot and no lob shot that will hit something breakable if ( enemyLOS4 && enemyCS4 && TIMER_Done( NPC, "noRapid" ) ) {//have a clear straight shot, so switch to primary NPCInfo->scriptFlags &= ~SCF_ALT_FIRE; NPC->alt_fire = qfalse; NPC_ChangeWeapon( WP_REPEATER ); //keep this weap for a bit TIMER_Set( NPC, "noLob", Q_irand( 500, 1000 ) ); } else { shoot4 = qfalse; } } else { vectoangles( velocity, angles ); NPCInfo->desiredYaw = AngleNormalize360( angles[YAW] ); NPCInfo->desiredPitch = AngleNormalize360( angles[PITCH] ); VectorCopy( velocity, NPC->client->hiddenDir ); NPC->client->hiddenDist = VectorNormalize ( NPC->client->hiddenDir ); } } else if ( faceEnemy4 ) {//face the enemy NPC_FaceEnemy( qtrue ); } if ( !TIMER_Done( NPC, "standTime" ) ) { move4 = qfalse; } if ( !(NPCInfo->scriptFlags&SCF_CHASE_ENEMIES) ) {//not supposed to chase my enemies if ( NPCInfo->goalEntity == NPC->enemy ) {//goal is my entity, so don't move move4 = qfalse; } } if ( move4 && !NPC->lockCount ) {//move toward goal if ( NPCInfo->goalEntity /*&& NPC->client->ps.legsAnim != BOTH_ALERT1 && NPC->client->ps.legsAnim != BOTH_ATTACK2 && NPC->client->ps.legsAnim != BOTH_ATTACK4 && NPC->client->ps.legsAnim != BOTH_ATTACK5 && NPC->client->ps.legsAnim != BOTH_ATTACK7*/ ) { move4 = GM_Move(); } else { move4 = qfalse; } } if ( !TIMER_Done( NPC, "flee" ) ) {//running away faceEnemy4 = qfalse; } //FIXME: check scf_face_move_dir here? if ( !faceEnemy4 ) {//we want to face in the dir we're running if ( !move4 ) {//if we haven't moved, we should look in the direction we last looked? VectorCopy( NPC->client->ps.viewangles, NPCInfo->lastPathAngles ); } if ( move4 ) {//don't run away and shoot NPCInfo->desiredYaw = NPCInfo->lastPathAngles[YAW]; NPCInfo->desiredPitch = 0; shoot4 = qfalse; } } NPC_UpdateAngles( qtrue, qtrue ); if ( NPCInfo->scriptFlags & SCF_DONT_FIRE ) { shoot4 = qfalse; } if ( NPC->enemy && NPC->enemy->enemy ) { if ( NPC->enemy->s.weapon == WP_SABER && NPC->enemy->enemy->s.weapon == WP_SABER ) {//don't shoot at an enemy jedi who is fighting another jedi, for fear of injuring one or causing rogue blaster deflections (a la Obi Wan/Vader duel at end of ANH) shoot4 = qfalse; } } //FIXME: don't shoot right away! if ( shoot4 ) {//try to shoot if it's time if ( TIMER_Done( NPC, "attackDelay" ) ) { if( !(NPCInfo->scriptFlags & SCF_FIRE_WEAPON) ) // we've already fired, no need to do it again here { WeaponThink( qtrue ); } } } //also: if ( NPC->enemy->s.weapon == WP_TURRET && !Q_stricmp( "PAS", NPC->enemy->classname ) ) {//crush turrets if ( G_BoundsOverlap( NPC->r.absmin, NPC->r.absmax, NPC->enemy->r.absmin, NPC->enemy->r.absmax ) ) {//have to do this test because placed turrets are not solid to NPCs (so they don't obstruct navigation) //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) if (0) { NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); } else { G_Damage( NPC->enemy, NPC, NPC, NULL, NPC->r.currentOrigin, 100, DAMAGE_NO_KNOCKBACK, MOD_CRUSH ); } } } else if ( NPCInfo->touchedByPlayer != NULL && NPCInfo->touchedByPlayer == NPC->enemy ) {//touched enemy //if ( NPC->client->ps.powerups[PW_GALAK_SHIELD] > 0 ) if (0) {//zap him! vec3_t smackDir; //animate me #if 0 NPC_SetAnim( NPC, SETANIM_BOTH, BOTH_ATTACK6, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); #endif TIMER_Set( NPC, "attackDelay", NPC->client->ps.torsoTimer ); TIMER_Set( NPC, "standTime", NPC->client->ps.legsTimer ); //FIXME: debounce this? NPCInfo->touchedByPlayer = NULL; //FIXME: some shield effect? NPC->client->ps.powerups[PW_BATTLESUIT] = level.time + ARMOR_EFFECT_TIME; VectorSubtract( NPC->enemy->r.currentOrigin, NPC->r.currentOrigin, smackDir ); smackDir[2] += 30; VectorNormalize( smackDir ); G_Damage( NPC->enemy, NPC, NPC, smackDir, NPC->r.currentOrigin, (g_spskill.integer+1)*Q_irand( 5, 10), DAMAGE_NO_KNOCKBACK, MOD_UNKNOWN ); //throw them G_Throw( NPC->enemy, smackDir, 100 ); //NPC->enemy->s.powerups |= ( 1 << PW_SHOCKED ); if ( NPC->enemy->client ) { // NPC->enemy->client->ps.powerups[PW_SHOCKED] = level.time + 1000; NPC->enemy->client->ps.electrifyTime = level.time + 1000; } //stop any attacks ucmd.buttons = 0; } } if ( NPCInfo->movementSpeech < 3 && NPCInfo->blockedSpeechDebounceTime <= level.time ) { if ( NPC->enemy && NPC->enemy->health > 0 && NPC->enemy->painDebounceTime > level.time ) { if ( NPC->enemy->health < 50 && NPCInfo->movementSpeech == 2 ) { G_AddVoiceEvent( NPC, EV_ANGER2, Q_irand( 2000, 4000 ) ); NPCInfo->movementSpeech = 3; } else if ( NPC->enemy->health < 75 && NPCInfo->movementSpeech == 1 ) { G_AddVoiceEvent( NPC, EV_ANGER1, Q_irand( 2000, 4000 ) ); NPCInfo->movementSpeech = 2; } else if ( NPC->enemy->health < 100 && NPCInfo->movementSpeech == 0 ) { G_AddVoiceEvent( NPC, EV_ANGER3, Q_irand( 2000, 4000 ) ); NPCInfo->movementSpeech = 1; } } } }
void CG_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins, const vec3_t maxs, float speed, int numChunks, material_t chunkType, int customChunk, float baseScale, int customSound = 0 ) { localEntity_t *le; refEntity_t *re; vec3_t dir; int i, j, k; int chunkModel = 0; leBounceSound_t bounce = LEBS_NONE; float r, speedMod = 1.0f; qboolean chunk = qfalse; if ( chunkType == MAT_NONE ) { // Well, we should do nothing return; } if ( customSound ) { if ( cgs.sound_precache[customSound] ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.sound_precache[customSound] ); } } // Set up our chunk sound info...breaking sounds are done here so they are done once on breaking..some return instantly because the chunks are done with effects instead of models switch( chunkType ) { case MAT_GLASS: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); } return; break; case MAT_GRATE1: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.grateSound ); } return; break; case MAT_ELECTRICAL:// (sparks) if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound (va("sound/ambience/spark%d.wav", Q_irand(1, 6))) ); } return; break; case MAT_DRK_STONE: case MAT_LT_STONE: case MAT_GREY_STONE: case MAT_WHITE_METAL: // not quite sure what this stuff is supposed to be...it's for Stu if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.rockBreakSound ); bounce = LEBS_ROCK; } speedMod = 0.5f; // rock blows up less break; case MAT_GLASS_METAL: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.glassChunkSound ); // FIXME: should probably have a custom sound bounce = LEBS_METAL; } break; case MAT_CRATE1: case MAT_CRATE2: if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.crateBreakSound[Q_irand(0,1)] ); } break; case MAT_METAL: case MAT_METAL2: case MAT_METAL3: case MAT_ELEC_METAL:// FIXME: maybe have its own sound? if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgs.media.chunkSound ); bounce = LEBS_METAL; } speedMod = 0.8f; // metal blows up a bit more break; case MAT_ROPE: /* if ( !customSound ) { cgi_S_StartSound( NULL, owner, CHAN_BODY, cgi_S_RegisterSound( "" )); FIXME: needs a sound } */ return; break; } if ( baseScale <= 0.0f ) { baseScale = 1.0f; } // Chunks for( i = 0; i < numChunks; i++ ) { if ( customChunk > 0 ) { // Try to use a custom chunk. if ( cgs.model_draw[customChunk] ) { chunk = qtrue; chunkModel = cgs.model_draw[customChunk]; } } if ( !chunk ) { // No custom chunk. Pick a random chunk type at run-time so we don't get the same chunks switch( chunkType ) { case MAT_METAL2: //bluegrey chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; break; case MAT_GREY_STONE://gray chunkModel = cgs.media.chunkModels[CHUNK_ROCK1][Q_irand(0, 3)]; break; case MAT_LT_STONE: //tan chunkModel = cgs.media.chunkModels[CHUNK_ROCK2][Q_irand(0, 3)]; break; case MAT_DRK_STONE://brown chunkModel = cgs.media.chunkModels[CHUNK_ROCK3][Q_irand(0, 3)]; break; case MAT_WHITE_METAL: chunkModel = cgs.media.chunkModels[CHUNK_WHITE_METAL][Q_irand(0, 3)]; break; case MAT_CRATE1://yellow multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE1][Q_irand(0, 3)]; break; case MAT_CRATE2://red multi-colored crate chunks chunkModel = cgs.media.chunkModels[CHUNK_CRATE2][Q_irand(0, 3)]; break; case MAT_ELEC_METAL: case MAT_GLASS_METAL: case MAT_METAL://grey chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; break; case MAT_METAL3: if ( rand() & 1 ) { chunkModel = cgs.media.chunkModels[CHUNK_METAL1][Q_irand(0, 3)]; } else { chunkModel = cgs.media.chunkModels[CHUNK_METAL2][Q_irand(0, 3)]; } break; } } // It wouldn't look good to throw a bunch of RGB axis models...so make sure we have something to work with. if ( chunkModel ) { le = CG_AllocLocalEntity(); re = &le->refEntity; re->hModel = chunkModel; le->leType = LE_FRAGMENT; le->endTime = cg.time + 1300 + random() * 900; // spawn chunk roughly in the bbox of the thing...bias towards center in case thing blowing up doesn't complete fill its bbox. for( j = 0; j < 3; j++ ) { r = random() * 0.8f + 0.1f; re->origin[j] = ( r * mins[j] + ( 1 - r ) * maxs[j] ); } VectorCopy( re->origin, le->pos.trBase ); // Move out from center of thing, otherwise you can end up things moving across the brush in an undesirable direction. Visually looks wrong VectorSubtract( re->origin, origin, dir ); VectorNormalize( dir ); VectorScale( dir, Q_flrand( speed * 0.5f, speed * 1.25f ) * speedMod, le->pos.trDelta ); // Angular Velocity VectorSet( le->angles.trBase, random() * 360, random() * 360, random() * 360 ); le->angles.trDelta[0] = crandom(); le->angles.trDelta[1] = crandom(); le->angles.trDelta[2] = 0; // don't do roll VectorScale( le->angles.trDelta, random() * 600.0f + 200.0f, le->angles.trDelta ); le->pos.trType = TR_GRAVITY; le->angles.trType = TR_LINEAR; le->pos.trTime = le->angles.trTime = cg.time; le->bounceFactor = 0.2f + random() * 0.2f; le->leFlags |= LEF_TUMBLE; le->ownerGentNum = owner; le->leBounceSoundType = bounce; // Make sure that we have the desired start size set le->radius = Q_flrand( baseScale * 0.75f, baseScale * 1.25f ); re->nonNormalizedAxes = qtrue; AxisCopy( axisDefault, re->axis ); // could do an angles to axis, but this is cheaper and works ok for( k = 0; k < 3; k++ ) { VectorScale( re->axis[k], le->radius, re->axis[k] ); } } } }
void CG_ParticleDust (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; VectorNegate (dir, dir); length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; i<dist; i++) { VectorMA (point, crittersize, forward, point); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 5.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; // RF, stay around for long enough to expand and dissipate naturally if (length) p->endtime = cg.time + 4500 + (crandom() * 3500); else p->endtime = cg.time + 750 + (crandom() * 500); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; // RF, expand while falling p->endheight = LARGESIZE*3.0; p->endwidth = LARGESIZE*3.0; if (!length) { p->width *= 0.2f; p->height *= 0.2f; p->endheight = NORMALSIZE; p->endwidth = NORMALSIZE; } p->type = P_SMOKE; VectorCopy( point, p->org ); p->vel[0] = crandom()*6; p->vel[1] = crandom()*6; p->vel[2] = random()*20; // RF, add some gravity/randomness p->accel[0] = crandom()*3; p->accel[1] = crandom()*3; p->accel[2] = -PARTICLE_GRAVITY*0.4; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; } }
void barrel_explode (edict_t *self) { vec3_t org; float spd; vec3_t save; T_RadiusDamage (self, self->activator, self->dmg, NULL, self->dmg+40, MOD_BARREL); VectorCopy (self->s.origin, save); VectorMA (self->absmin, 0.5, self->size, self->s.origin); // a few big chunks spd = 1.5 * (float)self->dmg / 200.0; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris1/tris.md2", spd, org); // bottom corners spd = 1.75 * (float)self->dmg / 200.0; VectorCopy (self->absmin, org); ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); VectorCopy (self->absmin, org); org[0] += self->size[0]; ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); VectorCopy (self->absmin, org); org[1] += self->size[1]; ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); VectorCopy (self->absmin, org); org[0] += self->size[0]; org[1] += self->size[1]; ThrowDebris (self, "models/objects/debris3/tris.md2", spd, org); // a bunch of little chunks spd = 2 * self->dmg / 200; org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); org[0] = self->s.origin[0] + crandom() * self->size[0]; org[1] = self->s.origin[1] + crandom() * self->size[1]; org[2] = self->s.origin[2] + crandom() * self->size[2]; ThrowDebris (self, "models/objects/debris2/tris.md2", spd, org); VectorCopy (save, self->s.origin); if (self->groundentity) BecomeExplosion2 (self); else BecomeExplosion1 (self); }
/* ===================== CG_AddParticleToScene ===================== */ void CG_AddParticleToScene (cparticle_t *p, vec3_t org, float alpha) { vec3_t point; polyVert_t verts[4]; float width; float height; float time, time2; float ratio; float invratio; vec3_t color; polyVert_t TRIverts[3]; vec3_t rright2, rup2; if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY || p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) {// create a front facing polygon if (p->type != P_WEATHER_FLURRY) { if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { if (org[2] > p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground p->org[2] = ( p->start + crandom () * 4 ); if (p->type == P_BUBBLE_TURBULENT) { p->vel[0] = crandom() * 4; p->vel[1] = crandom() * 4; } } } else { if (org[2] < p->end) { p->time = cg.time; VectorCopy (org, p->org); // Ridah, fixes rare snow flakes that flicker on the ground while (p->org[2] < p->end) { p->org[2] += (p->start - p->end); } if (p->type == P_WEATHER_TURBULENT) { p->vel[0] = crandom() * 16; p->vel[1] = crandom() * 16; } } } // Rafael snow pvs check if (!p->link) return; p->alpha = 1; } // Ridah, had to do this or MAX_POLYS is being exceeded in village1.bsp if (Distance( cg.snap->ps.origin, org ) > 1024) { return; } // done. if (p->type == P_BUBBLE || p->type == P_BUBBLE_TURBULENT) { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255 * p->alpha; VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255 * p->alpha; } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy( point, TRIverts[0].xyz ); TRIverts[0].st[0] = 1; TRIverts[0].st[1] = 0; TRIverts[0].modulate[0] = 255; TRIverts[0].modulate[1] = 255; TRIverts[0].modulate[2] = 255; TRIverts[0].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); VectorCopy (point, TRIverts[1].xyz); TRIverts[1].st[0] = 0; TRIverts[1].st[1] = 0; TRIverts[1].modulate[0] = 255; TRIverts[1].modulate[1] = 255; TRIverts[1].modulate[2] = 255; TRIverts[1].modulate[3] = 255 * p->alpha; VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); VectorCopy (point, TRIverts[2].xyz); TRIverts[2].st[0] = 0; TRIverts[2].st[1] = 1; TRIverts[2].modulate[0] = 255; TRIverts[2].modulate[1] = 255; TRIverts[2].modulate[2] = 255; TRIverts[2].modulate[3] = 255 * p->alpha; } } else if (p->type == P_SPRITE) { vec3_t rr, ru; vec3_t rotate_ang; #ifdef WOLF_PARTICLES VectorSet (color, 1.0, 1.0, 1.0); #else VectorSet (color, 1.0, 1.0, 0.5); #endif time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } else if (p->type == P_SMOKE || p->type == P_SMOKE_IMPACT) {// create a front rotating facing polygon if ( p->type == P_SMOKE_IMPACT && Distance( cg.snap->ps.origin, org ) > 1024) { return; } if (p->color == BLOODRED) VectorSet (color, 0.22f, 0.0f, 0.0f); else if (p->color == GREY75) { float len; float greyit; float val; len = Distance (cg.snap->ps.origin, org); if (!len) len = 1; val = 4096/len; greyit = 0.25 * val; if (greyit > 0.5) greyit = 0.5; VectorSet (color, greyit, greyit, greyit); } else VectorSet (color, 1.0, 1.0, 1.0); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (cg.time > p->startfade) { invratio = 1 - ( (cg.time - p->startfade) / (p->endtime - p->startfade) ); if (p->color == EMISIVEFADE) { float fval; fval = (invratio * invratio); if (fval < 0) fval = 0; VectorSet (color, fval , fval , fval ); } invratio *= p->alpha; } else invratio = 1 * p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) invratio = 1; if (invratio > 1) invratio = 1; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (p->type != P_SMOKE_IMPACT) { vec3_t temp; vectoangles (rforward, temp); p->accumroll += p->roll; temp[ROLL] += p->accumroll * 0.1; AngleVectors ( temp, NULL, rright2, rup2); } else { VectorCopy (rright, rright2); VectorCopy (rup, rup2); } if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, -height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, -p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, p->width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255 * invratio; if (p->rotate) { VectorMA (org, height, rup2, point); VectorMA (point, -width, rright2, point); } else { VectorMA (org, p->height, vup, point); VectorMA (point, -p->width, vright, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255 * invratio; } else if (p->type == P_BLEED) { vec3_t rr, ru; vec3_t rotate_ang; float alpha; alpha = p->alpha; if ( cgs.glconfig.hardwareType == GLHW_RAGEPRO ) alpha = 1; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } else { VectorCopy (vup, ru); VectorCopy (vright, rr); } VectorMA (org, -p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 111; verts[0].modulate[1] = 19; verts[0].modulate[2] = 9; verts[0].modulate[3] = 255 * alpha; VectorMA (org, -p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 111; verts[1].modulate[1] = 19; verts[1].modulate[2] = 9; verts[1].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, p->width, rr, point); VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 111; verts[2].modulate[1] = 19; verts[2].modulate[2] = 9; verts[2].modulate[3] = 255 * alpha; VectorMA (org, p->height, ru, point); VectorMA (point, -p->width, rr, point); VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 111; verts[3].modulate[1] = 19; verts[3].modulate[2] = 9; verts[3].modulate[3] = 255 * alpha; } else if (p->type == P_FLAT_SCALEUP) { float width, height; float sinR, cosR; if (p->color == BLOODRED) VectorSet (color, 1, 1, 1); else VectorSet (color, 0.5, 0.5, 0.5); time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); if (width > p->endwidth) width = p->endwidth; if (height > p->endheight) height = p->endheight; sinR = height * sin(DEG2RAD(p->roll)) * sqrt(2); cosR = width * cos(DEG2RAD(p->roll)) * sqrt(2); VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= sinR; verts[0].xyz[1] -= cosR; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255 * color[0]; verts[0].modulate[1] = 255 * color[1]; verts[0].modulate[2] = 255 * color[2]; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= cosR; verts[1].xyz[1] += sinR; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255 * color[0]; verts[1].modulate[1] = 255 * color[1]; verts[1].modulate[2] = 255 * color[2]; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += sinR; verts[2].xyz[1] += cosR; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255 * color[0]; verts[2].modulate[1] = 255 * color[1]; verts[2].modulate[2] = 255 * color[2]; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += cosR; verts[3].xyz[1] -= sinR; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255 * color[0]; verts[3].modulate[1] = 255 * color[1]; verts[3].modulate[2] = 255 * color[2]; verts[3].modulate[3] = 255; } else if (p->type == P_FLAT) { VectorCopy (org, verts[0].xyz); verts[0].xyz[0] -= p->height; verts[0].xyz[1] -= p->width; verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; VectorCopy (org, verts[1].xyz); verts[1].xyz[0] -= p->height; verts[1].xyz[1] += p->width; verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; VectorCopy (org, verts[2].xyz); verts[2].xyz[0] += p->height; verts[2].xyz[1] += p->width; verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; VectorCopy (org, verts[3].xyz); verts[3].xyz[0] += p->height; verts[3].xyz[1] -= p->width; verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // Ridah else if (p->type == P_ANIM) { vec3_t rr, ru; vec3_t rotate_ang; int i, j; time = cg.time - p->time; time2 = p->endtime - p->time; ratio = time / time2; if (ratio >= 1.0f) { ratio = 0.9999f; } width = p->width + ( ratio * ( p->endwidth - p->width) ); height = p->height + ( ratio * ( p->endheight - p->height) ); // if we are "inside" this sprite, don't draw if (Distance( cg.snap->ps.origin, org ) < width/1.5) { return; } i = p->shaderAnim; j = (int)floor(ratio * shaderAnimCounts[p->shaderAnim]); p->pshader = shaderAnims[i][j]; if (p->roll) { vectoangles( cg.refdef.viewaxis[0], rotate_ang ); rotate_ang[ROLL] += p->roll; AngleVectors ( rotate_ang, NULL, rr, ru); } if (p->roll) { VectorMA (org, -height, ru, point); VectorMA (point, -width, rr, point); } else { VectorMA (org, -height, vup, point); VectorMA (point, -width, vright, point); } VectorCopy (point, verts[0].xyz); verts[0].st[0] = 0; verts[0].st[1] = 0; verts[0].modulate[0] = 255; verts[0].modulate[1] = 255; verts[0].modulate[2] = 255; verts[0].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*height, ru, point); } else { VectorMA (point, 2*height, vup, point); } VectorCopy (point, verts[1].xyz); verts[1].st[0] = 0; verts[1].st[1] = 1; verts[1].modulate[0] = 255; verts[1].modulate[1] = 255; verts[1].modulate[2] = 255; verts[1].modulate[3] = 255; if (p->roll) { VectorMA (point, 2*width, rr, point); } else { VectorMA (point, 2*width, vright, point); } VectorCopy (point, verts[2].xyz); verts[2].st[0] = 1; verts[2].st[1] = 1; verts[2].modulate[0] = 255; verts[2].modulate[1] = 255; verts[2].modulate[2] = 255; verts[2].modulate[3] = 255; if (p->roll) { VectorMA (point, -2*height, ru, point); } else { VectorMA (point, -2*height, vup, point); } VectorCopy (point, verts[3].xyz); verts[3].st[0] = 1; verts[3].st[1] = 0; verts[3].modulate[0] = 255; verts[3].modulate[1] = 255; verts[3].modulate[2] = 255; verts[3].modulate[3] = 255; } // done. if (!p->pshader) { // (SA) temp commented out for DM // CG_Printf ("CG_AddParticleToScene type %d p->pshader == ZERO\n", p->type); return; } if (p->type == P_WEATHER || p->type == P_WEATHER_TURBULENT || p->type == P_WEATHER_FLURRY) trap_R_AddPolyToScene( p->pshader, 3, TRIverts ); else trap_R_AddPolyToScene( p->pshader, 4, verts ); }
void UI_DoSaber( vec3_t origin, vec3_t dir, float length, float lengthMax, float radius, saber_colors_t color ) { vec3_t mid, rgb={1,1,1}; qhandle_t blade = 0, glow = 0; refEntity_t saber; float radiusmult; float radiusRange; float radiusStart; if ( length < 0.5f ) { // if the thing is so short, just forget even adding me. return; } // Find the midpoint of the saber for lighting purposes VectorMA( origin, length * 0.5f, dir, mid ); switch( color ) { case SABER_RED: glow = redSaberGlowShader; blade = redSaberCoreShader; VectorSet( rgb, 1.0f, 0.2f, 0.2f ); break; case SABER_ORANGE: glow = orangeSaberGlowShader; blade = orangeSaberCoreShader; VectorSet( rgb, 1.0f, 0.5f, 0.1f ); break; case SABER_YELLOW: glow = yellowSaberGlowShader; blade = yellowSaberCoreShader; VectorSet( rgb, 1.0f, 1.0f, 0.2f ); break; case SABER_GREEN: glow = greenSaberGlowShader; blade = greenSaberCoreShader; VectorSet( rgb, 0.2f, 1.0f, 0.2f ); break; case SABER_BLUE: glow = blueSaberGlowShader; blade = blueSaberCoreShader; VectorSet( rgb, 0.2f, 0.4f, 1.0f ); break; case SABER_PURPLE: glow = purpleSaberGlowShader; blade = purpleSaberCoreShader; VectorSet( rgb, 0.9f, 0.2f, 1.0f ); break; default: break; } memset( &saber, 0, sizeof( refEntity_t )); // Saber glow is it's own ref type because it uses a ton of sprites, otherwise it would eat up too many // refEnts to do each glow blob individually saber.saberLength = length; // Jeff, I did this because I foolishly wished to have a bright halo as the saber is unleashed. // It's not quite what I'd hoped tho. If you have any ideas, go for it! --Pat if (length < lengthMax ) { radiusmult = 1.0 + (2.0 / length); // Note this creates a curve, and length cannot be < 0.5. } else { radiusmult = 1.0; } radiusRange = radius * 0.075f; radiusStart = radius-radiusRange; saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult; //saber.radius = (2.8f + crandom() * 0.2f)*radiusmult; VectorCopy( origin, saber.origin ); VectorCopy( dir, saber.axis[0] ); saber.reType = RT_SABER_GLOW; saber.customShader = glow; saber.shaderRGBA[0] = saber.shaderRGBA[1] = saber.shaderRGBA[2] = saber.shaderRGBA[3] = 0xff; //saber.renderfx = rfx; trap->R_AddRefEntityToScene( &saber ); // Do the hot core VectorMA( origin, length, dir, saber.origin ); VectorMA( origin, -1, dir, saber.oldorigin ); saber.customShader = blade; saber.reType = RT_LINE; radiusStart = radius/3.0f; saber.radius = (radiusStart + crandom() * radiusRange)*radiusmult; // saber.radius = (1.0 + crandom() * 0.2f)*radiusmult; trap->R_AddRefEntityToScene( &saber ); }
// the trigger was just activated // ent->activator should be set to the activator so it can be held through a delay // so wait for the delay time before firing void multi_trigger( gentity_t *ent, gentity_t *activator ) { gentity_t *rofftarget = NULL, *testent = NULL; gentity_t *te; int i = MAX_CLIENTS; if (ent->teamnodmg && activator && activator->client && ent->teamnodmg == activator->client->sess.sessionTeam && !g_ff_objectives.integer) { return; } if (ent->spawnflags & 1) { if (!activator->client) { return; } if (!(activator->client->pers.cmd.buttons & BUTTON_USE)) { return; } } ent->activator = activator; if ( ent->nextthink ) { return; // can't retrigger until the wait is over } if ( activator->client ) { if ( ( ent->spawnflags & 2 ) && activator->client->sess.sessionTeam != TEAM_RED ) { return; } if ( ( ent->spawnflags & 4 ) && activator->client->sess.sessionTeam != TEAM_BLUE ) { return; } } G_UseTargets (ent, ent->activator); if (ent->roffname && ent->roffid != -1) { if (ent->rofftarget) { while (i < MAX_GENTITIES) { testent = &g_entities[i]; if (testent && testent->targetname && strcmp(testent->targetname, ent->rofftarget) == 0) { rofftarget = testent; break; } i++; } } else { rofftarget = activator; } if (rofftarget) { trap_ROFF_Play(rofftarget->s.number, ent->roffid, qfalse); //Play it at the same time on the client, so that we can catch client-side notetrack events and not have to send //them over from the server (this wouldn't work for things like effects due to lack of ability to precache them //on the server) //remember the entity's original position in case of a server-side "loop" notetrack VectorCopy(rofftarget->s.pos.trBase, rofftarget->s.origin2); VectorCopy(rofftarget->s.apos.trBase, rofftarget->s.angles2); te = G_TempEntity( rofftarget->s.pos.trBase, EV_PLAY_ROFF ); te->s.eventParm = ent->roffid; te->s.weapon = rofftarget->s.number; te->s.trickedentindex = 0; //But.. this may not produce desired results for clients who connect while a ROFF is playing. rofftarget->roffid = ent->roffid; //let this entity know the most recent ROFF played on him } } if ( ent->wait > 0 ) { ent->think = multi_wait; ent->nextthink = level.time + ( ent->wait + ent->random * crandom() ) * 1000; } else { // we can't just remove (self) here, because this is a touch function // called while looping through area links... ent->touch = 0; ent->nextthink = level.time + FRAMETIME; ent->think = G_FreeEntity; } }
/* ================= fire_lead This is an internal support routine used for bullet/pellet based weapons. ================= */ static void fire_lead (edict_t *self, vec3_t start, vec3_t aimdir, int damage, int kick, int te_impact, int hspread, int vspread, int mod) { trace_t tr; vec3_t dir; vec3_t forward, right, up; vec3_t end; float r; float u; vec3_t water_start; qboolean water = false; int content_mask = MASK_SHOT | MASK_WATER; tr = gi.trace (self->s.origin, NULL, NULL, start, self, MASK_SHOT); if (!(tr.fraction < 1.0)) { vectoangles (aimdir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread; u = crandom()*vspread; VectorMA (start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); if (gi.pointcontents (start) & MASK_WATER) { water = true; VectorCopy (start, water_start); content_mask &= ~MASK_WATER; } tr = gi.trace (start, NULL, NULL, end, self, content_mask); // see if we hit water if (tr.contents & MASK_WATER) { int color; water = true; VectorCopy (tr.endpos, water_start); if (!VectorCompare (start, tr.endpos)) { if (tr.contents & CONTENTS_WATER) { if (strcmp(tr.surface->name, "*brwater") == 0) color = SPLASH_BROWN_WATER; else color = SPLASH_BLUE_WATER; } else if (tr.contents & CONTENTS_SLIME) color = SPLASH_SLIME; else if (tr.contents & CONTENTS_LAVA) color = SPLASH_LAVA; else color = SPLASH_UNKNOWN; if (color != SPLASH_UNKNOWN) { gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_SPLASH); gi.WriteByte (8); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.WriteByte (color); gi.multicast (tr.endpos, MULTICAST_PVS); } // change bullet's course when it enters water VectorSubtract (end, start, dir); vectoangles (dir, dir); AngleVectors (dir, forward, right, up); r = crandom()*hspread*2; u = crandom()*vspread*2; VectorMA (water_start, 8192, forward, end); VectorMA (end, r, right, end); VectorMA (end, u, up, end); } // re-trace ignoring water this time tr = gi.trace (water_start, NULL, NULL, end, self, MASK_SHOT); } } // send gun puff / flash if (!((tr.surface) && (tr.surface->flags & SURF_SKY))) { if (tr.fraction < 1.0) { if (tr.ent->takedamage) { T_Damage (tr.ent, self, self, aimdir, tr.endpos, tr.plane.normal, damage, kick, DAMAGE_BULLET, mod); } else { if (strncmp (tr.surface->name, "sky", 3) != 0) { gi.WriteByte (svc_temp_entity); gi.WriteByte (te_impact); gi.WritePosition (tr.endpos); gi.WriteDir (tr.plane.normal); gi.multicast (tr.endpos, MULTICAST_PVS); if (self->client) PlayerNoise(self, tr.endpos, PNOISE_IMPACT); } } } } // if went through water, determine where the end and make a bubble trail if (water) { vec3_t pos; VectorSubtract (tr.endpos, water_start, dir); VectorNormalize (dir); VectorMA (tr.endpos, -2, dir, pos); if (gi.pointcontents (pos) & MASK_WATER) VectorCopy (pos, tr.endpos); else tr = gi.trace (pos, NULL, NULL, water_start, tr.ent, MASK_WATER); VectorAdd (water_start, tr.endpos, pos); VectorScale (pos, 0.5, pos); gi.WriteByte (svc_temp_entity); gi.WriteByte (TE_BUBBLETRAIL); gi.WritePosition (water_start); gi.WritePosition (tr.endpos); gi.multicast (pos, MULTICAST_PVS); } }
/*QUAKED func_timer (0.3 0.1 0.6) (-8 -8 -8) (8 8 8) START_ON "wait" base time between triggering all targets, default is 1 "random" wait variance, default is 0 so, the basic time between firing is a random time between (wait - random) and (wait + random) "delay" delay before first firing when turned on, default is 0 "pausetime" additional delay used only the very first time and only if spawned with START_ON These can used but not touched. */ void func_timer_think (edict_t *self) { G_UseTargets (self, self->activator); self->nextthink = level.time + self->wait + crandom() * self->random; }
void Machinegun_Fire(edict_t * ent) { int i; vec3_t start; vec3_t forward, right; vec3_t angles; int damage = 8; int kick = 2; vec3_t offset; if (!(ent->client->buttons & BUTTON_ATTACK)) { ent->client->machinegun_shots = 0; ent->client->ps.gunframe++; return; } if (ent->client->ps.gunframe == 5) ent->client->ps.gunframe = 4; else ent->client->ps.gunframe = 5; if (ent->client->pers.inventory[ent->client->ammo_index] < 1) { ent->client->ps.gunframe = 6; if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); return; } if (is_quad) { damage *= 4; kick *= 4; } for (i = 1; i < 3; i++) { ent->client->kick_origin[i] = crandom() * 0.35; ent->client->kick_angles[i] = crandom() * 0.7; } ent->client->kick_origin[0] = crandom() * 0.35; ent->client->kick_angles[0] = ent->client->machinegun_shots * -1.5; // raise the gun as it is firing if (!deathmatch->value) { ent->client->machinegun_shots++; if (ent->client->machinegun_shots > 9) ent->client->machinegun_shots = 9; } // get start / end positions VectorAdd(ent->client->v_angle, ent->client->kick_angles, angles); AngleVectors(angles, forward, right, NULL); VectorSet(offset, 0, 8, ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_MACHINEGUN); gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte(MZ_MACHINEGUN | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) ent->client->pers.inventory[ent->client->ammo_index]--; ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - (int)(random() + 0.25); ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - (int)(random() + 0.25); ent->client->anim_end = FRAME_attack8; } }
void Use_Target_Delay(gentity_t *ent, gentity_t *other, gentity_t *activator) { ent->nextthink = level.time + (ent->wait + ent->random * crandom()) * 1000; ent->think = Think_Target_Delay; ent->activator = activator; }
void Chaingun_Fire(edict_t * ent) { int i; int shots; vec3_t start; vec3_t forward, right, up; float r, u; vec3_t offset; int damage; int kick = 2; if (deathmatch->value) damage = 6; else damage = 8; if (ent->client->ps.gunframe == 5) gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnu1a.wav"), 1, ATTN_IDLE, 0); if ((ent->client->ps.gunframe == 14) && !(ent->client->buttons & BUTTON_ATTACK)) { ent->client->ps.gunframe = 32; ent->client->weapon_sound = 0; return; } else if ((ent->client->ps.gunframe == 21) && (ent->client->buttons & BUTTON_ATTACK) && ent->client->pers.inventory[ent->client->ammo_index]) { ent->client->ps.gunframe = 15; } else { ent->client->ps.gunframe++; } if (ent->client->ps.gunframe == 22) { ent->client->weapon_sound = 0; gi.sound(ent, CHAN_AUTO, gi.soundindex("weapons/chngnd1a.wav"), 1, ATTN_IDLE, 0); } else { ent->client->weapon_sound = gi.soundindex("weapons/chngnl1a.wav"); } ent->client->anim_priority = ANIM_ATTACK; if (ent->client->ps.pmove.pm_flags & PMF_DUCKED) { ent->s.frame = FRAME_crattak1 - (ent->client->ps.gunframe & 1); ent->client->anim_end = FRAME_crattak9; } else { ent->s.frame = FRAME_attack1 - (ent->client->ps.gunframe & 1); ent->client->anim_end = FRAME_attack8; } if (ent->client->ps.gunframe <= 9) shots = 1; else if (ent->client->ps.gunframe <= 14) { if (ent->client->buttons & BUTTON_ATTACK) shots = 2; else shots = 1; } else shots = 3; if (ent->client->pers.inventory[ent->client->ammo_index] < shots) shots = ent->client->pers.inventory[ent->client->ammo_index]; if (!shots) { if (level.time >= ent->pain_debounce_time) { gi.sound(ent, CHAN_VOICE, gi.soundindex("weapons/noammo.wav"), 1, ATTN_NORM, 0); ent->pain_debounce_time = level.time + 1; } NoAmmoWeaponChange(ent); return; } if (is_quad) { damage *= 4; kick *= 4; } for (i = 0; i < 3; i++) { ent->client->kick_origin[i] = crandom() * 0.35; ent->client->kick_angles[i] = crandom() * 0.7; } for (i = 0; i < shots; i++) { // get start / end positions AngleVectors(ent->client->v_angle, forward, right, up); r = 7 + crandom() * 4; u = crandom() * 4; VectorSet(offset, 0, r, u + ent->viewheight - 8); P_ProjectSource(ent->client, ent->s.origin, offset, forward, right, start); fire_bullet(ent, start, forward, damage, kick, DEFAULT_BULLET_HSPREAD, DEFAULT_BULLET_VSPREAD, MOD_CHAINGUN); } // send muzzle flash gi.WriteByte(svc_muzzleflash); gi.WriteShort(ent - g_edicts); gi.WriteByte((MZ_CHAINGUN1 + shots - 1) | is_silenced); gi.multicast(ent->s.origin, MULTICAST_PVS); PlayerNoise(ent, start, PNOISE_WEAPON); if (!((int)dmflags->value & DF_INFINITE_AMMO)) ent->client->pers.inventory[ent->client->ammo_index] -= shots; }
void RT_Flying_Strafe( void ) { int side; vec3_t end, right, dir; trace_t tr; if ( random() > 0.7f || !NPC->enemy || !NPC->enemy->client ) { // Do a regular style strafe AngleVectors( NPC->client->renderInfo.eyeAngles, NULL, right, NULL ); // Pick a random strafe direction, then check to see if doing a strafe would be // reasonably valid side = ( rand() & 1 ) ? -1 : 1; VectorMA( NPC->currentOrigin, RT_FLYING_STRAFE_DIS * side, right, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID ); // Close enough if ( tr.fraction > 0.9f ) { float vel = RT_FLYING_STRAFE_VEL+Q_flrand(-20,20); VectorMA( NPC->client->ps.velocity, vel*side, right, NPC->client->ps.velocity ); if ( !Q_irand( 0, 3 ) ) { // Add a slight upward push float upPush = RT_FLYING_UPWARD_PUSH; if ( NPC->client->ps.velocity[2] < 300 ) { if ( NPC->client->ps.velocity[2] < 300+upPush ) { NPC->client->ps.velocity[2] += upPush; } else { NPC->client->ps.velocity[2] = 300; } } } // NPCInfo->standTime = level.time + 1000 + random() * 500; // Original // NPCInfo->standTime = level.time + 2000 + random() * 500; // Revision 1 NPCInfo->standTime = level.time + 1500 + random() * 500; // Revision 2 } } else { // Do a strafe to try and keep on the side of their enemy AngleVectors( NPC->enemy->client->renderInfo.eyeAngles, dir, right, NULL ); // Pick a random side side = ( rand() & 1 ) ? -1 : 1; float stDis = RT_FLYING_STRAFE_DIS*2.0f; VectorMA( NPC->enemy->currentOrigin, stDis * side, right, end ); // then add a very small bit of random in front of/behind the player action VectorMA( end, crandom() * 25, dir, end ); gi.trace( &tr, NPC->currentOrigin, NULL, NULL, end, NPC->s.number, MASK_SOLID ); // Close enough if ( tr.fraction > 0.9f ) { float vel = (RT_FLYING_STRAFE_VEL*4)+Q_flrand(-20,20); VectorSubtract( tr.endpos, NPC->currentOrigin, dir ); dir[2] *= 0.25; // do less upward change float dis = VectorNormalize( dir ); if ( dis > vel ) { dis = vel; } // Try to move the desired enemy side VectorMA( NPC->client->ps.velocity, dis, dir, NPC->client->ps.velocity ); if ( !Q_irand( 0, 3 ) ) { float upPush = RT_FLYING_UPWARD_PUSH; // Add a slight upward push if ( NPC->client->ps.velocity[2] < 300 ) { if ( NPC->client->ps.velocity[2] < 300+upPush ) { NPC->client->ps.velocity[2] += upPush; } else { NPC->client->ps.velocity[2] = 300; } } else if ( NPC->client->ps.velocity[2] > 300 ) { NPC->client->ps.velocity[2] = 300; } } // NPCInfo->standTime = level.time + 2500 + random() * 500; // Original // NPCInfo->standTime = level.time + 5000 + random() * 500; // Revision 1 NPCInfo->standTime = level.time + 3500 + random() * 500; // Revision 2 } } }
void CG_Particle_OilSlick (qhandle_t pshader, centity_t *cent) { cparticle_t *p; if (!pshader) CG_Printf ("CG_Particle_OilSlick == ZERO!\n"); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; if (cent->currentState.angles2[2]) p->endtime = cg.time + cent->currentState.angles2[2]; else p->endtime = cg.time + 60000; p->startfade = p->endtime; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = pshader; if (cent->currentState.angles2[0] || cent->currentState.angles2[1]) { p->width = cent->currentState.angles2[0]; p->height = cent->currentState.angles2[0]; p->endheight = cent->currentState.angles2[1]; p->endwidth = cent->currentState.angles2[1]; } else { p->width = 8; p->height = 8; p->endheight = 16; p->endwidth = 16; } p->type = P_FLAT_SCALEUP; p->snum = 1.0; VectorCopy(cent->currentState.origin, p->org ); p->org[2]+= 0.55 + (crandom() * 0.5); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = 0; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->alpha = 0.75; }
/* ================ LaunchItem Spawns an item and tosses it forward ================ */ gentity_t *LaunchItem( gitem_t *item, vec3_t origin, vec3_t velocity, char *target ) { gentity_t *dropped; dropped = G_Spawn(); dropped->s.eType = ET_ITEM; dropped->s.modelindex = item - bg_itemlist; // store item number in modelindex dropped->s.modelindex2 = 1; // This is non-zero is it's a dropped item dropped->classname = item->classname; dropped->item = item; // try using the "correct" mins/maxs first VectorSet( dropped->mins, item->mins[0], item->mins[1], item->mins[2] ); VectorSet( dropped->maxs, item->maxs[0], item->maxs[1], item->maxs[2] ); if ((!dropped->mins[0] && !dropped->mins[1] && !dropped->mins[2]) && (!dropped->maxs[0] && !dropped->maxs[1] && !dropped->maxs[2])) { VectorSet( dropped->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS ); VectorScale( dropped->maxs, -1, dropped->mins ); } dropped->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_TRIGGER;//not CONTENTS_BODY for dropped items, don't need to ID them if ( target && target[0] ) { dropped->target = G_NewString( target ); } else { // if not targeting something, auto-remove after 30 seconds // only if it's NOT a security or goodie key if (dropped->item->giTag != INV_SECURITY_KEY ) { dropped->e_ThinkFunc = thinkF_G_FreeEntity; dropped->nextthink = level.time + 30000; } if ( dropped->item->giType == IT_AMMO && dropped->item->giTag == AMMO_FORCE ) { dropped->nextthink = -1; dropped->e_ThinkFunc = thinkF_NULL; } } dropped->e_TouchFunc = touchF_Touch_Item; if ( item->giType == IT_WEAPON ) { // give weapon items zero pitch, a random yaw, and rolled onto their sides...but would be bad to do this for a bowcaster if ( item->giTag != WP_BOWCASTER && item->giTag != WP_THERMAL && item->giTag != WP_TRIP_MINE && item->giTag != WP_DET_PACK ) { VectorSet( dropped->s.angles, 0, crandom() * 180, 90.0f ); G_SetAngles( dropped, dropped->s.angles ); } } G_SetOrigin( dropped, origin ); dropped->s.pos.trType = TR_GRAVITY; dropped->s.pos.trTime = level.time; VectorCopy( velocity, dropped->s.pos.trDelta ); dropped->s.eFlags |= EF_BOUNCE_HALF; dropped->flags = FL_DROPPED_ITEM; gi.linkentity (dropped); return dropped; }
void CG_ParticleBloodCloud (centity_t *cent, vec3_t origin, vec3_t dir) { float length; float dist; float crittersize; vec3_t angles, forward; vec3_t point; cparticle_t *p; int i; dist = 0; length = VectorLength (dir); vectoangles (dir, angles); AngleVectors (angles, forward, NULL, NULL); crittersize = LARGESIZE; if (length) dist = length / crittersize; if (dist < 1) dist = 1; VectorCopy (origin, point); for (i=0; i<dist; i++) { VectorMA (point, crittersize, forward, point); if (!free_particles) return; p = free_particles; free_particles = p->next; p->next = active_particles; active_particles = p; p->time = cg.time; p->alpha = 1.0; p->alphavel = 0; p->roll = 0; p->pshader = cgs.media.smokePuffShader; p->endtime = cg.time + 350 + (crandom() * 100); p->startfade = cg.time; p->width = LARGESIZE; p->height = LARGESIZE; p->endheight = LARGESIZE; p->endwidth = LARGESIZE; p->type = P_SMOKE; VectorCopy( origin, p->org ); p->vel[0] = 0; p->vel[1] = 0; p->vel[2] = -1; VectorClear( p->accel ); p->rotate = qfalse; p->roll = rand()%179; p->color = BLOODRED; p->alpha = 0.75; } }
void CG_GibPlayer( vec3_t playerOrigin ) { vec3_t origin, velocity; if ( !cg_blood.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; if ( rand() & 1 ) { CG_LaunchGib( origin, velocity, cgs.media.gibSkull ); } else { CG_LaunchGib( origin, velocity, cgs.media.gibBrain ); } // allow gibs to be turned off for speed if ( !cg_gibs.integer ) { return; } VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibAbdomen ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibArm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibChest ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFist ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibFoot ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibForearm ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibIntestine ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); VectorCopy( playerOrigin, origin ); velocity[0] = crandom()*GIB_VELOCITY; velocity[1] = crandom()*GIB_VELOCITY; velocity[2] = GIB_JUMP + crandom()*GIB_VELOCITY; CG_LaunchGib( origin, velocity, cgs.media.gibLeg ); }
void ThrowGib (edict_t *self, char *gibname, int damage, int type) { edict_t *gib; vec3_t vd; vec3_t origin; vec3_t size; float vscale; if (!self || !gibname) { return; } if (level.framenum > lastgibframe) { gibsthisframe = 0; lastgibframe = level.framenum; } gibsthisframe++; if (gibsthisframe > 20) { return; } gib = G_Spawn(); VectorScale (self->size, 0.5, size); VectorAdd (self->absmin, size, origin); gib->s.origin[0] = origin[0] + crandom() * size[0]; gib->s.origin[1] = origin[1] + crandom() * size[1]; gib->s.origin[2] = origin[2] + crandom() * size[2]; gi.setmodel (gib, gibname); gib->solid = SOLID_NOT; gib->s.effects |= EF_GIB; gib->flags |= FL_NO_KNOCKBACK; gib->takedamage = DAMAGE_YES; gib->die = gib_die; if (type == GIB_ORGANIC) { gib->movetype = MOVETYPE_TOSS; gib->touch = gib_touch; vscale = 0.5; } else { gib->movetype = MOVETYPE_BOUNCE; vscale = 1.0; } VelocityForDamage (damage, vd); VectorMA (self->velocity, vscale, vd, gib->velocity); ClipGibVelocity (gib); gib->avelocity[0] = random()*600; gib->avelocity[1] = random()*600; gib->avelocity[2] = random()*600; gib->think = G_FreeEdict; gib->nextthink = level.time + 10 + random()*10; gi.linkentity (gib); }