/*QUAKED func_rotating (0 .5 .8) ? START_ON - X_AXIS Y_AXIS You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. "model2" .md3 model to also draw "speed" determines how fast it moves; default value is 100. "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_rotating (gentity_t *ent) { if ( !ent->speed ) { ent->speed = 100; } // set the axis of rotation ent->s.apos.trType = TR_LINEAR; if ( ent->spawnflags & 4 ) { ent->s.apos.trDelta[2] = ent->speed; } else if ( ent->spawnflags & 8 ) { ent->s.apos.trDelta[0] = ent->speed; } else { ent->s.apos.trDelta[1] = ent->speed; } if (!ent->damage) { ent->damage = 2; } trap_SetBrushModel( ent, ent->model ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin ); VectorCopy( ent->s.apos.trBase, ent->r.currentAngles ); trap_LinkEntity( ent ); }
/** QUAKED func_static (0 .5 .8) ? A bmodel that just sits there, doing nothing. Can be used for conditional walls and models. "model2" .md3 model to also draw "color" constantLight color "light" constantLight radius */ void SP_func_static(gentity_t *ent) { trap_SetBrushModel(ent, ent->model); InitMover(ent); VectorCopy(ent->s.origin, ent->s.pos.trBase); VectorCopy(ent->s.origin, ent->r.currentOrigin); }
/*QUAKED func_train (0 .5 .8) ? START_ON TOGGLE BLOCK_STOPS A train is a mover that moves between path_corner target points. Trains MUST HAVE AN ORIGIN BRUSH. The train spawns at the first target it is pointing at. "model2" .md3 model to also draw "speed" default 100 "dmg" default 2 "noise" looping sound to play when the train is in motion "target" next path corner "color" constantLight color "light" constantLight radius */ void SP_func_train (gentity_t *self) { VectorClear (self->s.angles); if (self->spawnflags & TRAIN_BLOCK_STOPS) { self->damage = 0; } else { if (!self->damage) { self->damage = 2; } } if ( !self->speed ) { self->speed = 100; } if ( !self->target ) { G_Printf ("func_train without a target at %s\n", vtos(self->r.absmin)); G_FreeEntity( self ); return; } trap_SetBrushModel( self, self->model ); InitMover( self ); self->reached = Reached_Train; // start trains on the second frame, to make sure their targets have had // a chance to spawn self->nextthink = level.time + FRAMETIME; self->think = Think_SetupTrainTargets; }
/*QUAKED func_pendulum (0 .5 .8) ? You need to have an origin brush as part of this entity. Pendulums always swing north / south on unrotated models. Add an angles field to the model to allow rotation in other directions. Pendulum frequency is a physical constant based on the length of the beam and gravity. "model2" .md3 model to also draw "speed" the number of degrees each way the pendulum swings, (30 default) "phase" the 0.0 to 1.0 offset in the cycle to start at "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_pendulum(gentity_t *ent) { float freq; float length; float phase; float speed; G_SpawnFloat( "speed", "30", &speed ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "phase", "0", &phase ); trap_SetBrushModel( ent, ent->model ); // find pendulum length length = fabs( ent->r.mins[2] ); if ( length < 8 ) { length = 8; } freq = 1 / ( M_PI * 2 ) * sqrt( g_gravity.value / ( 3 * length ) ); ent->s.pos.trDuration = ( 1000 / freq ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); ent->s.apos.trDuration = 1000 / freq; ent->s.apos.trTime = ent->s.apos.trDuration * phase; ent->s.apos.trType = TR_SINE; ent->s.apos.trDelta[2] = speed; }
/*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS Normally bobs on the Z axis "model2" .md3 model to also draw "height" amplitude of bob (32 default) "speed" seconds to complete a bob cycle (4 default) "phase" the 0.0 to 1.0 offset in the cycle to start at "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_bobbing (gentity_t *ent) { float height; float phase; G_SpawnFloat( "speed", "4", &ent->speed ); G_SpawnFloat( "height", "32", &height ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "phase", "0", &phase ); trap_SetBrushModel( ent, ent->model ); InitMover( ent ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); ent->s.pos.trDuration = ent->speed * 1000; ent->s.pos.trTime = ent->s.pos.trDuration * phase; ent->s.pos.trType = TR_SINE; // set the axis of bobbing if ( ent->spawnflags & 1 ) { ent->s.pos.trDelta[0] = height; } else if ( ent->spawnflags & 2 ) { ent->s.pos.trDelta[1] = height; } else { ent->s.pos.trDelta[2] = height; } }
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY Scripted brush entity. A simplified means of moving brushes around based on events. "modelscale" - Scale multiplier (defaults to 1, and scales uniformly) "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored "model2" optional md3 to draw over the solid clip brush "scriptname" name used for scripting purposes (like aiName in AI scripting) "health" optionally make this entity damagable */ void SP_script_mover( gentity_t *ent ) { float scale[3] = {1,1,1}; vec3_t scalevec; if ( !ent->model ) { G_Error( "script_model_med must have a \"model\"\n" ); } if ( !ent->scriptName ) { G_Error( "script_model_med must have a \"scriptname\"\n" ); } ent->blocked = script_mover_blocked; // first position at start VectorCopy( ent->s.origin, ent->pos1 ); // VectorCopy( ent->r.currentOrigin, ent->pos1 ); VectorCopy( ent->pos1, ent->pos2 ); // don't go anywhere just yet trap_SetBrushModel( ent, ent->model ); InitMover( ent ); ent->reached = NULL; if ( ent->spawnflags & 1 ) { ent->use = script_mover_use; trap_UnlinkEntity( ent ); // make sure it's not visible return; } G_SetAngle( ent, ent->s.angles ); G_SpawnInt( "health", "0", &ent->health ); if ( ent->health ) { ent->takedamage = qtrue; } ent->die = script_mover_die; ent->pain = script_mover_pain; // look for general scaling if ( G_SpawnFloat( "modelscale", "1", &scale[0] ) ) { scale[2] = scale[1] = scale[0]; } // look for axis specific scaling if ( G_SpawnVector( "modelscale_vec", "1 1 1", &scalevec[0] ) ) { VectorCopy( scalevec, scale ); } if ( scale[0] != 1 || scale[1] != 1 || scale[2] != 1 ) { // ent->s.eType = ET_MOVERSCALED; ent->s.density = ET_MOVERSCALED; // scale is stored in 'angles2' VectorCopy( scale, ent->s.angles2 ); } script_mover_spawn( ent ); }
void SP_func_usable( gentity_t *self ) { gi.SetBrushModel( self, self->model ); InitMover( self ); VectorCopy( self->s.origin, self->s.pos.trBase ); VectorCopy( self->s.origin, self->currentOrigin ); VectorCopy( self->s.origin, self->pos1 ); self->count = 1; if (self->spawnflags & 1) { self->spawnContents = self->contents; // It Navs can temporarly turn it "on" self->s.solid = 0; self->contents = 0; self->clipmask = 0; self->svFlags |= SVF_NOCLIENT; self->s.eFlags |= EF_NODRAW; self->count = 0; } if (self->spawnflags & 2) { self->s.eFlags |= EF_ANIM_ALLFAST; } if (self->spawnflags & 4) {//FIXME: need to be able to do change to something when it's done? Or not be usable until it's done? self->s.eFlags |= EF_ANIM_ONCE; } self->e_UseFunc = useF_func_usable_use; if ( self->health ) { self->takedamage = qtrue; self->e_DieFunc = dieF_func_usable_die; self->e_PainFunc = painF_func_usable_pain; } if ( self->endFrame > 0 ) { self->s.frame = self->startFrame = 0; self->s.eFlags |= EF_SHADER_ANIM; } gi.linkentity (self); int forceVisible = 0; G_SpawnInt( "forcevisible", "0", &forceVisible ); if ( forceVisible ) {//can see these through walls with force sight, so must be broadcast if ( VectorCompare( self->s.origin, vec3_origin ) ) {//no origin brush self->svFlags |= SVF_BROADCAST; } self->s.eFlags |= EF_FORCE_VISIBLE; } }
static void MoverProcessHelper(int X, int Y, int id, PMOVER pMover) { const FILM *pfilm; const MULTI_INIT *pmi; const FRAME *pFrame; IMAGE *pim; assert(BgPal()); // Can't start actor without a background palette assert(pMover->walkReels[0][FORWARD]); // Starting actor process without walk reels InitMover(pMover); InitialPathChecks(pMover, X, Y); pfilm = (const FILM *)LockMem(pMover->walkReels[0][FORWARD]); pmi = (const MULTI_INIT *)LockMem(FROM_32(pfilm->reels[0].mobj)); //--- pFrame = (const FRAME *)LockMem(FROM_32(pmi->hMulFrame)); // get pointer to image pim = (IMAGE *)LockMem(READ_32(pFrame)); // handle to image pim->hImgPal = TO_32(BgPal()); //--- pMover->actorObj = MultiInitObject(pmi); /**/ assert(pMover->actorID == id); pMover->actorID = id; // add it to display list MultiInsertObject(GetPlayfieldList(FIELD_WORLD), pMover->actorObj); storeActorReel(id, NULL, 0, pMover->actorObj, 0, 0, 0); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, FROM_32(pfilm->reels[0].script), ONE_SECOND / FROM_32(pfilm->frate)); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); // Make him the right size SetMoverStanding(pMover); //**** if added 18/11/94, am if (X != MAGICX && Y != MAGICY) { HideMover(pMover, 0); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden } pMover->bActive = true; }
void SP_func_usable( gentity_t *self ) { gi.SetBrushModel( self, self->model ); InitMover( self ); VectorCopy( self->s.origin, self->s.pos.trBase ); VectorCopy( self->s.origin, self->currentOrigin ); VectorCopy( self->s.origin, self->pos1 ); self->count = 1; if (self->spawnflags & 1) { self->s.solid = 0; self->contents = 0; self->clipmask = 0; self->svFlags |= SVF_NOCLIENT; self->s.eFlags |= EF_NODRAW; self->count = 0; } if (self->spawnflags & 2) { self->s.eFlags |= EF_ANIM_ALLFAST; } if (self->spawnflags & 4) {//FIXME: need to be able to do change to something when it's done? Or not be usable until it's done? self->s.eFlags |= EF_ANIM_ONCE; } self->e_UseFunc = useF_func_usable_use; if ( self->health ) { self->takedamage = qtrue; self->e_DieFunc = dieF_func_usable_die; self->e_PainFunc = painF_func_usable_pain; } if ( self->endFrame > 0 ) { self->s.frame = self->startFrame = 0; self->s.eFlags |= EF_SHADER_ANIM; } gi.linkentity (self); }
/** QUAKED func_button (0 .5 .8) ? When a button is touched, it moves some distance in the direction of its angle, triggers all of its targets, waits some time, then returns to its original position where it can be triggered again. "model2" .md3 model to also draw "angle" determines the opening direction "target" all entities with a matching targetname will be used "speed" override the default 40 speed "wait" override the default 1 second wait (-1 = never return) "lip" override the default 4 pixel lip remaining at end of move "health" if set, the button must be killed instead of touched "color" constantLight color "light" constantLight radius */ void SP_func_button(gentity_t *ent) { vec3_t abs_movedir; float distance; vec3_t size; float lip; ent->sound1to2 = G_SoundIndex("sound/movers/switches/butn2.wav"); if (!ent->speed) { ent->speed = 40; } if (!ent->wait) { ent->wait = 1; } ent->wait *= 1000; // first position VectorCopy(ent->s.origin, ent->pos1); // calculate second position trap_SetBrushModel(ent, ent->model); G_SpawnFloat("lip", "4", &lip); G_SetMovedir(ent->s.angles, ent->movedir); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); VectorSubtract(ent->r.maxs, ent->r.mins, size); distance = abs_movedir[0] * size[0] + abs_movedir[1] * size[1] + abs_movedir[2] * size[2] - lip; VectorMA(ent->pos1, distance, ent->movedir, ent->pos2); if (ent->health) { // shootable button ent->takedamage = qtrue; } else { // touchable button ent->touch = Touch_Button; } InitMover(ent); }
/*QUAKED func_button (0 .5 .8) ? When a button is touched, it moves some distance in the direction of its angle, triggers all of its targets, waits some time, then returns to its original position where it can be triggered again. "model2" .md3 model to also draw "angle" determines the opening direction "target" all entities with a matching targetname will be used "speed" override the default 40 speed "wait" override the default 1 second wait (-1 = never return) "lip" override the default 4 pixel lip remaining at end of move "health" if set, the button must be killed instead of touched "color" constantLight color "light" constantLight radius */ void SP_func_button( gentity_t *ent ) { vector3 abs_movedir; float distance; vector3 size; float lip; ent->sound1to2 = G_SoundIndex("sound/movers/switches/butn2.wav"); if ( !ent->speed ) { ent->speed = 40; } if ( !ent->wait ) { ent->wait = 1; } ent->wait *= 1000; // first position VectorCopy( &ent->s.origin, &ent->pos1 ); // calculate second position trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model ); G_SpawnFloat( "lip", "4", &lip ); G_SetMovedir( &ent->s.angles, &ent->movedir ); abs_movedir.x = fabsf(ent->movedir.x); abs_movedir.y = fabsf(ent->movedir.y); abs_movedir.z = fabsf(ent->movedir.z); VectorSubtract( &ent->r.maxs, &ent->r.mins, &size ); distance = abs_movedir.x * size.x + abs_movedir.y * size.y + abs_movedir.z * size.z - lip; VectorMA (&ent->pos1, distance, &ent->movedir, &ent->pos2); if (ent->health) { // shootable button ent->takedamage = qtrue; } else { // touchable button ent->touch = Touch_Button; } InitMover( ent ); }
/** QUAKED func_plat (0 .5 .8) ? Plats are always drawn in the extended position so they will light correctly. "lip" default 8, protrusion above rest position "height" total height of movement, defaults to model height "speed" overrides default 200. "dmg" overrides default 2 "model2" .md3 model to also draw "color" constantLight color "light" constantLight radius */ void SP_func_plat(gentity_t *ent) { float lip, height; ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/plats/pt1_strt.wav"); ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/plats/pt1_end.wav"); VectorClear (ent->s.angles); G_SpawnFloat("speed", "200", &ent->speed); G_SpawnInt("dmg", "2", &ent->damage); G_SpawnFloat("wait", "1", &ent->wait); G_SpawnFloat("lip", "8", &lip); ent->wait = 1000; // create second position trap_SetBrushModel(ent, ent->model); if (!G_SpawnFloat("height", "0", &height)) { height = (ent->r.maxs[2] - ent->r.mins[2]) - lip; } // pos1 is the rest (bottom) position, pos2 is the top VectorCopy(ent->s.origin, ent->pos2); VectorCopy(ent->pos2, ent->pos1); ent->pos1[2] -= height; InitMover(ent); // touch function keeps the plat from returning while // a live player is standing on it ent->touch = Touch_Plat; ent->blocked = Blocked_Door; ent->parent = ent; // so it can be treated as a door // spawn the trigger if one hasn't been custom made if (!ent->targetname) { SpawnPlatTrigger(ent); } }
void func_wait_return_solid( gentity_t *self ) { //once a frame, see if it's clear. self->clipmask = CONTENTS_BODY;//|CONTENTS_MONSTERCLIP|CONTENTS_BOTCLIP; if ( !(self->spawnflags&16) || G_TestEntityPosition( self ) == NULL ) { gi.SetBrushModel( self, self->model ); VectorCopy( self->currentOrigin, self->pos1 ); InitMover( self ); /* VectorCopy( self->s.origin, self->s.pos.trBase ); VectorCopy( self->s.origin, self->currentOrigin ); */ //if we moved, we want the *current* origin, not our start origin! VectorCopy( self->currentOrigin, self->s.pos.trBase ); gi.linkentity( self ); self->svFlags &= ~SVF_NOCLIENT; self->s.eFlags &= ~EF_NODRAW; self->e_UseFunc = useF_func_usable_use; self->clipmask = 0; if ( self->target2 && self->target2[0] ) { G_UseTargets2( self, self->activator, self->target2 ); } if ( self->s.eFlags & EF_ANIM_ONCE ) {//Start our anim self->s.frame = 0; } //NOTE: be sure to reset the brushmodel before doing this or else CONTENTS_OPAQUE may not be on when you call this if ( !(self->spawnflags&1) ) {//START_OFF doesn't effect area portals gi.AdjustAreaPortalState( self, qfalse ); } } else { self->clipmask = 0; self->e_ThinkFunc = thinkF_func_wait_return_solid; self->nextthink = level.time + FRAMETIME; } }
/*QUAKED func_rotating (0 .5 .8) ? START_ON - X_AXIS Y_AXIS You need to have an origin brush as part of this entity. The center of that brush will be the point around which it is rotated. It will rotate around the Z axis by default. You can check either the X_AXIS or Y_AXIS box to change that. "model2" .md3 model to also draw "speed" determines how fast it moves; default value is 100. "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_rotating (gentity_t *ent) { if ( !ent->speed ) { ent->speed = 100; } // set the axis of rotation ent->s.apos.trType = TR_LINEAR; if ( ent->spawnflags & 4 ) ent->s.apos.trDelta.roll = ent->speed; else if ( ent->spawnflags & 8 ) ent->s.apos.trDelta.pitch = ent->speed; else ent->s.apos.trDelta.yaw = ent->speed; if (!ent->damage) ent->damage = 2; trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model ); InitMover( ent ); VectorCopy( &ent->s.origin, &ent->s.pos.trBase ); VectorCopy( &ent->s.pos.trBase, &ent->r.currentOrigin ); VectorCopy( &ent->s.apos.trBase, &ent->r.currentAngles ); trap->SV_LinkEntity( (sharedEntity_t *)ent ); }
/*QUAKED func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS Normally bobs on the Z axis "model2" .md3 model to also draw "height" amplitude of bob (32 default) "speed" seconds to complete a bob cycle (4 default) "phase" the 0.0 to 1.0 offset in the cycle to start at "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius */ void SP_func_bobbing (gentity_t *ent) { float height; float phase; G_SpawnFloat( "speed", "4", &ent->speed ); G_SpawnFloat( "height", "32", &height ); G_SpawnInt( "dmg", "2", &ent->damage ); G_SpawnFloat( "phase", "0", &phase ); trap->SV_SetBrushModel( (sharedEntity_t *)ent, ent->model ); InitMover( ent ); VectorCopy( &ent->s.origin, &ent->s.pos.trBase ); VectorCopy( &ent->s.origin, &ent->r.currentOrigin ); ent->s.pos.trDuration = (int)(ent->speed * 1000); ent->s.pos.trTime = (int)(ent->s.pos.trDuration * phase); ent->s.pos.trType = TR_SINE; // set the axis of bobbing if ( ent->spawnflags & 1 ) ent->s.pos.trDelta.x = height; else if ( ent->spawnflags & 2 ) ent->s.pos.trDelta.y = height; else ent->s.pos.trDelta.z = height; }
/*QUAKED script_mover (0.5 0.25 1.0) ? TRIGGERSPAWN SOLID EXPLOSIVEDAMAGEONLY RESURECTABLE COMPASS ALLIED AXIS MOUNTED_GUN Scripted brush entity. A simplified means of moving brushes around based on events. "modelscale" - Scale multiplier (defaults to 1, and scales uniformly) "modelscale_vec" - Set scale per-axis. Overrides "modelscale", so if you have both the "modelscale" is ignored "model2" optional md3 to draw over the solid clip brush "scriptname" name used for scripting purposes (like aiName in AI scripting) "health" optionally make this entity damagable "description" used with health, if the entity is damagable, it draws a healthbar with this description above it. */ void SP_script_mover(gentity_t *ent) { float scale[3] = { 1, 1, 1 }; vec3_t scalevec; char *modelname; char *tagent; char *s; if (!ent->model) { G_Error("script_mover must have a \"model\"\n"); } if (!ent->scriptName) { G_Error("script_mover must have a \"scriptname\"\n"); } ent->blocked = script_mover_blocked; // first position at start VectorCopy(ent->s.origin, ent->pos1); VectorCopy(ent->pos1, ent->pos2); // don't go anywhere just yet trap_SetBrushModel(ent, ent->model); InitMover(ent); ent->reached = NULL; ent->s.animMovetype = 0; ent->s.density = 0; if (ent->spawnflags & 256) { ent->s.density |= 2; } if (ent->spawnflags & 8) { ent->use = script_mover_use; } if (ent->spawnflags & 16) { ent->s.time2 = 1; } else { ent->s.time2 = 0; } if (ent->spawnflags & 32) { ent->s.teamNum = TEAM_ALLIES; } else if (ent->spawnflags & 64) { ent->s.teamNum = TEAM_AXIS; } else { ent->s.teamNum = TEAM_FREE; } if (ent->spawnflags & 1) { ent->use = script_mover_use; trap_UnlinkEntity(ent); // make sure it's not visible return; } G_SetAngle(ent, ent->s.angles); G_SpawnInt("health", "0", &ent->health); if (ent->health) { ent->takedamage = qtrue; ent->count = ent->health; // client needs to know about it as well ent->s.effect1Time = ent->count; ent->s.dl_intensity = 255; if (G_SpawnString("description", "", &s)) { char cs[MAX_INFO_STRING]; trap_GetConfigstring(CS_SCRIPT_MOVER_NAMES, cs, sizeof (cs)); Info_SetValueForKey(cs, va("%d", (int)(ent - g_entities)), s); trap_SetConfigstring(CS_SCRIPT_MOVER_NAMES, cs); } } else { ent->count = 0; } ent->die = script_mover_die; // look for general scaling if (G_SpawnFloat("modelscale", "1", &scale[0])) { scale[2] = scale[1] = scale[0]; } if (G_SpawnString("model2", "", &modelname)) { char tagname[MAX_QPATH]; COM_StripExtension(modelname, tagname); Q_strcat(tagname, MAX_QPATH, ".tag"); ent->tagNumber = trap_LoadTag(tagname); } // look for axis specific scaling if (G_SpawnVector("modelscale_vec", "1 1 1", &scalevec[0])) { VectorCopy(scalevec, scale); } if (scale[0] != 1 || scale[1] != 1 || scale[2] != 1) { ent->s.density |= 1; // scale is stored in 'angles2' VectorCopy(scale, ent->s.angles2); } if (ent->spawnflags & 128) { ent->s.density |= 4; ent->waterlevel = 0; if (G_SpawnString("gun", "", &modelname) && !Q_stricmp(modelname, "browning")) { ent->s.density |= 8; } G_SpawnString("tagent", "", &tagent); Q_strncpyz(ent->tagBuffer, tagent, 16); ent->s.powerups = -1; } ent->think = script_mover_spawn; ent->nextthink = level.time + FRAMETIME; }
/** * Get it into our heads that there's nothing doing. * Called at the end of a scene. */ void DropMovers() { for (int i = 0; i < MAX_MOVERS; i++) InitMover(&g_Movers[i]); }
/*QUAKED func_door (0 .5 .8) ? START_OPEN x CRUSHER TOGGLE wait in both the start and end states for a trigger event. START_OPEN the door to moves to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). NOMONSTER monsters will not trigger this door "model2" .md3 model to also draw "angle" determines the opening direction "targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. "speed" movement speed (100 default) "wait" wait before returning (3 default, -1 = never return) "lip" lip remaining at end of move (8 default) "dmg" damage to inflict when blocked (2 default) "color" constantLight color "light" constantLight radius "health" if set, the door must be shot open */ void SP_func_door (gentity_t *ent) { vec3_t abs_movedir; float distance; vec3_t size; float lip; ent->sound1to2 = ent->sound2to1 = G_SoundIndex("sound/movers/doors/dr1_strt.wav"); ent->soundPos1 = ent->soundPos2 = G_SoundIndex("sound/movers/doors/dr1_end.wav"); ent->blocked = Blocked_Door; // default speed of 400 if (!ent->speed) ent->speed = 400; // default wait of 2 seconds if (!ent->wait) ent->wait = 2; ent->wait *= 1000; // default lip of 8 units G_SpawnFloat( "lip", "8", &lip ); // default damage of 2 points G_SpawnInt( "dmg", "2", &ent->damage ); // first position at start VectorCopy( ent->s.origin, ent->pos1 ); // calculate second position trap_SetBrushModel( ent, ent->model ); G_SetMovedir (ent->s.angles, ent->movedir); abs_movedir[0] = fabs(ent->movedir[0]); abs_movedir[1] = fabs(ent->movedir[1]); abs_movedir[2] = fabs(ent->movedir[2]); VectorSubtract( ent->r.maxs, ent->r.mins, size ); distance = DotProduct( abs_movedir, size ) - lip; VectorMA( ent->pos1, distance, ent->movedir, ent->pos2 ); // if "start_open", reverse position 1 and 2 if ( ent->spawnflags & 1 ) { vec3_t temp; VectorCopy( ent->pos2, temp ); VectorCopy( ent->s.origin, ent->pos2 ); VectorCopy( temp, ent->pos1 ); } InitMover( ent ); ent->nextthink = level.time + FRAMETIME; if ( ! (ent->flags & FL_TEAMSLAVE ) ) { int health; G_SpawnInt( "health", "0", &health ); if ( health ) { ent->takedamage = qtrue; } if ( ent->targetname || health ) { // non touch/shoot doors ent->think = Think_MatchTeam; } else { ent->think = Think_SpawnNewDoorTrigger; } } }
/** * Tinsel 2 Moving actor process * - 1 per moving actor in current scene. */ void T2MoverProcess(CORO_PARAM, const void *param) { CORO_BEGIN_CONTEXT; CORO_END_CONTEXT(_ctx); // Get the co-ordinates - copied to process when it was created const MAINIT *rpos = (const MAINIT *)param; PMOVER pMover = rpos->pMover; int i; FILM *pFilm; PMULTI_INIT pmi; CORO_BEGIN_CODE(_ctx); for (i = 0; i < TOTAL_SCALES; i++) { if (pMover->walkReels[i][FORWARD]) break; } assert(i < TOTAL_SCALES); InitMover(pMover); InitialPathChecks(pMover, rpos->X, rpos->Y); pFilm = (FILM *)LockMem(pMover->walkReels[i][FORWARD]); // Any old reel pmi = (PMULTI_INIT)LockMem(FROM_32(pFilm->reels[0].mobj)); // Poke in the background palette PokeInPalette(pmi); pMover->actorObj = MultiInitObject(pmi); // FIXME: This is what the original did. A bug, perhaps? // pMover->actorID = pMover->actorID; pMover->bActive = true; // add it to display list MultiInsertObject( GetPlayfieldList(FIELD_WORLD), pMover->actorObj ); InitStepAnimScript(&pMover->actorAnim, pMover->actorObj, pFilm->reels[0].script, ONE_SECOND/pFilm->frate); pMover->stepCount = 0; MultiSetAniXY(pMover->actorObj, pMover->objX, pMover->objY); // If no path, just use first path in the scene if (pMover->hCpath != NOPOLY) SetMoverZ(pMover, pMover->objY, GetPolyZfactor(pMover->hCpath)); else SetMoverZ(pMover, pMover->objY, GetPolyZfactor(FirstPathPoly())); // Make him the right size SetMoverStanding(pMover); HideMover(pMover); // Allows a play to come in before this appears pMover->bHidden = false; // ...but don't stay hidden for (;;) { if (pMover->bSpecReel) { if (!pMover->bHidden) StepAnimScript(&pMover->actorAnim); } else DoMoveActor(pMover); CheckBrightness(pMover); CORO_SLEEP(1); } CORO_END_CODE; }
/* =============== G_ScriptAction_GotoMarker syntax: gotomarker <targetname> <speed> [accel/deccel] [turntotarget] [wait] NOTE: speed may be modified to round the duration to the next 50ms for smooth transitions =============== */ qboolean G_ScriptAction_GotoMarker( gentity_t *ent, char *params ) { char *pString, *token; gentity_t *target; vec3_t vec; float speed, dist; qboolean wait = qfalse, turntotarget = qfalse; int trType; int duration, i; vec3_t diff; vec3_t angles; if ( params && ( ent->scriptStatus.scriptFlags & SCFL_GOING_TO_MARKER ) ) { // we can't process a new movement until the last one has finished return qfalse; } if ( !params || ent->scriptStatus.scriptStackChangeTime < level.time ) { // we are waiting for it to reach destination if ( ent->s.pos.trTime + ent->s.pos.trDuration <= level.time ) { // we made it ent->scriptStatus.scriptFlags &= ~SCFL_GOING_TO_MARKER; // set the angles at the destination BG_EvaluateTrajectory( &ent->s.apos, ent->s.apos.trTime + ent->s.apos.trDuration, ent->s.angles ); VectorCopy( ent->s.angles, ent->s.apos.trBase ); VectorCopy( ent->s.angles, ent->r.currentAngles ); ent->s.apos.trTime = level.time; ent->s.apos.trDuration = 0; ent->s.apos.trType = TR_STATIONARY; VectorClear( ent->s.apos.trDelta ); // stop moving BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->s.origin ); VectorCopy( ent->s.origin, ent->s.pos.trBase ); VectorCopy( ent->s.origin, ent->r.currentOrigin ); ent->s.pos.trTime = level.time; ent->s.pos.trDuration = 0; ent->s.pos.trType = TR_STATIONARY; VectorClear( ent->s.pos.trDelta ); script_linkentity( ent ); return qtrue; } } else { // we have just started this command pString = params; token = COM_ParseExt( &pString, qfalse ); if ( !token[0] ) { G_Error( "G_Scripting: gotomarker must have an targetname\n" ); } // find the entity with the given "targetname" target = G_Find( NULL, FOFS( targetname ), token ); if ( !target ) { G_Error( "G_Scripting: can't find entity with \"targetname\" = \"%s\"\n", token ); } VectorSubtract( target->r.currentOrigin, ent->r.currentOrigin, vec ); token = COM_ParseExt( &pString, qfalse ); if ( !token[0] ) { G_Error( "G_Scripting: gotomarker must have a speed\n" ); } speed = atof( token ); trType = TR_LINEAR_STOP; while ( token[0] ) { token = COM_ParseExt( &pString, qfalse ); if ( token[0] ) { if ( !Q_stricmp( token, "accel" ) ) { trType = TR_ACCELERATE; } else if ( !Q_stricmp( token, "deccel" ) ) { trType = TR_DECCELERATE; } else if ( !Q_stricmp( token, "wait" ) ) { wait = qtrue; } else if ( !Q_stricmp( token, "turntotarget" ) ) { turntotarget = qtrue; } } } // start the movement if ( ent->s.eType == ET_MOVER ) { VectorCopy( vec, ent->movedir ); VectorCopy( ent->r.currentOrigin, ent->pos1 ); VectorCopy( target->r.currentOrigin, ent->pos2 ); ent->speed = speed; dist = VectorDistance( ent->pos1, ent->pos2 ); // setup the movement with the new parameters InitMover( ent ); // start the movement SetMoverState( ent, MOVER_1TO2, level.time ); if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 ); ent->s.pos.trType = trType; } ent->reached = NULL; if ( turntotarget ) { duration = ent->s.pos.trDuration; VectorCopy( target->s.angles, angles ); for ( i = 0; i < 3; i++ ) { diff[i] = AngleDifference( angles[i], ent->s.angles[i] ); while ( diff[i] > 180 ) diff[i] -= 360; while ( diff[i] < -180 ) diff[i] += 360; } VectorCopy( ent->s.angles, ent->s.apos.trBase ); if ( duration ) { VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta ); } else { VectorClear( ent->s.apos.trDelta ); } ent->s.apos.trDuration = duration; ent->s.apos.trTime = level.time; ent->s.apos.trType = TR_LINEAR_STOP; if ( trType != TR_LINEAR_STOP ) { // allow for acceleration/decceleration ent->s.pos.trDuration = 1000.0 * dist / ( speed / 2.0 ); ent->s.pos.trType = trType; } } } else { // calculate the trajectory ent->s.pos.trType = TR_LINEAR_STOP; ent->s.pos.trTime = level.time; VectorCopy( ent->r.currentOrigin, ent->s.pos.trBase ); dist = VectorNormalize( vec ); VectorScale( vec, speed, ent->s.pos.trDelta ); ent->s.pos.trDuration = 1000 * ( dist / speed ); if ( turntotarget ) { duration = ent->s.pos.trDuration; VectorCopy( target->s.angles, angles ); for ( i = 0; i < 3; i++ ) { diff[i] = AngleDifference( angles[i], ent->s.angles[i] ); while ( diff[i] > 180 ) diff[i] -= 360; while ( diff[i] < -180 ) diff[i] += 360; } VectorCopy( ent->s.angles, ent->s.apos.trBase ); if ( duration ) { VectorScale( diff, 1000.0 / (float)duration, ent->s.apos.trDelta ); } else { VectorClear( ent->s.apos.trDelta ); } ent->s.apos.trDuration = duration; ent->s.apos.trTime = level.time; ent->s.apos.trType = TR_LINEAR_STOP; } } if ( !wait ) { // round the duration to the next 50ms if ( ent->s.pos.trDuration % 50 ) { float frac; frac = (float)( ( ( ent->s.pos.trDuration / 50 ) * 50 + 50 ) - ent->s.pos.trDuration ) / (float)( ent->s.pos.trDuration ); if ( frac < 1 ) { VectorScale( ent->s.pos.trDelta, 1.0 / ( 1.0 + frac ), ent->s.pos.trDelta ); ent->s.pos.trDuration = ( ent->s.pos.trDuration / 50 ) * 50 + 50; } } // set the goto flag, so we can keep processing the move until we reach the destination ent->scriptStatus.scriptFlags |= SCFL_GOING_TO_MARKER; return qtrue; // continue to next command } } BG_EvaluateTrajectory( &ent->s.pos, level.time, ent->r.currentOrigin ); BG_EvaluateTrajectory( &ent->s.apos, level.time, ent->r.currentAngles ); script_linkentity( ent ); return qfalse; }