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); }
private static void SharedFindPowerBanks(IItemsContainer container, ITempList <IItem> tempList) { foreach (var item in container.Items) { if (item.ProtoItem is IProtoItemPowerBank) { tempList.Add(item); } } }
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}"); }
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))
protected virtual void ClientFillSlotAttachmentSources(ITempList <string> folders) { folders.Add("Characters/Equipment/" + this.ShortId); }
protected override void ClientFillSlotAttachmentSources(ITempList <string> folders) { base.ClientFillSlotAttachmentSources(folders); // add generic pants folders.Add("Characters/Equipment/GenericPants"); }
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); } } }