void BG_PrecacheSabersForSiegeTeam(int team) { siegeTeam_t *t; saberInfo_t saber; char *saberName; int sNum; t = BG_SiegeFindThemeForTeam(team); if (t) { int i = 0; while (i < t->numClasses) { sNum = 0; while (sNum < MAX_SABERS) { switch (sNum) { case 0: saberName = &t->classes[i]->saber1[0]; break; case 1: saberName = &t->classes[i]->saber2[0]; break; default: saberName = NULL; break; } if (saberName && saberName[0]) { WP_SaberParseParms(saberName, &saber); if (!Q_stricmp(saberName, saber.name)) { //found the matching saber if (saber.model[0]) { BG_ModelCache(saber.model, NULL); } } } sNum++; } i++; } } }
void WP_SetSaber( int entNum, saberInfo_t *sabers, int saberNum, const char *saberName ) { if ( !sabers ) { return; } if ( Q_stricmp( "none", saberName ) == 0 || Q_stricmp( "remove", saberName ) == 0 ) { if (saberNum != 0) { //can't remove saber 0 ever WP_RemoveSaber( sabers, saberNum ); } return; } if ( entNum < MAX_CLIENTS && !WP_SaberValidForPlayerInMP( saberName ) ) { WP_SaberParseParms( "Kyle", &sabers[saberNum] );//get saber info } else { WP_SaberParseParms( saberName, &sabers[saberNum] );//get saber info } if (sabers[1].twoHanded) {//not allowed to use a 2-handed saber as second saber WP_RemoveSaber( sabers, 1 ); return; } else if (sabers[0].twoHanded && sabers[1].model[0]) { //you can't use a two-handed saber with a second saber, so remove saber 2 WP_RemoveSaber( sabers, 1 ); return; } }
void WP_SetSaber( gentity_t *ent, int saberNum, char *saberName ) { if ( !ent || !ent->client ) { return; } if ( Q_stricmp( "none", saberName ) == 0 || Q_stricmp( "remove", saberName ) == 0 ) { WP_RemoveSaber( ent, saberNum ); return; } if ( ent->weaponModel[saberNum] > 0 ) { gi.G2API_RemoveGhoul2Model(ent->ghoul2, ent->weaponModel[saberNum]); ent->weaponModel[saberNum] = -1; } WP_SaberParseParms( saberName, &ent->client->ps.saber[saberNum] );//get saber info if ( ent->client->ps.saber[saberNum].style ) { ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saber[saberNum].style); } if ( saberNum == 1 && ent->client->ps.saber[1].twoHanded ) {//not allowed to use a 2-handed saber as second saber WP_RemoveSaber( ent, saberNum ); return; } G_ModelIndex( ent->client->ps.saber[saberNum].model ); WP_SaberInitBladeData( ent ); //int boltNum = ent->handRBolt; if ( saberNum == 1 ) { ent->client->ps.dualSabers = qtrue; //boltNum = ent->handLBolt; } WP_SaberAddG2SaberModels( ent, saberNum ); ent->client->ps.saber[saberNum].SetLength( 0.0f ); ent->client->ps.saber[saberNum].Activate(); if ( ent->client->ps.saber[saberNum].style != SS_NONE ) {//change to the style we're supposed to be using ent->client->ps.saberAnimLevel = ent->client->ps.saber[saberNum].style; ent->client->ps.saberStylesKnown |= (1<<ent->client->ps.saberAnimLevel); if ( ent->s.number < MAX_CLIENTS ) { cg.saberAnimLevelPending = ent->client->ps.saberAnimLevel; } } }
void Pickup_Saber( gentity_t *self, qboolean hadSaber, qboolean saberSolo, char *saberType, char *saberColor ) { //G_RemoveWeaponModels( ent );//??? if ( Q_stricmp( "player", saberType ) == 0 ) {//"player" means use cvar info G_SetSabersFromCVars( self ); } else { saberInfo_t newSaber={0}; if ( WP_SaberParseParms( saberType, &newSaber ) ) {//successfully found a saber .sab entry to use //FIXME: what about dual sabers? int saberNum = 0; if ( saberSolo//only supposed to use this one saber when grab this pickup || newSaber.twoHanded //new saber is two-handed || (hadSaber && self->client->ps.saber[0].twoHanded) )//old saber is two-handed {//replace the old right-hand saber and remove the left hand one WP_RemoveSaber( self, 1 ); } else {//add it as a second saber saberNum = 1; } WP_SetSaber( self, saberNum, saberType ); WP_SaberInitBladeData( self ); if ( self->client->ps.saber[saberNum].style ) { self->client->ps.saberStylesKnown |= (1<<self->client->ps.saber[saberNum].style); } if ( saberColor != NULL ) {//NPC_targetname = saberColor saber_colors_t saber_color = TranslateSaberColor( saberColor ); for ( int bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ ) { self->client->ps.saber[saberNum].blade[bladeNum].color = saber_color; } } } WP_SaberFreeStrings(newSaber); } }
qboolean Pickup_Saber( gentity_t *self, qboolean hadSaber, gentity_t *pickUpSaber ) { //NOTE: loopAnim = saberSolo, alt_fire = saberLeftHand, NPC_type = saberType, NPC_targetname = saberColor qboolean foundIt = qfalse; if ( !pickUpSaber || !self || !self->client ) { return qfalse; } //G_RemoveWeaponModels( ent );//??? if ( Q_stricmp( "player", pickUpSaber->NPC_type ) == 0 ) {//"player" means use cvar info G_SetSabersFromCVars( self ); foundIt = qtrue; } else { saberInfo_t newSaber={0}; qboolean swapSabers = qfalse; if ( self->client->ps.weapon == WP_SABER && self->client->ps.weaponTime > 0 ) {//can't pick up a new saber while the old one is busy (also helps to work as a debouncer so you don't swap out sabers rapidly when touching more than one at a time) return qfalse; } if ( pickUpSaber->count == 1 && g_saberPickuppableDroppedSabers->integer ) { swapSabers = qtrue; } if ( WP_SaberParseParms( pickUpSaber->NPC_type, &newSaber ) ) {//successfully found a saber .sab entry to use int saberNum = 0; qboolean removeLeftSaber = qfalse; if ( pickUpSaber->alt_fire ) {//always go in the left hand if ( !hadSaber ) {//can't have a saber only in your left hand! return qfalse; } saberNum = 1; //just in case... removeLeftSaber = qtrue; } else if ( !hadSaber ) {//don't have a saber at all yet, put it in our right hand saberNum = 0; //just in case... removeLeftSaber = qtrue; } else if ( pickUpSaber->loopAnim//only supposed to use this one saber when grab this pickup || (newSaber.saberFlags&SFL_TWO_HANDED) //new saber is two-handed || (hadSaber && (self->client->ps.saber[0].saberFlags&SFL_TWO_HANDED)) )//old saber is two-handed {//replace the old right-hand saber and remove the left hand one saberNum = 0; removeLeftSaber = qtrue; } else {//have, at least, a saber in our right hand and the new one could go in either left or right hand if ( self->client->ps.dualSabers ) {//I already have 2 sabers vec3_t dir2Saber, rightDir; //to determine which one to replace, see which side of me it's on VectorSubtract( pickUpSaber->currentOrigin, self->currentOrigin, dir2Saber ); dir2Saber[2] = 0; AngleVectors( self->currentAngles, NULL, rightDir, NULL ); rightDir[2] = 0; if ( DotProduct( rightDir, dir2Saber ) > 0 ) { saberNum = 0; } else { saberNum = 1; //just in case... removeLeftSaber = qtrue; } } else {//just add it as a second saber saberNum = 1; //just in case... removeLeftSaber = qtrue; } } if ( saberNum == 0 ) {//want to reach out with right hand if ( self->client->ps.torsoAnim == BOTH_BUTTON_HOLD ) {//but only if already playing the pickup with left hand anim... NPC_SetAnim( self, SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } if ( swapSabers ) {//drop first one where the one we're picking up is G_DropSaberItem( self->client->ps.saber[saberNum].name, self->client->ps.saber[saberNum].blade[0].color, pickUpSaber->currentOrigin, (float *)vec3_origin, pickUpSaber->currentAngles, pickUpSaber ); if ( removeLeftSaber ) {//drop other one at my origin G_DropSaberItem( self->client->ps.saber[1].name, self->client->ps.saber[1].blade[0].color, self->currentOrigin, (float *)vec3_origin, self->currentAngles, pickUpSaber ); } } } else { if ( swapSabers ) { G_DropSaberItem( self->client->ps.saber[saberNum].name, self->client->ps.saber[saberNum].blade[0].color, pickUpSaber->currentOrigin, (float *)vec3_origin, pickUpSaber->currentAngles, pickUpSaber ); } } if ( removeLeftSaber ) { WP_RemoveSaber( self, 1 ); } WP_SetSaber( self, saberNum, pickUpSaber->NPC_type ); WP_SaberInitBladeData( self ); if ( self->client->ps.saber[saberNum].stylesLearned ) { self->client->ps.saberStylesKnown |= self->client->ps.saber[saberNum].stylesLearned; } if ( self->client->ps.saber[saberNum].singleBladeStyle ) { self->client->ps.saberStylesKnown |= self->client->ps.saber[saberNum].singleBladeStyle; } if ( pickUpSaber->NPC_targetname != NULL ) {//NPC_targetname = saberColor saber_colors_t saber_color = TranslateSaberColor( pickUpSaber->NPC_targetname ); for ( int bladeNum = 0; bladeNum < MAX_BLADES; bladeNum++ ) { self->client->ps.saber[saberNum].blade[bladeNum].color = saber_color; } } if ( self->client->ps.torsoAnim == BOTH_BUTTON_HOLD || self->client->ps.torsoAnim == BOTH_SABERPULL ) {//don't let them attack right away, force them to finish the anim self->client->ps.weaponTime = self->client->ps.torsoAnimTimer; } foundIt = qtrue; } WP_SaberFreeStrings(newSaber); } return foundIt; }
/* ================ G_BounceItem ================ */ void G_BounceItem( gentity_t *ent, trace_t *trace ) { vec3_t velocity; float dot; int hitTime; qboolean droppedSaber = qtrue; if ( ent->item && ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER && (ent->flags&FL_DROPPED_ITEM) ) { droppedSaber = qtrue; } // reflect the velocity on the trace plane hitTime = level.previousTime + ( level.time - level.previousTime ) * trace->fraction; EvaluateTrajectoryDelta( &ent->s.pos, hitTime, velocity ); dot = DotProduct( velocity, trace->plane.normal ); VectorMA( velocity, -2*dot, trace->plane.normal, ent->s.pos.trDelta ); // cut the velocity to keep from bouncing forever VectorScale( ent->s.pos.trDelta, ent->physicsBounce, ent->s.pos.trDelta ); if ( droppedSaber ) {//a dropped saber item //FIXME: use NPC_type (as saberType) to get proper bounce sound? WP_SaberFallSound( NULL, ent ); } // check for stop if ( trace->plane.normal[2] > 0 && ent->s.pos.trDelta[2] < 40 ) {//stop G_SetOrigin( ent, trace->endpos ); ent->s.groundEntityNum = trace->entityNum; if ( droppedSaber ) {//a dropped saber item //stop rotation VectorClear( ent->s.apos.trDelta ); ent->currentAngles[PITCH] = SABER_PITCH_HACK; ent->currentAngles[ROLL] = 0; if ( ent->NPC_type && ent->NPC_type[0] ) {//we have a valid saber for this saberInfo_t saber; if ( WP_SaberParseParms( ent->NPC_type, &saber ) ) { if ( (saber.saberFlags&SFL_BOLT_TO_WRIST) ) { ent->currentAngles[PITCH] = 0; } } } pitch_roll_for_slope( ent, trace->plane.normal, ent->currentAngles, qtrue ); G_SetAngles( ent, ent->currentAngles ); } return; } //bounce if ( droppedSaber ) {//a dropped saber item //change rotation VectorCopy( ent->currentAngles, ent->s.apos.trBase ); ent->s.apos.trType = TR_LINEAR; ent->s.apos.trTime = level.time; VectorSet( ent->s.apos.trDelta, Q_irand( -300, 300 ), Q_irand( -300, 300 ), Q_irand( -300, 300 ) ); } VectorAdd( ent->currentOrigin, trace->plane.normal, ent->currentOrigin); VectorCopy( ent->currentOrigin, ent->s.pos.trBase ); ent->s.pos.trTime = level.time; }
void FinishSpawningItem( gentity_t *ent ) { trace_t tr; vec3_t dest; gitem_t *item; int itemNum; itemNum=1; for ( item = bg_itemlist + 1 ; item->classname ; item++,itemNum++) { if (!strcmp(item->classname,ent->classname)) { break; } } // Set bounding box for item VectorSet( ent->mins, item->mins[0],item->mins[1] ,item->mins[2]); VectorSet( ent->maxs, item->maxs[0],item->maxs[1] ,item->maxs[2]); if ((!ent->mins[0] && !ent->mins[1] && !ent->mins[2]) && (!ent->maxs[0] && !ent->maxs[1] && !ent->maxs[2])) { VectorSet (ent->mins, -ITEM_RADIUS, -ITEM_RADIUS, -2);//to match the comments in the items.dat file! VectorSet (ent->maxs, ITEM_RADIUS, ITEM_RADIUS, ITEM_RADIUS); } if ((item->quantity) && (item->giType == IT_AMMO)) { ent->count = item->quantity; } if ((item->quantity) && (item->giType == IT_BATTERY)) { ent->count = item->quantity; } ent->s.radius = 20; VectorSet( ent->s.modelScale, 1.0f, 1.0f, 1.0f ); if ( ent->item->giType == IT_WEAPON && ent->item->giTag == WP_SABER && ent->NPC_type && ent->NPC_type[0] ) { saberInfo_t itemSaber; if ( Q_stricmp( "player", ent->NPC_type ) == 0 && g_saber->string && g_saber->string[0] && Q_stricmp( "none", g_saber->string ) && Q_stricmp( "NULL", g_saber->string ) ) {//player's saber WP_SaberParseParms( g_saber->string, &itemSaber ); } else {//specific saber WP_SaberParseParms( ent->NPC_type, &itemSaber ); } //NOTE: should I keep this string around for any reason? Will I ever need it later? //ent->??? = G_NewString( itemSaber.model ); gi.G2API_InitGhoul2Model( ent->ghoul2, itemSaber.model, G_ModelIndex( itemSaber.model ), NULL_HANDLE, NULL_HANDLE, 0, 0); WP_SaberFreeStrings(itemSaber); } else { gi.G2API_InitGhoul2Model( ent->ghoul2, ent->item->world_model, G_ModelIndex( ent->item->world_model ), NULL_HANDLE, NULL_HANDLE, 0, 0); } // Set crystal ammo amount based on skill level /* if ((itemNum == ITM_AMMO_CRYSTAL_BORG) || (itemNum == ITM_AMMO_CRYSTAL_DN) || (itemNum == ITM_AMMO_CRYSTAL_FORGE) || (itemNum == ITM_AMMO_CRYSTAL_SCAVENGER) || (itemNum == ITM_AMMO_CRYSTAL_STASIS)) { CrystalAmmoSettings(ent); } */ ent->s.eType = ET_ITEM; ent->s.modelindex = ent->item - bg_itemlist; // store item number in modelindex ent->s.modelindex2 = 0; // zero indicates this isn't a dropped item ent->contents = CONTENTS_TRIGGER|CONTENTS_ITEM;//CONTENTS_BODY;//CONTENTS_TRIGGER| ent->e_TouchFunc = touchF_Touch_Item; // useing an item causes it to respawn ent->e_UseFunc = useF_Use_Item; ent->svFlags |= SVF_PLAYER_USABLE;//so player can pick it up // Hang in air? ent->s.origin[2] += 1;//just to get it off the damn ground because coplanar = insolid if ( (ent->spawnflags&ITMSF_SUSPEND) || (ent->flags&FL_DROPPED_ITEM) ) { // suspended G_SetOrigin( ent, ent->s.origin ); } else { // drop to floor VectorSet( dest, ent->s.origin[0], ent->s.origin[1], MIN_WORLD_COORD ); gi.trace( &tr, ent->s.origin, ent->mins, ent->maxs, dest, ent->s.number, MASK_SOLID|CONTENTS_PLAYERCLIP, (EG2_Collision)0, 0 ); if ( tr.startsolid ) { if ( &g_entities[tr.entityNum] != NULL ) { gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin), g_entities[tr.entityNum].classname ); } else { gi.Printf (S_COLOR_RED"FinishSpawningItem: removing %s startsolid at %s (in a %s)\n", ent->classname, vtos(ent->s.origin) ); } assert( 0 && "item starting in solid"); if (!g_entities[ENTITYNUM_WORLD].s.radius){ //not a region delayedShutDown = level.time + 100; } G_FreeEntity( ent ); return; } // allow to ride movers ent->s.groundEntityNum = tr.entityNum; G_SetOrigin( ent, tr.endpos ); } /* ? don't need this // team slaves and targeted items aren't present at start if ( ( ent->flags & FL_TEAMSLAVE ) || ent->targetname ) { ent->s.eFlags |= EF_NODRAW; ent->contents = 0; return; } */ if ( ent->spawnflags & ITMSF_INVISIBLE ) // invisible { ent->s.eFlags |= EF_NODRAW; ent->contents = 0; } if ( ent->spawnflags & ITMSF_NOTSOLID ) // not solid { ent->contents = 0; } if ( (ent->spawnflags&ITMSF_STATIONARY) ) {//can't be pushed around ent->flags |= FL_NO_KNOCKBACK; } if ( (ent->flags&FL_DROPPED_ITEM) ) {//go away after 30 seconds ent->e_ThinkFunc = thinkF_G_FreeEntity; ent->nextthink = level.time + 30000; } gi.linkentity (ent); }