738 lines
44 KiB
C#
738 lines
44 KiB
C#
using KFCommonUtilityLib.Scripts.StaticManagers;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace KFCommonUtilityLib.Scripts.Utilities
|
|
{
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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<TagGroup.Global>.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<string> _buffActions, ItemActionAttack.AttackHitInfo _attackDetails, int _flags = 1, int _actionExp = 0,
|
|
float _actionExpBonus = 0f, ItemActionAttack rangeCheckedAction = null,
|
|
Dictionary<string, ItemActionAttack.Bonuses> _toolBonuses = null,
|
|
ItemActionAttack.EnumAttackMode _attackMode = ItemActionAttack.EnumAttackMode.RealNoHarvesting,
|
|
Dictionary<string, string> _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<TagGroup.Global>.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<TagGroup.Global> equipmentTags = FastTags<TagGroup.Global>.none;
|
|
if (hitEntityAlive.Health > 0)
|
|
{
|
|
equipmentTags = FastTags<TagGroup.Global>.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<ConnectionManager>.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<string> 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<TagGroup.Global>.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<string, ItemActionAttack.Bonuses> _toolBonuses, Dictionary<EnumDropEvent, List<Block.SItemDropProb>> _harvestItems)
|
|
{
|
|
if (!_harvestItems.ContainsKey(EnumDropEvent.Harvest))
|
|
{
|
|
return 1f;
|
|
}
|
|
List<Block.SItemDropProb> 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<TagGroup.Global> 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<TagGroup.Global> 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<TagGroup.Global> _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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} |