00001
00002
00003
00004 #include "g_local.h"
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 void MatchTeam( gentity_t *teamLeader, int moverState, int time );
00017
00018 typedef struct {
00019 gentity_t *ent;
00020 vec3_t origin;
00021 vec3_t angles;
00022 float deltayaw;
00023 } pushed_t;
00024 pushed_t pushed[MAX_GENTITIES], *pushed_p;
00025
00026 #define MOVER_START_ON 1
00027 #define MOVER_FORCE_ACTIVATE 2
00028 #define MOVER_CRUSHER 4
00029 #define MOVER_TOGGLE 8
00030 #define MOVER_LOCKED 16
00031 #define MOVER_GOODIE 32
00032 #define MOVER_PLAYER_USE 64
00033 #define MOVER_INACTIVE 128
00034
00035 int BMS_START = 0;
00036 int BMS_MID = 1;
00037 int BMS_END = 2;
00038
00039
00040
00041
00042
00043
00044
00045 void G_PlayDoorLoopSound( gentity_t *ent )
00046 {
00047 if (!ent->soundSet || !ent->soundSet[0])
00048 {
00049 return;
00050 }
00051
00052 ent->s.soundSetIndex = G_SoundSetIndex(ent->soundSet);
00053 ent->s.loopIsSoundset = qtrue;
00054 ent->s.loopSound = BMS_MID;
00055
00056
00057
00058
00059
00060 }
00061
00062
00063
00064
00065
00066
00067
00068 void G_PlayDoorSound( gentity_t *ent, int type )
00069 {
00070 if (!ent->soundSet || !ent->soundSet[0])
00071 {
00072 return;
00073 }
00074
00075 ent->s.soundSetIndex = G_SoundSetIndex(ent->soundSet);
00076
00077 G_AddEvent(ent, EV_PLAYDOORSOUND, type);
00078 }
00079
00080
00081
00082
00083
00084
00085
00086 gentity_t *G_TestEntityPosition( gentity_t *ent ) {
00087 trace_t tr;
00088 int mask;
00089
00090 if ( ent->clipmask ) {
00091 mask = ent->clipmask;
00092 } else {
00093 mask = MASK_SOLID;
00094 }
00095 if ( ent->client ) {
00096 vec3_t vMax;
00097 VectorCopy(ent->r.maxs, vMax);
00098 if (vMax[2] < 1)
00099 {
00100 vMax[2] = 1;
00101 }
00102 trap_Trace( &tr, ent->client->ps.origin, ent->r.mins, vMax, ent->client->ps.origin, ent->s.number, mask );
00103 } else {
00104 trap_Trace( &tr, ent->s.pos.trBase, ent->r.mins, ent->r.maxs, ent->s.pos.trBase, ent->s.number, mask );
00105 }
00106
00107 if (tr.startsolid)
00108 return &g_entities[ tr.entityNum ];
00109
00110 return NULL;
00111 }
00112
00113
00114
00115
00116
00117
00118 void G_CreateRotationMatrix(vec3_t angles, vec3_t matrix[3]) {
00119 AngleVectors(angles, matrix[0], matrix[1], matrix[2]);
00120 VectorInverse(matrix[1]);
00121 }
00122
00123
00124
00125
00126
00127
00128 void G_TransposeMatrix(vec3_t matrix[3], vec3_t transpose[3]) {
00129 int i, j;
00130 for (i = 0; i < 3; i++) {
00131 for (j = 0; j < 3; j++) {
00132 transpose[i][j] = matrix[j][i];
00133 }
00134 }
00135 }
00136
00137
00138
00139
00140
00141
00142 void G_RotatePoint(vec3_t point, vec3_t matrix[3]) {
00143 vec3_t tvec;
00144
00145 VectorCopy(point, tvec);
00146 point[0] = DotProduct(matrix[0], tvec);
00147 point[1] = DotProduct(matrix[1], tvec);
00148 point[2] = DotProduct(matrix[2], tvec);
00149 }
00150
00151
00152
00153
00154
00155
00156
00157
00158 qboolean G_TryPushingEntity( gentity_t *check, gentity_t *pusher, vec3_t move, vec3_t amove ) {
00159 vec3_t matrix[3], transpose[3];
00160 vec3_t org, org2, move2;
00161 gentity_t *block;
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172 if ( pusher->s.apos.trType != TR_STATIONARY
00173 && (pusher->spawnflags&16)
00174 && Q_stricmp( "func_rotating", pusher->classname ) == 0 )
00175 {
00176 G_Damage( check, pusher, pusher, NULL, NULL, pusher->damage, DAMAGE_NO_KNOCKBACK, MOD_CRUSH );
00177 return qtrue;
00178 }
00179
00180
00181 if (pushed_p > &pushed[MAX_GENTITIES]) {
00182 G_Error( "pushed_p > &pushed[MAX_GENTITIES]" );
00183 }
00184 pushed_p->ent = check;
00185 VectorCopy (check->s.pos.trBase, pushed_p->origin);
00186 VectorCopy (check->s.apos.trBase, pushed_p->angles);
00187 if ( check->client ) {
00188 pushed_p->deltayaw = check->client->ps.delta_angles[YAW];
00189 VectorCopy (check->client->ps.origin, pushed_p->origin);
00190 }
00191 pushed_p++;
00192
00193
00194
00195 G_CreateRotationMatrix( amove, transpose );
00196 G_TransposeMatrix( transpose, matrix );
00197 if ( check->client ) {
00198 VectorSubtract (check->client->ps.origin, pusher->r.currentOrigin, org);
00199 }
00200 else {
00201 VectorSubtract (check->s.pos.trBase, pusher->r.currentOrigin, org);
00202 }
00203 VectorCopy( org, org2 );
00204 G_RotatePoint( org2, matrix );
00205 VectorSubtract (org2, org, move2);
00206
00207 VectorAdd (check->s.pos.trBase, move, check->s.pos.trBase);
00208 VectorAdd (check->s.pos.trBase, move2, check->s.pos.trBase);
00209 if ( check->client ) {
00210 VectorAdd (check->client->ps.origin, move, check->client->ps.origin);
00211 VectorAdd (check->client->ps.origin, move2, check->client->ps.origin);
00212
00213 check->client->ps.delta_angles[YAW] += ANGLE2SHORT(amove[YAW]);
00214 }
00215
00216
00217 if ( check->s.groundEntityNum != pusher->s.number ) {
00218 check->s.groundEntityNum = ENTITYNUM_NONE;
00219 }
00220
00221 block = G_TestEntityPosition( check );
00222 if (!block) {
00223
00224 if ( check->client ) {
00225 VectorCopy( check->client->ps.origin, check->r.currentOrigin );
00226 } else {
00227 VectorCopy( check->s.pos.trBase, check->r.currentOrigin );
00228 }
00229 trap_LinkEntity (check);
00230 return qtrue;
00231 }
00232
00233 if (check->takedamage && !check->client && check->s.weapon && check->r.ownerNum < MAX_CLIENTS &&
00234 check->health < 500)
00235 {
00236 if (check->health > 0)
00237 {
00238 G_Damage(check, pusher, pusher, vec3_origin, check->r.currentOrigin, 999, 0, MOD_UNKNOWN);
00239 }
00240 return qfalse;
00241 }
00242
00243
00244
00245 VectorCopy( (pushed_p-1)->origin, check->s.pos.trBase);
00246 if ( check->client ) {
00247 VectorCopy( (pushed_p-1)->origin, check->client->ps.origin);
00248 }
00249 VectorCopy( (pushed_p-1)->angles, check->s.apos.trBase );
00250 block = G_TestEntityPosition (check);
00251 if ( !block ) {
00252 check->s.groundEntityNum = -1;
00253 pushed_p--;
00254 return qtrue;
00255 }
00256
00257
00258 return qfalse;
00259 }
00260
00261
00262 void G_ExplodeMissile( gentity_t *ent );
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273 qboolean G_MoverPush( gentity_t *pusher, vec3_t move, vec3_t amove, gentity_t **obstacle ) {
00274 int i, e;
00275 gentity_t *check;
00276 vec3_t mins, maxs;
00277 pushed_t *p;
00278 int entityList[MAX_GENTITIES];
00279 int listedEntities;
00280 vec3_t totalMins, totalMaxs;
00281
00282 *obstacle = NULL;
00283
00284
00285
00286
00287 if ( pusher->r.currentAngles[0] || pusher->r.currentAngles[1] || pusher->r.currentAngles[2]
00288 || amove[0] || amove[1] || amove[2] ) {
00289 float radius;
00290
00291 radius = RadiusFromBounds( pusher->r.mins, pusher->r.maxs );
00292 for ( i = 0 ; i < 3 ; i++ ) {
00293 mins[i] = pusher->r.currentOrigin[i] + move[i] - radius;
00294 maxs[i] = pusher->r.currentOrigin[i] + move[i] + radius;
00295 totalMins[i] = mins[i] - move[i];
00296 totalMaxs[i] = maxs[i] - move[i];
00297 }
00298 } else {
00299 for (i=0 ; i<3 ; i++) {
00300 mins[i] = pusher->r.absmin[i] + move[i];
00301 maxs[i] = pusher->r.absmax[i] + move[i];
00302 }
00303
00304 VectorCopy( pusher->r.absmin, totalMins );
00305 VectorCopy( pusher->r.absmax, totalMaxs );
00306 for (i=0 ; i<3 ; i++) {
00307 if ( move[i] > 0 ) {
00308 totalMaxs[i] += move[i];
00309 } else {
00310 totalMins[i] += move[i];
00311 }
00312 }
00313 }
00314
00315
00316 trap_UnlinkEntity( pusher );
00317
00318 listedEntities = trap_EntitiesInBox( totalMins, totalMaxs, entityList, MAX_GENTITIES );
00319
00320
00321 VectorAdd( pusher->r.currentOrigin, move, pusher->r.currentOrigin );
00322 VectorAdd( pusher->r.currentAngles, amove, pusher->r.currentAngles );
00323 trap_LinkEntity( pusher );
00324
00325
00326 for ( e = 0 ; e < listedEntities ; e++ ) {
00327 check = &g_entities[ entityList[ e ] ];
00328
00329
00330 if ( check->s.eType != ET_PLAYER && check->s.eType != ET_NPC && !check->physicsObject ) {
00331 continue;
00332 }
00333
00334
00335 if ( check->s.groundEntityNum != pusher->s.number ) {
00336
00337 if ( check->r.absmin[0] >= maxs[0]
00338 || check->r.absmin[1] >= maxs[1]
00339 || check->r.absmin[2] >= maxs[2]
00340 || check->r.absmax[0] <= mins[0]
00341 || check->r.absmax[1] <= mins[1]
00342 || check->r.absmax[2] <= mins[2] ) {
00343 continue;
00344 }
00345
00346
00347 if (!G_TestEntityPosition (check)) {
00348 continue;
00349 }
00350 }
00351
00352
00353 if ( G_TryPushingEntity( check, pusher, move, amove ) ) {
00354 continue;
00355 }
00356
00357 if (pusher->damage && check->client && (pusher->spawnflags & 32))
00358 {
00359 G_Damage( check, pusher, pusher, NULL, NULL, pusher->damage, 0, MOD_CRUSH );
00360 continue;
00361 }
00362
00363 if (check->s.eType == ET_BODY ||
00364 (check->s.eType == ET_PLAYER && check->health < 1))
00365 {
00366 G_Damage( check, pusher, pusher, NULL, NULL, 999, 0, MOD_CRUSH );
00367 continue;
00368 }
00369
00370
00371
00372
00373 if ( pusher->s.pos.trType == TR_SINE || pusher->s.apos.trType == TR_SINE ) {
00374 G_Damage( check, pusher, pusher, NULL, NULL, 99999, 0, MOD_CRUSH );
00375 continue;
00376 }
00377
00378
00379
00380 *obstacle = check;
00381
00382
00383
00384
00385 for ( p=pushed_p-1 ; p>=pushed ; p-- ) {
00386 VectorCopy (p->origin, p->ent->s.pos.trBase);
00387 VectorCopy (p->angles, p->ent->s.apos.trBase);
00388 if ( p->ent->client ) {
00389 p->ent->client->ps.delta_angles[YAW] = p->deltayaw;
00390 VectorCopy (p->origin, p->ent->client->ps.origin);
00391 }
00392 trap_LinkEntity (p->ent);
00393 }
00394 return qfalse;
00395 }
00396
00397 return qtrue;
00398 }
00399
00400
00401
00402
00403
00404
00405
00406 void G_MoverTeam( gentity_t *ent ) {
00407 vec3_t move, amove;
00408 gentity_t *part, *obstacle;
00409 vec3_t origin, angles;
00410
00411 obstacle = NULL;
00412
00413
00414
00415
00416 pushed_p = pushed;
00417 for (part = ent ; part ; part=part->teamchain) {
00418
00419 BG_EvaluateTrajectory( &part->s.pos, level.time, origin );
00420 BG_EvaluateTrajectory( &part->s.apos, level.time, angles );
00421 VectorSubtract( origin, part->r.currentOrigin, move );
00422 VectorSubtract( angles, part->r.currentAngles, amove );
00423 if ( !VectorCompare( move, vec3_origin )
00424 || !VectorCompare( amove, vec3_origin ) )
00425 {
00426 if ( !G_MoverPush( part, move, amove, &obstacle ) ) {
00427 break;
00428 }
00429 }
00430 }
00431
00432 if (part) {
00433
00434 for ( part = ent ; part ; part = part->teamchain ) {
00435 part->s.pos.trTime += level.time - level.previousTime;
00436 part->s.apos.trTime += level.time - level.previousTime;
00437 BG_EvaluateTrajectory( &part->s.pos, level.time, part->r.currentOrigin );
00438 BG_EvaluateTrajectory( &part->s.apos, level.time, part->r.currentAngles );
00439 trap_LinkEntity( part );
00440 }
00441
00442
00443 if (ent->blocked) {
00444 ent->blocked( ent, obstacle );
00445 }
00446 return;
00447 }
00448
00449
00450 for ( part = ent ; part ; part = part->teamchain ) {
00451
00452 if ( part->s.pos.trType == TR_LINEAR_STOP ||
00453 part->s.pos.trType == TR_NONLINEAR_STOP) {
00454 if ( level.time >= part->s.pos.trTime + part->s.pos.trDuration ) {
00455 if ( part->reached ) {
00456 part->reached( part );
00457 }
00458 }
00459 }
00460 }
00461 }
00462
00463
00464
00465
00466
00467
00468
00469 void G_RunMover( gentity_t *ent ) {
00470
00471
00472 if ( ent->flags & FL_TEAMSLAVE ) {
00473 return;
00474 }
00475
00476
00477 if ( ent->s.pos.trType != TR_STATIONARY || ent->s.apos.trType != TR_STATIONARY ) {
00478 G_MoverTeam( ent );
00479 }
00480
00481
00482 G_RunThink( ent );
00483 }
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501 void CalcTeamDoorCenter ( gentity_t *ent, vec3_t center )
00502 {
00503 vec3_t slavecenter;
00504 gentity_t *slave;
00505
00506
00507 VectorAdd(ent->r.mins, ent->r.maxs, center);
00508 VectorScale(center, 0.5, center);
00509 for ( slave = ent->teamchain ; slave ; slave = slave->teamchain )
00510 {
00511
00512 VectorAdd(slave->r.mins, slave->r.maxs, slavecenter);
00513 VectorScale(slavecenter, 0.5, slavecenter);
00514
00515 VectorAdd(center, slavecenter, center);
00516 VectorScale(center, 0.5, center);
00517 }
00518 }
00519
00520
00521
00522
00523
00524
00525 void SetMoverState( gentity_t *ent, moverState_t moverState, int time ) {
00526 vec3_t delta;
00527 float f;
00528
00529 ent->moverState = moverState;
00530
00531 ent->s.pos.trTime = time;
00532
00533 if ( ent->s.pos.trDuration <= 0 )
00534 {
00535 ent->s.pos.trDuration = 1;
00536 }
00537
00538 switch( moverState ) {
00539 case MOVER_POS1:
00540 VectorCopy( ent->pos1, ent->s.pos.trBase );
00541 ent->s.pos.trType = TR_STATIONARY;
00542 break;
00543 case MOVER_POS2:
00544 VectorCopy( ent->pos2, ent->s.pos.trBase );
00545 ent->s.pos.trType = TR_STATIONARY;
00546 break;
00547 case MOVER_1TO2:
00548 VectorCopy( ent->pos1, ent->s.pos.trBase );
00549 VectorSubtract( ent->pos2, ent->pos1, delta );
00550 f = 1000.0 / ent->s.pos.trDuration;
00551 VectorScale( delta, f, ent->s.pos.trDelta );
00552 if ( ent->alt_fire )
00553 {
00554 ent->s.pos.trType = TR_LINEAR_STOP;
00555 }
00556 else
00557 {
00558 ent->s.pos.trType = TR_NONLINEAR_STOP;
00559 }
00560
00561 break;
00562 case MOVER_2TO1:
00563 VectorCopy( ent->pos2, ent->s.pos.trBase );
00564 VectorSubtract( ent->pos1, ent->pos2, delta );
00565 f = 1000.0 / ent->s.pos.trDuration;
00566 VectorScale( delta, f, ent->s.pos.trDelta );
00567 if ( ent->alt_fire )
00568 {
00569 ent->s.pos.trType = TR_LINEAR_STOP;
00570 }
00571 else
00572 {
00573 ent->s.pos.trType = TR_NONLINEAR_STOP;
00574 }
00575
00576 break;
00577 }
00578 BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin );
00579 trap_LinkEntity( ent );
00580 }
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590 void MatchTeam( gentity_t *teamLeader, int moverState, int time ) {
00591 gentity_t *slave;
00592
00593 for ( slave = teamLeader ; slave ; slave = slave->teamchain ) {
00594 SetMoverState( slave, (moverState_t) moverState, time );
00595 }
00596 }
00597
00598
00599
00600
00601
00602
00603
00604
00605 void ReturnToPos1( gentity_t *ent ) {
00606 ent->think = 0;
00607 ent->nextthink = 0;
00608 ent->s.time = level.time;
00609
00610 MatchTeam( ent, MOVER_2TO1, level.time );
00611
00612
00613 G_PlayDoorLoopSound( ent );
00614 G_PlayDoorSound( ent, BMS_START );
00615 }
00616
00617
00618
00619
00620
00621
00622
00623
00624 void Reached_BinaryMover( gentity_t *ent )
00625 {
00626
00627 ent->s.loopSound = 0;
00628 ent->s.loopIsSoundset = qfalse;
00629
00630 if ( ent->moverState == MOVER_1TO2 )
00631 {
00632 vec3_t doorcenter;
00633
00634
00635 SetMoverState( ent, MOVER_POS2, level.time );
00636
00637 CalcTeamDoorCenter( ent, doorcenter );
00638
00639
00640 G_PlayDoorSound( ent, BMS_END );
00641
00642 if ( ent->wait < 0 )
00643 {
00644 ent->think = 0;
00645 ent->nextthink = 0;
00646 ent->use = 0;
00647 }
00648 else
00649 {
00650
00651 ent->think = ReturnToPos1;
00652 if(ent->spawnflags & 8)
00653 {
00654 ent->nextthink = -1;
00655 }
00656 else
00657 {
00658 ent->nextthink = level.time + ent->wait;
00659 }
00660 }
00661
00662
00663 if ( !ent->activator )
00664 {
00665 ent->activator = ent;
00666 }
00667 G_UseTargets2( ent, ent->activator, ent->opentarget );
00668 }
00669 else if ( ent->moverState == MOVER_2TO1 )
00670 {
00671 vec3_t doorcenter;
00672
00673
00674 SetMoverState( ent, MOVER_POS1, level.time );
00675
00676 CalcTeamDoorCenter( ent, doorcenter );
00677
00678
00679 G_PlayDoorSound( ent, BMS_END );
00680
00681
00682 if ( ent->teammaster == ent || !ent->teammaster )
00683 {
00684 trap_AdjustAreaPortalState( ent, qfalse );
00685 }
00686 G_UseTargets2( ent, ent->activator, ent->closetarget );
00687 }
00688 else
00689 {
00690 G_Error( "Reached_BinaryMover: bad moverState" );
00691 }
00692 }
00693
00694
00695
00696
00697
00698
00699
00700 void Use_BinaryMover_Go( gentity_t *ent )
00701 {
00702 int total;
00703 int partial;
00704
00705 gentity_t *activator = ent->activator;
00706
00707 ent->activator = activator;
00708
00709 if ( ent->moverState == MOVER_POS1 )
00710 {
00711 vec3_t doorcenter;
00712
00713
00714
00715 MatchTeam( ent, MOVER_1TO2, level.time + 50 );
00716
00717 CalcTeamDoorCenter( ent, doorcenter );
00718
00719
00720 G_PlayDoorLoopSound( ent );
00721 G_PlayDoorSound( ent, BMS_START );
00722 ent->s.time = level.time;
00723
00724
00725 if ( ent->teammaster == ent || !ent->teammaster ) {
00726 trap_AdjustAreaPortalState( ent, qtrue );
00727 }
00728 G_UseTargets( ent, ent->activator );
00729 return;
00730 }
00731
00732
00733 if ( ent->moverState == MOVER_POS2 ) {
00734
00735 ent->think = ReturnToPos1;
00736 if ( ent->spawnflags & 8 )
00737 {
00738 ent->nextthink = level.time + FRAMETIME;
00739 }
00740 else
00741 {
00742 ent->nextthink = level.time + ent->wait;
00743 }
00744 G_UseTargets2( ent, ent->activator, ent->target2 );
00745 return;
00746 }
00747
00748
00749 if ( ent->moverState == MOVER_2TO1 )
00750 {
00751 if ( ent->s.pos.trType == TR_NONLINEAR_STOP )
00752 {
00753 vec3_t curDelta;
00754 float fPartial;
00755 total = ent->s.pos.trDuration-50;
00756 VectorSubtract( ent->r.currentOrigin, ent->pos1, curDelta );
00757 fPartial = VectorLength( curDelta )/VectorLength( ent->s.pos.trDelta );
00758 VectorScale( ent->s.pos.trDelta, fPartial, curDelta );
00759 fPartial /= ent->s.pos.trDuration;
00760 fPartial /= 0.001f;
00761 fPartial = acos( fPartial );
00762 fPartial = RAD2DEG( fPartial );
00763 fPartial = (90.0f - fPartial)/90.0f*ent->s.pos.trDuration;
00764 partial = total - floor( fPartial );
00765 }
00766 else
00767 {
00768 total = ent->s.pos.trDuration;
00769 partial = level.time - ent->s.pos.trTime;
00770 }
00771
00772 if ( partial > total ) {
00773 partial = total;
00774 }
00775 ent->s.pos.trTime = level.time - ( total - partial );
00776
00777 MatchTeam( ent, MOVER_1TO2, ent->s.pos.trTime );
00778
00779 G_PlayDoorSound( ent, BMS_START );
00780
00781 return;
00782 }
00783
00784
00785 if ( ent->moverState == MOVER_1TO2 )
00786 {
00787 if ( ent->s.pos.trType == TR_NONLINEAR_STOP )
00788 {
00789 vec3_t curDelta;
00790 float fPartial;
00791 total = ent->s.pos.trDuration-50;
00792 VectorSubtract( ent->r.currentOrigin, ent->pos2, curDelta );
00793 fPartial = VectorLength( curDelta )/VectorLength( ent->s.pos.trDelta );
00794 VectorScale( ent->s.pos.trDelta, fPartial, curDelta );
00795 fPartial /= ent->s.pos.trDuration;
00796 fPartial /= 0.001f;
00797 fPartial = acos( fPartial );
00798 fPartial = RAD2DEG( fPartial );
00799 fPartial = (90.0f - fPartial)/90.0f*ent->s.pos.trDuration;
00800 partial = total - floor( fPartial );
00801 }
00802 else
00803 {
00804 total = ent->s.pos.trDuration;
00805 partial = level.time - ent->s.pos.trTime;
00806 }
00807 if ( partial > total ) {
00808 partial = total;
00809 }
00810
00811 ent->s.pos.trTime = level.time - ( total - partial );
00812 MatchTeam( ent, MOVER_2TO1, ent->s.pos.trTime );
00813
00814 G_PlayDoorSound( ent, BMS_START );
00815
00816 return;
00817 }
00818 }
00819
00820 void UnLockDoors(gentity_t *const ent)
00821 {
00822
00823
00824 gentity_t *slave = ent;
00825 do
00826 {
00827 if( !(slave->spawnflags & MOVER_TOGGLE) )
00828 {
00829 slave->targetname = NULL;
00830 }
00831 slave->spawnflags &= ~MOVER_LOCKED;
00832 slave->s.frame = 1;
00833 slave = slave->teamchain;
00834 } while ( slave );
00835 }
00836 void LockDoors(gentity_t *const ent)
00837 {
00838
00839
00840 gentity_t *slave = ent;
00841 do
00842 {
00843 slave->spawnflags |= MOVER_LOCKED;
00844 slave->s.frame = 0;
00845 slave = slave->teamchain;
00846 } while ( slave );
00847 }
00848
00849
00850
00851
00852
00853 void Use_BinaryMover( gentity_t *ent, gentity_t *other, gentity_t *activator )
00854 {
00855 if ( !ent->use )
00856 {
00857 return;
00858 }
00859
00860
00861 if ( ent->flags & FL_TEAMSLAVE )
00862 {
00863 Use_BinaryMover( ent->teammaster, other, activator );
00864 return;
00865 }
00866
00867 if ( ent->flags & FL_INACTIVE )
00868 {
00869 return;
00870 }
00871
00872 if ( ent->spawnflags & MOVER_LOCKED )
00873 {
00874 UnLockDoors(ent);
00875 return;
00876 }
00877
00878 G_ActivateBehavior(ent,BSET_USE);
00879
00880 ent->enemy = other;
00881 ent->activator = activator;
00882 if(ent->delay)
00883 {
00884 ent->think = Use_BinaryMover_Go;
00885 ent->nextthink = level.time + ent->delay;
00886 }
00887 else
00888 {
00889 Use_BinaryMover_Go(ent);
00890 }
00891 }
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903 void InitMoverTrData( gentity_t *ent )
00904 {
00905 vec3_t move;
00906 float distance;
00907
00908 ent->s.pos.trType = TR_STATIONARY;
00909 VectorCopy( ent->pos1, ent->s.pos.trBase );
00910
00911
00912 VectorSubtract( ent->pos2, ent->pos1, move );
00913 distance = VectorLength( move );
00914 if ( ! ent->speed )
00915 {
00916 ent->speed = 100;
00917 }
00918 VectorScale( move, ent->speed, ent->s.pos.trDelta );
00919 ent->s.pos.trDuration = distance * 1000 / ent->speed;
00920 if ( ent->s.pos.trDuration <= 0 )
00921 {
00922 ent->s.pos.trDuration = 1;
00923 }
00924 }
00925
00926 void InitMover( gentity_t *ent )
00927 {
00928 float light;
00929 vec3_t color;
00930 qboolean lightSet, colorSet;
00931
00932
00933
00934 if ( ent->model2 )
00935 {
00936 if ( strstr( ent->model2, ".glm" ))
00937 {
00938 ent->s.modelindex2 = 0;
00939 }
00940 else
00941 {
00942 ent->s.modelindex2 = G_ModelIndex( ent->model2 );
00943 }
00944 }
00945
00946
00947 lightSet = G_SpawnFloat( "light", "100", &light );
00948 colorSet = G_SpawnVector( "color", "1 1 1", color );
00949 if ( lightSet || colorSet ) {
00950 int r, g, b, i;
00951
00952 r = color[0] * 255;
00953 if ( r > 255 ) {
00954 r = 255;
00955 }
00956 g = color[1] * 255;
00957 if ( g > 255 ) {
00958 g = 255;
00959 }
00960 b = color[2] * 255;
00961 if ( b > 255 ) {
00962 b = 255;
00963 }
00964 i = light / 4;
00965 if ( i > 255 ) {
00966 i = 255;
00967 }
00968 ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
00969 }
00970
00971 ent->use = Use_BinaryMover;
00972 ent->reached = Reached_BinaryMover;
00973
00974 ent->moverState = MOVER_POS1;
00975 ent->r.svFlags = SVF_USE_CURRENT_ORIGIN;
00976 if ( ent->spawnflags & MOVER_INACTIVE )
00977 {
00978 ent->flags |= FL_INACTIVE;
00979 }
00980 if(ent->spawnflags & MOVER_PLAYER_USE)
00981 {
00982 ent->r.svFlags |= SVF_PLAYER_USABLE;
00983 }
00984 ent->s.eType = ET_MOVER;
00985 VectorCopy( ent->pos1, ent->r.currentOrigin );
00986 trap_LinkEntity( ent );
00987
00988 InitMoverTrData( ent );
00989 }
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008 void Blocked_Door( gentity_t *ent, gentity_t *other )
01009 {
01010 if ( ent->damage ) {
01011 G_Damage( other, ent, ent, NULL, NULL, ent->damage, 0, MOD_CRUSH );
01012 }
01013 if ( ent->spawnflags & MOVER_CRUSHER ) {
01014 return;
01015 }
01016
01017
01018 Use_BinaryMover( ent, ent, other );
01019 }
01020
01021
01022
01023
01024
01025
01026
01027 static void Touch_DoorTriggerSpectator( gentity_t *ent, gentity_t *other, trace_t *trace ) {
01028 int i, axis;
01029 trace_t tr;
01030 vec3_t pMins, pMaxs;
01031 vec3_t origin, dir, angles;
01032
01033 axis = ent->count;
01034 VectorClear(dir);
01035 if (fabs(other->s.origin[axis] - ent->r.absmax[axis]) <
01036 fabs(other->s.origin[axis] - ent->r.absmin[axis])) {
01037 origin[axis] = ent->r.absmin[axis] - 10;
01038 dir[axis] = -1;
01039 }
01040 else {
01041 origin[axis] = ent->r.absmax[axis] + 10;
01042 dir[axis] = 1;
01043 }
01044 for (i = 0; i < 3; i++) {
01045 if (i == axis) continue;
01046 origin[i] = (ent->r.absmin[i] + ent->r.absmax[i]) * 0.5;
01047 }
01048
01049 vectoangles(dir, angles);
01050
01051 VectorSet(pMins, -15.0f, -15.0f, DEFAULT_MINS_2);
01052 VectorSet(pMaxs, 15.0f, 15.0f, DEFAULT_MAXS_2);
01053 trap_Trace(&tr, origin, pMins, pMaxs, origin, other->s.number, other->clipmask);
01054 if (!tr.startsolid &&
01055 !tr.allsolid &&
01056 tr.fraction == 1.0f &&
01057 tr.entityNum == ENTITYNUM_NONE)
01058 {
01059 TeleportPlayer(other, origin, angles );
01060 }
01061 }
01062
01063
01064
01065
01066
01067
01068 void Touch_DoorTrigger( gentity_t *ent, gentity_t *other, trace_t *trace )
01069 {
01070 gentity_t *relockEnt = NULL;
01071
01072 if ( other->client && other->client->sess.sessionTeam == TEAM_SPECTATOR )
01073 {
01074
01075 if ( ent->parent->moverState != MOVER_1TO2 &&
01076 ent->parent->moverState != MOVER_POS2 )
01077 {
01078 Touch_DoorTriggerSpectator( ent, other, trace );
01079 }
01080 return;
01081 }
01082
01083 if (!ent->genericValue14 &&
01084 (!ent->parent || !ent->parent->genericValue14))
01085 {
01086 if (other->client && other->s.number >= MAX_CLIENTS &&
01087 other->s.eType == ET_NPC && other->s.NPC_class == CLASS_VEHICLE)
01088 {
01089 return;
01090 }
01091
01092 if (other->client && other->s.number < MAX_CLIENTS &&
01093 other->client->ps.m_iVehicleNum)
01094 {
01095 return;
01096 }
01097 }
01098
01099 if ( ent->flags & FL_INACTIVE )
01100 {
01101 return;
01102 }
01103
01104 if ( ent->parent->spawnflags & MOVER_LOCKED )
01105 {
01106 if ( !ent->parent->alliedTeam
01107 || !other->client
01108 || other->client->sess.sessionTeam != ent->parent->alliedTeam )
01109 {
01110 return;
01111 }
01112 else
01113 {
01114 if ( ent->parent->flags & FL_TEAMSLAVE )
01115 {
01116 relockEnt = ent->parent->teammaster;
01117 }
01118 else
01119 {
01120 relockEnt = ent->parent;
01121 }
01122 if ( relockEnt != NULL )
01123 {
01124 relockEnt->spawnflags &= ~MOVER_LOCKED;
01125 }
01126 }
01127 }
01128
01129 if ( ent->parent->moverState != MOVER_1TO2 )
01130 {
01131
01132
01133
01134
01135 Use_BinaryMover( ent->parent, ent, other );
01136 }
01137 if ( relockEnt != NULL )
01138 {
01139 relockEnt->spawnflags |= MOVER_LOCKED;
01140 }
01141
01142
01143
01144
01145
01146
01147
01148 }
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158 void Think_SpawnNewDoorTrigger( gentity_t *ent )
01159 {
01160 gentity_t *other;
01161 vec3_t mins, maxs;
01162 int i, best;
01163
01164
01165 if ( ent->takedamage )
01166 {
01167 for ( other = ent ; other ; other = other->teamchain )
01168 {
01169 other->takedamage = qtrue;
01170 }
01171 }
01172
01173
01174 VectorCopy (ent->r.absmin, mins);
01175 VectorCopy (ent->r.absmax, maxs);
01176
01177 for (other = ent->teamchain ; other ; other=other->teamchain) {
01178 AddPointToBounds (other->r.absmin, mins, maxs);
01179 AddPointToBounds (other->r.absmax, mins, maxs);
01180 }
01181
01182
01183 best = 0;
01184 for ( i = 1 ; i < 3 ; i++ ) {
01185 if ( maxs[i] - mins[i] < maxs[best] - mins[best] ) {
01186 best = i;
01187 }
01188 }
01189 maxs[best] += 120;
01190 mins[best] -= 120;
01191
01192
01193 other = G_Spawn ();
01194 VectorCopy (mins, other->r.mins);
01195 VectorCopy (maxs, other->r.maxs);
01196 other->parent = ent;
01197 other->r.contents = CONTENTS_TRIGGER;
01198 other->touch = Touch_DoorTrigger;
01199 trap_LinkEntity (other);
01200 other->classname = "trigger_door";
01201
01202 other->count = best;
01203
01204 MatchTeam( ent, ent->moverState, level.time );
01205 }
01206
01207 void Think_MatchTeam( gentity_t *ent )
01208 {
01209 MatchTeam( ent, ent->moverState, level.time );
01210 }
01211
01212 qboolean G_EntIsDoor( int entityNum )
01213 {
01214 gentity_t *ent;
01215
01216 if ( entityNum < 0 || entityNum >= ENTITYNUM_WORLD )
01217 {
01218 return qfalse;
01219 }
01220
01221 ent = &g_entities[entityNum];
01222 if ( ent && !Q_stricmp( "func_door", ent->classname ) )
01223 {
01224 return qtrue;
01225 }
01226 return qfalse;
01227 }
01228
01229 gentity_t *G_FindDoorTrigger( gentity_t *ent )
01230 {
01231 gentity_t *owner = NULL;
01232 gentity_t *door = ent;
01233 if ( door->flags & FL_TEAMSLAVE )
01234 {
01235 while ( door->teammaster && (door->flags&FL_TEAMSLAVE))
01236 {
01237 door = door->teammaster;
01238 }
01239 }
01240 if ( door->targetname )
01241 {
01242
01243 while ( (owner = G_Find( owner, FOFS( target ), door->targetname )) != NULL )
01244 {
01245 if ( owner && (owner->r.contents&CONTENTS_TRIGGER) )
01246 {
01247 return owner;
01248 }
01249 }
01250 owner = NULL;
01251 while ( (owner = G_Find( owner, FOFS( target2 ), door->targetname )) != NULL )
01252 {
01253 if ( owner && (owner->r.contents&CONTENTS_TRIGGER) )
01254 {
01255 return owner;
01256 }
01257 }
01258 }
01259
01260 owner = NULL;
01261 while ( (owner = G_Find( owner, FOFS( classname ), "trigger_door" )) != NULL )
01262 {
01263 if ( owner->parent == door )
01264 {
01265 return owner;
01266 }
01267 }
01268
01269 return NULL;
01270 }
01271
01272 qboolean G_EntIsUnlockedDoor( int entityNum )
01273 {
01274 if ( entityNum < 0 || entityNum >= ENTITYNUM_WORLD )
01275 {
01276 return qfalse;
01277 }
01278
01279 if ( G_EntIsDoor( entityNum ) )
01280 {
01281 gentity_t *ent = &g_entities[entityNum];
01282 gentity_t *owner = NULL;
01283 if ( ent->flags & FL_TEAMSLAVE )
01284 {
01285 while ( ent->teammaster && (ent->flags&FL_TEAMSLAVE))
01286 {
01287 ent = ent->teammaster;
01288 }
01289 }
01290 if ( ent->targetname )
01291 {
01292 owner = NULL;
01293
01294 while ( (owner = G_Find( owner, FOFS( target ), ent->targetname )) != NULL )
01295 {
01296 if ( !Q_stricmp( "trigger_multiple", owner->classname ) )
01297 {
01298 if ( !(owner->flags & FL_INACTIVE) )
01299 {
01300 return qtrue;
01301 }
01302 }
01303 }
01304 owner = NULL;
01305 while ( (owner = G_Find( owner, FOFS( target2 ), ent->targetname )) != NULL )
01306 {
01307 if ( !Q_stricmp( "trigger_multiple", owner->classname ) )
01308 {
01309 if ( !(owner->flags & FL_INACTIVE) )
01310 {
01311 return qtrue;
01312 }
01313 }
01314 }
01315 return qfalse;
01316 }
01317 else
01318 {
01319 owner = G_FindDoorTrigger( ent );
01320 if ( owner && (owner->flags&FL_INACTIVE) )
01321 {
01322 return qfalse;
01323 }
01324 }
01325 if ( !(ent->flags & FL_INACTIVE) &&
01326 !ent->health &&
01327 !(ent->spawnflags & MOVER_PLAYER_USE) &&
01328 !(ent->spawnflags & MOVER_FORCE_ACTIVATE) &&
01329 !(ent->spawnflags & MOVER_LOCKED))
01330
01331 {
01332 return qtrue;
01333 }
01334 }
01335 return qfalse;
01336 }
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370 void SP_func_door (gentity_t *ent)
01371 {
01372 vec3_t abs_movedir;
01373 float distance;
01374 vec3_t size;
01375 float lip;
01376
01377 G_SpawnInt("vehopen", "0", &ent->genericValue14);
01378
01379 ent->blocked = Blocked_Door;
01380
01381
01382 if (!ent->speed)
01383 ent->speed = 400;
01384
01385
01386 if (!ent->wait)
01387 ent->wait = 2;
01388 ent->wait *= 1000;
01389
01390 ent->delay *= 1000;
01391
01392
01393 G_SpawnFloat( "lip", "8", &lip );
01394
01395
01396 G_SpawnInt( "dmg", "2", &ent->damage );
01397 if ( ent->damage < 0 )
01398 {
01399 ent->damage = 0;
01400 }
01401
01402 G_SpawnInt( "teamallow", "0", &ent->alliedTeam );
01403
01404
01405 VectorCopy( ent->s.origin, ent->pos1 );
01406
01407
01408 trap_SetBrushModel( ent, ent->model );
01409 G_SetMovedir( ent->s.angles, ent->movedir );
01410 abs_movedir[0] = fabs( ent->movedir[0] );
01411 abs_movedir[1] = fabs( ent->movedir[1] );
01412 abs_movedir[2] = fabs( ent->movedir[2] );
01413 VectorSubtract( ent->r.maxs, ent->r.mins, size );
01414 distance = DotProduct( abs_movedir, size ) - lip;
01415 VectorMA( ent->pos1, distance, ent->movedir, ent->pos2 );
01416
01417
01418 if ( ent->spawnflags & 1 )
01419 {
01420 vec3_t temp;
01421
01422 VectorCopy( ent->pos2, temp );
01423 VectorCopy( ent->s.origin, ent->pos2 );
01424 VectorCopy( temp, ent->pos1 );
01425 }
01426
01427 if ( ent->spawnflags & MOVER_LOCKED )
01428 {
01429 ent->s.eFlags |= EF_SHADER_ANIM;
01430 ent->s.frame = 0;
01431 }
01432 InitMover( ent );
01433
01434 ent->nextthink = level.time + FRAMETIME;
01435
01436 if ( !(ent->flags&FL_TEAMSLAVE) )
01437 {
01438 int health;
01439
01440 G_SpawnInt( "health", "0", &health );
01441
01442 if ( health )
01443 {
01444 ent->takedamage = qtrue;
01445 }
01446
01447 if ( !(ent->spawnflags&MOVER_LOCKED) && (ent->targetname || health || ent->spawnflags & MOVER_PLAYER_USE || ent->spawnflags & MOVER_FORCE_ACTIVATE) )
01448 {
01449
01450 ent->think = Think_MatchTeam;
01451
01452 if (ent->spawnflags & MOVER_FORCE_ACTIVATE)
01453 {
01454 ent->s.bolt1 = 1;
01455 }
01456 }
01457 else
01458 {
01459 ent->think = Think_SpawnNewDoorTrigger;
01460 }
01461 }
01462 }
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479 void Touch_Plat( gentity_t *ent, gentity_t *other, trace_t *trace ) {
01480 if ( !other->client || other->client->ps.stats[STAT_HEALTH] <= 0 ) {
01481 return;
01482 }
01483
01484
01485 if ( ent->moverState == MOVER_POS2 ) {
01486 ent->nextthink = level.time + 1000;
01487 }
01488 }
01489
01490
01491
01492
01493
01494
01495
01496
01497 void Touch_PlatCenterTrigger(gentity_t *ent, gentity_t *other, trace_t *trace ) {
01498 if ( !other->client ) {
01499 return;
01500 }
01501
01502 if ( ent->parent->moverState == MOVER_POS1 ) {
01503 Use_BinaryMover( ent->parent, ent, other );
01504 }
01505 }
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517 void SpawnPlatTrigger( gentity_t *ent ) {
01518 gentity_t *trigger;
01519 vec3_t tmin, tmax;
01520
01521
01522
01523 trigger = G_Spawn();
01524 trigger->touch = Touch_PlatCenterTrigger;
01525 trigger->r.contents = CONTENTS_TRIGGER;
01526 trigger->parent = ent;
01527
01528 tmin[0] = ent->pos1[0] + ent->r.mins[0] + 33;
01529 tmin[1] = ent->pos1[1] + ent->r.mins[1] + 33;
01530 tmin[2] = ent->pos1[2] + ent->r.mins[2];
01531
01532 tmax[0] = ent->pos1[0] + ent->r.maxs[0] - 33;
01533 tmax[1] = ent->pos1[1] + ent->r.maxs[1] - 33;
01534 tmax[2] = ent->pos1[2] + ent->r.maxs[2] + 8;
01535
01536 if ( tmax[0] <= tmin[0] ) {
01537 tmin[0] = ent->pos1[0] + (ent->r.mins[0] + ent->r.maxs[0]) *0.5;
01538 tmax[0] = tmin[0] + 1;
01539 }
01540 if ( tmax[1] <= tmin[1] ) {
01541 tmin[1] = ent->pos1[1] + (ent->r.mins[1] + ent->r.maxs[1]) *0.5;
01542 tmax[1] = tmin[1] + 1;
01543 }
01544
01545 VectorCopy (tmin, trigger->r.mins);
01546 VectorCopy (tmax, trigger->r.maxs);
01547
01548 trap_LinkEntity (trigger);
01549 }
01550
01551
01552
01553
01554
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565
01566 void SP_func_plat (gentity_t *ent) {
01567 float lip, height;
01568
01569
01570
01571
01572 VectorClear (ent->s.angles);
01573
01574 G_SpawnFloat( "speed", "200", &ent->speed );
01575 G_SpawnInt( "dmg", "2", &ent->damage );
01576 G_SpawnFloat( "wait", "1", &ent->wait );
01577 G_SpawnFloat( "lip", "8", &lip );
01578
01579 ent->wait = 1000;
01580
01581
01582 trap_SetBrushModel( ent, ent->model );
01583
01584 if ( !G_SpawnFloat( "height", "0", &height ) ) {
01585 height = (ent->r.maxs[2] - ent->r.mins[2]) - lip;
01586 }
01587
01588
01589 VectorCopy( ent->s.origin, ent->pos2 );
01590 VectorCopy( ent->pos2, ent->pos1 );
01591 ent->pos1[2] -= height;
01592
01593 InitMover( ent );
01594
01595
01596
01597 ent->touch = Touch_Plat;
01598
01599 ent->blocked = Blocked_Door;
01600
01601 ent->parent = ent;
01602
01603
01604 if ( !ent->targetname ) {
01605 SpawnPlatTrigger(ent);
01606 }
01607 }
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623 void Touch_Button(gentity_t *ent, gentity_t *other, trace_t *trace ) {
01624 if ( !other->client ) {
01625 return;
01626 }
01627
01628 if ( ent->moverState == MOVER_POS1 ) {
01629 Use_BinaryMover( ent, other, other );
01630 }
01631 }
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650 void SP_func_button( gentity_t *ent ) {
01651 vec3_t abs_movedir;
01652 float distance;
01653 vec3_t size;
01654 float lip;
01655
01656
01657
01658 if ( !ent->speed ) {
01659 ent->speed = 40;
01660 }
01661
01662 if ( !ent->wait ) {
01663 ent->wait = 1;
01664 }
01665 ent->wait *= 1000;
01666
01667
01668 VectorCopy( ent->s.origin, ent->pos1 );
01669
01670
01671 trap_SetBrushModel( ent, ent->model );
01672
01673 G_SpawnFloat( "lip", "4", &lip );
01674
01675 G_SetMovedir( ent->s.angles, ent->movedir );
01676 abs_movedir[0] = fabs(ent->movedir[0]);
01677 abs_movedir[1] = fabs(ent->movedir[1]);
01678 abs_movedir[2] = fabs(ent->movedir[2]);
01679 VectorSubtract( ent->r.maxs, ent->r.mins, size );
01680 distance = abs_movedir[0] * size[0] + abs_movedir[1] * size[1] + abs_movedir[2] * size[2] - lip;
01681 VectorMA (ent->pos1, distance, ent->movedir, ent->pos2);
01682
01683 if (ent->health) {
01684
01685 ent->takedamage = qtrue;
01686 } else {
01687
01688 ent->touch = Touch_Button;
01689 }
01690
01691 InitMover( ent );
01692 }
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706 #define TRAIN_START_ON 1
01707 #define TRAIN_TOGGLE 2
01708 #define TRAIN_BLOCK_STOPS 4
01709
01710
01711
01712
01713
01714
01715
01716
01717 void Think_BeginMoving( gentity_t *ent ) {
01718 G_PlayDoorSound( ent, BMS_START );
01719 G_PlayDoorLoopSound( ent );
01720 ent->s.pos.trTime = level.time;
01721 ent->s.pos.trType = TR_LINEAR_STOP;
01722 }
01723
01724
01725
01726
01727
01728
01729 void Reached_Train( gentity_t *ent ) {
01730 gentity_t *next;
01731 float speed;
01732 vec3_t move;
01733 float length;
01734
01735
01736 next = ent->nextTrain;
01737 if ( !next || !next->nextTrain ) {
01738 return;
01739 }
01740
01741
01742 G_UseTargets( next, NULL );
01743
01744
01745 ent->nextTrain = next->nextTrain;
01746 VectorCopy( next->s.origin, ent->pos1 );
01747 VectorCopy( next->nextTrain->s.origin, ent->pos2 );
01748
01749
01750 if ( next->speed ) {
01751 speed = next->speed;
01752 } else {
01753
01754 speed = ent->speed;
01755 }
01756 if ( speed < 1 ) {
01757 speed = 1;
01758 }
01759
01760
01761 VectorSubtract( ent->pos2, ent->pos1, move );
01762 length = VectorLength( move );
01763
01764 ent->s.pos.trDuration = length * 1000 / speed;
01765
01766
01767 SetMoverState( ent, MOVER_1TO2, level.time );
01768
01769 G_PlayDoorSound( ent, BMS_END );
01770
01771
01772 if ( next->wait ) {
01773 ent->s.loopSound = 0;
01774 ent->s.loopIsSoundset = qfalse;
01775 ent->nextthink = level.time + next->wait * 1000;
01776 ent->think = Think_BeginMoving;
01777 ent->s.pos.trType = TR_STATIONARY;
01778 }
01779 else
01780 {
01781 G_PlayDoorLoopSound( ent );
01782 }
01783 }
01784
01785
01786
01787
01788
01789
01790
01791
01792 void Think_SetupTrainTargets( gentity_t *ent ) {
01793 gentity_t *path, *next, *start;
01794
01795 ent->nextTrain = G_Find( NULL, FOFS(targetname), ent->target );
01796 if ( !ent->nextTrain ) {
01797 Com_Printf( "func_train at %s with an unfound target\n",
01798 vtos(ent->r.absmin) );
01799
01800 return;
01801 }
01802
01803
01804
01805
01806
01807
01808 start = NULL;
01809 for ( path = ent->nextTrain ; path != start ; path = next ) {
01810 if ( !start ) {
01811 start = path;
01812 }
01813
01814 if ( !path->target ) {
01815
01816
01817
01818 break;
01819 }
01820
01821
01822
01823
01824 next = NULL;
01825 do {
01826 next = G_Find( next, FOFS(targetname), path->target );
01827 if ( !next ) {
01828
01829
01830
01831 break;
01832 }
01833 } while ( strcmp( next->classname, "path_corner" ) );
01834
01835 if ( next )
01836 {
01837 path->nextTrain = next;
01838 }
01839 else
01840 {
01841 break;
01842 }
01843 }
01844
01845 if ( !ent->targetname || (ent->spawnflags&1) )
01846 {
01847
01848 Reached_Train( ent );
01849 }
01850 else
01851 {
01852 G_SetOrigin( ent, ent->s.origin );
01853 }
01854 }
01855
01856
01857
01858
01859
01860
01861
01862
01863 void SP_path_corner( gentity_t *self ) {
01864 if ( !self->targetname ) {
01865 G_Printf ("path_corner with no targetname at %s\n", vtos(self->s.origin));
01866 G_FreeEntity( self );
01867 return;
01868 }
01869
01870 }
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887 void SP_func_train (gentity_t *self) {
01888 VectorClear (self->s.angles);
01889
01890 if (self->spawnflags & TRAIN_BLOCK_STOPS) {
01891 self->damage = 0;
01892 } else {
01893 if (!self->damage) {
01894 self->damage = 2;
01895 }
01896 }
01897
01898 if ( !self->speed ) {
01899 self->speed = 100;
01900 }
01901
01902 if ( !self->target ) {
01903 G_Printf ("func_train without a target at %s\n", vtos(self->r.absmin));
01904 G_FreeEntity( self );
01905 return;
01906 }
01907
01908 trap_SetBrushModel( self, self->model );
01909 InitMover( self );
01910
01911 self->reached = Reached_Train;
01912
01913
01914
01915 self->nextthink = level.time + FRAMETIME;
01916 self->think = Think_SetupTrainTargets;
01917 }
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927 void func_static_use ( gentity_t *self, gentity_t *other, gentity_t *activator );
01928
01929
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939
01940
01941
01942
01943
01944
01945
01946 void SP_func_static( gentity_t *ent )
01947 {
01948 int test;
01949 trap_SetBrushModel( ent, ent->model );
01950
01951 VectorCopy( ent->s.origin, ent->pos1 );
01952 VectorCopy( ent->s.origin, ent->pos2 );
01953
01954 InitMover( ent );
01955
01956 ent->use = func_static_use;
01957 ent->reached = 0;
01958
01959 G_SetOrigin( ent, ent->s.origin );
01960 G_SetAngles( ent, ent->s.angles );
01961
01962 if( ent->spawnflags & 2048 )
01963 {
01964 ent->r.svFlags |= SVF_BROADCAST;
01965 }
01966
01967 if ( ent->spawnflags & 4 )
01968 {
01969 ent->s.eFlags |= EF_SHADER_ANIM;
01970 ent->s.frame = 0;
01971 }
01972
01973 if ((ent->spawnflags & 1) || (ent->spawnflags & 2))
01974 {
01975 ent->s.bolt1 = 1;
01976 }
01977
01978 #ifdef _XBOX
01979 int tempModelScale;
01980 G_SpawnInt("model2scale", "0", &tempModelScale);
01981 ent->s.iModelScale = tempModelScale;
01982 #else
01983 G_SpawnInt("model2scale", "0", &ent->s.iModelScale);
01984 #endif
01985 if (ent->s.iModelScale < 0)
01986 {
01987 ent->s.iModelScale = 0;
01988 }
01989 else if (ent->s.iModelScale > 1023)
01990 {
01991 ent->s.iModelScale = 1023;
01992 }
01993
01994 G_SpawnInt( "hyperspace", "0", &test );
01995 if ( test )
01996 {
01997 ent->r.svFlags |= SVF_BROADCAST;
01998 ent->s.eFlags2 |= EF2_HYPERSPACE;
01999 }
02000
02001 trap_LinkEntity( ent );
02002
02003 if (level.mBSPInstanceDepth)
02004 {
02005 ent->s.eFlags = EF_PERMANENT;
02006 }
02007 }
02008
02009 void func_static_use ( gentity_t *self, gentity_t *other, gentity_t *activator )
02010 {
02011 G_ActivateBehavior( self, BSET_USE );
02012
02013 if ( self->spawnflags & 4 )
02014 {
02015 self->s.frame = self->s.frame ? 0 : 1;
02016 }
02017 G_UseTargets( self, activator );
02018 }
02019
02020
02021
02022
02023
02024
02025
02026
02027
02028 void func_rotating_use( gentity_t *self, gentity_t *other, gentity_t *activator )
02029 {
02030 if( self->s.apos.trType == TR_LINEAR )
02031 {
02032 self->s.apos.trType = TR_STATIONARY;
02033
02034 self->s.loopSound = 0;
02035 self->s.loopIsSoundset = qfalse;
02036
02037 if ( self->soundSet && self->soundSet[0] )
02038 {
02039 self->s.soundSetIndex = G_SoundSetIndex(self->soundSet);
02040 G_AddEvent( self, EV_BMODEL_SOUND, BMS_END );
02041 }
02042 }
02043 else
02044 {
02045 if ( self->soundSet && self->soundSet[0] )
02046 {
02047 self->s.soundSetIndex = G_SoundSetIndex(self->soundSet);
02048 G_AddEvent( self, EV_BMODEL_SOUND, BMS_START );
02049 self->s.loopSound = BMS_MID;
02050 self->s.loopIsSoundset = qtrue;
02051 }
02052 self->s.apos.trType = TR_LINEAR;
02053 }
02054 }
02055
02056
02057
02058
02059
02060
02061
02062
02063
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073
02074
02075
02076
02077
02078
02079
02080
02081
02082
02083
02084
02085
02086
02087
02088
02089
02090
02091
02092
02093
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115
02116
02117
02118
02119
02120 void SP_func_breakable( gentity_t *self );
02121 void SP_func_rotating (gentity_t *ent) {
02122 vec3_t spinangles;
02123 if ( ent->health )
02124 {
02125 int sav_spawnflags = ent->spawnflags;
02126 ent->spawnflags = 0;
02127 SP_func_breakable( ent );
02128 ent->spawnflags = sav_spawnflags;
02129 }
02130 else
02131 {
02132 trap_SetBrushModel( ent, ent->model );
02133 InitMover( ent );
02134
02135 VectorCopy( ent->s.origin, ent->s.pos.trBase );
02136 VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );
02137 VectorCopy( ent->s.apos.trBase, ent->r.currentAngles );
02138
02139 trap_LinkEntity( ent );
02140 }
02141
02142 #ifdef _XBOX
02143 int tempModelScale;
02144 G_SpawnInt("model2scale", "0", &tempModelScale);
02145 ent->s.iModelScale = tempModelScale;
02146 #else
02147 G_SpawnInt("model2scale", "0", &ent->s.iModelScale);
02148 #endif
02149 if (ent->s.iModelScale < 0)
02150 {
02151 ent->s.iModelScale = 0;
02152 }
02153 else if (ent->s.iModelScale > 1023)
02154 {
02155 ent->s.iModelScale = 1023;
02156 }
02157
02158 if ( G_SpawnVector( "spinangles", "0 0 0", spinangles ) )
02159 {
02160 ent->speed = VectorLength( spinangles );
02161
02162 VectorCopy( spinangles, ent->s.apos.trDelta );
02163 }
02164 else
02165 {
02166 if ( !ent->speed ) {
02167 ent->speed = 100;
02168 }
02169
02170 if ( ent->spawnflags & 4 ) {
02171 ent->s.apos.trDelta[2] = ent->speed;
02172 } else if ( ent->spawnflags & 8 ) {
02173 ent->s.apos.trDelta[0] = ent->speed;
02174 } else {
02175 ent->s.apos.trDelta[1] = ent->speed;
02176 }
02177 }
02178 ent->s.apos.trType = TR_LINEAR;
02179
02180 if (!ent->damage) {
02181 if ( (ent->spawnflags&16) )
02182 {
02183 ent->damage = 10000;
02184 }
02185 else
02186 {
02187 ent->damage = 2;
02188 }
02189 }
02190 if ( (ent->spawnflags&2) )
02191 {
02192 ent->s.speed = Distance( ent->r.absmin, ent->r.absmax )*0.5f;
02193 ent->s.eFlags |= EF_RADAROBJECT;
02194 }
02195 }
02196
02197
02198
02199
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214
02215
02216
02217 void SP_func_bobbing (gentity_t *ent) {
02218 float height;
02219 float phase;
02220
02221 G_SpawnFloat( "speed", "4", &ent->speed );
02222 G_SpawnFloat( "height", "32", &height );
02223 G_SpawnInt( "dmg", "2", &ent->damage );
02224 G_SpawnFloat( "phase", "0", &phase );
02225
02226 trap_SetBrushModel( ent, ent->model );
02227 InitMover( ent );
02228
02229 VectorCopy( ent->s.origin, ent->s.pos.trBase );
02230 VectorCopy( ent->s.origin, ent->r.currentOrigin );
02231
02232 ent->s.pos.trDuration = ent->speed * 1000;
02233 ent->s.pos.trTime = ent->s.pos.trDuration * phase;
02234 ent->s.pos.trType = TR_SINE;
02235
02236
02237 if ( ent->spawnflags & 1 ) {
02238 ent->s.pos.trDelta[0] = height;
02239 } else if ( ent->spawnflags & 2 ) {
02240 ent->s.pos.trDelta[1] = height;
02241 } else {
02242 ent->s.pos.trDelta[2] = height;
02243 }
02244 }
02245
02246
02247
02248
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258
02259
02260
02261
02262
02263
02264
02265
02266 void SP_func_pendulum(gentity_t *ent) {
02267 float freq;
02268 float length;
02269 float phase;
02270 float speed;
02271
02272 G_SpawnFloat( "speed", "30", &speed );
02273 G_SpawnInt( "dmg", "2", &ent->damage );
02274 G_SpawnFloat( "phase", "0", &phase );
02275
02276 trap_SetBrushModel( ent, ent->model );
02277
02278
02279 length = fabs( ent->r.mins[2] );
02280 if ( length < 8 ) {
02281 length = 8;
02282 }
02283
02284 freq = 1 / ( M_PI * 2 ) * sqrt( g_gravity.value / ( 3 * length ) );
02285
02286 ent->s.pos.trDuration = ( 1000 / freq );
02287
02288 InitMover( ent );
02289
02290 VectorCopy( ent->s.origin, ent->s.pos.trBase );
02291 VectorCopy( ent->s.origin, ent->r.currentOrigin );
02292
02293 VectorCopy( ent->s.angles, ent->s.apos.trBase );
02294
02295 ent->s.apos.trDuration = 1000 / freq;
02296 ent->s.apos.trTime = ent->s.apos.trDuration * phase;
02297 ent->s.apos.trType = TR_SINE;
02298 ent->s.apos.trDelta[2] = speed;
02299 }
02300
02301
02302
02303
02304
02305
02306
02307
02308
02309 static void CacheChunkEffects( material_t material )
02310 {
02311 switch( material )
02312 {
02313 case MAT_GLASS:
02314 G_EffectIndex( "chunks/glassbreak" );
02315 break;
02316 case MAT_GLASS_METAL:
02317 G_EffectIndex( "chunks/glassbreak" );
02318 G_EffectIndex( "chunks/metalexplode" );
02319 break;
02320 case MAT_ELECTRICAL:
02321 case MAT_ELEC_METAL:
02322 G_EffectIndex( "chunks/sparkexplode" );
02323 break;
02324 case MAT_METAL:
02325 case MAT_METAL2:
02326 case MAT_METAL3:
02327 case MAT_CRATE1:
02328 case MAT_CRATE2:
02329 G_EffectIndex( "chunks/metalexplode" );
02330 break;
02331 case MAT_GRATE1:
02332 G_EffectIndex( "chunks/grateexplode" );
02333 break;
02334 case MAT_DRK_STONE:
02335 case MAT_LT_STONE:
02336 case MAT_GREY_STONE:
02337 case MAT_WHITE_METAL:
02338 case MAT_SNOWY_ROCK:
02339 G_EffectIndex( "chunks/rockbreaklg" );
02340 G_EffectIndex( "chunks/rockbreakmed" );
02341 break;
02342 case MAT_ROPE:
02343 G_EffectIndex( "chunks/ropebreak" );
02344
02345 break;
02346 }
02347 }
02348
02349 void G_MiscModelExplosion( vec3_t mins, vec3_t maxs, int size, material_t chunkType )
02350 {
02351 gentity_t *te;
02352 vec3_t mid;
02353
02354 VectorAdd( mins, maxs, mid );
02355 VectorScale( mid, 0.5f, mid );
02356
02357 te = G_TempEntity( mid, EV_MISC_MODEL_EXP );
02358
02359 VectorCopy(maxs, te->s.origin2);
02360 VectorCopy(mins, te->s.angles2);
02361 te->s.time = size;
02362 te->s.eventParm = chunkType;
02363 }
02364
02365 void G_Chunks( int owner, vec3_t origin, const vec3_t normal, const vec3_t mins, const vec3_t maxs,
02366 float speed, int numChunks, material_t chunkType, int customChunk, float baseScale )
02367 {
02368 gentity_t *te = G_TempEntity( origin, EV_DEBRIS );
02369
02370
02371 te->s.owner = owner;
02372 VectorCopy(origin, te->s.origin);
02373 VectorCopy(normal, te->s.angles);
02374 VectorCopy(maxs, te->s.origin2);
02375 VectorCopy(mins, te->s.angles2);
02376 te->s.speed = speed;
02377 te->s.eventParm = numChunks;
02378 te->s.trickedentindex = chunkType;
02379 te->s.modelindex = customChunk;
02380 te->s.apos.trBase[0] = baseScale;
02381 }
02382
02383
02384 void funcBBrushDieGo (gentity_t *self)
02385 {
02386 vec3_t org, dir, up;
02387 gentity_t *attacker = self->enemy;
02388 float scale;
02389 int i, numChunks, size = 0;
02390 material_t chunkType = self->material;
02391
02392
02393 for ( i = 0; i < MAX_GENTITIES; i++ )
02394 {
02395 if ( g_entities[i].s.groundEntityNum == self->s.number && ( g_entities[i].s.eFlags & EF_MISSILE_STICK ))
02396 {
02397 G_Damage( &g_entities[i], self, self, NULL, NULL, 99999, 0, MOD_CRUSH );
02398 }
02399 }
02400
02401
02402 self->s.solid = 0;
02403 self->r.contents = 0;
02404 self->clipmask = 0;
02405 trap_LinkEntity(self);
02406
02407 VectorSet(up, 0, 0, 1);
02408
02409 if ( self->target && attacker != NULL )
02410 {
02411 G_UseTargets(self, attacker);
02412 }
02413
02414 VectorSubtract( self->r.absmax, self->r.absmin, org );
02415
02416 numChunks = random() * 6 + 18;
02417
02418
02419
02420 scale = sqrt( sqrt( org[0] * org[1] * org[2] )) * 1.75f;
02421
02422 if ( scale > 48 )
02423 {
02424 size = 2;
02425 }
02426 else if ( scale > 24 )
02427 {
02428 size = 1;
02429 }
02430
02431 scale = scale / numChunks;
02432
02433 if ( self->radius > 0.0f )
02434 {
02435
02436
02437 numChunks *= self->radius;
02438 }
02439
02440 VectorMA( self->r.absmin, 0.5, org, org );
02441 VectorAdd( self->r.absmin,self->r.absmax, org );
02442 VectorScale( org, 0.5f, org );
02443
02444 if ( attacker != NULL && attacker->client )
02445 {
02446 VectorSubtract( org, attacker->r.currentOrigin, dir );
02447 VectorNormalize( dir );
02448 }
02449 else
02450 {
02451 VectorCopy(up, dir);
02452 }
02453
02454 if ( !(self->spawnflags & 2048) )
02455 {
02456
02457 G_MiscModelExplosion( self->r.absmin, self->r.absmax, size, chunkType );
02458 }
02459
02460 if (self->genericValue15)
02461 {
02462 vec3_t ang;
02463 VectorSet(ang, 0.0f, 1.0f, 0.0f);
02464 G_PlayEffectID(self->genericValue15, org, ang);
02465 }
02466
02467 if ( self->splashDamage > 0 && self->splashRadius > 0 )
02468 {
02469 gentity_t *te;
02470
02471 G_RadiusDamage( org, self, self->splashDamage, self->splashRadius, self, NULL, MOD_UNKNOWN );
02472
02473 te = G_TempEntity( org, EV_GENERAL_SOUND );
02474 te->s.eventParm = G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
02475 }
02476
02477
02478 G_Chunks( self->s.number, org, dir, self->r.absmin, self->r.absmax, 300, numChunks, chunkType, 0, (scale*self->mass) );
02479
02480 trap_AdjustAreaPortalState( self, qtrue );
02481 self->think = G_FreeEntity;
02482 self->nextthink = level.time + 50;
02483
02484 }
02485
02486 void funcBBrushDie (gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
02487 {
02488 self->takedamage = qfalse;
02489
02490 self->enemy = attacker;
02491
02492 if(self->delay)
02493 {
02494 self->think = funcBBrushDieGo;
02495 self->nextthink = level.time + floor(self->delay * 1000.0f);
02496 return;
02497 }
02498
02499 funcBBrushDieGo(self);
02500 }
02501
02502 void funcBBrushUse (gentity_t *self, gentity_t *other, gentity_t *activator)
02503 {
02504 G_ActivateBehavior( self, BSET_USE );
02505 if(self->spawnflags & 64)
02506 {
02507 if(self->target && self->target[0])
02508 {
02509 G_UseTargets(self, activator);
02510 }
02511 }
02512 else
02513 {
02514 funcBBrushDie(self, other, activator, self->health, MOD_UNKNOWN);
02515 }
02516 }
02517
02518 void funcBBrushPain(gentity_t *self, gentity_t *attacker, int damage)
02519 {
02520 if ( self->painDebounceTime > level.time )
02521 {
02522 return;
02523 }
02524
02525 if ( self->paintarget && self->paintarget[0] )
02526 {
02527 if (!self->activator)
02528 {
02529 if (attacker && attacker->inuse && attacker->client)
02530 {
02531 G_UseTargets2 (self, attacker, self->paintarget);
02532 }
02533 }
02534 else
02535 {
02536 G_UseTargets2 (self, self->activator, self->paintarget);
02537 }
02538 }
02539
02540 G_ActivateBehavior( self, BSET_PAIN );
02541
02542 if ( self->material == MAT_DRK_STONE
02543 || self->material == MAT_LT_STONE
02544 || self->material == MAT_GREY_STONE
02545 || self->material == MAT_SNOWY_ROCK )
02546 {
02547 vec3_t org, dir;
02548 float scale;
02549 int numChunks;
02550 VectorSubtract( self->r.absmax, self->r.absmin, org );
02551
02552
02553 scale = VectorLength( org ) / 100.0f;
02554 VectorMA( self->r.absmin, 0.5, org, org );
02555 VectorAdd( self->r.absmin,self->r.absmax, org );
02556 VectorScale( org, 0.5f, org );
02557 if ( attacker != NULL && attacker->client )
02558 {
02559 VectorSubtract( attacker->r.currentOrigin, org, dir );
02560 VectorNormalize( dir );
02561 }
02562 else
02563 {
02564 VectorSet( dir, 0, 0, 1 );
02565 }
02566 numChunks = Q_irand( 1, 3 );
02567 if ( self->radius > 0.0f )
02568 {
02569
02570
02571 numChunks = ceil(numChunks*self->radius);
02572 }
02573 G_Chunks( self->s.number, org, dir, self->r.absmin, self->r.absmax, 300, numChunks, self->material, 0, (scale*self->mass) );
02574 }
02575
02576 if ( self->wait == -1 )
02577 {
02578 self->pain = 0;
02579 return;
02580 }
02581
02582 self->painDebounceTime = level.time + self->wait;
02583 }
02584
02585 static void InitBBrush ( gentity_t *ent )
02586 {
02587 float light;
02588 vec3_t color;
02589 qboolean lightSet, colorSet;
02590
02591 VectorCopy( ent->s.origin, ent->pos1 );
02592
02593 trap_SetBrushModel( ent, ent->model );
02594
02595 ent->die = funcBBrushDie;
02596
02597 ent->flags |= FL_BBRUSH;
02598
02599
02600
02601
02602
02603
02604
02605
02606 if ( ent->model2 && ent->model2[0] )
02607 {
02608 ent->s.modelindex2 = G_ModelIndex( ent->model2 );
02609 }
02610
02611
02612 lightSet = G_SpawnFloat( "light", "100", &light );
02613 colorSet = G_SpawnVector( "color", "1 1 1", color );
02614 if ( lightSet || colorSet )
02615 {
02616 int r, g, b, i;
02617
02618 r = color[0] * 255;
02619 if ( r > 255 ) {
02620 r = 255;
02621 }
02622 g = color[1] * 255;
02623 if ( g > 255 ) {
02624 g = 255;
02625 }
02626 b = color[2] * 255;
02627 if ( b > 255 ) {
02628 b = 255;
02629 }
02630 i = light / 4;
02631 if ( i > 255 ) {
02632 i = 255;
02633 }
02634 ent->s.constantLight = r | ( g << 8 ) | ( b << 16 ) | ( i << 24 );
02635 }
02636
02637 if(ent->spawnflags & 128)
02638 {
02639 ent->r.svFlags |= SVF_PLAYER_USABLE;
02640 }
02641
02642 ent->s.eType = ET_MOVER;
02643 trap_LinkEntity(ent);
02644
02645 ent->s.pos.trType = TR_STATIONARY;
02646 VectorCopy( ent->pos1, ent->s.pos.trBase );
02647 }
02648
02649 void funcBBrushTouch( gentity_t *ent, gentity_t *other, trace_t *trace )
02650 {
02651 }
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672
02673
02674
02675
02676
02677
02678
02679
02680
02681
02682
02683
02684
02685
02686
02687
02688
02689
02690
02691
02692
02693
02694
02695
02696
02697
02698
02699
02700
02701
02702
02703
02704
02705
02706
02707
02708
02709
02710
02711
02712
02713
02714
02715
02716
02717 void SP_func_breakable( gentity_t *self )
02718 {
02719 int t;
02720 char *s = NULL;
02721
02722 G_SpawnString("playfx", "", &s);
02723
02724 if (s && s[0])
02725 {
02726 self->genericValue15 = G_EffectIndex(s);
02727 }
02728 else
02729 {
02730 self->genericValue15 = 0;
02731 }
02732
02733 if(!(self->spawnflags & 1))
02734 {
02735 if(!self->health)
02736 {
02737 self->health = 10;
02738 }
02739 }
02740
02741 G_SpawnInt( "showhealth", "0", &t );
02742
02743 if (t)
02744 {
02745 self->maxHealth = self->health;
02746 G_ScaleNetHealth(self);
02747 }
02748
02749
02750
02751
02752
02753 if ( self->spawnflags & 16 )
02754 {
02755 self->flags |= FL_DMG_BY_SABER_ONLY;
02756 }
02757 else if ( self->spawnflags & 32 )
02758 {
02759 self->flags |= FL_DMG_BY_HEAVY_WEAP_ONLY;
02760 }
02761
02762 if (self->health)
02763 {
02764 self->takedamage = qtrue;
02765 }
02766
02767 G_SoundIndex("sound/weapons/explosions/cargoexplode.wav");
02768 G_SpawnFloat( "radius", "1", &self->radius );
02769 G_SpawnInt( "material", "0", (int*)&self->material );
02770
02771 G_SpawnInt( "splashDamage", "0", &self->splashDamage );
02772 G_SpawnInt( "splashRadius", "0", &self->splashRadius );
02773
02774 CacheChunkEffects( self->material );
02775
02776 self->use = funcBBrushUse;
02777
02778
02779 {
02780 self->pain = funcBBrushPain;
02781 }
02782
02783 self->touch = funcBBrushTouch;
02784
02785
02786
02787
02788
02789
02790
02791
02792
02793
02794
02795 if ( self->team && self->team[0] && g_gametype.integer == GT_SIEGE &&
02796 !self->teamnodmg)
02797 {
02798 self->teamnodmg = atoi(self->team);
02799 }
02800 self->team = NULL;
02801 if (!self->model) {
02802 G_Error("func_breakable with NULL model\n");
02803 }
02804 InitBBrush( self );
02805
02806 if ( !self->radius )
02807 {
02808 self->radius = 1.0f;
02809 }
02810 if ( !self->mass )
02811 {
02812 self->mass = 1.0f;
02813 }
02814 self->genericValue4 = 1;
02815 }
02816
02817 qboolean G_EntIsBreakable( int entityNum )
02818 {
02819 gentity_t *ent;
02820
02821 if ( entityNum < 0 || entityNum >= ENTITYNUM_WORLD )
02822 {
02823 return qfalse;
02824 }
02825
02826 ent = &g_entities[entityNum];
02827 if ( (ent->r.svFlags & SVF_GLASS_BRUSH) )
02828 {
02829 return qtrue;
02830 }
02831
02832
02833
02834
02835
02836
02837 if ( !Q_stricmp( "func_breakable", ent->classname ) )
02838 {
02839 return qtrue;
02840 }
02841
02842 if ( !Q_stricmp( "misc_model_breakable", ent->classname ) )
02843 {
02844 return qtrue;
02845 }
02846 if ( !Q_stricmp( "misc_maglock", ent->classname ) )
02847 {
02848 return qtrue;
02849 }
02850
02851 return qfalse;
02852 }
02853
02854
02855
02856
02857
02858
02859
02860
02861 void GlassDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
02862 {
02863 gentity_t *te;
02864 vec3_t dif;
02865
02866 if (self->genericValue5)
02867 {
02868 return;
02869 }
02870
02871 self->genericValue5 = 1;
02872
02873 dif[0] = (self->r.absmax[0]+self->r.absmin[0])/2;
02874 dif[1] = (self->r.absmax[1]+self->r.absmin[1])/2;
02875 dif[2] = (self->r.absmax[2]+self->r.absmin[2])/2;
02876
02877 G_UseTargets(self, attacker);
02878
02879 self->splashRadius = 40;
02880
02881 te = G_TempEntity( dif, EV_GLASS_SHATTER );
02882 te->s.genericenemyindex = self->s.number;
02883 VectorCopy(self->pos1, te->s.origin);
02884 VectorCopy(self->pos2, te->s.angles);
02885 te->s.trickedentindex = (int)self->splashRadius;
02886 te->s.pos.trTime = (int)self->genericValue3;
02887
02888 G_FreeEntity(self);
02889 }
02890
02891 void GlassDie_Old(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
02892 {
02893 gentity_t *te;
02894 vec3_t dif;
02895
02896 dif[0] = (self->r.absmax[0]+self->r.absmin[0])/2;
02897 dif[1] = (self->r.absmax[1]+self->r.absmin[1])/2;
02898 dif[2] = (self->r.absmax[2]+self->r.absmin[2])/2;
02899
02900 G_UseTargets(self, attacker);
02901
02902 te = G_TempEntity( dif, EV_GLASS_SHATTER );
02903 te->s.genericenemyindex = self->s.number;
02904 VectorCopy(self->r.maxs, te->s.origin);
02905 VectorCopy(self->r.mins, te->s.angles);
02906
02907 G_FreeEntity(self);
02908 }
02909
02910 void GlassPain(gentity_t *self, gentity_t *attacker, int damage)
02911 {
02912
02913
02914 }
02915
02916 void GlassUse(gentity_t *self, gentity_t *other, gentity_t *activator)
02917 {
02918 vec3_t temp1, temp2;
02919
02920
02921 VectorAdd( self->r.mins, self->r.maxs, temp1 );
02922 VectorScale( temp1, 0.5f, temp1 );
02923
02924 VectorAdd( other->r.mins, other->r.maxs, temp2 );
02925 VectorScale( temp2, 0.5f, temp2 );
02926
02927 VectorSubtract( temp1, temp2, self->pos2 );
02928 VectorCopy( temp1, self->pos1 );
02929
02930 VectorNormalize( self->pos2 );
02931 VectorScale( self->pos2, 390, self->pos2 );
02932
02933 GlassDie(self, other, activator, 100, MOD_UNKNOWN);
02934 }
02935
02936
02937
02938
02939
02940
02941
02942
02943 void SP_func_glass( gentity_t *ent ) {
02944 trap_SetBrushModel( ent, ent->model );
02945 InitMover( ent );
02946
02947 ent->r.svFlags = SVF_GLASS_BRUSH;
02948
02949 VectorCopy( ent->s.origin, ent->s.pos.trBase );
02950 VectorCopy( ent->s.origin, ent->r.currentOrigin );
02951 if (!ent->health)
02952 {
02953 ent->health = 1;
02954 }
02955
02956 G_SpawnInt("maxshards", "0", &ent->genericValue3);
02957
02958 ent->genericValue1 = 0;
02959
02960 ent->genericValue4 = 1;
02961
02962 ent->moverState = MOVER_POS1;
02963
02964 if (ent->spawnflags & 1)
02965 {
02966 ent->takedamage = qfalse;
02967 }
02968 else
02969 {
02970 ent->takedamage = qtrue;
02971 }
02972
02973 ent->die = GlassDie;
02974 ent->use = GlassUse;
02975 ent->pain = GlassPain;
02976 }
02977
02978 void func_usable_use (gentity_t *self, gentity_t *other, gentity_t *activator);
02979
02980 extern gentity_t *G_TestEntityPosition( gentity_t *ent );
02981 void func_wait_return_solid( gentity_t *self )
02982 {
02983
02984 self->clipmask = CONTENTS_BODY;
02985 if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL )
02986 {
02987 trap_SetBrushModel( self, self->model );
02988 InitMover( self );
02989 VectorCopy( self->s.origin, self->s.pos.trBase );
02990 VectorCopy( self->s.origin, self->r.currentOrigin );
02991 self->r.svFlags &= ~SVF_NOCLIENT;
02992 self->s.eFlags &= ~EF_NODRAW;
02993 self->use = func_usable_use;
02994 self->clipmask = 0;
02995 if ( self->target2 && self->target2[0] )
02996 {
02997 G_UseTargets2( self, self->activator, self->target2 );
02998 }
02999
03000
03001
03002
03003
03004 }
03005 else
03006 {
03007 self->clipmask = 0;
03008 self->think = func_wait_return_solid;
03009 self->nextthink = level.time + FRAMETIME;
03010 }
03011 }
03012
03013 void func_usable_think( gentity_t *self )
03014 {
03015 if ( self->spawnflags & 8 )
03016 {
03017 self->r.svFlags |= SVF_PLAYER_USABLE;
03018 self->use = func_usable_use;
03019 self->think = 0;
03020 }
03021 }
03022
03023 qboolean G_EntIsRemovableUsable( int entNum )
03024 {
03025 gentity_t *ent = &g_entities[entNum];
03026 if ( ent->classname && !Q_stricmp( "func_usable", ent->classname ) )
03027 {
03028 if ( !(ent->s.eFlags&EF_SHADER_ANIM) && !(ent->spawnflags&8) && ent->targetname )
03029 {
03030 return qtrue;
03031 }
03032 }
03033 return qfalse;
03034 }
03035
03036 void func_usable_use (gentity_t *self, gentity_t *other, gentity_t *activator)
03037 {
03038 G_ActivateBehavior( self, BSET_USE );
03039 if ( self->s.eFlags & EF_SHADER_ANIM )
03040 {
03041 self->s.frame++;
03042 if ( self->s.frame > self->genericValue5 )
03043 {
03044 self->s.frame = 0;
03045 }
03046 if ( self->target && self->target[0] )
03047 {
03048 G_UseTargets( self, activator );
03049 }
03050 }
03051 else if ( self->spawnflags & 8 )
03052 {
03053
03054 self->r.svFlags &= ~SVF_PLAYER_USABLE;
03055
03056 self->use = 0;
03057
03058 if(self->target && self->target[0])
03059 {
03060 G_UseTargets(self, activator);
03061 }
03062
03063 if ( self->wait )
03064 {
03065 self->think = func_usable_think;
03066 self->nextthink = level.time + ( self->wait * 1000 );
03067 }
03068
03069 return;
03070 }
03071 else if ( !self->count )
03072 {
03073 self->count = 1;
03074 func_wait_return_solid( self );
03075 }
03076 else
03077 {
03078 self->s.solid = 0;
03079 self->r.contents = 0;
03080 self->clipmask = 0;
03081 self->r.svFlags |= SVF_NOCLIENT;
03082 self->s.eFlags |= EF_NODRAW;
03083 self->count = 0;
03084
03085 if(self->target && self->target[0])
03086 {
03087 G_UseTargets(self, activator);
03088 }
03089 self->think = 0;
03090 self->nextthink = -1;
03091 }
03092 }
03093
03094 void func_usable_pain(gentity_t *self, gentity_t *attacker, int damage)
03095 {
03096 GlobalUse(self, attacker, attacker);
03097 }
03098
03099 void func_usable_die(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
03100 {
03101 self->takedamage = qfalse;
03102 GlobalUse(self, inflictor, attacker);
03103 }
03104
03105
03106
03107
03108
03109
03110
03111
03112
03113
03114
03115
03116
03117
03118
03119
03120
03121
03122
03123
03124
03125
03126 void SP_func_usable( gentity_t *self )
03127 {
03128 trap_SetBrushModel( self, self->model );
03129 InitMover( self );
03130 VectorCopy( self->s.origin, self->s.pos.trBase );
03131 VectorCopy( self->s.origin, self->r.currentOrigin );
03132 VectorCopy( self->s.origin, self->pos1 );
03133
03134 G_SpawnInt("endframe", "0", &self->genericValue5);
03135
03136 if ( self->model2 && self->model2[0] )
03137 {
03138 if ( strstr( self->model2, ".glm" ))
03139 {
03140 self->s.modelindex2 = 0;
03141 }
03142 else
03143 {
03144 self->s.modelindex2 = G_ModelIndex( self->model2 );
03145 }
03146 }
03147
03148 self->count = 1;
03149 if (self->spawnflags & 1)
03150 {
03151 self->s.solid = 0;
03152 self->r.contents = 0;
03153 self->clipmask = 0;
03154 self->r.svFlags |= SVF_NOCLIENT;
03155 self->s.eFlags |= EF_NODRAW;
03156 self->count = 0;
03157 }
03158
03159
03160
03161
03162
03163
03164
03165
03166
03167
03168
03169
03170
03171
03172 self->use = func_usable_use;
03173
03174 if ( self->health )
03175 {
03176 self->takedamage = qtrue;
03177 self->die = func_usable_die;
03178 self->pain = func_usable_pain;
03179 }
03180
03181 if ( self->genericValue5 > 0 )
03182 {
03183 self->s.frame = 0;
03184 self->s.eFlags |= EF_SHADER_ANIM;
03185 self->s.time = self->genericValue5 + 1;
03186 }
03187
03188 trap_LinkEntity (self);
03189 }
03190
03191
03192
03193
03194
03195
03196
03197
03198
03199
03200
03201 void use_wall( gentity_t *ent, gentity_t *other, gentity_t *activator )
03202 {
03203 G_ActivateBehavior(ent,BSET_USE);
03204
03205
03206 if (!(ent->r.contents & CONTENTS_SOLID))
03207 {
03208 ent->r.svFlags &= ~SVF_NOCLIENT;
03209 ent->s.eFlags &= ~EF_NODRAW;
03210 ent->r.contents = CONTENTS_SOLID;
03211 if ( !(ent->spawnflags&1) )
03212 {
03213 trap_AdjustAreaPortalState( ent, qfalse );
03214 }
03215 }
03216
03217 else
03218 {
03219 ent->r.contents = 0;
03220 ent->r.svFlags |= SVF_NOCLIENT;
03221 ent->s.eFlags |= EF_NODRAW;
03222 if ( !(ent->spawnflags&1) )
03223 {
03224 trap_AdjustAreaPortalState( ent, qtrue );
03225 }
03226 }
03227 }
03228
03229 #define FUNC_WALL_OFF 1
03230
03231
03232
03233
03234
03235
03236
03237
03238
03239
03240
03241
03242 void SP_func_wall( gentity_t *ent )
03243 {
03244 trap_SetBrushModel( ent, ent->model );
03245
03246 VectorCopy( ent->s.origin, ent->pos1 );
03247 VectorCopy( ent->s.origin, ent->pos2 );
03248
03249 InitMover( ent );
03250 VectorCopy( ent->s.origin, ent->s.pos.trBase );
03251 VectorCopy( ent->s.origin, ent->r.currentOrigin );
03252
03253
03254 if (ent->spawnflags & FUNC_WALL_OFF)
03255 {
03256 ent->r.contents = 0;
03257 ent->r.svFlags |= SVF_NOCLIENT;
03258 ent->s.eFlags |= EF_NODRAW;
03259 }
03260
03261 ent->use = use_wall;
03262
03263 trap_LinkEntity (ent);
03264
03265 }