Beispiel #1
0
/**
 * @brief Updates spin animations
 * @param cur float value to update
 * @param dest float the final desired speed (NOT the final angle!)
 * @param speed float is updated if it is not equal to dest
 * @param divisor int is the deltatime, it is not added before the call because speed may have to be updated
 * @return true if the desired speed is 0 and it is reached, false otherwise
 */
bool CUnitScript::DoSpin(float &cur, float dest, float &speed, float accel, int divisor)
{
	const float delta = dest - speed;

	// Check if we are not at the final speed and
	// make sure we dont go past desired speed
	if (math::fabsf(delta) <= accel) {
		speed = dest;
		if (speed == 0.0f)
			return true;
	}
	else {
		if (delta > 0.0f) {
			// accelerations are defined in speed/frame (at GAME_SPEED fps)
			speed += accel * (float(GAME_SPEED) / divisor);
		} else {
			speed -= accel * (float(GAME_SPEED) / divisor);
		}
	}

	cur += (speed / divisor);
	ClampRad(&cur);

	return false;
}
Beispiel #2
0
/**
 * @brief Updates turn animations
 * @param cur float value to update
 * @param dest float final value
 * @param speed float max increment per tick
 * @return returns true if destination was reached, false otherwise
 */
bool CUnitScript::TurnToward(float &cur, float dest, float speed)
{
	float delta = dest - cur;

	// clamp: -pi .. 0 .. +pi (remainder(x,TWOPI) would do the same but is slower due to streflop)
	if (delta > PI) {
		delta -= TWOPI;
	} else if (delta<=-PI) {
		delta += TWOPI;
	}

	if (math::fabsf(delta) <= speed) {
		cur = dest;
		return true;
	}

	if (delta > 0.0f) {
		cur += speed;
	} else {
		cur -= speed;
	}

	ClampRad(&cur);

	return false;
}
Beispiel #3
0
/**
 * @brief Updates spin animations
 * @param cur float value to update
 * @param dest float the final desired speed (NOT the final angle!)
 * @param speed float is updated if it is not equal to dest
 * @param divisor int is the deltatime, it is not added before the call because speed may have to be updated
 * @return true if the desired speed is 0 and it is reached, false otherwise
 */
