using System.Collections.Generic; public class ItemActionAttackRebirth { static float _KillExpMaxDist = 50f; static float _NPCKillExpMod = 1f; public static void Hit( WorldRayHitInfo hitInfo_, int attackerEntityId_, EnumDamageTypes damageType_, float blockDamage_, float entityDamage_, float staminaDamageMultiplier_, float weaponCondition_, float criticalHitChanceOLD_, // unused float dismemberChance_, string attackingDeviceMadeOf_, DamageMultiplier damageMultiplier_, List buffActions_, ItemActionAttack.AttackHitInfo attackDetails_, int flags_ = 1, int actionExp_ = 0, // unused float actionExpBonus_ = 0f, // unused ItemActionAttack rangeCheckedAction_ = null, Dictionary toolBonuses_ = null, ItemActionAttack.EnumAttackMode attackMode_ = ItemActionAttack.EnumAttackMode.RealNoHarvesting, Dictionary hitSoundOverrides_ = null, int ownedEntityId_ = -1, ItemValue damagingItemValue_ = null) { //Log.Out("ItemActionAttackPatches-Hit attackerEntityId_: " + attackerEntityId_); //Log.Out("ItemActionAttackPatches-Hit ownedEntityId_: " + ownedEntityId_); //Log.Out("ItemActionAttackPatches-Hit attackDetails_.WeaponTypeTag: " + attackDetails_.WeaponTypeTag); //Log.Out("ItemActionAttackPatches-Hit weaponCondition_: " + weaponCondition_); //Log.Out("ItemActionAttackPatches-Hit blockDamage_: " + blockDamage_); //Log.Out("ItemActionAttackPatches-Hit entityDamage_: " + entityDamage_); if (hitInfo_ == null || hitInfo_.tag == null) return; bool bypassXP = false; if (damagingItemValue_ != null && damagingItemValue_.ItemClass != null) { if (damagingItemValue_.ItemClass.Name.ToLower() == "autoturret" || damagingItemValue_.ItemClass.Name.ToLower() == "shotgunturret" ) { bypassXP = true; } } World world = GameManager.Instance.World; bool isNot_RNHOE = true; if (attackMode_ == ItemActionAttack.EnumAttackMode.RealNoHarvestingOrEffects) { isNot_RNHOE = false; attackMode_ = ItemActionAttack.EnumAttackMode.RealNoHarvesting; } if (attackDetails_ != null) { attackDetails_.itemsToDrop = null; attackDetails_.bBlockHit = false; attackDetails_.entityHit = null; } string particleCategory = null; string surfaceCategory = null; float lightValue = 1f; Color particleColor = Color.white; bool islphModLessThanOne = false; EntityAlive attackingEntity = world.GetEntity(attackerEntityId_) as EntityAlive; EntityNPCRebirth attackingNPC = attackingEntity as EntityNPCRebirth; bool isInventoryItem = false; if (attackingEntity != null) { if (damagingItemValue_ == null) { damagingItemValue_ = attackingEntity.inventory.holdingItemItemValue; } isInventoryItem = damagingItemValue_.Equals(attackingEntity.inventory.holdingItemItemValue); } bool isNotPlayerHit = true; #region Hit -> Block/Terrain if (GameUtils.IsBlockOrTerrain(hitInfo_.tag)) { // Does this work outside of Unity Editor mode? /*if (ItemAction.ShowDebugDisplayHit) { Vector3 position = Camera.main.transform.position; DebugLines.Create(null, attackingEntity.RootTransform, position + Origin.position, hitInfo_.hit.pos, new Color(1f, 0.5f, 1f), new Color(1f, 0f, 1f), ItemAction.DebugDisplayHitSize * 2f, ItemAction.DebugDisplayHitSize, ItemAction.DebugDisplayHitTime); }*/ // Nullify npc hire damage. Would be better to stop them from actually shooting/swinging in the first place if (attackingNPC != null && attackingNPC.Buffs.GetCustomVar("$Leader") > 0 && attackDetails_.damage == 0) { //Log.Out("ItemActionAttackPatches-Hit 7a, _attackDetails: " + _attackDetails.damage); return; } ChunkCluster chunkCluster = world.ChunkClusters[hitInfo_.hit.clrIdx]; if (chunkCluster == null) return; Vector3i vecHitBlockPos = hitInfo_.hit.blockPos; BlockValue blockValue = chunkCluster.GetBlock(vecHitBlockPos); if (blockValue.isair && hitInfo_.hit.blockValue.Block.IsDistantDecoration && hitInfo_.hit.blockValue.damage >= hitInfo_.hit.blockValue.Block.MaxDamage - 1) { blockValue = hitInfo_.hit.blockValue; world.SetBlockRPC(vecHitBlockPos, blockValue); } Block blockClass = blockValue.Block; if (blockClass == null) return; if (blockValue.ischild) { vecHitBlockPos = blockClass.multiBlockPos.GetParentPos(vecHitBlockPos, blockValue); blockValue = chunkCluster.GetBlock(vecHitBlockPos); blockClass = blockValue.Block; if (blockClass == null) return; } if (blockValue.isair) return; float lphModifier = world.GetLandProtectionHardnessModifier(hitInfo_.hit.blockPos, attackingEntity, world.GetGameManager().GetPersistentLocalPlayer()); if (world.IsWithinTraderArea(hitInfo_.hit.blockPos)) { lphModifier = 0f; } if (blockClass.blockMaterial.id == "Mbedrock") { lphModifier = 0f; } if (lphModifier != 1f) { if (attackingEntity != null && attackMode_ != ItemActionAttack.EnumAttackMode.Simulate && attackingEntity is EntityPlayer && !damagingItemValue_.ItemClass.ignoreKeystoneSound && !damagingItemValue_.ToBlockValue().Block.IgnoreKeystoneOverlay) { attackingEntity.PlayOneShot("keystone_impact_overlay"); } if (lphModifier < 1f) { islphModLessThanOne = true; } } if (vecHitBlockPos != attackDetails_.hitPosition || lphModifier != attackDetails_.hardnessScale || blockValue.type != attackDetails_.blockBeingDamaged.type || (isInventoryItem && damagingItemValue_.SelectedAmmoTypeIndex != attackDetails_.ammoIndex)) { float hardness = Mathf.Max(blockClass.GetHardness(), 0.1f) * lphModifier; float dmgPerHit = blockDamage_ * (damageMultiplier_ != null ? damageMultiplier_.Get(blockClass.blockMaterial.DamageCategory) : 1f); //Log.Out("ItemActionAttackPatches-Hit BEFORE dmgPerHit: " + dmgPerHit); if (attackingEntity != null) { dmgPerHit *= attackingEntity.GetBlockDamageScale(); } //Log.Out("ItemActionAttackPatches-Hit MID dmgPerHit: " + dmgPerHit); if (toolBonuses_ != null && toolBonuses_.Count > 0) { dmgPerHit *= CalculateHarvestToolDamageBonus(toolBonuses_, blockClass.itemsToDrop); attackDetails_.bHarvestTool = true; } //Log.Out("ItemActionAttackPatches-Hit AFTER dmgPerHit: " + dmgPerHit); //Log.Out("ItemActionAttackPatches-Hit hardness: " + hardness); //Log.Out("ItemActionAttackPatches-Hit (dmgPerHit / hardness): " + (dmgPerHit / hardness)); attackDetails_.damagePerHit = (!islphModLessThanOne) ? (dmgPerHit / hardness) : 0f; attackDetails_.damage = 0f; attackDetails_.hardnessScale = lphModifier; attackDetails_.hitPosition = vecHitBlockPos; attackDetails_.blockBeingDamaged = blockValue; if (isInventoryItem) { attackDetails_.ammoIndex = damagingItemValue_.SelectedAmmoTypeIndex; } } attackDetails_.raycastHitPosition = hitInfo_.hit.blockPos; Block block2 = hitInfo_.fmcHit.blockValue.Block; lightValue = world.GetLightBrightness(hitInfo_.fmcHit.blockPos); particleColor = block2.GetColorForSide(hitInfo_.fmcHit.blockValue, hitInfo_.fmcHit.blockFace); particleCategory = block2.GetParticleForSide(hitInfo_.fmcHit.blockValue, hitInfo_.fmcHit.blockFace); MaterialBlock materialForSide = block2.GetMaterialForSide(hitInfo_.fmcHit.blockValue, hitInfo_.fmcHit.blockFace); surfaceCategory = materialForSide.SurfaceCategory; //NEW CODE - START if (attackingEntity is EntityNPCRebirth) { staminaDamageMultiplier_ = 1; } //NEW CODE - END float dmgPerHitStam = attackDetails_.damagePerHit * staminaDamageMultiplier_; //Log.Out($"dmgPerHitStam: {dmgPerHitStam}, damagePerHit: {attackDetails_.damagePerHit}, staminaDamageMultiplier_: {staminaDamageMultiplier_}"); if (attackingEntity != null) { string dmgCategory = materialForSide.DamageCategory ?? string.Empty; dmgPerHitStam = (int)EffectManager.GetValue(PassiveEffects.DamageModifier, damagingItemValue_, dmgPerHitStam, attackingEntity, null, FastTags.Parse(dmgCategory) | attackDetails_.WeaponTypeTag | hitInfo_.fmcHit.blockValue.Block.Tags); } dmgPerHitStam = ItemActionAttack.DegradationModifier(dmgPerHitStam, weaponCondition_); dmgPerHitStam = (!islphModLessThanOne) ? Utils.FastMax(1f, dmgPerHitStam) : 0f; attackDetails_.damage += dmgPerHitStam; attackDetails_.bKilled = false; attackDetails_.damageTotalOfTarget = blockValue.damage + attackDetails_.damage; //Log.Out("ItemActionAttackPatches-Hit blockValue.damage: " + blockValue.damage); //Log.Out("ItemActionAttackPatches-Hit attackDetails_.damage: " + attackDetails_.damage); if (attackDetails_.damage > 0f) { Vector3 _hitFaceCenter; Vector3 _hitFaceNormal; BlockFace blockFaceFromHitInfo = GameUtils.GetBlockFaceFromHitInfo(vecHitBlockPos, blockValue, hitInfo_.hitCollider, hitInfo_.hitTriangleIdx, out _hitFaceCenter, out _hitFaceNormal); int blockFaceTexture = chunkCluster.GetBlockFaceTexture(vecHitBlockPos, blockFaceFromHitInfo); int curBlockDmg = blockValue.damage; // the amount of damage this block has currently taken. 0 if block hp is 100% bool isOverkill = curBlockDmg >= blockClass.MaxDamage; int entityIdThatDamaged = ((ownedEntityId_ != -1 && ownedEntityId_ != -2) ? ownedEntityId_ : attackerEntityId_); int blockDmgAmt = (attackMode_ != ItemActionAttack.EnumAttackMode.Simulate) ? blockClass.DamageBlock(world, chunkCluster.ClusterIdx, vecHitBlockPos, blockValue, (int)attackDetails_.damage, entityIdThatDamaged, attackDetails_, attackDetails_.bHarvestTool) : 0; //Log.Out($"blockVal curBlockDmg: {curBlockDmg}, blockDmgAmt: {blockDmgAmt}"); //Log.Out("ItemActionAttackPatches-Hit blockDmgAmt: " + blockDmgAmt); if (blockDmgAmt == 0) { attackDetails_.damage = 0f; } else { attackDetails_.damage -= blockDmgAmt - curBlockDmg; } //Log.Out("ItemActionAttackPatches-Hit curBlockDmg: " + curBlockDmg); //Log.Out("ItemActionAttackPatches-Hit attackDetails_.damage: " + attackDetails_.damage); if (attackMode_ != ItemActionAttack.EnumAttackMode.Simulate && isNot_RNHOE && attackingEntity is EntityPlayerLocal && blockFaceTexture > 0 && blockClass.MeshIndex == 0 && (float)blockDmgAmt >= (float)blockClass.MaxDamage * 1f) { try { ParticleEffect particleEffect = new ParticleEffect("paint_block", hitInfo_.fmcHit.pos - Origin.position, Utils.BlockFaceToRotation(hitInfo_.fmcHit.blockFace), lightValue, particleColor, null, null); particleEffect.opqueTextureId = BlockTextureData.list[blockFaceTexture].TextureID; GameManager.Instance.SpawnParticleEffectClient(particleEffect, attackerEntityId_); } catch { } } attackDetails_.damageGiven = (!isOverkill) ? (blockDmgAmt - curBlockDmg) : 0; // NEW CODE - START RebirthVariables.damageGiven = attackDetails_.damageGiven; // NEW CODE - END attackDetails_.damageMax = blockClass.MaxDamage; attackDetails_.bKilled = !isOverkill && blockDmgAmt >= blockClass.MaxDamage; attackDetails_.itemsToDrop = blockClass.itemsToDrop; attackDetails_.bBlockHit = true; attackDetails_.materialCategory = blockClass.blockMaterial.SurfaceCategory; if (attackingEntity != null && attackMode_ != ItemActionAttack.EnumAttackMode.Simulate) { attackingEntity.MinEventContext.ItemValue = damagingItemValue_; attackingEntity.MinEventContext.BlockValue = blockValue; attackingEntity.MinEventContext.Tags = blockClass.Tags; if (attackDetails_.bKilled) { attackingEntity.FireEvent(MinEventTypes.onSelfDestroyedBlock, isInventoryItem); attackingEntity.NotifyDestroyedBlock(attackDetails_); } else { attackingEntity.FireEvent(MinEventTypes.onSelfDamagedBlock, isInventoryItem); } } } } #endregion #region Hit -> Entity else if (hitInfo_.tag.StartsWith("E_")) { string bodyPartName; Entity hitEntity = ItemActionAttack.FindHitEntityNoTagCheck(hitInfo_, out bodyPartName); // need to check CanDamageEntity to see if it's related to no hit bug if (hitEntity == null || hitEntity.entityId == attackerEntityId_ || !hitEntity.CanDamageEntity(attackerEntityId_)) return; EntityAlive hitEntityAlive = hitEntity as EntityAlive; if (attackingEntity is EntityNPCRebirth && attackingEntity.HasAnyTags(FastTags.Parse("ranged"))) { int npcLevel = (int)attackingEntity.Buffs.GetCustomVar("$FR_NPC_Level"); int headshotChance = npcLevel - 1; int random = GameManager.Instance.World.GetGameRandom().RandomRange(1, 11); string tagName = "E_BP_Head"; if (headshotChance < random) { tagName = RebirthVariables.bodyTags[random]; } float numAccuracyModifier = float.Parse(RebirthVariables.customRangedNPCAccuracyModifier) / 100; float baseValue = 100 * numAccuracyModifier; int numMagazineSize = 0; float roundsPerMinute = 60; if (numMagazineSize == 0) { ItemActionRanged myAction = null; myAction = (ItemActionRanged)attackingEntity.inventory.holdingItem.Actions[1]; ItemActionData _actionData = attackingEntity.inventory.holdingItemData.actionData[1]; ItemActionRanged.ItemActionDataRanged itemActionDataRanged = (ItemActionRanged.ItemActionDataRanged)_actionData; float myDelay = 60f / EffectManager.GetValue(PassiveEffects.RoundsPerMinute, itemActionDataRanged.invData.itemValue, 60f / itemActionDataRanged.OriginalDelay, attackingEntity); roundsPerMinute = 60 / myDelay; numMagazineSize = (int)EffectManager.GetValue(PassiveEffects.MagazineSize, itemActionDataRanged.invData.itemValue, myAction.BulletsPerMagazine, attackingEntity); } if (numMagazineSize > 1) { baseValue = 60 * numAccuracyModifier; int baseRoundsPerMinute = 60; if (roundsPerMinute > baseRoundsPerMinute) { float multiplier = 1 - ((roundsPerMinute / baseRoundsPerMinute) / 15); baseValue *= multiplier; } } float missShotChance = baseValue + (npcLevel * 0.3f * numAccuracyModifier) * 10; //Log.Out("ItemActionMeleePatches-hitTheTarget level: " + attackingEntity.Buffs.GetCustomVar("$FR_NPC_Level")); //Log.Out("ItemActionMeleePatches-hitTheTarget A random: " + random); //Log.Out("ItemActionMeleePatches-hitTheTarget missShotChance: " + missShotChance); random = GameManager.Instance.World.GetGameRandom().RandomRange(1, 101); //Log.Out("ItemActionMeleePatches-hitTheTarget B random: " + random); if (missShotChance < random) { //Log.Out("ItemActionMeleePatches-hitTheTarget MISS"); return; } if (hitEntityAlive is EntityPlayer) { //Log.Out("ItemActionMeleePatches-hitTheTarget EntityName: " + hitEntityAlive.EntityName); ProgressionValue progressionValue = hitEntityAlive.Progression.GetProgressionValue("perkDodge"); bool hitTarget = true; if (progressionValue != null) { float chance = progressionValue.level * 5f; random = GameManager.Instance.World.GetGameRandom().RandomRange(0, 101); //Log.Out("ItemActionMeleePatches-hitTheTarget progressionValue.level: " + progressionValue.level); //Log.Out("ItemActionMeleePatches-hitTheTarget chance: " + chance); //Log.Out("ItemActionMeleePatches-hitTheTarget random: " + random); if (random < chance) { //Log.Out("ItemActionMeleePatches-hitTheTarget EXIT"); hitTarget = false; } } if (!hitTarget) { return; } } } Vector2 uvHit = Vector2.zero; MeshCollider meshCollider = Voxel.phyxRaycastHit.collider as MeshCollider; if (meshCollider != null && meshCollider.sharedMesh != null && meshCollider.sharedMesh.HasVertexAttribute(UnityEngine.Rendering.VertexAttribute.TexCoord0)) { uvHit = Voxel.phyxRaycastHit.textureCoord; } DamageSourceEntity damageSourceEntity = new DamageSourceEntity(EnumDamageSource.External, damageType_, attackerEntityId_, hitInfo_.ray.direction, hitInfo_.transform.name, hitInfo_.hit.pos, uvHit); damageSourceEntity.AttackingItem = damagingItemValue_; damageSourceEntity.DismemberChance = dismemberChance_; damageSourceEntity.CreatorEntityId = ownedEntityId_; bool isCriticalHit = attackDetails_.isCriticalHit; int entityDamage = (int)entityDamage_; //Log.Out($"Entity Damage: {entityDamage}, attack mode: {attackMode_}"); if (attackingEntity != null && hitEntityAlive != null) { FastTags fastTags = FastTags.none; if (hitEntityAlive.Health > 0) { fastTags = FastTags.Parse(damageSourceEntity.GetEntityDamageEquipmentSlotGroup(hitEntityAlive).ToStringCached()); } entityDamage = (int)EffectManager.GetValue(PassiveEffects.DamageModifier, damagingItemValue_, entityDamage, attackingEntity, null, fastTags | attackDetails_.WeaponTypeTag | hitEntityAlive.EntityClass.Tags); entityDamage = (int)EffectManager.GetValue(PassiveEffects.InternalDamageModifier, damagingItemValue_, entityDamage, hitEntityAlive, null, fastTags | damagingItemValue_.ItemClass.ItemTags); } if (!hitEntityAlive || hitEntityAlive.Health > 0) { entityDamage = Utils.FastMax(1, DifficultyModifier(entityDamage, world.GetEntity(attackerEntityId_), hitEntity)); } else if (toolBonuses_ != null) { entityDamage = (int)(entityDamage * CalculateHarvestToolDamageBonus(toolBonuses_, EntityClass.list[hitEntity.entityClass].itemsToDrop)); } // rebirth genetic level strengh mod if (hitEntityAlive is EntityPlayer) { bool bMelee = hitEntityAlive.inventory.holdingItem.ItemTags.ToString().ToLower().Contains("melee"); if (bMelee) { //bool bSalvage = hitEntityAlive.inventory.holdingItem.ItemTags.ToString().ToLower().Contains("perkSalvageOperations"); // Unused at the moment? entityDamage += entityDamage * RebirthUtilities.GetGeneticLevel("Strength") * 5 / 100; } } bool hitEntityIsDead = hitEntity.IsDead(); int corpseHP = (hitEntityAlive != null) ? hitEntityAlive.DeathHealth : 0; if (attackMode_ != ItemActionAttack.EnumAttackMode.Simulate) { if (attackingEntity != null) { MinEventParams minEventContext = attackingEntity.MinEventContext; minEventContext.Other = hitEntityAlive; minEventContext.ItemValue = damagingItemValue_; minEventContext.StartPosition = hitInfo_.ray.origin; } if (SingletonMonoBehaviour.Instance.IsServer && (attackingEntity as EntityPlayer == null || !attackingEntity.isEntityRemote) && hitEntity.isEntityRemote && rangeCheckedAction_ != null) { EntityPlayer hitPlayer = hitEntity as EntityPlayer; if (hitPlayer != null) // if the target is a player { isNotPlayerHit = false; // to do: check entity height and get a different ray origin. look ray might be too high for certain taller entitites that would cause them to consecutively miss shorter entities // nevermind, wrong place... Ray lookRay = attackingEntity.GetLookRay(); lookRay.origin -= lookRay.direction * 0.15f; float maxRange = Utils.FastMax(rangeCheckedAction_.Range, rangeCheckedAction_.BlockRange) * ItemActionAttack.attackRangeMultiplier; // Why are we multiplying by 1f? string buffActionsContext = null; List buffActions = buffActions_; if (buffActions != null) { if (hitEntityAlive != null) { buffActionsContext = ((bodyPartName != null) ? GameUtils.GetChildTransformPath(hitEntity.transform, hitInfo_.transform) : null); } else { buffActions = null; } } if (attackingEntity != null) { attackingEntity.FireEvent(MinEventTypes.onSelfAttackedOther, isInventoryItem); if (hitEntityAlive != null && hitEntityAlive.RecordedDamage.Strength > 0) { attackingEntity.FireEvent(MinEventTypes.onSelfDamagedOther, isInventoryItem); } } if (!hitEntityIsDead && hitEntity.IsDead() && attackingEntity != null) { attackingEntity.FireEvent(MinEventTypes.onSelfKilledOther, isInventoryItem); AddKillExpNPC(attackingNPC, hitEntityAlive); } surfaceCategory = ((!hitEntityAlive || hitEntityAlive.RecordedDamage.ArmorDamage <= hitEntityAlive.RecordedDamage.Strength) ? EntityClass.list[hitEntity.entityClass].Properties.Values["SurfaceCategory"] : "metal"); particleCategory = surfaceCategory; lightValue = hitEntity.GetLightBrightness(); string particleEffectName = $"impact_{attackingDeviceMadeOf_}_on_{particleCategory}"; string soundName = ((surfaceCategory != null) ? $"{attackingDeviceMadeOf_}hit{surfaceCategory}" : null); if (hitSoundOverrides_ != null && hitSoundOverrides_.ContainsKey(surfaceCategory)) { soundName = hitSoundOverrides_[surfaceCategory]; } ParticleEffect particleEffect2 = new ParticleEffect(particleEffectName, hitInfo_.fmcHit.pos, Utils.BlockFaceToRotation(hitInfo_.fmcHit.blockFace), lightValue, particleColor, soundName, null); hitPlayer.ServerNetSendRangeCheckedDamage(lookRay.origin, maxRange, damageSourceEntity, entityDamage, isCriticalHit, buffActions, buffActionsContext, particleEffect2); //Log.Out($"Rebirth Hit ServerNetSend - attackingEntity: {attackingEntity}, hitEntity: {hitEntity} entityDamage: {entityDamage}, stacktrace: {System.Environment.StackTrace}"); } } if (isNotPlayerHit) { // rebirth mod to recalculate damage if (hitInfo_.tag.Equals("E_BP_Head")) { EntityPlayer sourcePlayer = world.GetEntity(damageSourceEntity.getEntityId()) as EntityPlayer; if (sourcePlayer != null) { bool isLongRangeWeapon = sourcePlayer.inventory.holdingItem.HasAnyTags(FastTags.Parse("WeaponTier3LongRangeRifles")); if (isLongRangeWeapon) { int activeClassID = (int)sourcePlayer.Buffs.GetCustomVar("$ActiveClass_FR"); if (RebirthUtilities.IsClassActive(activeClassID)) { ProgressionValue progressionValue = sourcePlayer.Progression.GetProgressionValue("FuriousRamsayPerkLongRangeRifles"); if (progressionValue != null) { int progressionLevel = progressionValue.Level; float damageMultiplier = progressionLevel * 0.025f; bool isSingShotWeapon = sourcePlayer.inventory.holdingItem.HasAnyTags(FastTags.Parse("SingleShot")); if (isSingShotWeapon) damageMultiplier *= 2f; entityDamage *= (int)(1f + damageMultiplier); } } } } } int dmgResult = hitEntity.DamageEntity(damageSourceEntity, entityDamage, isCriticalHit); //Log.Out($"Rebirth Hit - attackingEntity: {attackingEntity}, hitEntity: {hitEntity} dmg entity result: {dmgResult}, stacktrace: {System.Environment.StackTrace}"); if (dmgResult != -1 && attackingEntity != null) { MinEventParams minEventContext2 = attackingEntity.MinEventContext; minEventContext2.Other = hitEntityAlive; minEventContext2.ItemValue = damagingItemValue_; minEventContext2.StartPosition = hitInfo_.ray.origin; if (ownedEntityId_ != -1) { damagingItemValue_.FireEvent(MinEventTypes.onSelfAttackedOther, attackingEntity.MinEventContext); } attackingEntity.FireEvent(MinEventTypes.onSelfAttackedOther, isInventoryItem); if (hitEntityAlive != null && hitEntityAlive.RecordedDamage.Strength > 0) { attackingEntity.FireEvent(MinEventTypes.onSelfDamagedOther, isInventoryItem); } } if (!hitEntityIsDead && hitEntity.IsDead() && attackingEntity != null) { attackingEntity.FireEvent(MinEventTypes.onSelfKilledOther, isInventoryItem); AddKillExpNPC(attackingNPC, hitEntityAlive); } if (dmgResult != -1 && hitEntityAlive != null && buffActions_ != null && buffActions_.Count > 0) { for (int i = 0; i < buffActions_.Count; i++) { BuffClass buff = BuffManager.GetBuff(buffActions_[i]); if (buff != null) { float originalValue = 1f; originalValue = EffectManager.GetValue(PassiveEffects.BuffProcChance, null, originalValue, attackingEntity, null, FastTags.Parse(buff.Name)); if (hitEntityAlive.rand.RandomFloat <= originalValue) { hitEntityAlive.Buffs.AddBuff(buffActions_[i], attackingEntity.entityId); } } } } } } surfaceCategory = (!hitEntityAlive || hitEntityAlive.RecordedDamage.ArmorDamage <= hitEntityAlive.RecordedDamage.Strength) ? EntityClass.list[hitEntity.entityClass].Properties.Values["SurfaceCategory"] : "metal"; particleCategory = surfaceCategory; lightValue = hitEntity.GetLightBrightness(); EntityPlayer attackingPlayer = attackingEntity as EntityPlayer; if (attackingPlayer != null) // The attacker is a Player { if (hitEntityIsDead && hitEntity.IsDead() && (bool)hitEntityAlive && hitEntityAlive.DeathHealth + entityDamage > -1 * EntityClass.list[hitEntity.entityClass].DeadBodyHitPoints) { attackDetails_.damageTotalOfTarget = -1 * hitEntityAlive.DeathHealth; attackDetails_.damageGiven = corpseHP + 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 (!hitEntityIsDead && (hitEntityAlive.IsDead() || hitEntityAlive.Health <= 0) && EntityClass.list.ContainsKey(hitEntity.entityClass)) { if ((flags_ & 2) > 0) { float value = EffectManager.GetValue(PassiveEffects.ElectricalTrapXP, attackingPlayer.inventory.holdingItemItemValue, 0f, attackingPlayer); if (value > 0f) { if (!bypassXP) { attackingPlayer.AddKillXP(hitEntityAlive, value); } } } else { if (!bypassXP) { attackingPlayer.AddKillXP(hitEntityAlive); } } } } if (hitEntity is EntityDrone) { attackDetails_.entityHit = hitEntity; } } #endregion if ((flags_ & 8) > 0) { isNot_RNHOE = false; } // Spawn Particle Effect if (isNotPlayerHit && attackMode_ != ItemActionAttack.EnumAttackMode.Simulate && isNot_RNHOE && particleCategory != null && ((attackDetails_.bBlockHit && !attackDetails_.bKilled) || !attackDetails_.bBlockHit)) { string particleName = $"impact_{attackingDeviceMadeOf_}_on_{particleCategory}"; if (attackMode_ == ItemActionAttack.EnumAttackMode.RealAndHarvesting && (flags_ & 4) > 0 && ParticleEffect.IsAvailable(particleName + "_harvest")) { particleName += "_harvest"; } string soundName2 = ((surfaceCategory != null) ? $"{attackingDeviceMadeOf_}hit{surfaceCategory}" : null); if (hitSoundOverrides_ != null && hitSoundOverrides_.ContainsKey(surfaceCategory)) { soundName2 = hitSoundOverrides_[surfaceCategory]; } world.GetGameManager().SpawnParticleEffectServer(new ParticleEffect(particleName, hitInfo_.fmcHit.pos, Utils.BlockFaceToRotation(hitInfo_.fmcHit.blockFace), lightValue, particleColor, soundName2, null), attackerEntityId_, _forceCreation: false, _worldSpawn: true); } // Update the attacking entity's toolbelt if ((flags_ & 1) > 0 && attackingEntity != null && attackingEntity.inventory != null) { attackingEntity.inventory.CallOnToolbeltChangedInternal(); } } static int DifficultyModifier(int _strength, Entity _attacker, Entity _target) { if (_attacker == null || _target == null || (_attacker.IsClientControlled() && _target.IsClientControlled()) || (!_attacker.IsClientControlled() && !_target.IsClientControlled())) return _strength; int difficultyLevel = GameStats.GetInt(EnumGameStats.GameDifficulty); if (_attacker.IsClientControlled()) { switch (difficultyLevel) { case 0: _strength = Mathf.RoundToInt((float)_strength * 2f); break; case 1: _strength = Mathf.RoundToInt((float)_strength * 1.5f); break; case 3: _strength = Mathf.RoundToInt((float)_strength * 0.83f); break; case 4: _strength = Mathf.RoundToInt((float)_strength * 0.66f); break; case 5: _strength = Mathf.RoundToInt((float)_strength * 0.5f); break; } } else { switch (difficultyLevel) { case 0: _strength = Mathf.RoundToInt((float)_strength * 0.5f); break; case 1: _strength = Mathf.RoundToInt((float)_strength * 0.75f); break; case 3: _strength = Mathf.RoundToInt((float)_strength * 1.5f); break; case 4: _strength = Mathf.RoundToInt((float)_strength * 2f); break; case 5: _strength = Mathf.RoundToInt((float)_strength * 2.5f); break; } } return _strength; } 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; } static void AddKillExpNPC(EntityNPCRebirth attackingNPC, EntityAlive entityTarget) { if (attackingNPC == null || entityTarget == null) return; int leaderID = RebirthUtilities.GetLeader(attackingNPC); if (leaderID <= 0) return; var leaderPlayer = attackingNPC.world.GetEntity(leaderID) as EntityPlayer; if (leaderPlayer == null) { Log.Warning($"NPC has leader, unable to locate the player. Leader id: {leaderID}"); return; } float distanceEntity = leaderPlayer.GetDistance(attackingNPC); if (distanceEntity > _KillExpMaxDist) return; // too far away bool isNaturalSelectionCountingOn = RebirthUtilities.IsNaturalSelectionCountingOn(); /*if (isNaturalSelectionCountingOn) { Log.Out($"ItemActionAttackRebirth-AddKillExpNPC $NaturalSelection_FR: {leaderPlayer.Buffs.GetCustomVar("$NaturalSelection_FR")}"); int numNS = (int)leaderPlayer.Buffs.GetCustomVar("$NaturalSelection_FR") + 1; leaderPlayer.Buffs.SetCustomVar("$NaturalSelection_FR", numNS); RebirthVariables.localConstants["$varFuriousRamsayTheyGrowStrongerKills_Cst"] = numNS; }*/ float expMod = _NPCKillExpMod; if (RebirthUtilities.IsHordeNight()) expMod *= RebirthVariables.hordeNightXPMultiplier; float partyMultiplierOption = 1; // float.Parse(RebirthVariables.customPartyXPMultiplier) / 100; expMod = expMod * partyMultiplierOption; float experienceGained = entityTarget.ExperienceValue * expMod; //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC entity xp: {entityTarget.ExperienceValue}"); //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC partyMultiplierOption: {partyMultiplierOption}"); //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC _NPCKillExpMod: {_NPCKillExpMod}"); //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC _HordeNightExpMod: {RebirthVariables.hordeNightXPMultiplier}"); //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC expMod: {expMod}"); //Log.Out($"ItemActionAttackRebirth-AddKillExpNPC experienceGained: {experienceGained}"); leaderPlayer.AddKillXP(entityTarget, expMod); leaderPlayer.Progression.bProgressionStatsChanged = true; leaderPlayer.bPlayerStatsChanged = true; if (entityTarget is EntityPlayer) leaderPlayer.KilledPlayers++; else leaderPlayer.KilledZombies++; } }