/// <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])); }
/// <see cref="IMapObjectDetailsView.GetHPCondition"/> public MapObjectConditionEnum GetHPCondition(int objectID) { if (objectID < 0) { throw new ArgumentOutOfRangeException("objectID"); } Entity entity = this.GetEntity(objectID); if (entity.Biometrics.HP == -1) { return(MapObjectConditionEnum.Undefined); } RCNumber hpNorm = entity.Biometrics.HP / entity.ElementType.MaxHP.Read(); if (hpNorm <= (RCNumber)1 / (RCNumber)3) { return(MapObjectConditionEnum.Critical); } else if (hpNorm <= (RCNumber)2 / (RCNumber)3) { return(MapObjectConditionEnum.Moderate); } else { return(MapObjectConditionEnum.Excellent); } }
/// <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); }
/// <summary> /// Constructs a VelocityGraph instance. /// </summary> /// <param name="maxSpeed">The maximum speed that this velocity graph is based on.</param> /// <param name="velocityGraph"> /// The dictionary that represents the velocity graph. /// The keys of this dictionary contains all the possible velocities. The values of this dictionary are the sets of /// the velocities admissible from the velocity stored by corresponding key. Each velocity in one of those /// lists must either be present as a key in this dictionary. The (0;0) vector must be present in this /// dictionary as a key. /// </param> protected VelocityGraph(RCNumber maxSpeed, Dictionary <RCNumVector, RCSet <RCNumVector> > velocityGraph) { if (maxSpeed < 0) { throw new ArgumentOutOfRangeException("maxSpeed", "Maximum speed cannot be negative!"); } if (velocityGraph == null) { throw new ArgumentNullException("velocityGraph"); } if (!velocityGraph.ContainsKey(new RCNumVector(0, 0))) { throw new ArgumentException("Velocity graph must contain the (0;0) vector as a key!", "velocityGraph"); } /// Check and save the new velocity graph. this.velocityGraph = new Dictionary <RCNumVector, RCSet <RCNumVector> >(); foreach (KeyValuePair <RCNumVector, RCSet <RCNumVector> > item in velocityGraph) { if (item.Value.Any(velocity => !velocityGraph.ContainsKey(velocity))) { throw new ArgumentException("Each velocity in the admissibility lists must either be present as a key in the velocity graph dictionary!", "velocityGraph"); } this.velocityGraph.Add(item.Key, new RCSet <RCNumVector>(item.Value)); } /// Save the maximum speed that this velocity graph is based on. this.maxSpeed = maxSpeed; }
/// <summary> /// Tries to align the minimap horizontally. /// </summary> /// <param name="minimapControlPixelSize">The size of the minimap control in pixels.</param> /// <param name="mapCellSize">The size of the map in cells.</param> /// <param name="minimapPosition">The position of the minimap on the minimap control in pixels.</param> /// <param name="transformation">The transformation between the map (A) and minimap (B) coordinate-systems.</param> /// <returns>True if the alignment was successfully; otherwise false.</returns> private static bool TryAlignMinimapHorizontally( RCIntVector minimapControlPixelSize, RCIntVector mapCellSize, out RCIntRectangle minimapPosition, out RCCoordTransformation transformation) { RCNumber horzAlignedMinimapHeight = (RCNumber)(minimapControlPixelSize.X * mapCellSize.Y) / (RCNumber)mapCellSize.X; if (horzAlignedMinimapHeight > minimapControlPixelSize.Y) { /// Cannot align horizontally. minimapPosition = RCIntRectangle.Undefined; transformation = null; return(false); } /// Align horizontally int minimapPixelHeight = horzAlignedMinimapHeight > (int)horzAlignedMinimapHeight ? (int)horzAlignedMinimapHeight + 1 : (int)horzAlignedMinimapHeight; minimapPosition = new RCIntRectangle(0, (minimapControlPixelSize.Y - minimapPixelHeight) / 2, minimapControlPixelSize.X, minimapPixelHeight); /// Create the coordinate transformation RCNumVector pixelSizeOnMap = new RCNumVector((RCNumber)mapCellSize.X / (RCNumber)minimapControlPixelSize.X, (RCNumber)mapCellSize.X / (RCNumber)minimapControlPixelSize.X); RCNumVector nullVectorOfMinimap = (pixelSizeOnMap / 2) - (new RCNumVector(1, 1) / 2); RCNumVector baseVectorOfMinimapX = new RCNumVector(pixelSizeOnMap.X, 0); RCNumVector baseVectorOfMinimapY = new RCNumVector(0, pixelSizeOnMap.Y); transformation = new RCCoordTransformation(nullVectorOfMinimap, baseVectorOfMinimapX, baseVectorOfMinimapY); return(true); }
/// <summary> /// Tries to align the minimap vertically. /// </summary> /// <param name="minimapControlPixelSize">The size of the minimap control in pixels.</param> /// <param name="mapCellSize">The size of the map in cells.</param> /// <param name="minimapPosition">The position of the minimap on the minimap control in pixels.</param> /// <param name="transformation">The transformation between the map (A) and minimap (B) coordinate-systems.</param> /// <returns>True if the alignment was successfully; otherwise false.</returns> private static bool TryAlignMinimapVertically( RCIntVector minimapControlPixelSize, RCIntVector mapCellSize, out RCIntRectangle minimapPosition, out RCCoordTransformation transformation) { RCNumber vertAlignedMinimapWidth = (RCNumber)(minimapControlPixelSize.Y * mapCellSize.X) / (RCNumber)mapCellSize.Y; if (vertAlignedMinimapWidth > minimapControlPixelSize.X) { /// Cannot align vertically. minimapPosition = RCIntRectangle.Undefined; transformation = null; return(false); } /// Align vertically int minimapPixelWidth = vertAlignedMinimapWidth > (int)vertAlignedMinimapWidth ? (int)vertAlignedMinimapWidth + 1 : (int)vertAlignedMinimapWidth; minimapPosition = new RCIntRectangle((minimapControlPixelSize.X - minimapPixelWidth) / 2, 0, minimapPixelWidth, minimapControlPixelSize.Y); /// Create the coordinate transformation RCNumVector pixelSizeOnMap = new RCNumVector((RCNumber)mapCellSize.Y / (RCNumber)minimapControlPixelSize.Y, (RCNumber)mapCellSize.Y / (RCNumber)minimapControlPixelSize.Y); RCNumVector nullVectorOfMinimap = (pixelSizeOnMap / 2) - (new RCNumVector(1, 1) / 2); RCNumVector baseVectorOfMinimapX = new RCNumVector(pixelSizeOnMap.X, 0); RCNumVector baseVectorOfMinimapY = new RCNumVector(0, pixelSizeOnMap.Y); transformation = new RCCoordTransformation(nullVectorOfMinimap, baseVectorOfMinimapX, baseVectorOfMinimapY); return(true); }
/// <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> /// Sets the speed of the corresponding element type. /// </summary> public void SetSpeed(RCNumber speed) { if (this.metadata.IsFinalized) { throw new InvalidOperationException("Already finalized!"); } this.speed = new ConstValue <RCNumber>(speed); }
/// <summary> /// Computes the distance between two points on the map. /// </summary> /// <param name="fromCoords">The first point on the map.</param> /// <param name="toCoords">The second point on the map.</param> /// <returns>The computed distance between the given points.</returns> public static RCNumber ComputeDistance(RCNumVector fromCoords, RCNumVector toCoords) { RCNumber horz = (toCoords.X - fromCoords.X).Abs(); RCNumber vert = (toCoords.Y - fromCoords.Y).Abs(); RCNumber diff = (horz - vert).Abs(); return((horz < vert ? horz : vert) * RCNumber.ROOT_OF_TWO + diff); }
/// <see cref="Weapon.IsInRange"/> protected override bool IsInRange(RCNumber distance) { if (this.attachedWeapon.Read() == null) { throw new InvalidOperationException("Custom weapon is not attached to this stub!"); } return(this.attachedWeapon.Read().IsInRange(distance)); }
/// <summary> /// Converts the distance given in cells to a distance given in quadratic tiles. /// </summary> /// <param name="cellDistance">The distance given in cells.</param> /// <returns>The distance given in quadratic tiles.</returns> public static RCNumber CellToQuadDistance(RCNumber cellDistance) { if (cellDistance < 0) { throw new ArgumentOutOfRangeException("cellDistance", "Distance cannot be negative!"); } return(cellDistance / MapStructure.NAVCELL_PER_QUAD); }
/// <see cref="Weapon.IsInRange"/> protected override bool IsInRange(RCNumber distance) { if (this.weaponData.RangeMax.Read() == 0) { return(distance <= Weapon.NEARBY_DISTANCE); } RCNumber quadDistance = MapUtils.CellToQuadDistance(distance); return(quadDistance >= this.weaponData.RangeMin.Read() && quadDistance <= this.weaponData.RangeMax.Read()); }
/// <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> /// Internal method for calculating the ground level for this quadratic tile. /// </summary> /// <returns>The calculated ground level.</returns> private int CalculateGroundLevel() { RCNumber groundLevelSum = 0; for (int row = 0; row < MapStructure.NAVCELL_PER_QUAD; row++) { for (int col = 0; col < MapStructure.NAVCELL_PER_QUAD; col++) { groundLevelSum += this.cells[col, row].GroundLevel; } } return((groundLevelSum / (MapStructure.NAVCELL_PER_QUAD * MapStructure.NAVCELL_PER_QUAD)).Round()); }
/// <summary> /// Begins the landing of the owner of this motion control. /// </summary> /// <param name="landOnTheSpot">True to land without transition.</param> /// <exception cref="InvalidOperationException">If the status of this motion control is not MotionControlStatusEnum.Flying.</exception> /// <returns>True if the landing operation started successfully; otherwise false.</returns> public bool BeginLand(bool landOnTheSpot) { if (this.position.Read() == RCNumVector.Undefined) { throw new InvalidOperationException("The owner is currently detached from the map!"); } if (this.Status != MotionControlStatusEnum.InAir) { throw new InvalidOperationException("The owner is currently not in the air!"); } this.StopMoving(); /// Calculate position after land. RCNumVector positionAfterLand = RCNumVector.Undefined; if (landOnTheSpot) { /// Landing on the spot. positionAfterLand = this.position.Read(); } else { /// Normal landing. RCNumber bottomToMapEdgeDistance = this.owner.Read().Scenario.Map.CellSize.Y + (RCNumber)1 / (RCNumber)2 - this.owner.Read().Area.Bottom; if (bottomToMapEdgeDistance < 0) { return(false); } RCNumber transitionValue = bottomToMapEdgeDistance <= Constants.MAX_VTOL_TRANSITION ? bottomToMapEdgeDistance : Constants.MAX_VTOL_TRANSITION; positionAfterLand = this.position.Read() + new RCNumVector(0, transitionValue); } /// Try to reserve the position on the ground and remove the reservation from the air. if (!this.groundPathTracker.Read().OnAttaching(positionAfterLand)) { return(false); } this.airPathTracker.Read().OnDetached(); /// Initialize the VTOL operation for landing. this.SetStatus(MotionControlStatusEnum.Landing); this.vtolOperationProgress.Write(0); this.vtolInitialPosition.Write(this.position.Read()); this.vtolFinalPosition.Write(positionAfterLand); this.currentPathTracker.Write(null); return(true); }
/// <summary> /// Perform a repair step on the owner entity. /// </summary> /// <returns>True if the repair step performed successfully; otherwise false.</returns> public bool Repair() { if (this.owner.Read().ElementType.BuildTime == null) { throw new InvalidOperationException("Unable to repair entities if build time is not defined in the metadata!"); } if (this.hp.Read() == -1) { throw new InvalidOperationException("Unable to repair non-attackable entities!"); } if (this.IsUnderConstruction) { throw new InvalidOperationException("Unable to repair entities under construction!"); } /// If we reached the maximum HP -> repair step failed to perform. if (this.hp.Read() == this.owner.Read().ElementType.MaxHP.Read()) { return(false); } /// Calculate the HP to add and the resources to take in this repair step. RCNumber hpToAdd = (RCNumber)this.owner.Read().ElementType.MaxHP.Read() / (RCNumber)this.owner.Read().ElementType.BuildTime.Read(); RCNumber totalOriginalMineralCost = this.owner.Read().ElementType.MineralCost != null ? this.owner.Read().ElementType.MineralCost.Read() : 0; RCNumber mineralsToTake = totalOriginalMineralCost * REPAIR_COST_RATE / this.owner.Read().ElementType.BuildTime.Read(); RCNumber totalOriginalGasCost = this.owner.Read().ElementType.GasCost != null ? this.owner.Read().ElementType.GasCost.Read() : 0; RCNumber gasToTake = totalOriginalGasCost * REPAIR_COST_RATE / this.owner.Read().ElementType.BuildTime.Read(); /// Try to take the necessary resources from the player of the owner entity (if it has a player). if (this.owner.Read().Owner != null && !this.owner.Read().Owner.TakeResources(mineralsToTake, gasToTake)) { /// Not enough resources -> repair step failed to perform. return(false); } /// Necessary resources taken successfully (or the entity is neutral) -> modify the HP value and return with success. RCNumber newHP = this.hp.Read() + hpToAdd; if (newHP > this.owner.Read().ElementType.MaxHP.Read()) { newHP = this.owner.Read().ElementType.MaxHP.Read(); } this.hp.Write(newHP); return(true); }
/// <summary> /// Gives the given amount of minerals and vespene gas to this player. /// </summary> /// <param name="minerals">The amount of minerals to be given.</param> /// <param name="vespeneGas">The amount of vespene gas to be given.</param> public void GiveResources(RCNumber minerals, RCNumber vespeneGas) { if (minerals < 0) { throw new ArgumentOutOfRangeException("minerals", "The amount of minerals to be given cannot be negative!"); } if (vespeneGas < 0) { throw new ArgumentOutOfRangeException("vespeneGas", "The amount of vespene gas to be given cannot be negative!"); } this.minerals.Write(this.minerals.Read() + minerals); this.vespeneGas.Write(this.vespeneGas.Read() + vespeneGas); }
/// <see cref="EntityBehavior.UpdateMapObject"/> public override void UpdateMapObject(Entity entity) { if (entity.Biometrics.IsUnderConstruction) { /// Entity is under construction -> play the animation that belongs to the current construction progress. RCNumber progressPerIndex = (RCNumber)entity.ElementType.BuildTime.Read() / (RCNumber)(this.constructionAnimations.Length); this.PlayConstructionAnimation(entity, (int)(entity.Biometrics.ConstructionProgress / progressPerIndex)); } else { /// Entity is not under construction -> stop every construction animations. this.StopStartAnimations(entity, this.constructionAnimationsSet, new RCSet <string>()); } }
/// <summary> /// Begins or continues the construction progress of the owner entity. /// </summary> public void Construct() { if (this.owner.Read().ElementType.BuildTime == null) { throw new InvalidOperationException("Unable to construct entities if build time is not defined in the metadata!"); } if (this.hp.Read() == -1) { throw new InvalidOperationException("Unable to construct non-attackable entities!"); } /// Do nothing if the HP reached 0. if (this.hp.Read() == 0) { return; } if (this.constructionProgress.Read() == -1) { /// Begin the construction starting from 10% of MaxHP. this.constructionProgress.Write(0); this.hp.Write((RCNumber)this.owner.Read().ElementType.MaxHP.Read() / (RCNumber)10); if (this.energy.Read() != -1) { /// Starting from 50 (or MaxEnergy) at the beginning of the construction. this.energy.Write(Math.Min(this.owner.Read().ElementType.MaxEnergy.Read(), INITIAL_ENERGY)); } } else { /// Do nothing if the construction has already completed. if (!this.IsUnderConstruction) { return; } /// Continue construction and increment HP accordingly. this.constructionProgress.Write(this.constructionProgress.Read() + 1); RCNumber hpIncrement = ((RCNumber)this.owner.Read().ElementType.MaxHP.Read() * (RCNumber)9) / ((RCNumber)this.owner.Read().ElementType.BuildTime.Read() * (RCNumber)10); this.hp.Write(this.hp.Read() + hpIncrement); if (this.hp.Read() > this.owner.Read().ElementType.MaxHP.Read()) { this.hp.Write(this.owner.Read().ElementType.MaxHP.Read()); } } }
/// <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); } }
/// <see cref="ISelectionIndicatorView.GetVisibleSelIndicators"/> public List <SelIndicatorRenderInfo> GetVisibleSelIndicators() { RCSet <int> currentSelection = this.selectionManager.CurrentSelection; if (currentSelection.Count == 0) { return(new List <SelIndicatorRenderInfo>()); } /// Display the selection indicators of the currently visible entities inside the currently visible window of quadratic tiles. List <SelIndicatorRenderInfo> retList = new List <SelIndicatorRenderInfo>(); foreach (MapObject mapObject in this.fogOfWarBC.GetAllMapObjectsToUpdate()) { Entity entity = mapObject.Owner as Entity; if (entity != null && currentSelection.Contains(entity.ID.Read())) { SelIndicatorTypeEnum indicatorType = entity.Owner != null ? (entity.Owner.PlayerIndex == (int)this.selectionManager.LocalPlayer ? SelIndicatorTypeEnum.Friendly : SelIndicatorTypeEnum.Enemy) : SelIndicatorTypeEnum.Neutral; RCNumber hpNorm = entity.Biometrics.HP != -1 ? entity.Biometrics.HP / entity.ElementType.MaxHP.Read() : -1; RCNumber energyNorm = -1; if (entity.Owner != null && entity.Owner.PlayerIndex == (int)this.selectionManager.LocalPlayer) { energyNorm = entity.Biometrics.Energy != -1 ? entity.Biometrics.Energy / entity.ElementType.MaxEnergy.Read() : -1; } retList.Add(new SelIndicatorRenderInfo() { ObjectID = entity.ID.Read(), SelIndicatorType = indicatorType, IndicatorRect = this.MapWindowBC.AttachedWindow.MapToWindowRect(entity.Area), HpNormalized = hpNorm, EnergyNormalized = energyNorm, ShieldNormalized = -1, // TODO: must be based on real data after Protoss will have been implemented! }); } } return(retList); }
/// <summary> /// Loads an RCNumVector defined in the given string. /// </summary> /// <param name="fromStr">The string to load from.</param> /// <returns>The loaded RCNumVector.</returns> public static RCNumVector LoadNumVector(string fromStr) { if (fromStr == null) { throw new ArgumentNullException("fromStr"); } string[] componentStrings = fromStr.Split(';'); if (componentStrings.Length != 2) { throw new ConfigurationException("Vector format error!"); } RCNumber vectorX = LoadNum(componentStrings[0]); RCNumber vectorY = LoadNum(componentStrings[1]); return(new RCNumVector(vectorX, vectorY)); }
/// <summary> /// Loads an RCNumRectangle defined in the given string in format: "X;Y;Width;Height". /// </summary> /// <param name="fromStr">The string to load from.</param> /// <returns>The loaded RCNumRectangle.</returns> public static RCNumRectangle LoadNumRectangle(string fromStr) { if (fromStr == null) { throw new ArgumentNullException("fromStr"); } string[] componentStrings = fromStr.Split(';'); if (componentStrings.Length != 4) { throw new ConfigurationException("Rectangle format error!"); } RCNumber rectX = LoadNum(componentStrings[0]); RCNumber rectY = LoadNum(componentStrings[1]); RCNumber rectWidth = LoadNum(componentStrings[2]); RCNumber rectHeight = LoadNum(componentStrings[3]); return(new RCNumRectangle(rectX, rectY, rectWidth, rectHeight)); }
/// <summary> /// Takes the given amount of minerals and vespene gas from this player. /// </summary> /// <param name="minerals">The amount of minerals to be taken.</param> /// <param name="vespeneGas">The amount of vespene gas to be taken.</param> /// <returns>True if the given amount of resources has been taken successfully.</returns> public bool TakeResources(RCNumber minerals, RCNumber vespeneGas) { if (minerals < 0) { throw new ArgumentOutOfRangeException("minerals", "The amount of minerals to be taken cannot be negative!"); } if (vespeneGas < 0) { throw new ArgumentOutOfRangeException("vespeneGas", "The amount of vespene gas to be taken cannot be negative!"); } if (this.minerals.Read() < minerals || this.vespeneGas.Read() < vespeneGas) { return(false); } this.minerals.Write(this.minerals.Read() - minerals); this.vespeneGas.Write(this.vespeneGas.Read() - vespeneGas); return(true); }
/// <summary> /// Calculates the map direction out of the given heading vector. /// </summary> /// <param name="headingVector">The heading vector.</param> private void CalculateMapDirection(RCNumVector headingVector) { // New heading -> recalculate the map direction. if (headingVector.X == 0 && headingVector.Y == 0) { this.cachedMapDirection = MapDirection.Undefined; } // In case of 0 heading vector -> map direction is undefined. else if (headingVector.X == 0) { this.cachedMapDirection = headingVector.Y > 0 ? MapDirection.South : MapDirection.North; } // In case of vertical heading vector -> map direction is South or North based on the Y-coordinate. else { // In any other cases -> map direction is calculated from the tangent of the heading vector. RCNumber tangentOfVelocity = headingVector.Y / headingVector.X; if (SE_NW_TANGENT_LOWERBOUND < tangentOfVelocity && tangentOfVelocity <= SE_NW_TANGENT_UPPERBOUND) { // SouthEast or NorthWest this.cachedMapDirection = headingVector.X > 0 && headingVector.Y > 0 ? MapDirection.SouthEast : MapDirection.NorthWest; } else if (SW_NE_TANGENT_LOWERBOUND < tangentOfVelocity && tangentOfVelocity <= SW_NE_TANGENT_UPPERBOUND) { // SouthWest or NorthEast this.cachedMapDirection = headingVector.X < 0 && headingVector.Y > 0 ? MapDirection.SouthWest : MapDirection.NorthEast; } else if (W_E_TANGENT_LOWERBOUND < tangentOfVelocity && tangentOfVelocity <= W_E_TANGENT_UPPERBOUND) { // West or East this.cachedMapDirection = headingVector.X < 0 ? MapDirection.West : MapDirection.East; } else { // North or South this.cachedMapDirection = headingVector.Y < 0 ? MapDirection.North : MapDirection.South; } } }
/// <see cref="UIObject.Render_i"/> protected override void Render_i(IUIRenderContext renderContext) { /// Render the progress bar sprite. renderContext.RenderSprite(this.progressBarSprite, PROGRESSBAR_SPRITE_POS); /// Render the progress of the current production job. RCNumber progressNorm = this.productionDetailsView.ProductionLineProgressNormalized; if (progressNorm != -1) { int lineWidth = (int)(PROGRESSBAR_INNER_RECT.Width * progressNorm); if (lineWidth > 0) { renderContext.RenderRectangle(this.progressBarBrush, new RCIntRectangle(PROGRESSBAR_INNER_RECT.Left, PROGRESSBAR_INNER_RECT.Top, lineWidth, PROGRESSBAR_INNER_RECT.Height)); } } }
/// <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> /// Makes an absolute damage on the owner of this biometrics. /// </summary> /// <param name="damageValue">The amount of the damage.</param> public void Damage(RCNumber damageValue) { if (this.owner.Read().ElementType.Size == null) { throw new InvalidOperationException("Unable to make damage on non-attackable entities!"); } if (this.owner.Read().ElementType.Armor == null) { throw new InvalidOperationException("Unable to make damage on non-attackable entities!"); } if (this.hp.Read() == -1) { throw new InvalidOperationException("Unable to make damage on non-attackable entities!"); } RCNumber newHP = this.hp.Read() - damageValue; if (newHP < 0) { newHP = 0; } this.hp.Write(newHP); }
/// <summary> /// Calculates the bounding box of the given entities. /// </summary> /// <param name="entities">The entities.</param> /// <returns>The bounding box of the given entities.</returns> private RCNumRectangle CalculateBoundingBox(RCSet <Entity> entities) { RCNumber top = -1, left = -1, bottom = -1, right = -1; foreach (Entity entity in entities) { RCNumVector entityPosition = entity.MotionControl.PositionVector.Read(); if (top == -1 || entityPosition.Y < top) { top = entityPosition.Y; } if (left == -1 || entityPosition.X < left) { left = entityPosition.X; } if (bottom == -1 || entityPosition.Y > bottom) { bottom = entityPosition.Y; } if (right == -1 || entityPosition.X > right) { right = entityPosition.X; } } if (right - left == 0) { left -= (RCNumber)1 / (RCNumber)2; right += (RCNumber)1 / (RCNumber)2; } if (bottom - top == 0) { top -= (RCNumber)1 / (RCNumber)2; bottom += (RCNumber)1 / (RCNumber)2; } return(new RCNumRectangle(left, top, right - left, bottom - top)); }
/// <summary> /// Begins the takeoff of the owner of this motion control. /// </summary> /// <exception cref="InvalidOperationException">If the status of this motion control is not MotionControlStatusEnum.Fixed.</exception> /// <returns>True if the takeoff operation started successfully; otherwise false.</returns> public bool BeginTakeOff() { if (this.position.Read() == RCNumVector.Undefined) { throw new InvalidOperationException("The owner is currently detached from the map!"); } if (this.Status != MotionControlStatusEnum.Fixed) { throw new InvalidOperationException("The owner is currently not fixed!"); } /// Calculate position after take-off. RCNumber topToMapEdgeDistance = this.owner.Read().Area.Top + (RCNumber)1 / (RCNumber)2; if (topToMapEdgeDistance < 0) { return(false); } RCNumber transitionValue = topToMapEdgeDistance <= Constants.MAX_VTOL_TRANSITION ? topToMapEdgeDistance : Constants.MAX_VTOL_TRANSITION; RCNumVector positionAfterTakeOff = this.owner.Read().MotionControl.PositionVector.Read() - new RCNumVector(0, transitionValue); /// Try to reserve the position in the air and remove the reservation from the ground. if (!this.airPathTracker.Read().OnAttaching(positionAfterTakeOff)) { return(false); } this.groundPathTracker.Read().OnDetached(); /// Initialize the VTOL operation for take-off. this.Unfix(); this.SetStatus(MotionControlStatusEnum.TakingOff); this.vtolOperationProgress.Write(0); this.vtolInitialPosition.Write(this.position.Read()); this.vtolFinalPosition.Write(positionAfterTakeOff); this.currentPathTracker.Write(null); return(true); }