bool CUnitScript::DoSpin(float &cur, float dest, float &speed, float accel, int divisor)
{
	const float delta = dest - speed;

	// Check if we are not at the final speed and
	// make sure we dont go past desired speed
	if (streflop::fabsf(delta) <= accel) {
		speed = dest;
		if (speed == 0.0f)
			return true;
	}
	else {
		if (delta > 0.0f) {
			//TA obviously defines accelerations in speed/frame (at 30 fps)
			speed += accel * (30.0f / divisor);
		} else {
			speed -= accel * (30.0f / divisor);
		}
	}

	cur += (speed / divisor);
	ClampRad(&cur);

	return false;
}
Beispiel #4
0
//Overwrites old information. This means that threads blocking on turn completion
//will now wait for this new turn instead. Not sure if this is the expected behaviour
//Other option would be to kill them. Or perhaps unblock them.
void CUnitScript::AddAnim(AnimType type, int piece, int axis, float speed, float dest, float accel, bool interpolated)
{
	if (!PieceExists(piece)) {
		ShowScriptError("Invalid piecenumber");
		return;
	}

	float destf;
	if (type == AMove) {
		destf = pieces[piece]->original->offset[axis] + dest;
	} else {
		destf = dest;
		if (type == ATurn) {
			ClampRad(&destf);
		}
	}

	struct AnimInfo *ai;

	//Turns override spins.. Not sure about the other way around? If so the system should probably be redesigned
	//to only have two types of anims.. turns and moves, with spin as a bool
	//todo: optimize, atm RemoveAnim and FindAnim search twice through all anims
	if (type == ATurn)
		RemoveAnim(ASpin, piece, axis);
	if (type == ASpin)
		RemoveAnim(ATurn, piece, axis);

	ai = FindAnim(type, piece, axis);
	if (!ai) {
		// If we were not animating before, inform the engine of this so it can schedule us
		if (anims.empty()) {
			GUnitScriptEngine.AddInstance(this);
		}

		ai = new struct AnimInfo;
		ai->type = type;
		ai->piece = piece;
		ai->axis = axis;
		anims.push_back(ai);
	}

	ai->dest  = destf;
	ai->speed = speed;
	ai->accel = accel;
	ai->interpolated = interpolated;
}
Beispiel #5
0
void CUnitScript::TurnSmooth(int piece, int axis, float destination, int delta, int deltaTime)
{
	if (!PieceExists(piece)) {
		ShowScriptError("Invalid piecenumber");
		return;
	}

	AnimInfo *ai = FindAnim(ATurn, piece, axis);
	if (ai) {
		if (!ai->interpolated) {
			TurnNow(piece, axis, destination);
			return;
		}
	}

	// not sure the ClampRad() call is necessary here
	float cur = ClampRad(pieces[piece]->rot[axis]);
	float dist = streflop::fabsf(destination - cur);
	int timeFactor = (1000 * 1000) / (deltaTime * deltaTime);
	float speed = (dist * timeFactor) / delta;

	Turn(piece, axis, speed, destination, true);
}
Beispiel #6
0
//Overwrites old information. This means that threads blocking on turn completion
//will now wait for this new turn instead. Not sure if this is the expected behaviour
//Other option would be to kill them. Or perhaps unblock them.
void CUnitScript::AddAnim(AnimType type, int piece, int axis, float speed, float dest, float accel)
{
	if (!PieceExists(piece)) {
		ShowScriptError("Invalid piecenumber");
		return;
	}

	float destf = 0.0f;

	if (type == AMove) {
		destf = pieces[piece]->original->offset[axis] + dest;
	} else {
		destf = dest;
		if (type == ATurn) {
			ClampRad(&destf);
		}
	}

	std::list<AnimInfo*>::iterator animInfoIt;
	AnimInfo* ai = NULL;
	AnimType overrideType = ANone;

	// first find an animation of a type we override
	// Turns override spins.. Not sure about the other way around? If so
	// the system should probably be redesigned to only have two types of
	// anims (turns and moves), with spin as a bool
	switch (type) {
		case ATurn: {
			overrideType = ASpin;
			animInfoIt = FindAnim(overrideType, piece, axis);
		} break;
		case ASpin: {
			overrideType = ATurn;
			animInfoIt = FindAnim(overrideType, piece, axis);
		} break;
		case AMove: {
			// ensure we never remove an animation of this type
			overrideType = AMove;
			animInfoIt = anims[overrideType].end();
		} break;
		default: {
		} break;
	}

	if (animInfoIt != anims[overrideType].end())
		RemoveAnim(overrideType, animInfoIt);

	// now find an animation of our own type
	animInfoIt = FindAnim(type, piece, axis);

	if (animInfoIt == anims[type].end()) {
		// If we were not animating before, inform the engine of this so it can schedule us
		// FIXME: this could be done in a cleaner way
		if (!HaveAnimations()) {
			GUnitScriptEngine.AddInstance(this);
		}

		ai = new AnimInfo();
		ai->type = type;
		ai->piece = piece;
		ai->axis = axis;
		anims[type].push_back(ai);
	} else {
		ai = *animInfoIt;
	}

	ai->dest  = destf;
	ai->speed = speed;
	ai->accel = accel;
	ai->done = false;
}
Beispiel #7
0
void CWeapon::Update()
{
	if (hasCloseTarget) {
		int weaponPiece = -1;
		bool weaponAimed = (useWeaponPosForAim == 0);

		// if we couldn't get a line of fire from the
		// muzzle, try if we can get it from the aim
		// piece
		if (!weaponAimed) {
			weaponPiece = owner->script->QueryWeapon(weaponNum);
		} else {
			weaponPiece = owner->script->AimFromWeapon(weaponNum);
		}

		relWeaponMuzzlePos = owner->script->GetPiecePos(weaponPiece);

		if (!weaponAimed) {
			weaponPiece = owner->script->AimFromWeapon(weaponNum);
		}

		relWeaponPos = owner->script->GetPiecePos(weaponPiece);
	}

	if (targetType == Target_Unit) {
		if (lastErrorVectorUpdate < gs->frameNum - UNIT_SLOWUPDATE_RATE) {
			float3 newErrorVector(gs->randVector());
			errorVectorAdd = (newErrorVector - errorVector) * (1.0f / UNIT_SLOWUPDATE_RATE);
			lastErrorVectorUpdate = gs->frameNum;
		}
		errorVector += errorVectorAdd;
		if (predict > 50000) {
			// to prevent runaway prediction (happens sometimes when a missile
			// is moving *away* from its target), we may need to disable missiles
			// in case they fly around too long
			predict = 50000;
		}

		float3 lead = targetUnit->speed * (weaponDef->predictBoost+predictSpeedMod * (1.0f - weaponDef->predictBoost)) * predict;

		if (weaponDef->leadLimit >= 0.0f && lead.SqLength() > Square(weaponDef->leadLimit + weaponDef->leadBonus * owner->experience)) {
			lead *= (weaponDef->leadLimit + weaponDef->leadBonus*owner->experience) / (lead.Length() + 0.01f);
		}

		targetPos =
			helper->GetUnitErrorPos(targetUnit, owner->allyteam) + lead +
			errorVector * (weaponDef->targetMoveError * GAME_SPEED * targetUnit->speed.Length() * (1.0f - owner->limExperience));

		const float appHeight = ground->GetApproximateHeight(targetPos.x, targetPos.z) + 2.0f;

		if (targetPos.y < appHeight)
			targetPos.y = appHeight;

		if (!weaponDef->waterweapon && targetPos.y < 1.0f)
			targetPos.y = 1.0f;
	}

	if (weaponDef->interceptor) {
		CheckIntercept();
	}
	if (targetType != Target_None) {
		if (onlyForward) {
			float3 goaldir = (targetPos - owner->pos).Normalize();
			angleGood = (owner->frontdir.dot(goaldir) > maxAngleDif);
		} else if (lastRequestedDir.dot(wantedDir) < maxAngleDif || (lastRequest + 15 < gs->frameNum)) {
			angleGood = false;
			lastRequestedDir = wantedDir;
			lastRequest = gs->frameNum;

			const float heading = GetHeadingFromVectorF(wantedDir.x, wantedDir.z);
			const float pitch = math::asin(Clamp(wantedDir.dot(owner->updir), -1.0f, 1.0f));
			// for COB, this sets anglegood to return value of aim script when it finished,
			// for Lua, there exists a callout to set the anglegood member.
			// FIXME: convert CSolidObject::heading to radians too.
			owner->script->AimWeapon(weaponNum, ClampRad(heading - owner->heading * TAANG2RAD), pitch);
		}
	}
	if(weaponDef->stockpile && numStockpileQued){
		float p=1.0f/stockpileTime;
		if(teamHandler->Team(owner->team)->metal>=metalFireCost*p && teamHandler->Team(owner->team)->energy>=energyFireCost*p){
			owner->UseEnergy(energyFireCost*p);
			owner->UseMetal(metalFireCost*p);
			buildPercent+=p;
		} else {
			// update the energy and metal required counts
			teamHandler->Team(owner->team)->energyPull += energyFireCost*p;
			teamHandler->Team(owner->team)->metalPull += metalFireCost*p;
		}
		if(buildPercent>=1){
			const int oldCount = numStockpiled;
			buildPercent=0;
			numStockpileQued--;
			numStockpiled++;
			owner->commandAI->StockpileChanged(this);
			eventHandler.StockpileChanged(owner, this, oldCount);
		}
	}

	if ((salvoLeft == 0)
	    && (owner->fpsControlPlayer == NULL || owner->fpsControlPlayer->fpsController.mouse1
	                                        || owner->fpsControlPlayer->fpsController.mouse2)
	    && (targetType != Target_None)
	    && angleGood
	    && subClassReady
	    && (reloadStatus <= gs->frameNum)
	    && (!weaponDef->stockpile || numStockpiled)
	    && (weaponDef->fireSubmersed || (weaponMuzzlePos.y > 0))
	    && ((((owner->unitDef->maxFuel == 0) || (owner->currentFuel > 0) || (fuelUsage == 0)) &&
	       !isBeingServicedOnPad(owner)))
	   )
	{
		if ((weaponDef->stockpile ||
		     (teamHandler->Team(owner->team)->metal >= metalFireCost &&
		      teamHandler->Team(owner->team)->energy >= energyFireCost)))
		{
			const int piece = owner->script->QueryWeapon(weaponNum);
			owner->script->GetEmitDirPos(piece, relWeaponMuzzlePos, weaponDir);
			weaponMuzzlePos = owner->pos + owner->frontdir * relWeaponMuzzlePos.z +
			                               owner->updir    * relWeaponMuzzlePos.y +
			                               owner->rightdir * relWeaponMuzzlePos.x;
			useWeaponPosForAim = reloadTime / 16 + 8;
			weaponDir = owner->frontdir * weaponDir.z +
			            owner->updir    * weaponDir.y +
			            owner->rightdir * weaponDir.x;

			weaponDir.SafeNormalize();

			if (TryTarget(targetPos, haveUserTarget, targetUnit) && !CobBlockShot(targetUnit)) {
				if (weaponDef->stockpile) {
					const int oldCount = numStockpiled;
					numStockpiled--;
					owner->commandAI->StockpileChanged(this);
					eventHandler.StockpileChanged(owner, this, oldCount);
				} else {
					owner->UseEnergy(energyFireCost);
					owner->UseMetal(metalFireCost);
					owner->currentFuel = std::max(0.0f, owner->currentFuel - fuelUsage);
				}
				reloadStatus = gs->frameNum + (int)(reloadTime / owner->reloadSpeed);

				salvoLeft = salvoSize;
				nextSalvo = gs->frameNum;
				salvoError = gs->randVector() * (owner->isMoving? weaponDef->movingAccuracy: accuracy);

				if (targetType == Target_Pos || (targetType == Target_Unit && !(targetUnit->losStatus[owner->allyteam] & LOS_INLOS))) {
					// area firing stuff is too effective at radar firing...
					salvoError *= 1.3f;
				}

				owner->lastMuzzleFlameSize = muzzleFlareSize;
				owner->lastMuzzleFlameDir = wantedDir;
				owner->script->FireWeapon(weaponNum);
			}
		} else {
			// FIXME  -- never reached?
			if (TryTarget(targetPos, haveUserTarget, targetUnit) && !weaponDef->stockpile) {
				// update the energy and metal required counts
				const int minPeriod = std::max(1, (int)(reloadTime / owner->reloadSpeed));
				const float averageFactor = 1.0f / (float)minPeriod;
				teamHandler->Team(owner->team)->energyPull += averageFactor * energyFireCost;
				teamHandler->Team(owner->team)->metalPull += averageFactor * metalFireCost;
			}
		}
	}
	if (salvoLeft && nextSalvo <= gs->frameNum) {
		salvoLeft--;
		nextSalvo = gs->frameNum + salvoDelay;
		owner->lastFireWeapon = gs->frameNum;

		int projectiles = projectilesPerShot;

		while (projectiles > 0) {
			--projectiles;

			// add to the commandShotCount if this is the last salvo,
			// and it is being directed towards the current target
			// (helps when deciding if a queued ground attack order has been completed)
			if (((salvoLeft == 0) && (owner->commandShotCount >= 0) &&
			    ((targetType == Target_Pos) && (targetPos == owner->userAttackPos))) ||
					((targetType == Target_Unit) && (targetUnit == owner->userTarget))) {
				owner->commandShotCount++;
			}

			owner->script->Shot(weaponNum);

			int piece = owner->script->AimFromWeapon(weaponNum);
			relWeaponPos = owner->script->GetPiecePos(piece);

			piece = owner->script->/*AimFromWeapon*/QueryWeapon(weaponNum);
			owner->script->GetEmitDirPos(piece, relWeaponMuzzlePos, weaponDir);

			weaponPos=owner->pos+owner->frontdir*relWeaponPos.z+owner->updir*relWeaponPos.y+owner->rightdir*relWeaponPos.x;

			weaponMuzzlePos=owner->pos+owner->frontdir*relWeaponMuzzlePos.z+owner->updir*relWeaponMuzzlePos.y+owner->rightdir*relWeaponMuzzlePos.x;
			weaponDir = owner->frontdir * weaponDir.z + owner->updir * weaponDir.y + owner->rightdir * weaponDir.x;
			weaponDir.SafeNormalize();

			if (owner->unitDef->decloakOnFire && (owner->scriptCloak <= 2)) {
				if (owner->isCloaked) {
					owner->isCloaked = false;
					eventHandler.UnitDecloaked(owner);
				}
				owner->curCloakTimeout = gs->frameNum + owner->cloakTimeout;
			}

			Fire();
		}

		//Rock the unit in the direction of the fireing
		if (owner->script->HasRockUnit()) {
			float3 rockDir = wantedDir;
			rockDir.y = 0;
			rockDir = -rockDir.Normalize();
			owner->script->RockUnit(rockDir);
		}

		owner->commandAI->WeaponFired(this);

		if(salvoLeft==0){
			owner->script->EndBurst(weaponNum);
		}
#ifdef TRACE_SYNC
	tracefile << "Weapon fire: ";
	tracefile << weaponPos.x << " " << weaponPos.y << " " << weaponPos.z << " " << targetPos.x << " " << targetPos.y << " " << targetPos.z << "\n";
#endif
	}
}