/* ================ MatchTeam All entities in a mover team will move from pos1 to pos2 in the same amount of time ================ */ void MatchTeam( gentity_t *teamLeader, int moverState, int time ) { gentity_t *slave; for ( slave = teamLeader ; slave ; slave = slave->teamchain ) { SetMoverState( slave, moverState, time ); } }
/* ================ Reached_BinaryMover ================ */ void Reached_BinaryMover( gentity_t *ent ) { // stop the looping sound ent->s.loopSound = ent->soundLoop; if ( ent->moverState == MOVER_1TO2 ) { // reached pos2 SetMoverState( ent, MOVER_POS2, level.time ); // play sound if ( ent->soundPos2 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos2 ); } // return to pos1 after a delay ent->think = ReturnToPos1; ent->nextthink = level.time + ent->wait; // fire targets if ( !ent->activator ) { ent->activator = ent; } G_UseTargets( ent, ent->activator ); } else if ( ent->moverState == MOVER_2TO1 ) { // reached pos1 SetMoverState( ent, MOVER_POS1, level.time ); // play sound if ( ent->soundPos1 ) { G_AddEvent( ent, EV_GENERAL_SOUND, ent->soundPos1 ); } // close areaportals if ( ent->teammaster == ent || !ent->teammaster ) { trap_AdjustAreaPortalState( ent, qfalse ); } } else { G_Error( "Reached_BinaryMover: bad moverState" ); } }
/* =============== Reached_Train =============== */ void Reached_Train( gentity_t *ent ) { gentity_t *next; float speed; vec3_t move; float length; // copy the apropriate values next = ent->nextTrain; if ( !next || !next->nextTrain ) { return; // just stop } // fire all other targets G_UseTargets( next, NULL ); // set the new trajectory ent->nextTrain = next->nextTrain; VectorCopy( next->s.origin, ent->pos1 ); VectorCopy( next->nextTrain->s.origin, ent->pos2 ); // if the path_corner has a speed, use that if ( next->speed ) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.pos.trDuration = length * 1000 / speed; // looping sound ent->s.loopSound = next->soundLoop; // start it going SetMoverState( ent, MOVER_1TO2, level.time ); // if there is a "wait" value on the target, don't start moving yet if ( next->wait ) { ent->nextthink = level.time + next->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } }
/* =============== Reached_Train =============== */ void Reached_Train( gentity_t *ent ) { gentity_t *next; float speed; vec3_t move; float length; // copy the apropriate values next = ent->nextTrain; if ( !next || !next->nextTrain ) { return; // just stop } // fire all other targets G_UseTargets( next, NULL ); // set the new trajectory ent->nextTrain = next->nextTrain; VectorCopy( next->s.origin, ent->pos1 ); VectorCopy( next->nextTrain->s.origin, ent->pos2 ); // if the path_corner has a speed, use that if ( next->speed ) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.pos.trDuration = length * 1000 / speed; // Tequila comment: Be sure to send to clients after any fast move case ent->r.svFlags &= ~SVF_NOCLIENT; // Tequila comment: Fast move case if(ent->s.pos.trDuration<1) { // Tequila comment: As trDuration is used later in a division, we need to avoid that case now // With null trDuration, // the calculated rocks bounding box becomes infinite and the engine think for a short time // any entity is riding that mover but not the world entity... In rare case, I found it // can also stuck every map entities after func_door are used. // The desired effect with very very big speed is to have instant move, so any not null duration // lower than a frame duration should be sufficient. // Afaik, the negative case don't have to be supported. ent->s.pos.trDuration=1; // Tequila comment: Don't send entity to clients so it becomes really invisible ent->r.svFlags |= SVF_NOCLIENT; } // looping sound ent->s.loopSound = next->soundLoop; // start it going SetMoverState( ent, MOVER_1TO2, level.time ); // if there is a "wait" value on the target, don't start moving yet if ( next->wait ) { ent->nextthink = level.time + next->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } }
/* =========== G_ReachedPath =========== */ qboolean G_ReachedPath(gentity_t *ent, qboolean check) { gentity_t *point; qboolean backward; if (!(ent->s.eFlags & EF_PATHMODE)) { return qfalse; } if (ent->player) { backward = (ent->player->ps.eFlags & EF_TRAINBACKWARD); } else { backward = (ent->s.eFlags & EF_TRAINBACKWARD); } if (backward) { point = ent->prevTrain; } else { point = ent->nextTrain; } if (!point && ent->player) { // Previous point if (backward) point = ent->nextTrain; else point = ent->prevTrain; // Stop train return qfalse; } else if (!point && !ent->player) { // end of path if (!(ent->spawnflags & 1)) { // Stop train return qfalse; } // Previous point if (backward) point = ent->nextTrain; else point = ent->prevTrain; // Go back the way you came backward = !backward; ent->s.eFlags ^= EF_TRAINBACKWARD; // Next point if (backward) point = ent->prevTrain; else point = ent->nextTrain; if (!point) { return qfalse; } } if ((!backward && point == point->nextTrain) || (backward && point == point->prevTrain)) { // Entity points to self... G_Printf("DEBUG: Entity points to self!\n"); return qfalse; } // ZTM: Check if we have made it to the next train // Doesn't work with PATHF_AXIS! if (check) { vec3_t targetPos; vec3_t origin; vec_t dist; VectorCopy(point->s.origin, targetPos); if (ent->player) VectorCopy(ent->player->ps.origin, origin); else VectorCopy(ent->s.origin, origin); if (ent->player && ent->player->ps.pathMode == PATHMODE_SIDE) { // "2D" path origin[2] = targetPos[2] = 0; // Don't compare Z } dist = Distance(origin, targetPos); if (dist > 20.0f) { return qfalse; } } // fire all other targets G_UseTargets( point, ent ); // Setup next move if (backward) { ent->prevTrain = point->prevTrain; if (ent->prevTrain) { ent->nextTrain = point; } } else { ent->nextTrain = point->nextTrain; if (ent->nextTrain) { ent->prevTrain = point; } } // set the new trajectory if (ent->prevTrain) { VectorCopy( ent->prevTrain->s.origin, ent->pos1 ); } if (ent->nextTrain) { VectorCopy( ent->nextTrain->s.origin, ent->pos2 ); } if (ent->s.eType == ET_MOVER) { float speed; vec3_t move; float length; // if the path_corner has a speed, use that if ( point->speed ) { speed = point->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.pos.trDuration = length * 1000 / speed; // Tequila comment: Be sure to send to clients after any fast move case ent->r.svFlags &= ~SVF_NOCLIENT; // Tequila comment: Fast move case if(ent->s.pos.trDuration<1) { // Tequila comment: As trDuration is used later in a division, we need to avoid that case now // With null trDuration, // the calculated rocks bounding box becomes infinite and the engine think for a short time // any entity is riding that mover but not the world entity... In rare case, I found it // can also stuck every map entities after func_door are used. // The desired effect with very very big speed is to have instant move, so any not null duration // lower than a frame duration should be sufficient. // Afaik, the negative case don't have to be supported. ent->s.pos.trDuration=1; // Tequila comment: Don't send entity to clients so it becomes really invisible ent->r.svFlags |= SVF_NOCLIENT; } // looping sound ent->s.loopSound = point->soundLoop; // start it going if (backward) SetMoverState( ent, MOVER_2TO1, level.time ); else SetMoverState( ent, MOVER_1TO2, level.time ); // if there is a "wait" value on the target, don't start moving yet if ( point->wait ) { ent->nextthink = level.time + point->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } } else if (ent->player) { vec3_t dir; vec3_t viewAngles; int mode; // Set path movement style mode = point->moverState; // Unset style means use pervious style. if (!mode) { mode = ent->player->ps.pathMode; } VectorCopy( ent->pos1, ent->player->ps.grapplePoint ); VectorCopy( ent->pos2, ent->player->ps.nextPoint ); if (backward) VectorSubtract( ent->pos1, ent->player->ps.origin, dir ); else VectorSubtract( ent->pos2, ent->player->ps.origin, dir ); vectoangles( dir, viewAngles ); viewAngles[ROLL] = ent->player->ps.viewangles[ROLL]; viewAngles[PITCH] = ent->player->ps.viewangles[PITCH]; SetPlayerViewAngle(ent, viewAngles); if (mode == PATHMODE_SIDE) { if (backward) { ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW]-90; } else { ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW]+90; } } else { ent->player->ps.stats[STAT_DEAD_YAW] = viewAngles[YAW]; } if (mode != ent->player->ps.pathMode) { // ZTM: Do we need to do anything when the mode changes? ent->player->ps.pathMode = mode; } } return qtrue; }
/* =============== 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; }
void Reached_Tramcar( gentity_t *ent ) { gentity_t *next; float speed; vec3_t move; float length; // copy the apropriate values next = ent->nextTrain; if ( !next || !next->nextTrain ) { return; // just stop } // Rafael if ( next->wait == -1 && next->count ) { // G_Printf ("stoped wait = -1 count %i\n",next->count); return; } if ( !Q_stricmp( ent->classname, "props_me109" ) ) { vec3_t vec, angles; float diff; if ( next->spawnflags & 8 ) { // laps next->count--; if ( !next->count ) { next->count = next->count2; GetNextTrack( ent ); Think_SetupAirplaneWaypoints( ent ); next = ent->nextTrain; G_Printf( "changed track to %s\n", next->targetname ); } else { G_Printf( "%s lap %i\n", next->targetname, next->count ); } } else if ( ( next->spawnflags & 1 ) && !( next->count ) && ent->health > 0 ) { // SCRIPT flag GetNextTrack( ent ); Think_SetupAirplaneWaypoints( ent ); } else if ( ( next->spawnflags & 2 ) && ( ent->spawnflags & 8 ) && ent->health <= 0 && ent->takedamage ) { // death path ent->takedamage = qfalse; GetNextTrack( ent ); Think_SetupAirplaneWaypoints( ent ); } else if ( ( next->spawnflags & 4 ) ) { // explode the plane ExplodePlaneSndFx( ent ); ent->s.modelindex = crash_part; // spawn the wing at the player effect ent->nextTrain = NULL; G_UseTargets( next, NULL ); return; } VectorSubtract( ent->nextTrain->nextTrain->s.origin, ent->r.currentOrigin, vec ); vectoangles( vec, angles ); diff = AngleSubtract( ent->r.currentAngles [YAW], angles[YAW] ); // diff = AngleSubtract (ent->TargetAngles [YAW], angles[YAW]); ent->rotate[1] = 1; ent->angle = -diff; //if (angles[YAW] == 0) // ent->s.apos.trDuration = ent->s.pos.trDuration; //else // ent->s.apos.trDuration = 1000; { VectorCopy( next->s.origin, ent->pos1 ); VectorCopy( next->nextTrain->s.origin, ent->pos2 ); // if the path_corner has a speed, use that if ( next->speed ) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.apos.trDuration = length * 1000 / speed; //testing // ent->gDuration = ent->s.apos.trDuration; ent->gDurationBack = ent->gDuration = ent->s.apos.trDuration; // ent->gDeltaBack = ent->gDelta = } VectorClear( ent->s.apos.trDelta ); SetMoverState( ent, MOVER_1TO2ROTATE, level.time ); VectorCopy( ent->r.currentAngles, ent->s.apos.trBase ); trap_LinkEntity( ent ); ent->think = props_me109_think; ent->nextthink = level.time + 50; } else if ( !Q_stricmp( ent->classname, "truck_cam" ) ) { G_Printf( "target: %s\n", next->targetname ); if ( next->spawnflags & 2 ) { // END ent->s.loopSound = 0; // stop sound ent->nextTrain = NULL; return; } else { vec3_t vec, angles; float diff; if ( next->spawnflags & 4 ) { // reverse ent->props_frame_state = truck_reverse; VectorSubtract( ent->r.currentOrigin, ent->nextTrain->nextTrain->s.origin, vec ); } else { ent->props_frame_state = truck_moving; VectorSubtract( ent->nextTrain->nextTrain->s.origin, ent->r.currentOrigin, vec ); } vectoangles( vec, angles ); diff = AngleSubtract( ent->r.currentAngles [YAW], angles[YAW] ); ent->rotate[1] = 1; ent->angle = -diff; if ( angles[YAW] == 0 ) { ent->s.apos.trDuration = ent->s.pos.trDuration; } else { ent->s.apos.trDuration = 1000; } //testing ent->gDuration = ent->s.pos.trDuration; VectorClear( ent->s.apos.trDelta ); SetMoverState( ent, MOVER_1TO2ROTATE, level.time ); VectorCopy( ent->r.currentAngles, ent->s.apos.trBase ); trap_LinkEntity( ent ); } if ( next->wait == -1 ) { ent->props_frame_state = truck_idle; } if ( next->count2 == 1 ) { ent->props_frame_state = truck_gear1; } else if ( next->count2 == 2 ) { ent->props_frame_state = truck_gear2; } else if ( next->count2 == 3 ) { ent->props_frame_state = truck_gear3; } switch ( ent->props_frame_state ) { case truck_idle: ent->s.loopSound = truck_idle_snd; break; case truck_gear1: ent->s.loopSound = truck_gear1_snd; break; case truck_gear2: ent->s.loopSound = truck_gear2_snd; break; case truck_gear3: ent->s.loopSound = truck_gear3_snd; break; case truck_reverse: ent->s.loopSound = truck_reverse_snd; break; case truck_moving: ent->s.loopSound = truck_moving_snd; break; case truck_breaking: ent->s.loopSound = truck_breaking_snd; break; case truck_bouncy1: ent->s.loopSound = truck_bouncy1_snd; break; case truck_bouncy2: ent->s.loopSound = truck_bouncy2_snd; break; case truck_bouncy3: ent->s.loopSound = truck_bouncy3_snd; break; } //testing ent->s.loopSound = truck_sound; ent->think = truck_cam_think; ent->nextthink = level.time + ( FRAMETIME / 2 ); } else if ( !Q_stricmp( ent->classname, "camera_cam" ) ) { } // fire all other targets G_UseTargets( next, NULL ); // set the new trajectory ent->nextTrain = next->nextTrain; if ( next->wait == -1 ) { next->count = 1; } VectorCopy( next->s.origin, ent->pos1 ); VectorCopy( next->nextTrain->s.origin, ent->pos2 ); // if the path_corner has a speed, use that if ( next->speed ) { speed = next->speed; } else { // otherwise use the train's speed speed = ent->speed; } if ( speed < 1 ) { speed = 1; } // calculate duration VectorSubtract( ent->pos2, ent->pos1, move ); length = VectorLength( move ); ent->s.pos.trDuration = length * 1000 / speed; //testing // ent->gDuration = ent->s.pos.trDuration; ent->gDurationBack = ent->gDuration = ent->s.pos.trDuration; // ent->gDeltaBack = ent->gDelta = ; // looping sound if ( next->soundLoop ) { ent->s.loopSound = next->soundLoop; } // start it going SetMoverState( ent, MOVER_1TO2, level.time ); // if there is a "wait" value on the target, don't start moving yet // if ( next->wait ) if ( next->wait && next->wait != -1 ) { ent->nextthink = level.time + next->wait * 1000; ent->think = Think_BeginMoving; ent->s.pos.trType = TR_STATIONARY; } }
/*** Moves an entity like a func_train entity. Targets have to be path_corner entities. @function AsTrain @param mover Entity to move. @param target path_corner entity to move to. @param speed Speed to move with to the first path_corner. @return Success or failure. */ static int Mover_AsTrain(lua_State * L) { lent_t *lent, *tlent; gentity_t *ent = NULL; gentity_t *targ = NULL; vec3_t move; float length; int id = 0, tid = 0; float speed = (float)luaL_checknumber(L, 3); if(lua_isnumber(L, 1)) { id = luaL_checkint(L, 1); if(id < 0 || id > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } ent = &g_entities[id]; if(ent == NULL) { lua_pushboolean(L, qfalse); return 1; } } else { lent = Lua_GetEntity(L, 1); if(lent == NULL || lent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } ent = lent->e; } if(luaL_checkint(L, 2)) { tid = luaL_checkint(L, 2); if(tid < 0 || tid > MAX_GENTITIES - 1) { lua_pushboolean(L, qfalse); return 1; } targ = &g_entities[tid]; if(targ == NULL) { lua_pushboolean(L, qfalse); return 1; } } else { tlent = Lua_GetEntity(L, 2); if(!tlent || tlent->e == NULL) { lua_pushboolean(L, qfalse); return 1; } targ = tlent->e; } LUA_DEBUG("Mover_AsTrain - start: ent=%d target=%d speed=%f", ent->s.number, targ->s.number, speed); if(ent == NULL || targ == NULL) { LUA_DEBUG("Mover_AsTrain - return: ent or/and target missing"); lua_pushboolean(L, qfalse); return 1; } if(speed < 1) { LUA_DEBUG("Mover_AsTrain - moving: speed less than 1 fixed"); speed = 1; } if(ent->nextTrain) { LUA_DEBUG("Mover_AsTrain - pathing: NextTrain=%d ", ent->nextTrain->s.number); } ent->speed = speed; ent->nextTrain = targ; ent->reached = Reached_Train; ent->target = G_NewString(targ->targetname); Think_SetupTrainTargets(ent); BG_EvaluateTrajectory(&ent->s.pos, level.time, ent->r.currentOrigin); VectorCopy(ent->r.currentOrigin, ent->s.origin); VectorCopy(ent->s.origin, ent->pos1); VectorCopy(ent->nextTrain->s.origin, ent->pos2); VectorSubtract(ent->pos2, ent->pos1, move); length = VectorLength(move); if(length <= 0.05) { G_SetOrigin(ent, ent->pos2); LUA_DEBUG("Mover_AsTrain - return: snapped to target, length too small length=%f", length); lua_pushboolean(L, qtrue); return 1; } ent->s.pos.trDuration = length * 1000 / speed; ent->s.loopSound = ent->nextTrain->soundLoop; SetMoverState(ent, MOVER_1TO2, level.time); LUA_DEBUG("Mover_AsTrain - return: moving to target, length=%f duration=%d", length, ent->s.pos.trDuration); lua_pushboolean(L, qtrue); return 1; }