using KFCommonUtilityLib.Scripts.StaticManagers; using System.Collections.Generic; using UnityEngine; namespace KFCommonUtilityLib.Scripts.Utilities { /// /// This new script make following changes: /// - projectile ItemValue fields are reused for custom passive calculation: /// -- Meta => launcher ItemClass id /// -- SelectedAmmoIndex => launcher action index /// -- Activated => launcher data strain perc /// -- cosmetics and mods reference launcher ItemValue /// -- Quality and Durability is copied from launcher ItemValue /// - MinEventParams.itemActionData is set to correct launcher data. /// public class CustomProjectileMoveScript : ProjectileMoveScript { public override void checkCollision() { if (this.firingEntity == null || state != State.Active || gameManager == null) return; World world = gameManager?.World; if (world == null) { return; } Vector3 checkPos; if (bOnIdealPos) { checkPos = transform.position + Origin.position; } else { checkPos = idealPosition; } Vector3 dir = checkPos - previousPosition; float magnitude = dir.magnitude; if (magnitude < 0.04f) { return; } EntityAlive firingEntity = (EntityAlive)this.firingEntity; Ray ray = new Ray(previousPosition, dir.normalized); waterCollisionParticles.CheckCollision(ray.origin, ray.direction, magnitude, (firingEntity != null) ? firingEntity.entityId : (-1)); int prevLayer = 0; if (firingEntity != null && firingEntity.emodel != null) { prevLayer = firingEntity.GetModelLayer(); firingEntity.SetModelLayer(2); } int hitmask = ((hmOverride == 0) ? 80 : hmOverride); bool flag = Voxel.Raycast(world, ray, magnitude, -538750997, hitmask, 0); if (firingEntity != null && firingEntity.emodel != null) { firingEntity.SetModelLayer(prevLayer); } if (flag && (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag) || Voxel.voxelRayHitInfo.tag.StartsWith("E_"))) { if (firingEntity != null && !firingEntity.isEntityRemote) { firingEntity.MinEventContext.Other = ItemActionAttack.FindHitEntity(Voxel.voxelRayHitInfo) as EntityAlive; firingEntity.MinEventContext.ItemActionData = actionData; firingEntity.MinEventContext.ItemValue = itemValueLauncher; firingEntity.MinEventContext.Position = Voxel.voxelRayHitInfo.hit.pos; ItemActionAttack.AttackHitInfo attackHitInfo = new ItemActionAttack.AttackHitInfo { WeaponTypeTag = ItemActionAttack.RangedTag }; float strainPerc = itemValueProjectile.Activated / (float)byte.MaxValue; MultiActionProjectileRewrites.ProjectileHit(Voxel.voxelRayHitInfo, ProjectileOwnerID, EnumDamageTypes.Piercing, Mathf.Lerp(1f, MultiActionProjectileRewrites.GetProjectileDamageBlock(itemActionProjectile, itemValueProjectile, ItemActionAttack.GetBlockHit(world, Voxel.voxelRayHitInfo), firingEntity, actionData.indexInEntityOfAction), strainPerc), Mathf.Lerp(1f, MultiActionProjectileRewrites.GetProjectileDamageEntity(itemActionProjectile, itemValueProjectile, firingEntity, actionData.indexInEntityOfAction), strainPerc), 1f, 1f, MultiActionReversePatches.ProjectileGetValue(PassiveEffects.CriticalChance, itemValueProjectile, itemProjectile.CritChance.Value, firingEntity, null, itemProjectile.ItemTags, true, false), ItemAction.GetDismemberChance(actionData, Voxel.voxelRayHitInfo), itemProjectile.MadeOfMaterial.SurfaceCategory, itemActionProjectile.GetDamageMultiplier(), getBuffActions(), attackHitInfo, 1, itemActionProjectile.ActionExp, itemActionProjectile.ActionExpBonusMultiplier, null, null, ItemActionAttack.EnumAttackMode.RealNoHarvesting, null, -1, itemValueProjectile, itemValueLauncher); if (firingEntity.MinEventContext.Other == null) { firingEntity.FireEvent(MinEventTypes.onSelfPrimaryActionMissEntity, false); } firingEntity.FireEvent(MinEventTypes.onProjectileImpact, false); MinEventParams.CachedEventParam.Self = firingEntity; MinEventParams.CachedEventParam.Position = Voxel.voxelRayHitInfo.hit.pos; MinEventParams.CachedEventParam.ItemValue = itemValueProjectile; MinEventParams.CachedEventParam.ItemActionData = actionData; MinEventParams.CachedEventParam.Other = firingEntity.MinEventContext.Other; itemProjectile.FireEvent(MinEventTypes.onProjectileImpact, MinEventParams.CachedEventParam); if (itemActionProjectile.Explosion.ParticleIndex > 0) { Vector3 vector3 = Voxel.voxelRayHitInfo.hit.pos - dir.normalized * 0.1f; Vector3i vector3i = World.worldToBlockPos(vector3); if (!world.GetBlock(vector3i).isair) { BlockFace blockFace; vector3i = Voxel.OneVoxelStep(vector3i, vector3, -dir.normalized, out vector3, out blockFace); } gameManager.ExplosionServer(Voxel.voxelRayHitInfo.hit.clrIdx, vector3, vector3i, Quaternion.identity, itemActionProjectile.Explosion, ProjectileOwnerID, 0f, false, itemValueProjectile); SetState(State.Dead); return; } if (itemProjectile.IsSticky) { GameRandom gameRandom = world.GetGameRandom(); if (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag)) { if (gameRandom.RandomFloat < MultiActionReversePatches.ProjectileGetValue(PassiveEffects.ProjectileStickChance, itemValueProjectile, 0.5f, firingEntity, null, itemProjectile.ItemTags | FastTags.Parse(Voxel.voxelRayHitInfo.fmcHit.blockValue.Block.blockMaterial.SurfaceCategory), true, false)) { RestoreProjectileValue(); ProjectileID = ProjectileManager.AddProjectileItem(transform, -1, Voxel.voxelRayHitInfo.hit.pos, dir.normalized, itemValueProjectile.type); SetState(State.Sticky); } else { gameManager.SpawnParticleEffectServer(new ParticleEffect("impact_metal_on_wood", Voxel.voxelRayHitInfo.hit.pos, Utils.BlockFaceToRotation(Voxel.voxelRayHitInfo.fmcHit.blockFace), 1f, Color.white, string.Format("{0}hit{1}", Voxel.voxelRayHitInfo.fmcHit.blockValue.Block.blockMaterial.SurfaceCategory, itemProjectile.MadeOfMaterial.SurfaceCategory), null), firingEntity.entityId, false, false); SetState(State.Dead); } } else if (gameRandom.RandomFloat < MultiActionReversePatches.ProjectileGetValue(PassiveEffects.ProjectileStickChance, itemValueProjectile, 0.5f, firingEntity, null, itemProjectile.ItemTags, true, false)) { RestoreProjectileValue(); ProjectileID = ProjectileManager.AddProjectileItem(transform, -1, Voxel.voxelRayHitInfo.hit.pos, dir.normalized, itemValueProjectile.type); Utils.SetLayerRecursively(ProjectileManager.GetProjectile(ProjectileID).gameObject, 14, null); SetState(State.Sticky); } else { gameManager.SpawnParticleEffectServer(new ParticleEffect("impact_metal_on_wood", Voxel.voxelRayHitInfo.hit.pos, Utils.BlockFaceToRotation(Voxel.voxelRayHitInfo.fmcHit.blockFace), 1f, Color.white, "bullethitwood", null), firingEntity.entityId, false, false); SetState(State.Dead); } } else { SetState(State.Dead); } } else { SetState(State.Dead); } if (state == State.Active) { SetState(State.Dead); } } previousPosition = checkPos; } private void RestoreProjectileValue() { itemValueProjectile.Modifications = ItemValue.emptyItemValueArray; itemValueProjectile.CosmeticMods = ItemValue.emptyItemValueArray; itemValueProjectile.Quality = 0; itemValueProjectile.UseTimes = 0; itemValueProjectile.Meta = 0; itemValueProjectile.SelectedAmmoTypeIndex = 0; } } public static class MultiActionProjectileRewrites { public static void ProjectileHit(WorldRayHitInfo hitInfo, int _attackerEntityId, EnumDamageTypes _damageType, float _blockDamage, float _entityDamage, float _staminaDamageMultiplier, float _weaponCondition, float _criticalHitChanceOLD, float _dismemberChance, string _attackingDeviceMadeOf, DamageMultiplier _damageMultiplier, List _buffActions, ItemActionAttack.AttackHitInfo _attackDetails, int _flags = 1, int _actionExp = 0, float _actionExpBonus = 0f, ItemActionAttack rangeCheckedAction = null, Dictionary _toolBonuses = null, ItemActionAttack.EnumAttackMode _attackMode = ItemActionAttack.EnumAttackMode.RealNoHarvesting, Dictionary _hitSoundOverrides = null, int ownedEntityId = -1, ItemValue projectileValue = null, ItemValue launcherValue = null) { if (_attackDetails != null) { _attackDetails.hitPosition = Vector3i.zero; _attackDetails.bKilled = false; } if (hitInfo == null || hitInfo.tag == null) { return; } World world = GameManager.Instance.World; bool canHarvest = true; if (_attackMode == ItemActionAttack.EnumAttackMode.RealNoHarvestingOrEffects) { canHarvest = false; _attackMode = ItemActionAttack.EnumAttackMode.RealNoHarvesting; } if (_attackDetails != null) { _attackDetails.itemsToDrop = null; _attackDetails.bBlockHit = false; _attackDetails.entityHit = null; } string blockFaceParticle = null; string surfaceCategory = null; float lightValueAtBlockPos = 1f; Color blockFaceColor = Color.white; bool isProtectionApplied = false; EntityAlive attackerEntity = world.GetEntity(_attackerEntityId) as EntityAlive; bool isHoldingDamageItem = false; if (attackerEntity != null) { if (launcherValue == null) { launcherValue = attackerEntity.inventory.holdingItemItemValue; } isHoldingDamageItem = launcherValue.Equals(attackerEntity.inventory.holdingItemItemValue); } bool isHitTargetPlayer = true; //if hits block or terrain if (GameUtils.IsBlockOrTerrain(hitInfo.tag)) { if (ItemAction.ShowDebugDisplayHit) { DebugLines.Create(null, attackerEntity.RootTransform, Camera.main.transform.position + Origin.position, hitInfo.hit.pos, new Color(1f, 0.5f, 1f), new Color(1f, 0f, 1f), ItemAction.DebugDisplayHitSize * 2f, ItemAction.DebugDisplayHitSize, ItemAction.DebugDisplayHitTime); } ChunkCluster hittedChunk = world.ChunkClusters[hitInfo.hit.clrIdx]; if (hittedChunk == null) { return; } Vector3i hitBlockPos = hitInfo.hit.blockPos; BlockValue hitBlockValue = hittedChunk.GetBlock(hitBlockPos); if (hitBlockValue.isair && hitInfo.hit.blockValue.Block.IsDistantDecoration && hitInfo.hit.blockValue.damage >= hitInfo.hit.blockValue.Block.MaxDamage - 1) { hitBlockValue = hitInfo.hit.blockValue; world.SetBlockRPC(hitBlockPos, hitBlockValue); } Block hitBlock = hitBlockValue.Block; if (hitBlock == null) { return; } if (hitBlockValue.ischild) { hitBlockPos = hitBlock.multiBlockPos.GetParentPos(hitBlockPos, hitBlockValue); hitBlockValue = hittedChunk.GetBlock(hitBlockPos); hitBlock = hitBlockValue.Block; if (hitBlock == null) { return; } } if (hitBlockValue.isair) { return; } float landProtectionModifier = 0; if (!world.IsWithinTraderArea(hitInfo.hit.blockPos) && hitBlock.blockMaterial.id != "Mbedrock") { landProtectionModifier = world.GetLandProtectionHardnessModifier(hitInfo.hit.blockPos, attackerEntity, world.GetGameManager().GetPersistentLocalPlayer()); } if (landProtectionModifier != 1f) { if (attackerEntity && _attackMode != ItemActionAttack.EnumAttackMode.Simulate && attackerEntity is EntityPlayer && !launcherValue.ItemClass.ignoreKeystoneSound && !launcherValue.ToBlockValue().Block.IgnoreKeystoneOverlay) { attackerEntity.PlayOneShot("keystone_impact_overlay", false); } if (landProtectionModifier < 1f) { isProtectionApplied = true; } } if (hitBlockPos != _attackDetails.hitPosition || landProtectionModifier != _attackDetails.hardnessScale || hitBlockValue.type != _attackDetails.blockBeingDamaged.type || (isHoldingDamageItem && projectileValue.SelectedAmmoTypeIndex != _attackDetails.ammoIndex)) { float finalHardness = Mathf.Max(hitBlock.GetHardness(), 0.1f) * landProtectionModifier; float finalBlockDamage = _blockDamage * ((_damageMultiplier != null) ? _damageMultiplier.Get(hitBlock.blockMaterial.DamageCategory) : 1f); if (attackerEntity) { finalBlockDamage *= attackerEntity.GetBlockDamageScale(); } if (_toolBonuses != null && _toolBonuses.Count > 0) { finalBlockDamage *= calculateHarvestToolDamageBonus(_toolBonuses, hitBlock.itemsToDrop); _attackDetails.bHarvestTool = true; } _attackDetails.damagePerHit = isProtectionApplied ? 0f : (finalBlockDamage / finalHardness); _attackDetails.damage = 0f; _attackDetails.hardnessScale = landProtectionModifier; _attackDetails.hitPosition = hitBlockPos; _attackDetails.blockBeingDamaged = hitBlockValue; if (isHoldingDamageItem) { _attackDetails.ammoIndex = projectileValue.SelectedAmmoTypeIndex; } } _attackDetails.raycastHitPosition = hitInfo.hit.blockPos; Block fmcHitBlock = hitInfo.fmcHit.blockValue.Block; lightValueAtBlockPos = world.GetLightBrightness(hitInfo.fmcHit.blockPos); blockFaceColor = fmcHitBlock.GetColorForSide(hitInfo.fmcHit.blockValue, hitInfo.fmcHit.blockFace); blockFaceParticle = fmcHitBlock.GetParticleForSide(hitInfo.fmcHit.blockValue, hitInfo.fmcHit.blockFace); MaterialBlock materialForSide = fmcHitBlock.GetMaterialForSide(hitInfo.fmcHit.blockValue, hitInfo.fmcHit.blockFace); surfaceCategory = materialForSide.SurfaceCategory; float modifiedBlockDamage = _attackDetails.damagePerHit * _staminaDamageMultiplier; if (attackerEntity) { string blockFaceDamageCategory = materialForSide.DamageCategory ?? string.Empty; modifiedBlockDamage = (int)MultiActionReversePatches.ProjectileGetValue(PassiveEffects.DamageModifier, projectileValue, modifiedBlockDamage, attackerEntity, null, FastTags.Parse(blockFaceDamageCategory) | _attackDetails.WeaponTypeTag | hitInfo.fmcHit.blockValue.Block.Tags, true, false); } modifiedBlockDamage = ItemActionAttack.DegradationModifier(modifiedBlockDamage, _weaponCondition); modifiedBlockDamage = isProtectionApplied ? 0f : Utils.FastMax(1f, modifiedBlockDamage); _attackDetails.damage += modifiedBlockDamage; _attackDetails.bKilled = false; _attackDetails.damageTotalOfTarget = hitBlockValue.damage + _attackDetails.damage; if (_attackDetails.damage > 0f) { BlockFace blockFaceFromHitInfo = GameUtils.GetBlockFaceFromHitInfo(hitBlockPos, hitBlockValue, hitInfo.hitCollider, hitInfo.hitTriangleIdx, out _, out _); int blockFaceTexture = hittedChunk.GetBlockFaceTexture(hitBlockPos, blockFaceFromHitInfo); int blockCurDamage = hitBlockValue.damage; bool isBlockBroken = blockCurDamage >= hitBlock.MaxDamage; int ownerAttackerID = ((ownedEntityId != -1 && ownedEntityId != -2) ? ownedEntityId : _attackerEntityId); int blockNextDamage = (_attackMode != ItemActionAttack.EnumAttackMode.Simulate) ? hitBlock.DamageBlock(world, hittedChunk.ClusterIdx, hitBlockPos, hitBlockValue, (int)_attackDetails.damage, ownerAttackerID, _attackDetails, _attackDetails.bHarvestTool, false) : 0; if (blockNextDamage == 0) { _attackDetails.damage = 0f; } else { _attackDetails.damage -= blockNextDamage - blockCurDamage; } if (_attackMode != ItemActionAttack.EnumAttackMode.Simulate && canHarvest && attackerEntity is EntityPlayerLocal && blockFaceTexture > 0 && hitBlock.MeshIndex == 0 && blockNextDamage >= hitBlock.MaxDamage * 1f) { ParticleEffect particleEffect = new ParticleEffect("paint_block", hitInfo.fmcHit.pos - Origin.position, Utils.BlockFaceToRotation(hitInfo.fmcHit.blockFace), lightValueAtBlockPos, blockFaceColor, null, null) { opqueTextureId = BlockTextureData.list[blockFaceTexture].TextureID }; GameManager.Instance.SpawnParticleEffectClient(particleEffect, _attackerEntityId, false, false); } _attackDetails.damageGiven = ((!isBlockBroken) ? (blockNextDamage - blockCurDamage) : 0); _attackDetails.damageMax = hitBlock.MaxDamage; _attackDetails.bKilled = !isBlockBroken && blockNextDamage >= hitBlock.MaxDamage; _attackDetails.itemsToDrop = hitBlock.itemsToDrop; _attackDetails.bBlockHit = true; _attackDetails.materialCategory = hitBlock.blockMaterial.SurfaceCategory; if (attackerEntity != null && _attackMode != ItemActionAttack.EnumAttackMode.Simulate) { attackerEntity.MinEventContext.BlockValue = hitBlockValue; attackerEntity.MinEventContext.Tags = hitBlock.Tags; if (_attackDetails.bKilled) { attackerEntity.FireEvent(MinEventTypes.onSelfDestroyedBlock, isHoldingDamageItem); attackerEntity.NotifyDestroyedBlock(_attackDetails); } else { attackerEntity.FireEvent(MinEventTypes.onSelfDamagedBlock, isHoldingDamageItem); } } } } else if (hitInfo.tag.StartsWith("E_")) { Entity hitEntity = ItemActionAttack.FindHitEntityNoTagCheck(hitInfo, out string hitBodyPart); if (hitEntity == null) { return; } if (hitEntity.entityId == _attackerEntityId) { return; } if (!hitEntity.CanDamageEntity(_attackerEntityId)) { return; } EntityAlive hitEntityAlive = hitEntity as EntityAlive; DamageSourceEntity damageSourceEntity = new DamageSourceEntity(EnumDamageSource.External, _damageType, _attackerEntityId, hitInfo.ray.direction, hitInfo.transform.name, hitInfo.hit.pos, Voxel.phyxRaycastHit.textureCoord); damageSourceEntity.AttackingItem = projectileValue; damageSourceEntity.DismemberChance = _dismemberChance; damageSourceEntity.CreatorEntityId = ownedEntityId; bool isCriticalHit = _attackDetails.isCriticalHit; int finalEntityDamage = (int)_entityDamage; if (attackerEntity != null && hitEntityAlive != null) { FastTags equipmentTags = FastTags.none; if (hitEntityAlive.Health > 0) { equipmentTags = FastTags.Parse(damageSourceEntity.GetEntityDamageEquipmentSlotGroup(hitEntityAlive).ToStringCached()); equipmentTags |= DamagePatches.GetBodyPartTags(damageSourceEntity.GetEntityDamageBodyPart(hitEntityAlive)); } finalEntityDamage = (int)MultiActionReversePatches.ProjectileGetValue(PassiveEffects.DamageModifier, projectileValue, finalEntityDamage, attackerEntity, null, equipmentTags | _attackDetails.WeaponTypeTag | hitEntityAlive.EntityClass.Tags, true, false); finalEntityDamage = (int)MultiActionReversePatches.ProjectileGetValue(PassiveEffects.InternalDamageModifier, projectileValue, finalEntityDamage, hitEntityAlive, null, equipmentTags | projectileValue.ItemClass.ItemTags, true, false); } if (!hitEntityAlive || hitEntityAlive.Health > 0) { finalEntityDamage = Utils.FastMax(1, ItemActionAttack.difficultyModifier(finalEntityDamage, world.GetEntity(_attackerEntityId), hitEntity)); } else if (_toolBonuses != null) { finalEntityDamage = (int)(finalEntityDamage * calculateHarvestToolDamageBonus(_toolBonuses, EntityClass.list[hitEntity.entityClass].itemsToDrop)); } //Log.Out("Final entity damage: " + finalEntityDamage); bool isAlreadyDead = hitEntity.IsDead(); int deathHealth = (hitEntityAlive != null) ? hitEntityAlive.DeathHealth : 0; if (_attackMode != ItemActionAttack.EnumAttackMode.Simulate) { if (attackerEntity != null) { MinEventParams minEventContext = attackerEntity.MinEventContext; minEventContext.Other = hitEntityAlive; minEventContext.StartPosition = hitInfo.ray.origin; } if (SingletonMonoBehaviour.Instance.IsServer && (attackerEntity as EntityPlayer == null || !attackerEntity.isEntityRemote) && hitEntity.isEntityRemote && rangeCheckedAction != null) { EntityPlayer hitPlayer = hitEntity as EntityPlayer; if (hitPlayer != null) { isHitTargetPlayer = false; Ray lookRay = attackerEntity.GetLookRay(); lookRay.origin -= lookRay.direction * 0.15f; float range = Utils.FastMax(rangeCheckedAction.Range, rangeCheckedAction.BlockRange) * ItemActionAttack.attackRangeMultiplier; string hitTransformPath = null; List list_buffs = _buffActions; if (list_buffs != null) { if (hitEntityAlive) { hitTransformPath = (hitBodyPart != null) ? GameUtils.GetChildTransformPath(hitEntity.transform, hitInfo.transform) : null; } else { list_buffs = null; } } if (attackerEntity != null) { attackerEntity.FireEvent(MinEventTypes.onSelfAttackedOther, isHoldingDamageItem); if (hitEntityAlive != null && hitEntityAlive.RecordedDamage.Strength > 0) { attackerEntity.FireEvent(MinEventTypes.onSelfDamagedOther, isHoldingDamageItem); } } if (!isAlreadyDead && hitEntity.IsDead() && attackerEntity != null) { attackerEntity.FireEvent(MinEventTypes.onSelfKilledOther, isHoldingDamageItem); } if (hitEntityAlive && hitEntityAlive.RecordedDamage.ArmorDamage > hitEntityAlive.RecordedDamage.Strength) { surfaceCategory = "metal"; } else { surfaceCategory = EntityClass.list[hitEntity.entityClass].Properties.Values["SurfaceCategory"]; } blockFaceParticle = surfaceCategory; lightValueAtBlockPos = hitEntity.GetLightBrightness(); string hitParticle = string.Format("impact_{0}_on_{1}", _attackingDeviceMadeOf, blockFaceParticle); string hitSound = (surfaceCategory != null) ? string.Format("{0}hit{1}", _attackingDeviceMadeOf, surfaceCategory) : null; if (_hitSoundOverrides != null && _hitSoundOverrides.ContainsKey(surfaceCategory)) { hitSound = _hitSoundOverrides[surfaceCategory]; } ParticleEffect particleEffect2 = new ParticleEffect(hitParticle, hitInfo.fmcHit.pos, Utils.BlockFaceToRotation(hitInfo.fmcHit.blockFace), lightValueAtBlockPos, blockFaceColor, hitSound, null); hitPlayer.ServerNetSendRangeCheckedDamage(lookRay.origin, range, damageSourceEntity, finalEntityDamage, isCriticalHit, list_buffs, hitTransformPath, particleEffect2); } } if (isHitTargetPlayer) { int damageDealt = hitEntity.DamageEntity(damageSourceEntity, finalEntityDamage, isCriticalHit, 1f); if (damageDealt != -1 && attackerEntity) { MinEventParams attackerMinEventParams = attackerEntity.MinEventContext; attackerMinEventParams.Other = hitEntityAlive; attackerMinEventParams.StartPosition = hitInfo.ray.origin; if (ownedEntityId != -1) { launcherValue.FireEvent(MinEventTypes.onSelfAttackedOther, attackerEntity.MinEventContext); } attackerEntity.FireEvent(MinEventTypes.onSelfAttackedOther, isHoldingDamageItem); if (hitEntityAlive && hitEntityAlive.RecordedDamage.Strength > 0) { attackerEntity.FireEvent(MinEventTypes.onSelfDamagedOther, isHoldingDamageItem); } } if (!isAlreadyDead && hitEntity.IsDead() && attackerEntity) { attackerEntity.FireEvent(MinEventTypes.onSelfKilledOther, isHoldingDamageItem); } if (damageDealt != -1 && hitEntityAlive && _buffActions != null && _buffActions.Count > 0) { for (int i = 0; i < _buffActions.Count; i++) { BuffClass buff = BuffManager.GetBuff(_buffActions[i]); if (buff != null) { float bufProcChance = MultiActionReversePatches.ProjectileGetValue(PassiveEffects.BuffProcChance, null, 1f, attackerEntity, null, FastTags.Parse(buff.Name), true, false); if (hitEntityAlive.rand.RandomFloat <= bufProcChance) { hitEntityAlive.Buffs.AddBuff(_buffActions[i], attackerEntity.entityId, true, false, -1f); } } } } } } if (hitEntityAlive && hitEntityAlive.RecordedDamage.ArmorDamage > hitEntityAlive.RecordedDamage.Strength) { surfaceCategory = "metal"; } else { surfaceCategory = EntityClass.list[hitEntity.entityClass].Properties.Values["SurfaceCategory"]; } blockFaceParticle = surfaceCategory; lightValueAtBlockPos = hitEntity.GetLightBrightness(); EntityPlayer attackerPlayer = attackerEntity as EntityPlayer; if (attackerPlayer) { if (isAlreadyDead && hitEntity.IsDead() && hitEntityAlive && hitEntityAlive.DeathHealth + finalEntityDamage > -1 * EntityClass.list[hitEntity.entityClass].DeadBodyHitPoints) { _attackDetails.damageTotalOfTarget = (float)(-1 * hitEntityAlive.DeathHealth); _attackDetails.damageGiven = deathHealth + Mathf.Min(EntityClass.list[hitEntity.entityClass].DeadBodyHitPoints, Mathf.Abs(hitEntityAlive.DeathHealth)); _attackDetails.damageMax = EntityClass.list[hitEntity.entityClass].DeadBodyHitPoints; _attackDetails.bKilled = -1 * hitEntityAlive.DeathHealth >= EntityClass.list[hitEntity.entityClass].DeadBodyHitPoints; _attackDetails.itemsToDrop = EntityClass.list[hitEntity.entityClass].itemsToDrop; _attackDetails.entityHit = hitEntity; _attackDetails.materialCategory = surfaceCategory; } if (!isAlreadyDead && (hitEntityAlive.IsDead() || hitEntityAlive.Health <= 0) && EntityClass.list.ContainsKey(hitEntity.entityClass)) { if ((_flags & 2) > 0) { float trapXP = MultiActionReversePatches.ProjectileGetValue(PassiveEffects.ElectricalTrapXP, attackerPlayer.inventory.holdingItemItemValue, 0f, attackerPlayer, null, default, true, false); if (trapXP > 0f) { attackerPlayer.AddKillXP(hitEntityAlive, trapXP); } } else { attackerPlayer.AddKillXP(hitEntityAlive, 1f); } } } if (hitEntity is EntityDrone) { _attackDetails.entityHit = hitEntity; } } if ((_flags & 8) > 0) { canHarvest = false; } if (isHitTargetPlayer && _attackMode != ItemActionAttack.EnumAttackMode.Simulate && canHarvest && blockFaceParticle != null && ((_attackDetails.bBlockHit && !_attackDetails.bKilled) || !_attackDetails.bBlockHit)) { string hitParticle = string.Format("impact_{0}_on_{1}", _attackingDeviceMadeOf, blockFaceParticle); if (_attackMode == ItemActionAttack.EnumAttackMode.RealAndHarvesting && (_flags & 4) > 0 && ParticleEffect.IsAvailable(hitParticle + "_harvest")) { hitParticle += "_harvest"; } string hitSound = (surfaceCategory != null) ? string.Format("{0}hit{1}", _attackingDeviceMadeOf, surfaceCategory) : null; if (_hitSoundOverrides != null && _hitSoundOverrides.ContainsKey(surfaceCategory)) { hitSound = _hitSoundOverrides[surfaceCategory]; } world.GetGameManager().SpawnParticleEffectServer(new ParticleEffect(hitParticle, hitInfo.fmcHit.pos, Utils.BlockFaceToRotation(hitInfo.fmcHit.blockFace), lightValueAtBlockPos, blockFaceColor, hitSound, null), _attackerEntityId, false, true); } if ((_flags & 1) > 0 && attackerEntity != null && attackerEntity.inventory != null) { attackerEntity.inventory.CallOnToolbeltChangedInternal(); } } private static float calculateHarvestToolDamageBonus(Dictionary _toolBonuses, Dictionary> _harvestItems) { if (!_harvestItems.ContainsKey(EnumDropEvent.Harvest)) { return 1f; } List list = _harvestItems[EnumDropEvent.Harvest]; for (int i = 0; i < list.Count; i++) { if (list[i].toolCategory != null && _toolBonuses.ContainsKey(list[i].toolCategory)) { return _toolBonuses[list[i].toolCategory].Damage; } } return 1f; } public static float GetProjectileDamageBlock(this ItemActionAttack self, ItemValue _itemValue, BlockValue _blockValue, EntityAlive _holdingEntity = null, int actionIndex = 0) { FastTags tmpTag = ((actionIndex != 1) ? ItemActionAttack.PrimaryTag : ItemActionAttack.SecondaryTag); ItemClass launcherClass = ItemClass.GetForId(_itemValue.Meta); tmpTag |= ((launcherClass == null) ? ItemActionAttack.MeleeTag : launcherClass.ItemTags); if (_holdingEntity != null) { tmpTag |= _holdingEntity.CurrentStanceTag | _holdingEntity.CurrentMovementTag; } tmpTag |= _blockValue.Block.Tags; float value = MultiActionReversePatches.ProjectileGetValue(PassiveEffects.BlockDamage, _itemValue, self.damageBlock, _holdingEntity, null, tmpTag, true, false)/* * GetProjectileBlockDamagePerc(_itemValue, _holdingEntity)*/; //Log.Out($"block damage {value} base damage {self.GetBaseDamageBlock(null)} action index {actionIndex} launcher {launcherClass.Name} projectile {_itemValue.ItemClass.Name}"); return value; } public static float GetProjectileDamageEntity(this ItemActionAttack self, ItemValue _itemValue, EntityAlive _holdingEntity = null, int actionIndex = 0) { FastTags tmpTag = ((actionIndex != 1) ? ItemActionAttack.PrimaryTag : ItemActionAttack.SecondaryTag); ItemClass launcherClass = ItemClass.GetForId(_itemValue.Meta); tmpTag |= ((launcherClass == null) ? ItemActionAttack.MeleeTag : launcherClass.ItemTags); if (_holdingEntity != null) { tmpTag |= _holdingEntity.CurrentStanceTag | _holdingEntity.CurrentMovementTag; } var res = MultiActionReversePatches.ProjectileGetValue(PassiveEffects.EntityDamage, _itemValue, self.damageEntity, _holdingEntity, null, tmpTag, true, false)/* * GetProjectileEntityDamagePerc(_itemValue, _holdingEntity)*/; #if DEBUG Log.Out($"get projectile damage entity for action index {actionIndex}, item {launcherClass.Name}, result {res}"); #endif return res; } //public static float GetProjectileBlockDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity) //{ // float value = MultiActionReversePatches.ProjectileGetValue(CustomEnums.ProjectileImpactDamagePercentBlock, _itemValue, 1, _holdingEntity, null); // //Log.Out("Block damage perc: " + value); // return value; //} //public static float GetProjectileEntityDamagePerc(ItemValue _itemValue, EntityAlive _holdingEntity) //{ // float value = MultiActionReversePatches.ProjectileGetValue(CustomEnums.ProjectileImpactDamagePercentEntity, _itemValue, 1, _holdingEntity, null); // //Log.Out("Entity damage perc: " + value); // return value; //} public static void ProjectileValueModifyValue(this ItemValue _projectileItemValue, EntityAlive _entity, ItemValue _originalItemValue, PassiveEffects _passiveEffect, ref float _originalValue, ref float _perc_value, FastTags _tags, bool _useMods = true, bool _useDurability = false) { if (_originalItemValue != null) { Log.Warning($"original item value present: item {_originalItemValue.ItemClass.Name}"); return; } int seed = MinEventParams.CachedEventParam.Seed; if (_entity != null) { seed = _entity.MinEventContext.Seed; } ItemClass launcherClass = ItemClass.GetForId(_projectileItemValue.Meta); int actionIndex = _projectileItemValue.SelectedAmmoTypeIndex; if (launcherClass != null) { if (launcherClass.Actions != null && launcherClass.Actions.Length != 0 && launcherClass.Actions[actionIndex] is ItemActionRanged) { ItemClass ammoClass = _projectileItemValue.ItemClass; if (ammoClass != null && ammoClass.Effects != null) { ammoClass.Effects.ModifyValue(_entity, _passiveEffect, ref _originalValue, ref _perc_value, 0f, _tags); } } if (launcherClass.Effects != null) { MinEventParams.CachedEventParam.Seed = (int)_projectileItemValue.Seed + (int)((_projectileItemValue.Seed != 0) ? _passiveEffect : PassiveEffects.None); if (_entity != null) { _entity.MinEventContext.Seed = MinEventParams.CachedEventParam.Seed; } float prevOriginal = _originalValue; launcherClass.Effects.ModifyValue(_entity, _passiveEffect, ref _originalValue, ref _perc_value, _projectileItemValue.Quality, _tags); if (_useDurability) { float percentUsesLeft = _projectileItemValue.PercentUsesLeft; switch (_passiveEffect) { case PassiveEffects.PhysicalDamageResist: if (percentUsesLeft < 0.5f) { float diff = _originalValue - prevOriginal; _originalValue = prevOriginal + diff * percentUsesLeft * 2f; } break; case PassiveEffects.ElementalDamageResist: if (percentUsesLeft < 0.5f) { float diff = _originalValue - prevOriginal; _originalValue = prevOriginal + diff * percentUsesLeft * 2f; } break; case PassiveEffects.BuffResistance: if (percentUsesLeft < 0.5f) { float diff = _originalValue - prevOriginal; _originalValue = prevOriginal + diff * percentUsesLeft * 2f; } break; } } } } else { Log.Warning($"launcher class not found: item id{_projectileItemValue.Meta}"); } if (_useMods) { for (int i = 0; i < _projectileItemValue.CosmeticMods.Length; i++) { if (_projectileItemValue.CosmeticMods[i] != null && _projectileItemValue.CosmeticMods[i].ItemClass is ItemClassModifier && !MultiActionManager.ShouldExcludePassive(_projectileItemValue.type, _projectileItemValue.CosmeticMods[i].type, actionIndex)) { _projectileItemValue.CosmeticMods[i].ModifyValue(_entity, _projectileItemValue, _passiveEffect, ref _originalValue, ref _perc_value, _tags); } } for (int i = 0; i < _projectileItemValue.Modifications.Length; i++) { if (_projectileItemValue.Modifications[i] != null && _projectileItemValue.Modifications[i].ItemClass is ItemClassModifier && !MultiActionManager.ShouldExcludePassive(_projectileItemValue.type, _projectileItemValue.Modifications[i].type, actionIndex)) { _projectileItemValue.Modifications[i].ModifyValue(_entity, _projectileItemValue, _passiveEffect, ref _originalValue, ref _perc_value, _tags); } } } } } }