/* ============== BotUpdateInput ============== */ void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //change the bot view angles BotChangeViewAngles(bs, (float) elapsed_time / 1000); //retrieve the bot input botlib_export->ea.EA_GetInput(bs->client, (float) time / 1000, &bi); //respawn hack if (bi.actionflags & ACTION_RESPAWN) { if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK); } //convert the bot input to a usercmd BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } }
/* ============== BotChangeViewAngle ============== */ float BotChangeViewAngle( float angle, float ideal_angle, float speed ) { float move; angle = AngleMod( angle ); ideal_angle = AngleMod( ideal_angle ); if ( angle == ideal_angle ) { return angle; } move = ideal_angle - angle; if ( ideal_angle > angle ) { if ( move > 180.0 ) { move -= 360.0; } } else { if ( move < -180.0 ) { move += 360.0; } } if ( move > 0 ) { if ( move > speed ) { move = speed; } } else { if ( move < -speed ) { move = -speed; } } return AngleMod( angle + move ); }
/* ============== BotUpdateInput ============== */ void BotUpdateInput( bot_state_t *bs, int time ) { bot_input_t bi; int j; //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } // BotChangeViewAngles( bs, (float) time / 1000 ); trap_EA_GetInput( bs->client, (float) time / 1000, &bi ); //respawn hack if ( bi.actionflags & ACTION_RESPAWN ) { if ( bs->lastucmd.buttons & BUTTON_ATTACK ) { bi.actionflags &= ~( ACTION_RESPAWN | ACTION_ATTACK ); } } // BotInputToUserCommand( &bi, &bs->lastucmd, bs->cur_ps.delta_angles, time ); bs->lastucmd.serverTime = time; //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } }
/* ============== AICast_InFieldOfVision ============== */ qboolean AICast_InFieldOfVision( vec3_t viewangles, float fov, vec3_t angles ) { int i; float diff, angle; for ( i = 0; i < 2; i++ ) { angle = AngleMod( viewangles[i] ); angles[i] = AngleMod( angles[i] ); diff = angles[i] - angle; if ( angles[i] > angle ) { if ( diff > 180.0 ) { diff -= 360.0; } } else { if ( diff < -180.0 ) { diff += 360.0; } } if ( diff > 0 ) { if ( diff > fov * 0.5 ) { return qfalse; } } else { if ( diff < -fov * 0.5 ) { return qfalse; } } } return qtrue; }
/* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; // if (bs->enemy >= 0) { factor = botlib_export->ai.Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_FACTOR, 0.01f, 1); maxchange = botlib_export->ai.Characteristic_BFloat(bs->character, CHARACTERISTIC_VIEW_MAXCHANGE, 1, 1800); } else { factor = 0.05f; maxchange = 360; } if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { // if (bot_challenge->integer) { //smooth slowdown view model diff = fabs(AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i])); anglespeed = diff * factor; if (anglespeed > maxchange) anglespeed = maxchange; bs->viewangles[i] = BotChangeViewAngle(bs->viewangles[i], bs->ideal_viewangles[i], anglespeed); } else { //over reaction view model bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); //demping bs->viewanglespeed[i] *= 0.45 * (1 - factor); } //BotAI_Print(PRT_MESSAGE, "ideal_angles %f %f\n", bs->ideal_viewangles[0], bs->ideal_viewangles[1], bs->ideal_viewangles[2]);` //bs->viewangles[i] = bs->ideal_viewangles[i]; } //bs->viewangles[PITCH] = 0; if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; //elementary action: view botlib_export->ea.EA_View(bs->client, bs->viewangles); }
/* ================== UI_SwingAngles ================== */ static void UI_SwingAngles( float destination, float swingTolerance, float clampTolerance, float speed, float *angle, qbool *swinging ) { float swing; float move; float scale; if ( !*swinging ) { // see if a swing should be started swing = AngleSubtract( *angle, destination ); if ( swing > swingTolerance || swing < -swingTolerance ) { *swinging = qtrue; } } if ( !*swinging ) { return; } // modify the speed depending on the delta // so it doesn't seem so linear swing = AngleSubtract( destination, *angle ); scale = fabs( swing ); if ( scale < swingTolerance * 0.5 ) { scale = 0.5; } else if ( scale < swingTolerance ) { scale = 1.0; } else { scale = 2.0; } // swing towards the destination angle if ( swing >= 0 ) { move = uiInfo.uiDC.frameTime * scale * speed; if ( move >= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } else if ( swing < 0 ) { move = uiInfo.uiDC.frameTime * scale * -speed; if ( move <= swing ) { move = swing; *swinging = qfalse; } *angle = AngleMod( *angle + move ); } // clamp to no more than tolerance swing = AngleSubtract( destination, *angle ); if ( swing > clampTolerance ) { *angle = AngleMod( destination - (clampTolerance - 1) ); } else if ( swing < -clampTolerance ) { *angle = AngleMod( destination + (clampTolerance - 1) ); } }
void ME_RotateSelection( int delta ) { IGME_vehicle_t* veh; sbox3_t box; bool boxinit = false; vec3_t mins = { -1, -1, -1 }, maxs = { 1, 1, 1 }; vec3_t center; vec3_t dir, angles; int i; float dist; for( i = 0; i < IGME_MAX_VEHICLES; ++i ) { veh = &cgs.IGME.vehicles[i]; if( !veh->selected ) continue; if( !boxinit ) { VectorAdd( veh->origin, mins, box.mins ); VectorAdd( veh->origin, maxs, box.maxs ); boxinit = true; } else { AddToBox( &box, veh->origin ); } } BoxCenter( &box, ¢er ); for( i = 0; i < IGME_MAX_VEHICLES; ++i ) { veh = &cgs.IGME.vehicles[i]; if( !veh->selected ) continue; // get direction vector and dist VectorSubtract( veh->origin, center, dir ); dist = VectorNormalize(dir); vectoangles( dir, angles ); // rotate veh->angles[1] += delta; AngleMod( veh->angles[1] ); angles[1] += delta; AngleMod( angles[1] ); // move object AngleVectors(angles, dir, 0, 0); VectorScale(dir, dist, dir); VectorAdd(center, dir, veh->origin); } // if( rotate ) { // veh->angles[1] -= x; // AngleMod( veh->angles[1] ); // } }
void CRayEffectPainter::PaintHit (CG16bitImage &Dest, int x, int y, const CVector &vHitPos, SViewportPaintCtx &Ctx) // PaintHit // // Paint the effect when hit { // Make sure we've computed all our temporaries CalcIntermediates(); // Compute the two end points of the line. We paint from the head to the tail. int iLength = (Ctx.iMaxLength != -1 ? Min(Ctx.iMaxLength, m_iLength) : m_iLength); CVector vFrom = PolarToVector(AngleMod(Ctx.iRotation + m_iXformRotation), -iLength); int xTo = x + (int)(vFrom.GetX() + 0.5); int yTo = y - (int)(vFrom.GetY() + 0.5); int xFrom; int yFrom; Ctx.XFormRel.Transform(vHitPos, &xFrom, &yFrom); // Paint the ray PaintRay(Dest, xFrom, yFrom, xTo, yTo, Ctx); }
void CRayEffectPainter::Paint (CG16bitImage &Dest, int x, int y, SViewportPaintCtx &Ctx) // Paint // // Paint the effect { DEBUG_TRY // Make sure we've computed all our temporaries CalcIntermediates(); // Compute the two end points of the line. We paint from the head to the tail. int iLength = (Ctx.iMaxLength != -1 ? Min(Ctx.iMaxLength, m_iLength) : m_iLength); CVector vFrom = PolarToVector(AngleMod(Ctx.iRotation + m_iXformRotation), -iLength); int xTo = x + (int)(vFrom.GetX() + 0.5); int yTo = y - (int)(vFrom.GetY() + 0.5); int xFrom = x; int yFrom = y; // Paint the ray PaintRay(Dest, xFrom, yFrom, xTo, yTo, Ctx); DEBUG_CATCH }
/* ====================== UI_MachinegunSpinAngle ====================== */ float UI_MachinegunSpinAngle( playerInfo_t *pi ) { int delta; float angle; float speed; int torsoAnim; delta = dp_realtime - pi->barrelTime; if ( pi->barrelSpinning ) { angle = pi->barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = pi->barrelAngle + delta * speed; } torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; if( torsoAnim == TORSO_RIFLE_ATTACK ) { torsoAnim = TORSO_PISTOL_ATTACK; } if ( pi->barrelSpinning == !(torsoAnim == TORSO_PISTOL_ATTACK) ) { pi->barrelTime = dp_realtime; pi->barrelAngle = AngleMod( angle ); pi->barrelSpinning = !!(torsoAnim == TORSO_PISTOL_ATTACK); } return angle; }
static float CG_MachinegunSpinAngle( centity_t *cent, qboolean firing ) { int delta; float angle; float speed; delta = cg.time - cent->pe.barrelTime; if( cent->pe.barrelSpinning ) angle = cent->pe.barrelAngle + delta * SPIN_SPEED; else { if( delta > COAST_TIME ) delta = COAST_TIME; speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = cent->pe.barrelAngle + delta * speed; } if( cent->pe.barrelSpinning == !firing ) { cent->pe.barrelTime = cg.time; cent->pe.barrelAngle = AngleMod( angle ); cent->pe.barrelSpinning = firing; } return angle; }
/* ====================== UI_MachinegunSpinAngle ====================== */ float UI_MachinegunSpinAngle( uiPlayerInfo_t *pi ) { int delta; float angle; float speed; int torsoAnim; delta = dp_realtime - pi->barrelTime; if ( pi->barrelSpinning ) { angle = pi->barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = pi->barrelAngle + delta * speed; } torsoAnim = pi->torsoAnim & ~ANIM_TOGGLEBIT; #ifdef TA_WEAPSYS if (BG_PlayerAttackAnim(torsoAnim)) #else if( torsoAnim == TORSO_ATTACK2 ) #endif { torsoAnim = TORSO_ATTACK; } if ( pi->barrelSpinning == !(torsoAnim == TORSO_ATTACK) ) { pi->barrelTime = dp_realtime; pi->barrelAngle = AngleMod( angle ); pi->barrelSpinning = !!(torsoAnim == TORSO_ATTACK); } return angle; }
/* =============== UI_PlayerAngles =============== */ static void UI_PlayerAngles( playerInfo_t *pi, vec3_t legs[ 3 ], vec3_t torso[ 3 ], vec3_t head[ 3 ] ) { vec3_t legsAngles, torsoAngles, headAngles; float dest; float adjust; VectorCopy( pi->viewAngles, headAngles ); headAngles[ YAW ] = AngleMod( headAngles[ YAW ] ); VectorClear( legsAngles ); VectorClear( torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { // if not standing still, always point all in the same direction pi->torso.yawing = qtrue; // always center pi->torso.pitching = qtrue; // always center pi->legs.yawing = qtrue; // always center } // adjust legs for movement dir adjust = UI_MovedirAdjustment( pi ); legsAngles[ YAW ] = headAngles[ YAW ] + adjust; torsoAngles[ YAW ] = headAngles[ YAW ] + 0.25 * adjust; // torso UI_SwingAngles( torsoAngles[ YAW ], 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing ); UI_SwingAngles( legsAngles[ YAW ], 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing ); torsoAngles[ YAW ] = pi->torso.yawAngle; legsAngles[ YAW ] = pi->legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles[ PITCH ] > 180 ) { dest = ( -360 + headAngles[ PITCH ] ) * 0.75; } else { dest = headAngles[ PITCH ] * 0.75; } UI_SwingAngles( dest, 15, 30, 0.1, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles[ PITCH ] = pi->torso.pitchAngle; // pull the angles back out of the hierarchial chain AnglesSubtract( headAngles, torsoAngles, headAngles ); AnglesSubtract( torsoAngles, legsAngles, torsoAngles ); AnglesSubtract( legsAngles, pi->moveAngles, legsAngles ); // NERVE - SMF AnglesToAxis( legsAngles, legs ); AnglesToAxis( torsoAngles, torso ); AnglesToAxis( headAngles, head ); }
CVector CDockingPorts::GetPortPos (CSpaceObject *pOwner, const SDockingPort &Port, CSpaceObject *pShip, bool *retbPaintInFront, int *retiRotation) const // GetPortPos // // Get the absolute position of the port (or relative, if pOwner == NULL) { if (pOwner == NULL) { if (retbPaintInFront) *retbPaintInFront = (Port.iLayer != plSendToBack); if (retiRotation) *retiRotation = Port.iRotation; return Port.vPos; } else if (pOwner->GetRotation() == 0) { const CVector &vOwnerPos = pOwner->GetPos(); if (retbPaintInFront) { switch (Port.iLayer) { case plBringToFront: *retbPaintInFront = true; break; case plSendToBack: *retbPaintInFront = false; break; default: *retbPaintInFront = (Port.vPos.GetY() < 0.0); } } if (retiRotation) *retiRotation = Port.iRotation; return (vOwnerPos + Port.vPos + (pShip ? pShip->GetDockingPortOffset(Port.iRotation) : NullVector)); } else { const CVector &vOwnerPos = pOwner->GetPos(); CVector vPortPos = Port.vPos.Rotate(pOwner->GetRotation()); int iNewRotation = AngleMod(Port.iRotation + pOwner->GetRotation()); if (retbPaintInFront) *retbPaintInFront = (vPortPos.GetY() < 0.0); if (retiRotation) *retiRotation = iNewRotation; return (vOwnerPos + vPortPos + (pShip ? pShip->GetDockingPortOffset(iNewRotation) : NullVector)); } }
int CIntegralRotationDesc::GetFrameIndex (int iAngle) const // GetFrameIndex // // Returns the frame index, 0 to m_iCount-1, that corresponds to the given // angle. Remember that frame 0 points straight up and frames rotate clockwise. { Metric rIndex = AngleMod(90 - iAngle) * m_iCount / 360.0; return (int)rIndex; }
static void UI_PlayerAngles( playerInfo_t *pi, matrix3 legs, matrix3 torso, matrix3 head ) { vector3 legsAngles, torsoAngles, headAngles; float dest; float adjust; VectorCopy( &pi->viewAngles, &headAngles ); headAngles.yaw = AngleMod( headAngles.yaw ); VectorClear( &legsAngles ); VectorClear( &torsoAngles ); // --------- yaw ------------- // allow yaw to drift a bit if ( ( pi->legsAnim & ~ANIM_TOGGLEBIT ) != LEGS_IDLE || ( pi->torsoAnim & ~ANIM_TOGGLEBIT ) != TORSO_STAND ) { // if not standing still, always point all in the same direction pi->torso.yawing = qtrue; // always center pi->torso.pitching = qtrue; // always center pi->legs.yawing = qtrue; // always center } // adjust legs for movement dir adjust = UI_MovedirAdjustment( pi ); legsAngles.yaw = headAngles.yaw + adjust; torsoAngles.yaw = headAngles.yaw + 0.25f * adjust; // torso UI_SwingAngles( torsoAngles.yaw, 25, 90, SWINGSPEED, &pi->torso.yawAngle, &pi->torso.yawing ); UI_SwingAngles( legsAngles.yaw, 40, 90, SWINGSPEED, &pi->legs.yawAngle, &pi->legs.yawing ); torsoAngles.yaw = pi->torso.yawAngle; legsAngles.yaw = pi->legs.yawAngle; // --------- pitch ------------- // only show a fraction of the pitch angle in the torso if ( headAngles.pitch > 180 ) { dest = (-360 + headAngles.pitch) * 0.75f; } else { dest = headAngles.pitch * 0.75f; } UI_SwingAngles( dest, 15, 30, 0.1f, &pi->torso.pitchAngle, &pi->torso.pitching ); torsoAngles.pitch = pi->torso.pitchAngle; // pull the angles back out of the hierarchial chain AnglesSubtract( &headAngles, &torsoAngles, &headAngles ); AnglesSubtract( &torsoAngles, &legsAngles, &torsoAngles ); AnglesToAxis( &legsAngles, legs ); AnglesToAxis( &torsoAngles, torso ); AnglesToAxis( &headAngles, head ); }
/* ============== BotChangeViewAngles ============== */ void BotChangeViewAngles(bot_state_t *bs, float thinktime) { float diff, factor, maxchange, anglespeed, disired_speed; int i; if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360; factor = bs->skills.turnspeed; if (factor > 1) { factor = 1; } if (factor < 0.25) { factor = 0.25f; } maxchange = bs->skills.maxturn; //if (maxchange < 240) maxchange = 240; maxchange *= thinktime; for (i = 0; i < 2; i++) { bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]); diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]); disired_speed = diff * factor; bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed); if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange; if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange; anglespeed = bs->viewanglespeed[i]; if (anglespeed > maxchange) anglespeed = maxchange; if (anglespeed < -maxchange) anglespeed = -maxchange; bs->viewangles[i] += anglespeed; bs->viewangles[i] = AngleMod(bs->viewangles[i]); bs->viewanglespeed[i] *= 0.45 * (1 - factor); } if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360; trap_EA_View(bs->client, bs->viewangles); }
/* =================== PM_HeloAccelerate =================== */ static void PM_HeloAccelerate() { float throttle = pm->ps->fixed_throttle; int maxthrottle = availableVehicles[pm->vehicle].maxthrottle; // int minthrottle = availableVehicles[pm->vehicle].minthrottle; float maxforwardspeed = availableVehicles[pm->vehicle].maxspeed; float maxrightspeed = availableVehicles[pm->vehicle].turnspeed[YAW]; float maxliftspeed = maxforwardspeed*0.00525; float maxspeed = sqrt(maxforwardspeed*maxforwardspeed + maxrightspeed*maxrightspeed + maxliftspeed*maxliftspeed); float stallspeed = static_cast<float>(availableVehicles[pm->vehicle].stallspeed); float curforwardspeed; float curliftspeed; float curliftspeedadjust; float currightspeed; float curspeed; float curspeedadjust; float totalthrottle; vec3_t vehdir; // Copy Vehicle Direction VectorCopy( pm->ps->vehicleAngles, vehdir ); vehdir[YAW] = AngleMod( vehdir[YAW] ); curforwardspeed = (vehdir[PITCH]/MAX_HELO_PITCH)*availableVehicles[pm->vehicle].maxspeed; currightspeed = (vehdir[ROLL]/MAX_HELO_ROLL)*availableVehicles[pm->vehicle].turnspeed[YAW]; curliftspeed = throttle > maxthrottle ? -(throttle-maxthrottle)*20 : throttle * 25; // real vert speed curliftspeedadjust = throttle > maxthrottle ? 0 : throttle * 25; // adjusted vert speed, don't want down movement to effect fuel usage "more" curspeed = sqrt(curforwardspeed*curforwardspeed + currightspeed*currightspeed + curliftspeed*curliftspeed); // real total speed curspeedadjust = sqrt(curforwardspeed*curforwardspeed + currightspeed*currightspeed + curliftspeedadjust*curliftspeedadjust); // adjusted total speed, dont want down movement to effect throttle more totalthrottle = (curspeedadjust/maxspeed)*10; // check for fuel if( pm->ps->stats[STAT_FUEL] <= 0 ) { pm->ps->throttle = 0; } if( (pm->ps->ONOFF & OO_LANDED) && curspeed > stallspeed * 1.5f ) curspeed = stallspeed *1.5f; pm->ps->speed = curspeed*10; pm->ps->throttle = throttle; // fuel flow PM_Helo_FuelFlow( totalthrottle ); }
static float CG_MachinegunSpinAngle( centity_t *cent ) { float angle, speed, delta = (cg.time - cent->pe.barrelTime) + cg.timeFraction; if ( cent->pe.barrelSpinning ) { angle = cent->pe.barrelAngle + delta * SPIN_SPEED; } else { if ( delta > COAST_TIME ) { delta = COAST_TIME; } speed = 0.5 * ( SPIN_SPEED + (float)( COAST_TIME - delta ) / COAST_TIME ); angle = cent->pe.barrelAngle + delta * speed; } if ( cent->pe.barrelSpinning == !(cent->currentState.eFlags & EF_FIRING) ) { cent->pe.barrelTime = cg.time; cent->pe.barrelAngle = AngleMod( angle ); cent->pe.barrelSpinning = !!(cent->currentState.eFlags & EF_FIRING); } return angle; }
void CIntegralRotationDesc::InitRotationCount (int iCount) // InitRotationCount // // Initialize count { int i; // If we're in backwards compatibility mode and if we've got a different // count, then we need to recompute our degrees per tick. if (m_iManeuverability && iCount != m_iCount && iCount > 0) { m_rDegreesPerTick = (STD_SECONDS_PER_UPDATE * 360.0) / (iCount * m_iManeuverability); m_rAccelPerTick = m_rDegreesPerTick; m_rAccelPerTickStop = m_rDegreesPerTick; } // Initialize count m_iCount = iCount; m_Rotations.DeleteAll(); if (m_iCount > 0) { m_iMaxRotationRate = Max(1, mathRound(ROTATION_FRACTION * m_rDegreesPerTick * m_iCount / 360.0)); m_iRotationAccel = Max(1, mathRound(ROTATION_FRACTION * m_rAccelPerTick * m_iCount / 360.0)); m_iRotationAccelStop = Max(1, mathRound(ROTATION_FRACTION * m_rAccelPerTickStop * m_iCount / 360.0)); Metric rFrameAngle = 360.0 / m_iCount; m_Rotations.InsertEmpty(m_iCount); for (i = 0; i < m_iCount; i++) m_Rotations[i].iRotation = AngleMod(mathRound(90.0 - i * rFrameAngle)); } else { m_iMaxRotationRate = 0; m_iRotationAccel = 0; } }
static float RankAngle (float a) { vec3_t wishdir; float delta; vec3_t ang1, ang2; vec3_t newvel; wishdir[0] = cos(a / 180 * M_PI); wishdir[1] = sin(a / 180 * M_PI); wishdir[2] = 0; VectorCopy (ra_curvel, newvel); Friction (newvel); Accelerate (wishdir, movevars.maxspeed, newvel); vectoangles (newvel, ang1); vectoangles (ra_intentions, ang2); delta = AngleMod(ang2[YAW] - ang1[YAW]); return -fabs(delta); }
/* ============== BotAI ============== */ int BotAI(int playernum, float thinktime) { bot_state_t *bs; char buf[1024], *args; int j; EA_ResetInput(playernum); // bs = botstates[playernum]; if (!bs || !bs->inuse) { BotAI_Print(PRT_FATAL, "BotAI: player %d is not setup\n", playernum); return qfalse; } //retrieve the current player state if (!BotAI_GetPlayerState( playernum, &bs->cur_ps )) { BotAI_Print(PRT_FATAL, "BotAI: failed to get player state for player %d\n", playernum); return qfalse; } //retrieve any waiting server commands while( trap_BotGetServerCommand(playernum, buf, sizeof(buf)) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' '); if (!args) continue; *args++ = '\0'; //remove color espace sequences from the arguments RemoveColorEscapeSequences( args ); if (!Q_stricmp(buf, "cp ")) { /*CenterPrintf*/ } else if (!Q_stricmp(buf, "cs")) { /*ConfigStringModified*/ } else if (!Q_stricmp(buf, "print")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_NORMAL, args); } else if (!Q_stricmp(buf, "chat") || !Q_stricmp(buf, "tell")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } else if (!Q_stricmp(buf, "tchat")) { //remove first and last quote from the chat message memmove(args, args+1, strlen(args)); args[strlen(args)-1] = '\0'; BotQueueConsoleMessage(bs->cs, CMS_CHAT, args); } #ifdef MISSIONPACK else if (!Q_stricmp(buf, "vchat")) { BotVoiceChatCommand(bs, SAY_ALL, args); } else if (!Q_stricmp(buf, "vtchat")) { BotVoiceChatCommand(bs, SAY_TEAM, args); } else if (!Q_stricmp(buf, "vtell")) { BotVoiceChatCommand(bs, SAY_TELL, args); } #endif else if (!Q_stricmp(buf, "scores")) { /*FIXME: parse scores?*/ } else if (!Q_stricmp(buf, "clientLevelShot")) { /*ignore*/ } } //add the delta angles to the bot's current view angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy(bs->cur_ps.origin, bs->origin); //eye coordinates of the bot VectorCopy(bs->cur_ps.origin, bs->eye); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum(bs->origin); //the real AI BotDeathmatchAI(bs, thinktime); //set the weapon selection every AI frame EA_SelectWeapon(bs->playernum, bs->weaponnum); //subtract the delta angles for (j = 0; j < 3; j++) { bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j])); } //everything was ok return qtrue; }
void PM_BoatMove( void ) { vec3_t viewdir; vec3_t vehdir; vec3_t turretdir; vec3_t diff; vec3_t turnspeed; vec3_t forward, up; vec3_t temp; bool dead = (pm->ps->stats[STAT_HEALTH] <= 0); int i; float smove = pm->cmd.rightmove; float turret_yaw = pm->ps->turretAngle; float gun_pitch = pm->ps->gunAngle; float speed; bool reverse = false; float lean = 0; // set speed PM_BoatAccelerate(); // speed speed = (float)pm->ps->speed/10; VectorCopy( pm->ps->vehicleAngles, vehdir ); vehdir[0] = 0; if( pm->ps->ONOFF & OO_STALLED ) { AngleVectors( vehdir, forward, NULL, NULL ); VectorNegate( forward, forward ); reverse = true; } else { AngleVectors( vehdir, forward, NULL, NULL ); } VectorNormalize( pm->ps->velocity ); VectorAdd( forward, pm->ps->velocity, forward ); VectorScale( forward, speed, pm->ps->velocity ); PM_SlideMove_Boat(); // get the turnspeeds for( i = HULL_YAW; i <= GUN_PITCH; i++ ) { turnspeed[i] = availableVehicles[pm->vehicle].turnspeed[i] * pml.frametime; } // set the hull angle dependent on speed if( !reverse ) { pm->ps->vehicleAngles[0] = (speed/(float)availableVehicles[pm->vehicle].maxspeed) * availableVehicles[pm->vehicle].tailangle; if( smove > 0 ) { lean = (speed/(float)availableVehicles[pm->vehicle].maxspeed) * availableVehicles[pm->vehicle].gearheight; } else if( smove < 0 ) { lean = -(speed/(float)availableVehicles[pm->vehicle].maxspeed) * availableVehicles[pm->vehicle].gearheight; } } if( pm->ps->vehicleAngles[2] != lean ) { diff[2] = lean - pm->ps->vehicleAngles[2]; if( diff[2] < -turnspeed[2] ) { pm->ps->vehicleAngles[2] -= turnspeed[2]; } else if( diff[2] > turnspeed[2] ) { pm->ps->vehicleAngles[2] += turnspeed[2]; } else { pm->ps->vehicleAngles[2] += diff[2]; } } // get the actual turret angles AngleVectors( pm->ps->vehicleAngles, forward, 0, up ); RotatePointAroundVector( temp, up, forward, turret_yaw ); vectoangles( temp, turretdir ); turretdir[PITCH] += gun_pitch; // Com_Printf( "vehd = %.1f %.1f %.1f (%.1f %.1f)\n", pm->ps->vehicleAngles[0], pm->ps->vehicleAngles[1], // pm->ps->vehicleAngles[2], turret_yaw, gun_pitch ); // Com_Printf( "view = %.1f %.1f %.1f\n", pm->ps->viewangles[0], pm->ps->viewangles[1], // pm->ps->viewangles[2] ); // Com_Printf( "turr = %.1f %.1f %.1f\n", turretdir[0], turretdir[1], turretdir[2] ); // local vectors VectorCopy( pm->ps->vehicleAngles, vehdir ); if( (pm->cmd.buttons & BUTTON_FREELOOK) && !dead ) { // freelook VectorCopy( turretdir, viewdir ); } else { // normal - turret follows camera VectorCopy( pm->ps->viewangles, viewdir ); } // set to 0<=x<=360 viewdir[YAW] = AngleMod( viewdir[YAW] ); vehdir[YAW] = AngleMod( vehdir[YAW] ); turretdir[YAW] = AngleMod( turretdir[YAW] ); viewdir[PITCH] = AngleMod( viewdir[PITCH] ); vehdir[PITCH] = AngleMod( vehdir[PITCH] ); turretdir[PITCH] = AngleMod( turretdir[PITCH] ); // turn the hull if( pm->ps->ONOFF & OO_LANDED ) { // create a turning modifier for normal wheeled vehicles float topSpeed = availableVehicles[pm->vehicle].maxspeed; // maxium turn is allowed at a percentage of the top speed (0.5f behind 50% of top speed) float turnModifier = speed/(topSpeed * 0.2f); MF_LimitFloat( &turnModifier, 0.0f, 1.0f ); if( smove > 0 ) { vehdir[YAW] -= (turnspeed[HULL_YAW] * turnModifier); } else if( smove < 0 ) { vehdir[YAW] += (turnspeed[HULL_YAW] * turnModifier); } } if( !(pm->cmd.buttons & BUTTON_FREELOOK) ) { float min, max; // get the angle difference for( i = PITCH; i <= YAW; i++ ) { diff[i] = viewdir[i] - turretdir[i]; if( diff[i] > 180 ) diff[i] -= 360; else if( diff[i] < -180 ) diff[i] += 360; } // turn the turret if( diff[YAW] < -turnspeed[TURRET_YAW] ) turret_yaw -= turnspeed[TURRET_YAW]; else if( diff[YAW] > turnspeed[TURRET_YAW] ) turret_yaw += turnspeed[TURRET_YAW]; else turret_yaw += diff[YAW]; if( turret_yaw > 180 ) turret_yaw -= 360;// limit to +- 180 min = availableWeapons[pm->ps->weaponIndex].minturns[1]; max = availableWeapons[pm->ps->weaponIndex].maxturns[1]; if( max > min ) { if( turret_yaw > max ) turret_yaw = max; else if( turret_yaw < min ) turret_yaw = min; } else { if( turret_yaw > 0 && turret_yaw < min ) turret_yaw = min; else if( turret_yaw < 0 && turret_yaw > max ) turret_yaw = max; } if( turret_yaw < 0 ) turret_yaw += 360;// clamp back to pos else if( turret_yaw > 360 ) turret_yaw -= 360; // turn the gun if( diff[PITCH] < -turnspeed[GUN_PITCH] ) gun_pitch -= turnspeed[GUN_PITCH]; else if( diff[PITCH] > turnspeed[GUN_PITCH] ) gun_pitch += turnspeed[GUN_PITCH]; else gun_pitch += diff[PITCH]; min = availableWeapons[pm->ps->weaponIndex].minturns[0]; max = availableWeapons[pm->ps->weaponIndex].maxturns[0]; if( gun_pitch > 180 ) gun_pitch -= 360;// limit to +-180 to make it easier if( gun_pitch > max ) gun_pitch = max; else if( gun_pitch < min ) gun_pitch = min; if( gun_pitch < 0 ) gun_pitch += 360;// clamp it back to pos else if( gun_pitch > 360 ) gun_pitch -= 360; // Com_Printf( "gun %.1f (d %.1f)\n", gun_pitch, diff[PITCH] ); } // return angles VectorCopy( vehdir, pm->ps->vehicleAngles ); pm->ps->turretAngle = turret_yaw; pm->ps->gunAngle = gun_pitch; }
/* ============== BotAI ============== */ int BotAI( int client, float thinktime ) { bot_state_t *bs; char buf[1024], *args; int j; trap_EA_ResetInput( client, NULL ); // bs = botstates[client]; if ( !bs || !bs->inuse ) { BotAI_Print( PRT_FATAL, "client %d hasn't been setup\n", client ); return BLERR_AICLIENTNOTSETUP; } //retrieve the current client state BotAI_GetClientState( client, &bs->cur_ps ); //retrieve any waiting console messages while ( trap_BotGetServerCommand( client, buf, sizeof( buf ) ) ) { //have buf point to the command and args to the command arguments args = strchr( buf, ' ' ); if ( !args ) { continue; } *args++ = '\0'; //remove color espace sequences from the arguments Q_CleanStr( args ); //botai_import.Print(PRT_MESSAGE, "ConsoleMessage: \"%s\"\n", buf); if ( !Q_stricmp( buf, "cp " ) ) { /*CenterPrintf*/ } else if ( !Q_stricmp( buf, "cs" ) ) { /*ConfigStringModified*/ } else if ( !Q_stricmp( buf, "print" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_NORMAL, args ); } else if ( !Q_stricmp( buf, "chat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "tchat" ) ) { trap_BotQueueConsoleMessage( bs->cs, CMS_CHAT, args ); } else if ( !Q_stricmp( buf, "scores" ) ) { /*FIXME: parse scores?*/ } else if ( !Q_stricmp( buf, "clientLevelShot" ) ) { /*ignore*/ } } //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //increase the local time of the bot bs->ltime += thinktime; // bs->thinktime = thinktime; //origin of the bot VectorCopy( bs->cur_ps.origin, bs->origin ); //eye coordinates of the bot VectorCopy( bs->cur_ps.origin, bs->eye ); bs->eye[2] += bs->cur_ps.viewheight; //get the area the bot is in bs->areanum = BotPointAreaNum( bs->origin ); //the real AI BotDeathmatchAI( bs, thinktime ); //set the weapon selection every AI frame trap_EA_SelectWeapon( bs->client, bs->weaponnum ); //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } //everything was ok return BLERR_NOERROR; }
void CLH2_SetRefEntAxis( refEntity_t* entity, vec3_t entityAngles, vec3_t angleAdd, int scale, int absoluteLight, int drawFlags ) { if ( drawFlags & H2DRF_TRANSLUCENT ) { entity->renderfx |= RF_TRANSLUCENT; entity->shaderRGBA[ 3 ] = 100; } vec3_t angles; if ( R_IsMeshModel( entity->hModel ) ) { if ( R_ModelFlags( entity->hModel ) & H2MDLEF_FACE_VIEW ) { // yaw and pitch must be 0 so that renderer can safely multply matrices. angles[ PITCH ] = 0; angles[ YAW ] = 0; angles[ ROLL ] = entityAngles[ ROLL ]; AnglesToAxis( angles, entity->axis ); } else { if ( R_ModelFlags( entity->hModel ) & H2MDLEF_ROTATE ) { angles[ YAW ] = AngleMod( ( entity->origin[ 0 ] + entity->origin[ 1 ] ) * 0.8 + ( 108 * cl.serverTime * 0.001 ) ); } else { angles[ YAW ] = entityAngles[ YAW ]; } angles[ ROLL ] = entityAngles[ ROLL ]; // stupid quake bug angles[ PITCH ] = -entityAngles[ PITCH ]; if ( angleAdd[ 0 ] || angleAdd[ 1 ] || angleAdd[ 2 ] ) { float BaseAxis[ 3 ][ 3 ]; AnglesToAxis( angles, BaseAxis ); // For clientside rotation stuff float AddAxis[ 3 ][ 3 ]; AnglesToAxis( angleAdd, AddAxis ); MatrixMultiply( AddAxis, BaseAxis, entity->axis ); } else { AnglesToAxis( angles, entity->axis ); } } if ( ( R_ModelFlags( entity->hModel ) & H2MDLEF_ROTATE ) || ( scale != 0 && scale != 100 ) ) { entity->renderfx |= RF_LIGHTING_ORIGIN; VectorCopy( entity->origin, entity->lightingOrigin ); } if ( R_ModelFlags( entity->hModel ) & H2MDLEF_ROTATE ) { // Floating motion float delta = sin( entity->origin[ 0 ] + entity->origin[ 1 ] + ( cl.serverTime * 0.001 * 3 ) ) * 5.5; VectorMA( entity->origin, delta, entity->axis[ 2 ], entity->origin ); absoluteLight = 60 + 34 + sin( entity->origin[ 0 ] + entity->origin[ 1 ] + ( cl.serverTime * 0.001 * 3.8 ) ) * 34; drawFlags |= H2MLS_ABSLIGHT; } if ( scale != 0 && scale != 100 ) { float entScale = ( float )scale / 100.0; float esx; float esy; float esz; switch ( drawFlags & H2SCALE_TYPE_MASKIN ) { case H2SCALE_TYPE_UNIFORM: esx = entScale; esy = entScale; esz = entScale; break; case H2SCALE_TYPE_XYONLY: esx = entScale; esy = entScale; esz = 1; break; case H2SCALE_TYPE_ZONLY: esx = 1; esy = 1; esz = entScale; break; } float etz; switch ( drawFlags & H2SCALE_ORIGIN_MASKIN ) { case H2SCALE_ORIGIN_CENTER: etz = 0.5; break; case H2SCALE_ORIGIN_BOTTOM: etz = 0; break; case H2SCALE_ORIGIN_TOP: etz = 1.0; break; } vec3_t Out; R_CalculateModelScaleOffset( entity->hModel, esx, esy, esz, etz, Out ); VectorMA( entity->origin, Out[ 0 ], entity->axis[ 0 ], entity->origin ); VectorMA( entity->origin, Out[ 1 ], entity->axis[ 1 ], entity->origin ); VectorMA( entity->origin, Out[ 2 ], entity->axis[ 2 ], entity->origin ); VectorScale( entity->axis[ 0 ], esx, entity->axis[ 0 ] ); VectorScale( entity->axis[ 1 ], esy, entity->axis[ 1 ] ); VectorScale( entity->axis[ 2 ], esz, entity->axis[ 2 ] ); entity->nonNormalizedAxes = true; } } else { angles[ YAW ] = entityAngles[ YAW ]; angles[ ROLL ] = entityAngles[ ROLL ]; angles[ PITCH ] = entityAngles[ PITCH ]; AnglesToAxis( angles, entity->axis ); } int mls = drawFlags & H2MLS_MASKIN; if ( mls == H2MLS_ABSLIGHT ) { entity->renderfx |= RF_ABSOLUTE_LIGHT; entity->absoluteLight = absoluteLight / 128.0; } else if ( mls != H2MLS_NONE ) { // Use a model light style (25-30) entity->renderfx |= RF_ABSOLUTE_LIGHT; entity->absoluteLight = cl_lightstyle[ 24 + mls ].value[ 0 ] / 2; } }
/* ============ AICast_CreateCharacter returns 0 if unable to create the character ============ */ gentity_t *AICast_CreateCharacter( gentity_t *ent, float *attributes, cast_weapon_info_t *weaponInfo, char *castname, char *model, char *head, char *sex, char *color, char *handicap ) { gentity_t *newent; gclient_t *client; cast_state_t *cs; char **ppStr; int j; if (g_gametype.integer != GT_SINGLE_PLAYER) { // no cast AI in multiplayer return NULL; } // are bots enabled? if ( !trap_Cvar_VariableIntegerValue( "bot_enable" ) ) { G_Printf( S_COLOR_RED "ERROR: Unable to spawn %s, 'bot_enable' is not set\n", ent->classname ); return NULL; } // // make sure we have a free slot for them // if (level.numPlayingClients+1 > aicast_maxclients) { G_Error( "Exceeded sv_maxclients (%d), unable to create %s\n", aicast_maxclients, ent->classname ); return NULL; } // // add it to the list (only do this if everything else passed) // newent = AICast_AddCastToGame( ent, castname, model, head, sex, color, handicap ); if (!newent) { return NULL; } client = newent->client; // // setup the character.. // cs = AICast_GetCastState( newent->s.number ); // // setup the attributes memcpy( cs->attributes, attributes, sizeof(cs->attributes) ); ppStr = &ent->aiAttributes; AICast_CheckLevelAttributes( cs, ent, ppStr ); // AICast_SetAASIndex( cs ); // make sure they face the right direction VectorCopy( ent->s.angles, cs->bs->ideal_viewangles ); // factor in the delta_angles for (j = 0; j < 3; j++) { cs->bs->viewangles[j] = AngleMod(newent->s.angles[j] - SHORT2ANGLE(newent->client->ps.delta_angles[j])); } VectorCopy( ent->s.angles, newent->s.angles ); VectorCopy( ent->s.origin, cs->startOrigin ); // cs->lastEnemy = -1; cs->bs->enemy = -1; cs->leaderNum = -1; cs->castScriptStatus.scriptGotoEnt = -1; cs->aiCharacter = ent->aiCharacter; // newent->aiName = ent->aiName; newent->aiTeam = ent->aiTeam; newent->targetname = ent->targetname; // newent->AIScript_AlertEntity = ent->AIScript_AlertEntity; newent->aiInactive = ent->aiInactive; newent->aiCharacter = cs->aiCharacter; // // parse the AI script for this character (if applicable) cs->aiFlags |= AIFL_CORPSESIGHTING; // this is on by default for all characters, disabled if they have a "friendlysightcorpse" script event AICast_ScriptParse( cs ); // // setup bounding boxes //VectorCopy( mins, client->ps.mins ); //VectorCopy( maxs, client->ps.maxs ); AIChar_SetBBox( newent, cs ); client->ps.friction = cs->attributes[RUNNING_SPEED]/300.0; // // clear weapons/ammo client->ps.weapon = 0; memcpy( client->ps.weapons, weaponInfo->startingWeapons, sizeof(weaponInfo->startingWeapons) ); memcpy( client->ps.ammo, weaponInfo->startingAmmo, sizeof(client->ps.ammo) ); // // starting health if (ent->health) { newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = ent->health; } else { newent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] = cs->attributes[STARTING_HEALTH]; } // cs->weaponInfo = weaponInfo; // cs->lastThink = level.time; // newent->pain = AICast_Pain; newent->die = AICast_Die; // //update the attack inventory values AICast_UpdateBattleInventory(cs, cs->bs->enemy); //----(SA) make sure all clips are loaded so we don't hear everyone loading up // (we don't want to do this inside AICast_UpdateBattleInventory(), only on spawn or giveweapon) for (j=0; j<MAX_WEAPONS; j++) { Fill_Clip (&client->ps, j); } //----(SA) end // select a weapon AICast_ChooseWeapon( cs, qfalse ); // // set the default function, overwrite if necessary cs->aiFlags |= AIFL_JUST_SPAWNED; AIFunc_DefaultStart( cs ); // numcast++; // return newent; }
/* ============ AICast_Think entry point for all cast AI ============ */ void AICast_Think( int client, float thinktime ) { gentity_t *ent; cast_state_t *cs; int i; int animIndex; animation_t *anim; // if (saveGamePending || (strlen( g_missionStats.string ) > 2 )) { // return; // } // // get the cast ready for processing // cs = AICast_GetCastState( client ); ent = &g_entities[client]; // // make sure we are using the right AAS data for this entity (one's that don't get set will default to the player's AAS data) trap_AAS_SetCurrentWorld( cs->aasWorldIndex ); // // make sure we have a valid navigation system // if ( !trap_AAS_Initialized() ) { return; } // trap_EA_ResetInput( client, NULL ); cs->aiFlags &= ~AIFL_VIEWLOCKED; //cs->bs->weaponnum = ent->client->ps.weapon; // // turn off flags that are set each frame if needed ent->client->ps.eFlags &= ~( EF_NOSWINGANGLES | EF_MONSTER_EFFECT | EF_MONSTER_EFFECT2 | EF_MONSTER_EFFECT3 ); // conditional flags if ( ent->aiCharacter == AICHAR_ZOMBIE ) { if ( COM_BitCheck( ent->client->ps.weapons, WP_MONSTER_ATTACK1 ) ) { cs->aiFlags |= AIFL_NO_FLAME_DAMAGE; SET_FLAMING_ZOMBIE( ent->s, 1 ); } else { SET_FLAMING_ZOMBIE( ent->s, 0 ); } } // // if we're dead, do special stuff only if ( ent->health <= 0 || cs->revivingTime || cs->rebirthTime ) { // if ( cs->revivingTime && cs->revivingTime < level.time ) { // start us thinking again ent->client->ps.pm_type = PM_NORMAL; cs->revivingTime = 0; } // if ( cs->rebirthTime && cs->rebirthTime < level.time ) { vec3_t mins, maxs; int touch[10], numTouch; float oldmaxZ; oldmaxZ = ent->r.maxs[2]; // make sure the area is clear AIChar_SetBBox( ent, cs ); VectorAdd( ent->r.currentOrigin, ent->r.mins, mins ); VectorAdd( ent->r.currentOrigin, ent->r.maxs, maxs ); trap_UnlinkEntity( ent ); numTouch = trap_EntitiesInBox( mins, maxs, touch, 10 ); if ( numTouch ) { for ( i = 0; i < numTouch; i++ ) { //if (!g_entities[touch[i]].client || g_entities[touch[i]].r.contents == CONTENTS_BODY) if ( g_entities[touch[i]].r.contents & MASK_PLAYERSOLID ) { break; } } if ( i == numTouch ) { numTouch = 0; } } if ( numTouch == 0 ) { // ok to spawn // give them health when they start reviving, so we won't gib after // just a couple shots while reviving ent->health = ent->client->ps.stats[STAT_HEALTH] = ent->client->ps.stats[STAT_MAX_HEALTH] = ( ( cs->attributes[STARTING_HEALTH] - 50 ) > 30 ? ( cs->attributes[STARTING_HEALTH] - 50 ) : 30 ); ent->r.contents = CONTENTS_BODY; ent->clipmask = MASK_PLAYERSOLID; ent->takedamage = qtrue; ent->waterlevel = 0; ent->watertype = 0; ent->flags = 0; ent->die = AICast_Die; ent->client->ps.eFlags &= ~EF_DEAD; ent->s.eFlags &= ~EF_DEAD; cs->rebirthTime = 0; cs->deathTime = 0; // play the revive animation cs->revivingTime = level.time + BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_REVIVE, qfalse, qtrue );; } else { // can't spawn yet, so set bbox back, and wait ent->r.maxs[2] = oldmaxZ; ent->client->ps.maxs[2] = ent->r.maxs[2]; } trap_LinkEntity( ent ); } // ZOMBIE should set effect flag if really dead if ( cs->aiCharacter == AICHAR_ZOMBIE && !ent->r.contents ) { ent->client->ps.eFlags |= EF_MONSTER_EFFECT2; } // if ( ent->health > GIB_HEALTH && cs->deathTime && cs->deathTime < ( level.time - 3000 ) ) { /* // been dead for long enough, set our animation to the end frame switch ( ent->s.legsAnim & ~ANIM_TOGGLEBIT ) { case BOTH_DEATH1: case BOTH_DEAD1: anim = BOTH_DEAD1; break; case BOTH_DEATH2: case BOTH_DEAD2: anim = BOTH_DEAD2; break; case BOTH_DEATH3: case BOTH_DEAD3: anim = BOTH_DEAD3; break; default: G_Error( "%s has unknown death animation\n", ent->classname); } ent->client->ps.torsoAnim = ( ( ent->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; ent->client->ps.legsAnim = ( ( ent->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim; */ cs->deathTime = 0; ent->r.svFlags &= ~SVF_BROADCAST; } // // no more thinking required return; } // // set some anim conditions if ( cs->secondDeadTime ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qtrue, qfalse ); } else { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_SECONDLIFE, qfalse, qfalse ); } // set health value if ( ent->health <= 0.25 * cs->attributes[STARTING_HEALTH] ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 3, qfalse ); } else if ( ent->health <= 0.5 * cs->attributes[STARTING_HEALTH] ) { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 2, qfalse ); } else { BG_UpdateConditionValue( cs->entityNum, ANIM_COND_HEALTH_LEVEL, 1, qfalse ); } // cs->speedScale = 1.0; // reset each frame, set if required cs->actionFlags = 0; // FIXME: move this to a Cast AI movement init function! //retrieve the current client state BotAI_GetClientState( client, &( cs->bs->cur_ps ) ); // // setup movement speeds for the given state // walking animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALK ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[WALKING_SPEED] = anim->moveSpeed; } // crouching animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_WALKCR ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[CROUCHING_SPEED] = anim->moveSpeed; } // running animIndex = BG_GetAnimScriptAnimation( cs->entityNum, ent->client->ps.aiState, ANIM_MT_RUN ); if ( animIndex >= 0 ) { anim = BG_GetAnimationForIndex( cs->entityNum, animIndex ); cs->attributes[RUNNING_SPEED] = anim->moveSpeed; } // update crouch speed scale ent->client->ps.crouchSpeedScale = cs->attributes[CROUCHING_SPEED] / cs->attributes[RUNNING_SPEED]; // // only enable headlook if we want to this frame ent->client->ps.eFlags &= ~EF_HEADLOOK; if ( cs->bs->enemy >= 0 ) { ent->client->ps.eFlags &= ~EF_STAND_IDLE2; // never use alt idle if fighting } // // check for dead leader if ( cs->leaderNum >= 0 && g_entities[cs->leaderNum].health <= 0 ) { cs->leaderNum = -1; } // #if 0 // HACK for village2, if they are stuck, find a good position (there is a friendly guy placed inside a table) { trace_t tr; vec3_t org; trap_Trace( &tr, cs->bs->cur_ps.origin, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, cs->bs->cur_ps.origin, cs->entityNum, CONTENTS_SOLID ); while ( tr.startsolid ) { VectorCopy( cs->bs->cur_ps.origin, org ); org[0] += 96 * crandom(); org[1] += 96 * crandom(); org[2] += 16 * crandom(); trap_Trace( &tr, org, cs->bs->cur_ps.mins, cs->bs->cur_ps.maxs, org, cs->entityNum, CONTENTS_SOLID ); G_SetOrigin( &g_entities[cs->entityNum], org ); VectorCopy( org, g_entities[cs->entityNum].client->ps.origin ); } } #endif //add the delta angles to the cast's current view angles for ( i = 0; i < 3; i++ ) { cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] + SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) ); } // //increase the local time of the cast cs->bs->ltime += thinktime; // cs->bs->thinktime = thinktime; //origin of the cast VectorCopy( cs->bs->cur_ps.origin, cs->bs->origin ); //eye coordinates of the cast VectorCopy( cs->bs->cur_ps.origin, cs->bs->eye ); cs->bs->eye[2] += cs->bs->cur_ps.viewheight; //get the area the cast is in cs->bs->areanum = BotPointAreaNum( cs->bs->origin ); // clear flags each frame cs->bs->flags = 0; // // check enemy health if ( cs->bs->enemy >= 0 && g_entities[cs->bs->enemy].health <= 0 ) { cs->bs->enemy = -1; } // // if the previous movetype was temporary, set it back if ( cs->movestateType == MSTYPE_TEMPORARY ) { cs->movestate = MS_DEFAULT; cs->movestateType = MSTYPE_NONE; } // crouching? if ( ( cs->bs->attackcrouch_time > trap_AAS_Time() ) && ( ( cs->lastAttackCrouch > level.time - 500 ) || ( cs->thinkFuncChangeTime < level.time - 1000 ) ) ) { // if we are not moving, and we are firing, always stand, unless we are allowed to crouch + fire if ( VectorLength( cs->bs->cur_ps.velocity ) || ( cs->lastWeaponFired < level.time - 2000 ) || ( cs->aiFlags & AIFL_ATTACK_CROUCH ) ) { cs->lastAttackCrouch = level.time; trap_EA_Crouch( cs->bs->client ); } } // //if (cs->bs->enemy >= 0) { //update the attack inventory values AICast_UpdateBattleInventory( cs, cs->bs->enemy ); //} // // if we don't have ammo for the current weapon, get rid of it if ( !( COM_BitCheck( cs->bs->cur_ps.weapons, cs->bs->weaponnum ) ) || !AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum ) ) { // select a weapon AICast_ChooseWeapon( cs, qfalse ); // if still no ammo, select a blank weapon //if (!AICast_GotEnoughAmmoForWeapon( cs, cs->bs->weaponnum )) { // cs->bs->weaponnum = WP_NONE; //} } // // in query mode, we do special handling (pause scripting, check for transition to alert/combat, etc) if ( cs->aiState == AISTATE_QUERY ) { AICast_QueryThink( cs ); } else if ( cs->pauseTime < level.time ) { // do the thinking AICast_ProcessAIFunctions( cs, thinktime ); // // make sure the correct weapon is selected trap_EA_SelectWeapon( cs->bs->client, cs->bs->weaponnum ); // // process current script if it exists cs->castScriptStatusCurrent = cs->castScriptStatus; AICast_ScriptRun( cs, qfalse ); } // // set special movestate if necessary if ( cs->movestateType != MSTYPE_NONE ) { switch ( cs->movestate ) { case MS_WALK: cs->actionFlags |= CASTACTION_WALK; break; case MS_CROUCH: trap_EA_Crouch( cs->entityNum ); break; default: break; // TTimo gcc: MS_DEFAULT MS_RUN not handled in switch } } // //subtract the delta angles for ( i = 0; i < 3; i++ ) { cs->bs->viewangles[i] = AngleMod( cs->bs->viewangles[i] - SHORT2ANGLE( cs->bs->cur_ps.delta_angles[i] ) ); } }
/* ============== AICast_UpdateInput ============== */ void AICast_UpdateInput( cast_state_t *cs, int time ) { bot_input_t bi; bot_state_t *bs; int j; float speed; bs = cs->bs; //add the delta angles to the bot's current view angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] + SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } // AICast_ChangeViewAngles( cs, (float) time / 1000 ); // if ( cs->pauseTime > level.time ) { trap_EA_View( bs->client, bs->viewangles ); trap_EA_GetInput( bs->client, (float) time / 1000, &bi ); AICast_InputToUserCommand( cs, &bi, &bs->lastucmd, bs->cur_ps.delta_angles ); g_entities[cs->bs->entitynum].client->ps.pm_flags &= ~PMF_RESPAWNED; // //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } // return; } // trap_EA_GetInput( bs->client, (float) time / 1000, &bi ); // // restrict the speed according to the character and their current speedScale // HACK, don't slow down while crouching if ( bi.actionflags & ACTION_CROUCH && cs->speedScale < 1.0 ) { cs->speedScale = 1.0; } // // check some Cast AI specific movement flags if ( cs->actionFlags & CASTACTION_WALK ) { if ( cs->speedScale > ( cs->attributes[WALKING_SPEED] / cs->attributes[RUNNING_SPEED] ) ) { cs->speedScale = ( cs->attributes[WALKING_SPEED] / cs->attributes[RUNNING_SPEED] ); } } // don't ever let the speed get too low if ( cs->speedScale < 0.25 ) { cs->speedScale = 0.25; } if ( cs->speedScale > 1.2 ) { cs->speedScale = 1.2; } // speed = cs->speedScale * cs->attributes[RUNNING_SPEED]; // //if (speed <= (cs->attributes[WALKING_SPEED] + (cs->attributes[WALKING_SPEED] + 50 < cs->attributes[RUNNING_SPEED] ? 50 : -1))) // do a fast shuffle if slightly over walking speed if ( speed <= cs->attributes[WALKING_SPEED] ) { cs->actionFlags |= CASTACTION_WALK; } // // we use 300 here, because the default player speed is 300, so Cast AI's can't move faster than that if ( ( bi.speed / 400.0 ) > ( speed / 300.0 ) ) { bi.speed = 400.0 * ( speed / 300.0 ); if ( bi.speed > 400.0 ) { bi.speed = 400.0; // just in case, we should never exceed this } } // // do a fast shuffle if slightly over walking speed if ( bi.speed <= ( 400.0 / 300.0 ) * ( cs->attributes[WALKING_SPEED] + ( cs->attributes[WALKING_SPEED] + 50 < cs->attributes[RUNNING_SPEED] ? 50 : -1 ) ) ) { cs->actionFlags |= CASTACTION_WALK; } // AICast_InputToUserCommand( cs, &bi, &bs->lastucmd, bs->cur_ps.delta_angles ); // // check some Cast AI specific movement flags if ( cs->actionFlags & CASTACTION_WALK ) { bs->lastucmd.buttons |= BUTTON_WALKING; // play the walking animation } // //subtract the delta angles for ( j = 0; j < 3; j++ ) { bs->viewangles[j] = AngleMod( bs->viewangles[j] - SHORT2ANGLE( bs->cur_ps.delta_angles[j] ) ); } // // make sure the respawn flag is disabled (causes problems after multiple "map xxx" commands) g_entities[cs->bs->entitynum].client->ps.pm_flags &= ~PMF_RESPAWNED; // set the aiState g_entities[cs->bs->entitynum].client->ps.aiState = cs->aiState; }
/* ============ AICast_GetAvoid ============ */ qboolean AICast_GetAvoid( cast_state_t *cs, bot_goal_t *goal, vec3_t outpos, qboolean reverse, int blockEnt ) { float yaw, oldyaw, distmoved, bestmoved, bestyaw; vec3_t bestpos; aicast_predictmove_t castmove; usercmd_t ucmd; qboolean enemyVisible; float angleDiff; // TTimo might be used uninitialized int starttraveltime = 0; int besttraveltime, traveltime; int invert; float inc; qboolean averting = qfalse; float maxYaw, simTime; static int lastTime; VectorCopy( vec3_origin, bestpos ); // // if we are in the air, no chance of avoiding if ( cs->bs->cur_ps.groundEntityNum == ENTITYNUM_NONE && g_entities[cs->entityNum].waterlevel <= 1 ) { return qfalse; } // if ( cs->lastAvoid > level.time - rand() % 500 ) { return qfalse; } cs->lastAvoid = level.time + 50 + rand() % 500; // if ( lastTime == level.time ) { return qfalse; } lastTime = level.time; // if they have an enemy, and can currently see them, don't move out of their view enemyVisible = ( cs->bs->enemy >= 0 ) && ( AICast_CheckAttack( cs, cs->bs->enemy, qfalse ) ); // // look for a good direction to move out of the way bestmoved = 0; bestyaw = 360; besttraveltime = 9999999; if ( goal ) { starttraveltime = trap_AAS_AreaTravelTimeToGoalArea( cs->bs->areanum, cs->bs->origin, goal->areanum, cs->travelflags ); } memcpy( &ucmd, &cs->bs->lastucmd, sizeof( usercmd_t ) ); ucmd.forwardmove = 127; ucmd.rightmove = 0; ucmd.upmove = 0; if ( cs->dangerEntity >= 0 && cs->dangerEntityValidTime >= level.time ) { averting = qtrue; } else if ( !goal ) { averting = qtrue; // not heading for a goal, so we must be getting out of someone's way } // maxYaw = 0; simTime = 1.2; // if ( averting ) { // avoiding danger, go anywhere! angleDiff = 300; inc = 60; invert = 1; } else { if ( level.time % 1000 < 500 ) { invert = 1; } else { invert = -1; } angleDiff = 140; inc = 35; } if ( blockEnt > aicast_maxclients ) { maxYaw = angleDiff; simTime = 0.5; } // for ( yaw = -angleDiff * invert; yaw*invert <= maxYaw; yaw += inc * invert ) { if ( !averting && !yaw ) { continue; } oldyaw = cs->bs->cur_ps.viewangles[YAW]; cs->bs->cur_ps.viewangles[YAW] += yaw + reverse * 180; // ucmd.angles[YAW] = ANGLE2SHORT( AngleMod( cs->bs->cur_ps.viewangles[YAW] ) ); // AICast_PredictMovement( cs, 5, 0.4, &castmove, &ucmd, -1 ); // if we have a danger entity, try and get away from it at all costs if ( cs->dangerEntity >= 0 && cs->dangerEntityValidTime >= level.time ) { distmoved = Distance( castmove.endpos, cs->dangerEntityPos ); } else if ( goal ) { //distmoved = 99999 - trap_AAS_AreaTravelTimeToGoalArea( BotPointAreaNum(castmove.endpos), castmove.endpos, goal->areanum, cs->travelflags ); distmoved = 99999 - Distance( castmove.endpos, goal->origin ); } else { distmoved = Distance( castmove.endpos, cs->bs->cur_ps.origin ); } if ( ( distmoved > bestmoved ) //&& ((cs->bs->origin[2] - castmove.endpos[2]) < 64) // allow up, but not down (falling) && ( castmove.groundEntityNum != ENTITYNUM_NONE ) ) { // they all passed, check any other stuff if ( !enemyVisible || AICast_CheckAttackAtPos( cs->entityNum, cs->bs->enemy, castmove.endpos, qfalse, qfalse ) ) { if ( !goal || ( traveltime = trap_AAS_AreaTravelTimeToGoalArea( BotPointAreaNum( castmove.endpos ), castmove.endpos, goal->areanum, cs->travelflags ) ) < ( starttraveltime + 200 ) ) { bestyaw = yaw; bestmoved = distmoved; besttraveltime = traveltime; VectorCopy( castmove.endpos, bestpos ); } } } // cs->bs->cur_ps.viewangles[YAW] = oldyaw; } // if ( bestmoved > 0 ) { VectorCopy( bestpos, outpos ); return qtrue; } else { return qfalse; } //G_Printf("GetAvoid: %i ms\n", -pretime + Sys_MilliSeconds() ); }
bool CDeviceClass::SetItemProperty (CItemCtx &Ctx, const CString &sName, ICCItem *pValue, CString *retsError) // SetItemProperty // // Sets a device property. Subclasses should call this if they do not // understand the property. { CCodeChain &CC = g_pUniverse->GetCC(); // Get the installed device. We need this to set anything. CInstalledDevice *pDevice = Ctx.GetDevice(); if (pDevice == NULL) { *retsError = CONSTLIT("Item is not an installed device on object."); return false; } // Figure out what to set if (strEquals(sName, PROPERTY_FIRE_ARC)) { // A value of nil means no fire arc (and no omni) if (pValue == NULL || pValue->IsNil()) { pDevice->SetOmniDirectional(false); pDevice->SetFireArc(0, 0); } // A value of "omnidirectional" counts else if (strEquals(pValue->GetStringValue(), PROPERTY_OMNIDIRECTIONAL)) { pDevice->SetOmniDirectional(true); pDevice->SetFireArc(0, 0); } // A single value means that we just point in a direction else if (pValue->GetCount() == 1) { int iMinFireArc = AngleMod(pValue->GetElement(0)->GetIntegerValue()); pDevice->SetOmniDirectional(false); pDevice->SetFireArc(iMinFireArc, iMinFireArc); } // Otherwise we expect a list with two elements else if (pValue->GetCount() >= 2) { int iMinFireArc = AngleMod(pValue->GetElement(0)->GetIntegerValue()); int iMaxFireArc = AngleMod(pValue->GetElement(1)->GetIntegerValue()); pDevice->SetOmniDirectional(false); pDevice->SetFireArc(iMinFireArc, iMaxFireArc); } // Invalid else { *retsError = CONSTLIT("Invalid fireArc parameter."); return false; } } else if (strEquals(sName, PROPERTY_LINKED_FIRE_OPTIONS)) { // Parse the options DWORD dwOptions; if (!::GetLinkedFireOptions(pValue, &dwOptions, retsError)) return false; // Set pDevice->SetLinkedFireOptions(dwOptions); } else if (strEquals(sName, PROPERTY_POS)) { // Get the parameters. We accept a single list parameter with angle/radius/z. // (The latter is compatible with the return of objGetDevicePos.) int iPosAngle; int iPosRadius; int iZ; if (pValue == NULL || pValue->IsNil()) { iPosAngle = 0; iPosRadius = 0; iZ = 0; } else if (pValue->GetCount() >= 2) { iPosAngle = pValue->GetElement(0)->GetIntegerValue(); iPosRadius = pValue->GetElement(1)->GetIntegerValue(); if (pValue->GetCount() >= 3) iZ = pValue->GetElement(2)->GetIntegerValue(); else iZ = 0; } else { *retsError = CONSTLIT("Invalid angle and radius"); return false; } // Set it pDevice->SetPosAngle(iPosAngle); pDevice->SetPosRadius(iPosRadius); pDevice->SetPosZ(iZ); } else if (strEquals(sName, PROPERTY_SECONDARY)) { if (pValue == NULL || !pValue->IsNil()) pDevice->SetSecondary(true); else pDevice->SetSecondary(false); } else { *retsError = strPatternSubst(CONSTLIT("Unknown item property: %s."), sName); return false; } return true; }