public override void ClientOnWeaponHitOrTrace( ICharacter firingCharacter, Vector2D worldPositionSource, IProtoItemWeapon protoWeapon, IProtoItemAmmo protoAmmo, IProtoCharacter protoCharacter, in Vector2Ushort fallbackCharacterPosition,
public static void ServerTrySpawnMobsCustom( IProtoCharacter protoMob, int countToSpawn, RectangleInt excludeBounds, int maxSpawnDistanceFromExcludeBounds, double noObstaclesCheckRadius, int maxAttempts) { using var tempList = Api.Shared.GetTempList <ICharacter>(); ServerTrySpawnMobsCustom(protoMob, spawnedCollection: tempList.AsList(), countToSpawn, excludeBounds, maxSpawnDistanceFromExcludeBounds, noObstaclesCheckRadius, maxAttempts); }
private void ClientRemote_OnWeaponShot( ICharacter whoFires, IProtoItemWeapon protoWeapon, IProtoCharacter fallbackProtoCharacter, Vector2D fallbackPosition) { if (whoFires != null && !whoFires.IsInitialized) { whoFires = null; } WeaponSystemClientDisplay.OnWeaponShot(whoFires, protoWeapon, fallbackProtoCharacter, fallbackPosition); }
private void ClientRemote_OnWeaponHitOrTrace( ICharacter firingCharacter, IProtoItemWeapon protoWeapon, IProtoItemAmmo protoAmmo, IProtoCharacter protoCharacter, Vector2Ushort fallbackCharacterPosition, WeaponHitData[] hitObjects, Vector2D endPosition, bool endsWithHit) { WeaponSystemClientDisplay.ClientOnWeaponHitOrTrace(firingCharacter, protoWeapon, protoAmmo, protoCharacter, fallbackCharacterPosition, hitObjects, endPosition, endsWithHit); }
public static void ClientOnWeaponHitOrTrace( ICharacter firingCharacter, IProtoItemWeapon protoWeapon, IProtoItemAmmo protoAmmo, IProtoCharacter protoCharacter, Vector2Ushort fallbackCharacterPosition, IReadOnlyList <WeaponHitData> hitObjects, Vector2D endPosition, bool endsWithHit) { if (firingCharacter is not null && !firingCharacter.IsInitialized) { firingCharacter = null; } var weaponTracePreset = protoWeapon.FireTracePreset ?? protoAmmo?.FireTracePreset; var worldPositionSource = SharedCalculateWeaponShotWorldPositon( firingCharacter, protoWeapon, protoCharacter, fallbackCharacterPosition.ToVector2D(), hasTrace: weaponTracePreset?.HasTrace ?? false); protoWeapon.ClientOnWeaponHitOrTrace(firingCharacter, worldPositionSource, protoWeapon, protoAmmo, protoCharacter, fallbackCharacterPosition, hitObjects, endPosition, endsWithHit); if (weaponTracePreset?.HasTrace ?? false) { ComponentWeaponTrace.Create(weaponTracePreset, worldPositionSource, endPosition, hasHit: endsWithHit, lastHitData: hitObjects.LastOrDefault(t => t.WorldObject is not null)); } foreach (var hitData in hitObjects) { var hitWorldObject = hitData.WorldObject; if (hitWorldObject is not null && !hitWorldObject.IsInitialized) { hitWorldObject = null; } var protoWorldObject = hitData.FallbackProtoWorldObject; double delay; { var worldObjectPosition = CalculateWorldObjectPosition(hitWorldObject, hitData); delay = weaponTracePreset?.HasTrace ?? false ? SharedCalculateTimeToHit(weaponTracePreset, worldPositionSource : worldPositionSource, endPosition : worldObjectPosition + hitData.HitPoint.ToVector2D()) : 0; } ClientTimersSystem.AddAction( delay, () => { // re-calculate the world object position var worldObjectPosition = CalculateWorldObjectPosition(hitWorldObject, hitData); var fireScatterPreset = protoAmmo?.OverrideFireScatterPreset ?? protoWeapon.FireScatterPreset; var projectilesCount = fireScatterPreset.ProjectileAngleOffets.Length; var objectMaterial = hitData.FallbackObjectMaterial; if (hitWorldObject is ICharacter hitCharacter && hitCharacter.IsInitialized) { objectMaterial = ((IProtoCharacterCore)hitCharacter.ProtoCharacter) .SharedGetObjectMaterialForCharacter(hitCharacter); } protoWeapon.ClientPlayWeaponHitSound(hitWorldObject, protoWorldObject, fireScatterPreset, objectMaterial, worldObjectPosition); if (weaponTracePreset is not null) { ClientAddHitSparks(weaponTracePreset.HitSparksPreset, hitData, hitWorldObject, protoWorldObject, worldObjectPosition, projectilesCount, objectMaterial, randomizeHitPointOffset: !weaponTracePreset.HasTrace, randomRotation: !weaponTracePreset.HasTrace, drawOrder: weaponTracePreset.DrawHitSparksAsLight ? DrawOrder.Light : DrawOrder.Default); } });
public static void ServerTrySpawnMobsCustom( IProtoCharacter protoMob, ICollection <ICharacter> spawnedCollection, int countToSpawn, RectangleInt excludeBounds, int maxSpawnDistanceFromExcludeBounds, double noObstaclesCheckRadius, int maxAttempts) { if (countToSpawn <= 0) { return; } var spawnBounds = excludeBounds.Inflate(maxSpawnDistanceFromExcludeBounds, maxSpawnDistanceFromExcludeBounds); var physicsSpace = Api.Server.World.GetPhysicsSpace(); while (maxAttempts-- > 0) { var position = new Vector2D(spawnBounds.Left + RandomHelper.NextDouble() * spawnBounds.Width, spawnBounds.Bottom + RandomHelper.NextDouble() * spawnBounds.Height); if (IsTooClose(position)) { continue; } var character = ServerTrySpawnMob(position); if (character is null) { // cannot spawn there continue; } spawnedCollection.Add(character); countToSpawn--; if (countToSpawn == 0) { return; } } bool IsTooClose(in Vector2D position) => position.X >= excludeBounds.X && position.Y >= excludeBounds.Y && position.X < excludeBounds.X + excludeBounds.Width && position.Y < excludeBounds.Y + excludeBounds.Height; ICharacter ServerTrySpawnMob(Vector2D worldPosition) { foreach (var _ in physicsSpace.TestCircle(worldPosition, radius: noObstaclesCheckRadius, CollisionGroups.Default, sendDebugEvent: false).EnumerateAndDispose()) { // position is not valid for spawning return(null); } if (!ServerCharacterSpawnHelper.IsValidSpawnTile( Api.Server.World.GetTile(worldPosition.ToVector2Ushort()), checkNeighborTiles: true)) { return(null); } return(Api.Server.Characters.SpawnCharacter(protoMob, worldPosition)); } }
public static void ServerSpawnBossMinionsOnDeath( Vector2Ushort epicenterPosition, double bossDifficultyCoef, IProtoCharacter minionProto, int minionsDefaultCount, double minionsRadius) { var countToSpawnRemains = minionsDefaultCount; // apply difficulty coefficient countToSpawnRemains = (int)Math.Ceiling(countToSpawnRemains * bossDifficultyCoef); var attemptsRemains = 3000; while (countToSpawnRemains > 0) { attemptsRemains--; if (attemptsRemains <= 0) { // attempts exceeded return; } // calculate random distance from the explosion epicenter var distance = RandomHelper.Range(2, minionsRadius); // ensure we spawn more objects closer to the epicenter var spawnProbability = 1 - (distance / minionsRadius); spawnProbability = Math.Pow(spawnProbability, 1.25); if (!RandomHelper.RollWithProbability(spawnProbability)) { // random skip continue; } var angle = RandomHelper.NextDouble() * MathConstants.DoublePI; var spawnPosition = new Vector2Ushort( (ushort)(epicenterPosition.X + distance * Math.Cos(angle)), (ushort)(epicenterPosition.Y + distance * Math.Sin(angle))); if (ServerTrySpawnMinion(spawnPosition)) { // spawned successfully! countToSpawnRemains--; } } bool ServerTrySpawnMinion(Vector2Ushort spawnPosition) { var worldPosition = spawnPosition.ToVector2D(); if (!ServerCharacterSpawnHelper.IsPositionValidForCharacterSpawn(worldPosition, isPlayer: false)) { // position is not valid for spawning return(false); } var spawnedCharacter = ServerCharacters.SpawnCharacter(minionProto, worldPosition); return(spawnedCharacter is not null); } }
public static void OnWeaponShot( ICharacter character, IProtoItemWeapon protoWeapon, IProtoCharacter fallbackProtoCharacter, Vector2D fallbackPosition) { var position = character?.Position ?? fallbackPosition; position += (0, fallbackProtoCharacter.CharacterWorldWeaponOffsetRanged); ClientSoundCueManager.OnSoundEvent(position); const float volume = SoundConstants.VolumeWeapon; var pitch = RandomHelper.Range(0.95f, 1.05f); IComponentSoundEmitter emitter; var soundPresetWeapon = protoWeapon.SoundPresetWeapon; if (soundPresetWeapon.HasSound(WeaponSound.Shot)) { // play shot sound from weapon if (character != null) { soundPresetWeapon.PlaySound(WeaponSound.Shot, character, out emitter, volume: volume, pitch: pitch); } else { soundPresetWeapon.PlaySound(WeaponSound.Shot, protoWorldObject: fallbackProtoCharacter, worldPosition: position, out emitter, volume: volume, pitch: pitch); } } else { // play sounds from the skeleton instead ProtoCharacterSkeleton characterSkeleton = null; if (character != null) { var clientState = character.GetClientState <BaseCharacterClientState>(); if (clientState.HasWeaponAnimationAssigned) { characterSkeleton = clientState.CurrentProtoSkeleton; } } else { fallbackProtoCharacter.SharedGetSkeletonProto(character: null, out var characterSkeleton1, out _); characterSkeleton = (ProtoCharacterSkeleton)characterSkeleton1; } if (characterSkeleton == null) { emitter = null; } else { if (character != null) { if (!characterSkeleton.SoundPresetWeapon.PlaySound(WeaponSound.Shot, character, out emitter, volume)) { // no method returned true // fallback to the default weapon sound (if there is no, it will be logged into the audio log) soundPresetWeapon.PlaySound(WeaponSound.Shot, character, out emitter, volume: volume, pitch: pitch); } } else if (!characterSkeleton.SoundPresetWeapon.PlaySound(WeaponSound.Shot, protoWorldObject: fallbackProtoCharacter, worldPosition: position, out emitter, volume)) { // no method returned true // fallback to the default weapon sound (if there is no, it will be logged into the audio log) soundPresetWeapon.PlaySound(WeaponSound.Shot, protoWorldObject: fallbackProtoCharacter, worldPosition: position, out emitter, volume: volume, pitch: pitch); } } } if (emitter != null) { var distance = protoWeapon.SoundPresetWeaponDistance; emitter.CustomMinDistance = distance.min; emitter.CustomMaxDistance = distance.max; } if (protoWeapon is IProtoItemWeaponRanged rangedWeapon && character != null && ReferenceEquals(protoWeapon, GetCharacterCurrentWeaponProto(character))) { // add muzzle flash var clientState = character.GetClientState <BaseCharacterClientState>(); if (!clientState.HasWeaponAnimationAssigned) { return; } var skeletonRenderer = clientState.SkeletonRenderer; CreateMuzzleFlash(rangedWeapon, character, skeletonRenderer); var recoilAnimationName = rangedWeapon.CharacterAnimationAimingRecoilName; if (recoilAnimationName != null && rangedWeapon.CharacterAnimationAimingRecoilPower > 0 && rangedWeapon.CharacterAnimationAimingRecoilDuration > 0) { SetRecoilAnimation( character, rangedWeapon, skeletonRenderer); } } }