/// <see cref="SCVBuildExecutionBase.ContinueMovingToTarget"/> protected override bool ContinueMovingToTarget() { /// Check the distance between the SCV and the target area. RCNumber distance = MapUtils.ComputeDistance(this.RecipientSCV.Area, this.targetArea.Read()); if (distance > Weapon.NEARBY_DISTANCE) { /// Distance not reached yet -> continue execution if SCV is still moving. return(!this.RecipientSCV.MotionControl.IsMoving); } TerranBuildingConstructionJob job = new TerranBuildingConstructionJob( this.RecipientSCV, this.RecipientSCV.Owner.Metadata.GetBuildingType(this.buildingTypeName), this.topLeftQuadTile.Read()); if (!job.LockResources()) { /// Unable to lock the necessary resources -> abort the job and cancel. job.Dispose(); return(true); } if (!job.Start()) { /// Unable to start the job -> abort the job and cancel. job.Dispose(); return(true); } this.Status = SCVBuildExecutionStatusEnum.Constructing; return(false); }
/// <see cref="SCVBuildExecutionBase.ContinueMovingToTarget"/> protected override bool ContinueMovingToTarget() { /// First try to retrieve the target building from the scenario. TerranBuilding targetBuilding = this.Scenario.GetElementOnMap <TerranBuilding>(this.targetBuildingID.Read(), MapObjectLayerEnum.GroundObjects); if (targetBuilding == null) { /// Target building not found -> finish command execution. return(true); } /// Check the distance between the SCV and the target building. RCNumber distance = MapUtils.ComputeDistance(this.RecipientSCV.Area, targetBuilding.Area); if (distance > Weapon.NEARBY_DISTANCE) { /// Distance not reached yet -> continue execution if SCV is still moving. return(!this.RecipientSCV.MotionControl.IsMoving); } /// Check if the target building has an inactive construction job. if (targetBuilding.ConstructionJob == null || targetBuilding.ConstructionJob.AttachedSCV != null) { /// Target building doesn't have an inactive construction job -> finish command execution. return(true); } /// Attach the SCV to the construction job of the target building. targetBuilding.ConstructionJob.AttachSCV(this.RecipientSCV); this.Status = SCVBuildExecutionStatusEnum.Constructing; return(false); }
/// <summary> /// Gets the list of all velocities that are admissible from the given velocity. /// </summary> /// <param name="currentVelocity">The given velocity.</param> /// <returns>All velocities that are admissible from the given velocity.</returns> public List <RCNumVector> GetAdmissibleVelocities(RCNumVector currentVelocity) { if (!this.velocityGraph.ContainsKey(currentVelocity)) { /// Search the velocity that has a minimum difference to the current velocity. RCNumber minDiff = 0; RCNumVector closestVelocity = RCNumVector.Undefined; foreach (RCNumVector velocity in this.velocityGraph.Keys) { RCNumber diff = MapUtils.ComputeDistance(currentVelocity, velocity); if (closestVelocity == RCNumVector.Undefined || diff < minDiff) { minDiff = diff; closestVelocity = velocity; } } if (closestVelocity == RCNumVector.Undefined) { throw new InvalidOperationException("Impossible case!"); } currentVelocity = closestVelocity; } return(new List <RCNumVector>(this.velocityGraph[currentVelocity])); }
/// <summary> /// Updates this missile if it is in Launched state. /// </summary> private bool UpdateLaunchedState() { /// Update the target entity position and height if it is still on the map. if (this.targetEntity.Read().HasMapObject(MapObjectLayerEnum.GroundObjects, MapObjectLayerEnum.AirObjects)) { this.lastKnownTargetEntityPos.Write(this.targetEntity.Read().MotionControl.PositionVector.Read()); this.lastKnownTargetEntityIsFlying.Write(this.targetEntity.Read().MotionControl.IsFlying ? (byte)0x01 : (byte)0x00); } /// Move immediately to the Impacted state if this is an instant missile. /// Note: a missile is instant if it has no speed defined. if (this.missileData.MissileType.Speed == null) { if (this.Impact != null) { this.Impact(this); } this.currentStatus.Write((byte)Status.Impacted); if (this.missileIndicator != null && !this.missileIndicator.IsDestroyed) { this.DestroyMapObject(this.missileIndicator); } return(true); } /// Calculate the new position and velocity of the missile. this.UpdateVelocity(); this.missilePosition.Write(this.missilePosition.Read() + this.missileVelocity.Read()); /// Check if the missile impacts. if (MapUtils.ComputeDistance(this.missilePosition.Read(), this.lastKnownTargetEntityPos.Read()) <= this.missileData.MissileType.Speed.Read()) { if (this.Impact != null) { this.Impact(this); } this.currentStatus.Write((byte)Status.Impacted); if (this.missileIndicator != null && !this.missileIndicator.IsDestroyed) { this.DestroyMapObject(this.missileIndicator); } return(true); } /// Create a missile indicator map object if it has not yet been created and the missile type defines a flying animation... if (this.missileIndicator == null && this.missileData.MissileType.FlyingAnimation != null) { this.missileIndicator = this.CreateMapObject(this.CalculateArea(this.missilePosition.Read()), this.lastKnownTargetEntityIsFlying.Read() == 0x01 ? MapObjectLayerEnum.AirMissiles : MapObjectLayerEnum.GroundMissiles); this.missileIndicator.StartAnimation(this.missileData.MissileType.FlyingAnimation, this.missileVelocity); } else if (this.missileIndicator != null && !this.missileIndicator.IsDestroyed) { /// ... or update its position if already exists. this.missileIndicator.SetLocation(this.CalculateArea(this.missilePosition.Read())); } return(true); }
/// <summary> /// Checks whether the given target entity is in the attack range of this weapon. /// </summary> /// <param name="targetEntity">The target entity.</param> /// <returns>True if the given target entity is in the attack range of this weapon; otherwise false.</returns> public bool IsEntityInRange(Entity targetEntity) { if (!this.CanTargetEntity(targetEntity)) { return(false); } RCNumRectangle ownerArea = this.owner.Read().Area; RCNumRectangle targetArea = targetEntity.Area; return(this.IsInRange(MapUtils.ComputeDistance(ownerArea, targetArea))); }
/// <summary> /// Selects an enemy that can be attacked by the owner. /// </summary> /// <returns>An enemy that can be attacked by the owner or null if no such enemy has been found.</returns> public Entity SelectEnemyForStandardWeapons() { if (this.standardWeapons.Count == 0) { /// The owner has no standard weapons -> no enemy can be attacked. return(null); } /// Select the nearest enemy. RCNumber nearestEnemyDistance = 0; Entity nearestEnemy = null; foreach (Entity locatedEntity in this.owner.Read().Locator.LocateEntities()) { /// Check if the located entity is an enemy or not. if (locatedEntity.Owner == null || locatedEntity.Owner == this.owner.Read().Owner) { continue; } /// Check if any of the standard weapons can target the located entity. bool hasWeaponCanTargetEntity = false; foreach (Weapon standardWeapon in this.standardWeapons) { if (standardWeapon.CanTargetEntity(locatedEntity)) { hasWeaponCanTargetEntity = true; break; } } if (!hasWeaponCanTargetEntity) { /// The owner has no weapons that can attack the located enemy -> continue with the next located enemy. continue; } if (nearestEnemy == null) { nearestEnemy = locatedEntity; } else { RCNumber distance = MapUtils.ComputeDistance(this.owner.Read().Area, locatedEntity.Area); if (distance < nearestEnemyDistance) { nearestEnemy = locatedEntity; } } } return(nearestEnemy); }
/// <summary> /// Calculates the next position of the controlled entity from its current position and the next waypoint. /// </summary> /// <returns>The calculated next position of the controlled entity.</returns> private RCNumVector CalculateNextPosition() { RCNumVector currentPosition = this.controlledEntity.Read().MotionControl.PositionVector.Read(); RCNumber distToWP = MapUtils.ComputeDistance(currentPosition, this.CurrentWaypoint); if (distToWP > this.controlledEntity.Read().ElementType.Speed.Read()) { // Move towards the next waypoint. RCNumVector translationVect = (this.controlledEntity.Read().ElementType.Speed.Read() / distToWP) * (this.CurrentWaypoint - currentPosition); return(currentPosition + translationVect); } else { // Next waypoint can be reached in this step. return(this.CurrentWaypoint); } }
/// <summary> /// Calculates the list of visible quadratic coordinates from the last known value of the sight range. /// </summary> private void CalculateRelativeQuadCoordsInSight() { if (this.lastKnownSightRange == -1) { this.relativeQuadCoordsInSight = null; } else { RCIntVector nullVector = new RCIntVector(0, 0); this.relativeQuadCoordsInSight = new RCSet <RCIntVector>(); for (int x = -this.lastKnownSightRange; x <= this.lastKnownSightRange; x++) { for (int y = -this.lastKnownSightRange; y <= this.lastKnownSightRange; y++) { RCIntVector quadCoord = new RCIntVector(x, y); if (MapUtils.ComputeDistance(nullVector, quadCoord) < this.lastKnownSightRange) { this.relativeQuadCoordsInSight.Add(quadCoord); } } } } }
/// <summary> /// Continue the execution in case of a follow command. /// </summary> /// <returns>True if execution is finished; otherwise false.</returns> private void ContinueFollow() { /// Check if target entity still can be located. this.targetEntity.Write(this.LocateEntity(this.targetEntityID.Read())); if (!this.HasToFollowTarget) { return; } /// Calculate its distance from the recipient entity. RCNumber distance = MapUtils.ComputeDistance(this.recipientEntity.Read().Area, this.targetEntity.Read().Area); if (distance > MAX_DISTANCE) { /// Too far -> start approaching again. this.recipientEntity.Read().MotionControl.StartMoving(this.targetEntity.Read().MotionControl.PositionVector.Read()); } //else //{ // /// Close enough -> stop the recipient entity. // this.recipientEntity.Read().MotionControl.StopMoving(); //} }
/// <summary> /// Sets the sight range of the corresponding element type. /// </summary> public void SetSightRange(int sightRange) { if (this.metadata.IsFinalized) { throw new InvalidOperationException("Already finalized!"); } this.sightRange = new ConstValue <int>(sightRange); RCIntVector nullVector = new RCIntVector(0, 0); this.relativeQuadCoordsInSight = new RCSet <RCIntVector>(); for (int x = -this.sightRange.Read(); x <= this.sightRange.Read(); x++) { for (int y = -this.sightRange.Read(); y <= this.sightRange.Read(); y++) { RCIntVector quadCoord = new RCIntVector(x, y); if (MapUtils.ComputeDistance(nullVector, quadCoord) < this.sightRange.Read()) { this.relativeQuadCoordsInSight.Add(quadCoord); } } } }
/// <summary> /// Updates the velocity of this missile. /// </summary> private void UpdateVelocity() { RCNumVector vectorToTarget = this.lastKnownTargetEntityPos.Read() - this.missilePosition.Read(); if (this.missileData.MissileType.Speed == null) { this.missileVelocity.Write(vectorToTarget); return; } int currVelocityIndex = 0; int bestVelocityIndex = -1; RCNumber minDistanceToTarget = 0; List <RCNumVector> admissibleVelocities = new List <RCNumVector>( this.velocityGraph.GetAdmissibleVelocities(this.missileVelocity.Read() != RCNumVector.Undefined ? this.missileVelocity.Read() : vectorToTarget)); foreach (RCNumVector admissibleVelocity in admissibleVelocities) { RCNumber distanceToTarget = MapUtils.ComputeDistance(vectorToTarget, admissibleVelocity); if (bestVelocityIndex == -1 || distanceToTarget < minDistanceToTarget) { minDistanceToTarget = distanceToTarget; bestVelocityIndex = currVelocityIndex; } currVelocityIndex++; } if (bestVelocityIndex == -1) { throw new InvalidOperationException("Unable to select best velocity for missile!"); } this.missileVelocity.Write(admissibleVelocities[bestVelocityIndex]); }