private static bool SharedHasEnergyCharge(ITempList <IItem> tempItemsList, uint energyRequired) { var charge = SharedCalculateTotalEnergyCharge(tempItemsList, stopIfEnergyExceeds: energyRequired); return(charge >= energyRequired); }
private void SelectRandomEntries(ITempList <Entry> result) { var countToSelect = this.Outputs + RandomHelper.Next(0, this.OutputsRandom + 1); if (countToSelect >= this.frozenEntries.Count) { // simply return all the entries foreach (var entry in this.entries) { result.Add(entry.Value); } return; } if (countToSelect == 1) { // simply return a single random selected entry result.Add(this.frozenEntries.GetSingleRandomElement()); return; } // need to select multiple random entries // ReSharper disable once SuspiciousTypeConversion.Global this.frozenEntries.SelectRandomElements((List <Entry>)result, countToSelect); }
public virtual void SharedGetVehiclesOnPlatform( IStaticWorldObject vehicleAssemblyBay, ITempList <IDynamicWorldObject> result) { var noObstaclesBounds = this.BoundsNoObstaclesTest; noObstaclesBounds = new BoundsDouble( offset: noObstaclesBounds.Offset + vehicleAssemblyBay.PhysicsBody.Position, size: noObstaclesBounds.Size); // test with different collision zones (required to handle hoverboards which don't have physical colliders) var defaultCollisionGroup = CollisionGroups.Default; CollectVehicles(defaultCollisionGroup); CollectVehicles(CollisionGroups.HitboxMelee); void CollectVehicles(CollisionGroup collisionGroup) { foreach (var testResult in vehicleAssemblyBay.PhysicsBody.PhysicsSpace.TestRectangle( position: noObstaclesBounds.Offset, size: noObstaclesBounds.Size, collisionGroup: collisionGroup).EnumerateAndDispose()) { if (testResult.PhysicsBody.AssociatedWorldObject is IDynamicWorldObject dynamicWorldObject && dynamicWorldObject.ProtoGameObject is IProtoVehicle) { result.AddIfNotContains(dynamicWorldObject); } } } }
private static void ServerAddEnergyInternal(double energyAmountToAdd, ITempList <IItem> powerBanks) { foreach (var item in powerBanks) { if (item.IsDestroyed) { continue; } var privateState = SharedGetPrivateState(item); var protoItem = (IProtoItemPowerBank)item.ProtoItem; var capacity = protoItem.EnergyCapacity; var charge = privateState.EnergyCharge; if (charge >= capacity) { // cannot add charge there continue; } var newCharge = charge + energyAmountToAdd; if (newCharge <= capacity) { // this battery can take the whole remaining energyAmountToAdd privateState.EnergyCharge = newCharge; return; } // add as much energy to this item as possible and go to the next item privateState.EnergyCharge = capacity; var energyAdded = capacity - charge; energyAmountToAdd -= energyAdded; } }
public static void SharedVisualizeTestResults( ITempList <TestResult> physicsTestResults, CollisionGroup collisionGroup) { if (IsClient) { using var testResults = Api.Shared.GetTempList <Vector2D>(); AddTestResults(physicsTestResults.AsList(), testResults.AsList()); ClientComponentPhysicsSpaceVisualizer.VisualizeTestResults(testResults.AsList(), collisionGroup, isClient: true); } else // if server { Api.Assert(Api.IsEditor, "This is Editor-only server code"); var allPlayers = Server.Characters.EnumerateAllPlayerCharacters(onlyOnline: true, exceptSpectators: false); var testResults = new List <Vector2D>(); AddTestResults(physicsTestResults.AsList(), testResults); var collisionGroupId = CollisionGroups.GetCollisionGroupId(collisionGroup); instance.CallClient( allPlayers, // ReSharper disable once AccessToDisposedClosure _ => _.ClientRemote_ProcessServerTestResults(testResults, collisionGroupId)); } }
private static void SharedFindPowerBanks(IItemsContainer container, ITempList <IItem> tempList) { foreach (var item in container.Items) { if (item.ProtoItem is IProtoItemPowerBank) { tempList.Add(item); } } }
private static async Task <ITextureResource> GenerateChunkProceduralTexture( ITempList <Tile> tiles, Vector2Ushort chunkStartPosition, ProceduralTextureRequest request) { var renderingService = Api.Client.Rendering; var renderingTag = request.TextureName; var textureSize = new Vector2Ushort(WorldChunkMapTextureSize, WorldChunkMapTextureSize); // create camera and render texture var renderTexture = renderingService.CreateRenderTexture(renderingTag, textureSize.X, textureSize.Y); var cameraObject = Api.Client.Scene.CreateSceneObject(renderingTag); var camera = renderingService.CreateCamera(cameraObject, renderingTag, drawOrder: -100); camera.RenderTarget = renderTexture; camera.ClearColor = Colors.Magenta; // to make potential issues visible clear with magenta color camera.SetOrthographicProjection(textureSize.X, textureSize.Y); // create tile renderers foreach (var tile in tiles) { var drawPosition = tile.Position.ToVector2D() - chunkStartPosition.ToVector2D(); drawPosition = ( drawPosition.X * WorldTileTextureSize, // Y is reversed (drawPosition.Y - ScriptingConstants.WorldChunkSize + 1) * WorldTileTextureSize); renderingService.CreateSpriteRenderer( cameraObject, tile.ProtoTile.GetWorldMapTexture(tile), positionOffset: drawPosition, // draw down spritePivotPoint: (0, 1), renderingTag: renderingTag); } tiles.Dispose(); await camera.DrawAsync(); cameraObject.Destroy(); request.ThrowIfCancelled(); var generatedTexture = await renderTexture.SaveToTexture(isTransparent : false); renderTexture.Dispose(); request.ThrowIfCancelled(); return(generatedTexture); }
private static uint SharedCalculateTotalEnergyCapacity(ITempList <IItem> tempItemsList) { uint result = 0; foreach (var item in tempItemsList) { var protoItem = (IProtoItemPowerBank)item.ProtoItem; result += protoItem.EnergyCapacity; } return(result); }
public FilePathsList(string sourceFolderPath, ITempList <string> filesInFolder) { this.SourceFolderPath = sourceFolderPath; this.tempList = filesInFolder; foreach (var path in this.FilesInFolder) { if (!path.EndsWith(".png")) { throw new Exception( "All loadable attachments must be a PNG-files: wrong file - " + path); } } }
public static void SharedGetAreasInBounds(RectangleInt bounds, ITempList <ILogicObject> result) { if (result.Count > 0) { result.Clear(); } // TODO: the lookup is really slow on the populated servers. Consider Grid or QuadTree optimization to locate areas quickly. foreach (var area in sharedLandClaimAreas) { var areaBounds = SharedGetLandClaimAreaBounds(area); if (areaBounds.IntersectsLoose(bounds)) { result.Add(area); } } }
private void SelectRandomEntries(ITempList <Entry> result, DropItemContext dropItemContext) { var countToSelect = this.Outputs + RandomHelper.Next(0, this.OutputsRandom + 1); if (countToSelect >= this.frozenEntries.Count) { // simply return all the entries foreach (var entry in this.entries) { result.Add(entry.Value); } return; } if (countToSelect == 1) { // simply return a single random selected entry that is satisfying the condition Entry entry = default; for (var attempt = 0; attempt < 100; attempt++) { entry = this.frozenEntries.GetSingleRandomElement(); if (entry.Condition is null) { break; } try { if (entry.Condition(dropItemContext)) { break; } // condition not match, select another entry continue; } catch (Exception ex) { Api.Logger.Exception(ex, "Exception during checking condition for droplist item " + entry); break; } } if (!entry.Equals(default))
private static uint SharedCalculateTotalEnergyCharge( ITempList <IItem> tempItemsList, uint stopIfEnergyExceeds = uint.MaxValue) { ulong result = 0; foreach (var item in tempItemsList.AsList()) { var privateState = SharedGetPrivateState(item); var charge = privateState.DurabilityCurrent; result += charge; if (result >= stopIfEnergyExceeds) { break; } } return((uint)Math.Min(uint.MaxValue, result)); }
protected virtual void ClientFillSlotAttachmentSources(ITempList <string> folders) { var baseProtoItem = this.BaseProtoItem; if (baseProtoItem is not null) { folders.Add( $"Characters/Equipment/{baseProtoItem.ShortId}/{this.ShortId.Substring(baseProtoItem.ShortId.Length)}"); return; } var path = $"Characters/Equipment/{this.ShortId}/Default"; if (Api.Shared.IsFolderExists(ContentPaths.Textures + path)) { folders.Add(path); return; } folders.Add($"Characters/Equipment/{this.ShortId}"); }
private static double SharedCalculateTotalEnergyCharge( ITempList <IItem> tempItemsList, double stopIfEnergyExceeds = double.NaN) { var result = 0.0; var hasStopCondition = !double.IsNaN(stopIfEnergyExceeds); foreach (var item in tempItemsList) { var privateState = SharedGetPrivateState(item); var charge = privateState.EnergyCharge; result += charge; if (hasStopCondition && result >= stopIfEnergyExceeds) { return(result); } } return(result); }
protected virtual void ClientFillSlotAttachmentSources(ITempList <string> folders) { folders.Add("Characters/Equipment/" + this.ShortId); }
public void Dispose() { this.Overlay?.Dispose(); this.Overlay = null; }
public WallChunkWithOverlays(WallPattern primary, ITempList <WallPattern> overlay) { this.Primary = primary; this.Overlay = overlay; }
protected override void ClientFillSlotAttachmentSources(ITempList <string> folders) { base.ClientFillSlotAttachmentSources(folders); // add generic pants folders.Add("Characters/Equipment/GenericPants"); }
private static void SharedGatherOccupiedAndNeighborTiles(IStaticWorldObject structure, ITempList <Tile> tempList) { // gather the occupied tiles and theirs direct neighbors foreach (var tile in structure.OccupiedTiles) { if (!tile.IsValidTile) { continue; } tempList.AddIfNotContains(tile); foreach (var neighborTile in tile.EightNeighborTiles) { if (neighborTile.IsValidTile) { tempList.AddIfNotContains(neighborTile); } } } }
private static void SharedShotWeaponHitscan( ICharacter character, IProtoItemWeapon protoWeapon, Vector2D fromPosition, WeaponFinalCache weaponCache, Vector2D?customTargetPosition, IProtoCharacterCore characterProtoCharacter, double fireSpreadAngleOffsetDeg, CollisionGroup collisionGroup, bool isMeleeWeapon, IDynamicWorldObject characterCurrentVehicle, ProtoSkillWeapons protoWeaponSkill, PlayerCharacterSkills playerCharacterSkills, ITempList <IWorldObject> allHitObjects) { Vector2D toPosition; var rangeMax = weaponCache.RangeMax; if (customTargetPosition.HasValue) { var direction = customTargetPosition.Value - fromPosition; // ensure the max range is not exceeded direction = direction.ClampMagnitude(rangeMax); toPosition = fromPosition + direction; } else { toPosition = fromPosition + new Vector2D(rangeMax, 0) .RotateRad(characterProtoCharacter.SharedGetRotationAngleRad(character) + fireSpreadAngleOffsetDeg * Math.PI / 180.0); } using var lineTestResults = character.PhysicsBody.PhysicsSpace.TestLine( fromPosition: fromPosition, toPosition: toPosition, collisionGroup: collisionGroup); var damageMultiplier = 1d; var hitObjects = new List <WeaponHitData>(isMeleeWeapon ? 1 : lineTestResults.Count); var characterTileHeight = character.Tile.Height; if (IsClient || Api.IsEditor) { SharedEditorPhysicsDebugger.SharedVisualizeTestResults(lineTestResults, collisionGroup); } var isDamageRayStopped = false; foreach (var testResult in lineTestResults.AsList()) { var testResultPhysicsBody = testResult.PhysicsBody; var attackedProtoTile = testResultPhysicsBody.AssociatedProtoTile; if (attackedProtoTile != null) { if (attackedProtoTile.Kind != TileKind.Solid) { // non-solid obstacle - skip continue; } var attackedTile = IsServer ? Server.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position) : Client.World.GetTile((Vector2Ushort)testResultPhysicsBody.Position); if (attackedTile.Height < characterTileHeight) { // attacked tile is below - ignore it continue; } // tile on the way - blocking damage ray isDamageRayStopped = true; var hitData = new WeaponHitData(testResult.PhysicsBody.Position + SharedOffsetHitWorldPositionCloserToTileHitboxCenter( testResultPhysicsBody, testResult.Penetration, isRangedWeapon: !isMeleeWeapon)); hitObjects.Add(hitData); weaponCache.ProtoWeapon .SharedOnHit(weaponCache, null, 0, hitData, out _); break; } var damagedObject = testResultPhysicsBody.AssociatedWorldObject; if (ReferenceEquals(damagedObject, character) || ReferenceEquals(damagedObject, characterCurrentVehicle)) { // ignore collision with self continue; } if (!(damagedObject.ProtoGameObject is IDamageableProtoWorldObject damageableProto)) { // shoot through this object continue; } // don't allow damage is there is no direct line of sight on physical colliders layer between the two objects if (SharedHasTileObstacle(character.Position, characterTileHeight, damagedObject, targetPosition: testResult.PhysicsBody.Position + testResult.PhysicsBody.CenterOffset)) { continue; } using (CharacterDamageContext.Create(attackerCharacter: character, damagedObject as ICharacter, protoWeaponSkill)) { if (!damageableProto.SharedOnDamage( weaponCache, damagedObject, damageMultiplier, damagePostMultiplier: 1.0, out var obstacleBlockDamageCoef, out var damageApplied)) { // not hit continue; } var hitData = new WeaponHitData(damagedObject, testResult.Penetration.ToVector2F()); weaponCache.ProtoWeapon .SharedOnHit(weaponCache, damagedObject, damageApplied, hitData, out var isDamageStop); if (isDamageStop) { obstacleBlockDamageCoef = 1; } if (IsServer) { if (damageApplied > 0 && damagedObject is ICharacter damagedCharacter) { CharacterUnstuckSystem.ServerTryCancelUnstuckRequest(damagedCharacter); } if (damageApplied > 0) { // give experience for damage protoWeaponSkill?.ServerOnDamageApplied(playerCharacterSkills, damagedObject, damageApplied); } } if (obstacleBlockDamageCoef < 0 || obstacleBlockDamageCoef > 1) { Logger.Error( "Obstacle block damage coefficient should be >= 0 and <= 1 - wrong calculation by " + damageableProto); break; } hitObjects.Add(hitData); if (isMeleeWeapon) { // currently melee weapon could attack only one object on the ray isDamageRayStopped = true; break; } damageMultiplier *= 1.0 - obstacleBlockDamageCoef; if (damageMultiplier <= 0) { // target blocked the damage ray isDamageRayStopped = true; break; } } } var shotEndPosition = GetShotEndPosition(isDamageRayStopped, hitObjects, toPosition, isRangedWeapon: !isMeleeWeapon); if (hitObjects.Count == 0) { protoWeapon.SharedOnMiss(weaponCache, shotEndPosition); } SharedCallOnWeaponHitOrTrace(character, protoWeapon, weaponCache.ProtoAmmo, shotEndPosition, hitObjects, endsWithHit: isDamageRayStopped); foreach (var entry in hitObjects) { if (!entry.IsCliffsHit && !allHitObjects.Contains(entry.WorldObject)) { allHitObjects.Add(entry.WorldObject); } } }