Upload from upload_mods.ps1

This commit is contained in:
Nathaniel Cosford
2025-06-04 16:13:36 +09:30
commit e3c48b54dc
17 changed files with 1157 additions and 0 deletions

View File

@@ -0,0 +1,138 @@
using Audio;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine.Scripting;
using UnityEngine;
using FullautoLauncher.Scripts.ProjectileManager;
using static ItemActionLauncher;
using static ItemActionRanged;
[Preserve]
public class ItemActionBetterLauncher : ItemActionRanged
{
private IProjectileItemGroup group;
public override ItemActionData CreateModifierData(ItemInventoryData _invData, int _indexInEntityOfAction)
{
return new ItemActionDataBetterLauncher(_invData, _indexInEntityOfAction);
}
public override void ReadFrom(DynamicProperties _props)
{
base.ReadFrom(_props);
}
public override void StartHolding(ItemActionData _actionData)
{
base.StartHolding(_actionData);
ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_actionData;
ItemValue launcherValue = ItemActionDataBetterLauncher.invData.itemValue;
ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[launcherValue.SelectedAmmoTypeIndex], false).type);
group = CustomProjectileManager.Get(forId.Name);
if (launcherValue.Meta != 0 && GetMaxAmmoCount(ItemActionDataBetterLauncher) != 0)
{
group.Pool(launcherValue.Meta * (int)EffectManager.GetValue(PassiveEffects.RoundRayCount, launcherValue, 1f, ItemActionDataBetterLauncher.invData.holdingEntity));
}
ItemActionDataBetterLauncher.info = new ProjectileParams.ItemInfo()
{
actionData = ItemActionDataBetterLauncher,
itemProjectile = forId,
itemActionProjectile = (ItemActionProjectile)((forId.Actions[0] is ItemActionProjectile) ? forId.Actions[0] : forId.Actions[1]),
itemValueLauncher = launcherValue,
itemValueProjectile = new ItemValue(forId.Id)
};
}
public override void OnModificationsChanged(ItemActionData _data)
{
base.OnModificationsChanged(_data);
}
public override void StopHolding(ItemActionData _data)
{
base.StopHolding(_data);
ItemActionDataBetterLauncher itemActionDataLauncher = (ItemActionDataBetterLauncher)_data;
itemActionDataLauncher.info = null;
}
public override void SwapAmmoType(EntityAlive _entity, int _ammoItemId = -1)
{
base.SwapAmmoType(_entity, _ammoItemId);
ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_entity.inventory.holdingItemData.actionData[ActionIndex];
ItemValue itemValue = ItemActionDataBetterLauncher.invData.itemValue;
ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[itemValue.SelectedAmmoTypeIndex], false).type);
group = CustomProjectileManager.Get(forId.Name);
ItemActionDataBetterLauncher.info = new ProjectileParams.ItemInfo()
{
actionData = ItemActionDataBetterLauncher,
itemProjectile = forId,
itemActionProjectile = (ItemActionProjectile)((forId.Actions[0] is ItemActionProjectile) ? forId.Actions[0] : forId.Actions[1]),
itemValueLauncher = itemValue,
itemValueProjectile = new ItemValue(forId.Id)
};
}
public override Vector3 fireShot(int _shotIdx, ItemActionDataRanged _actionData, ref bool hitEntity)
{
hitEntity = true;
return Vector3.zero;
}
public override void ItemActionEffects(GameManager _gameManager, ItemActionData _actionData, int _firingState, Vector3 _startPos, Vector3 _direction, int _userData = 0)
{
base.ItemActionEffects(_gameManager, _actionData, _firingState, _startPos, _direction, _userData);
if (_firingState == 0)
{
return;
}
EntityAlive entity = _actionData.invData.holdingEntity;
if (entity.isEntityRemote && GameManager.IsDedicatedServer)
{
return;
}
ItemActionDataBetterLauncher ItemActionDataBetterLauncher = (ItemActionDataBetterLauncher)_actionData;
ItemValue holdingItemItemValue = _actionData.invData.holdingEntity.inventory.holdingItemItemValue;
ItemClass forId = ItemClass.GetForId(ItemClass.GetItem(MagazineItemNames[holdingItemItemValue.SelectedAmmoTypeIndex], false).type);
int projCount = (int)EffectManager.GetValue(PassiveEffects.RoundRayCount, ItemActionDataBetterLauncher.invData.itemValue, 1f, ItemActionDataBetterLauncher.invData.holdingEntity);
if (projCount <= 0)
{
return;
}
if (ItemActionDataBetterLauncher.info == null)
{
Log.Error("null info!");
return;
}
if (ItemActionDataBetterLauncher.projectileJoint == null)
{
return;
}
Vector3 realStartPosition = ItemActionDataBetterLauncher.projectileJoint.position + Origin.position;
for (int i = 0; i < projCount; i++)
{
var par = group.Fire(entity.entityId, ItemActionDataBetterLauncher.info, _startPos, realStartPosition, getDirectionOffset(ItemActionDataBetterLauncher, _direction, i), entity, hitmaskOverride);
}
}
public override void getImageActionEffectsStartPosAndDirection(ItemActionData _actionData, out Vector3 _startPos, out Vector3 _direction)
{
Ray lookRay = _actionData.invData.holdingEntity.GetLookRay();
_startPos = lookRay.origin;
_direction = lookRay.direction;//getDirectionOffset(ItemActionDataBetterLauncher, lookRay.direction, 0);
}
public class ItemActionDataBetterLauncher : ItemActionDataRanged
{
public ItemActionDataBetterLauncher(ItemInventoryData _invData, int _indexInEntityOfAction)
: base(_invData, _indexInEntityOfAction)
{
projectileJoint = (_invData.model?.FindInChilds("ProjectileJoint", false));
}
public Transform projectileJoint;
public ProjectileParams.ItemInfo info;
}
}

View File

@@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public static class CustomProjectileManager
{
private static readonly Dictionary<string, IProjectileItemGroup> dict_item_groups = new Dictionary<string, IProjectileItemGroup>();
public static IProjectileItemGroup Get(string name) => dict_item_groups[name];
public static void InitClass(ItemClass item, string typename)
{
if (dict_item_groups.ContainsKey(item.Name))
{
return;
}
Type type = ReflectionHelpers.GetTypeWithPrefix("PIG", typename);
IProjectileItemGroup group = (IProjectileItemGroup)Activator.CreateInstance(type, new object[] {item});
dict_item_groups.Add(item.Name, group);
}
public static void Update()
{
foreach (var group in dict_item_groups.Values)
{
group.Update();
}
}
public static void FixedUpdate()
{
foreach (var group in dict_item_groups.Values)
{
group.FixedUpdate();
}
}
public static void Cleanup()
{
foreach (var group in dict_item_groups.Values)
{
group.Cleanup();
}
dict_item_groups.Clear();
}
}
}

View File

@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public class PHGameObject : ParameterHolderAbs, IDisposable
{
public Transform Transform { get; }
public PHGameObject(Transform transform, ProjectileParams par) : base(par)
{
Transform = transform;
}
public override void Fire()
{
Transform.gameObject.SetActive(true);
UpdatePosition();
}
public override void UpdatePosition()
{
Vector3 realPos = par.renderPosition - Origin.position;
Transform.position = realPos;
Transform.LookAt(realPos + par.moveDir);
}
public void Dispose()
{
GameObject.Destroy(Transform.gameObject);
}
}
}

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using static AstarManager;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public class SimpleMeshTransformData
{
public readonly Matrix4x4 TRS;
public readonly Bounds bounds;
public SimpleMeshTransformData(Matrix4x4 TRS, Bounds bounds)
{
this.TRS = TRS;
this.bounds = bounds;
}
}
public class PHSimpleMesh : ParameterHolderAbs
{
public Matrix4x4 finalMat;
public RenderParams renderParams;
private SimpleMeshTransformData data;
public PHSimpleMesh(ProjectileParams par, SimpleMeshTransformData data, in RenderParams renderPar) : base(par)
{
this.data = data;
this.renderParams = renderPar;
}
public override void Fire()
{
UpdatePosition();
}
public override void UpdatePosition()
{
finalMat = Matrix4x4.Translate(par.renderPosition - Origin.position) * Matrix4x4.Rotate(Quaternion.LookRotation(par.moveDir)) * data.TRS;
Bounds bounds = data.bounds;
bounds.center = finalMat.MultiplyPoint3x4(bounds.center);
renderParams.worldBounds = bounds;
}
}
}

View File

@@ -0,0 +1,49 @@
using FullautoLauncher.Scripts.ProjectileManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
public class PIGGameObject : ProjectileItemGroupAbs<PHGameObject>
{
public PIGGameObject(ItemClass item) : base(item)
{
}
public override void Cleanup()
{
foreach (var proj in queue_pool)
{
proj.Dispose();
}
queue_pool.Clear();
foreach (var set in dict_fired_projectiles.Values)
{
foreach (var proj in set)
{
proj.Dispose();
}
}
dict_fired_projectiles.Clear();
}
public override void Pool(PHGameObject par)
{
base.Pool(par);
par.Transform.gameObject.SetActive(false);
}
public override void Update()
{
}
protected override PHGameObject Create(ProjectileParams par)
{
return new PHGameObject(item.CloneModel(GameManager.Instance.World, new ItemValue(item.Id), Vector3.zero, null), par);
}
}

View File

@@ -0,0 +1,54 @@
using FullautoLauncher.Scripts.ProjectileManager;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
public class PIGSimpleMesh : ProjectileItemGroupAbs<PHSimpleMesh>
{
private Transform renderTrans;
private Mesh mesh;
private RenderParams renderParams;
private SimpleMeshTransformData data;
public PIGSimpleMesh(ItemClass item) : base(item)
{
renderTrans = item.CloneModel(GameManager.Instance.World, new ItemValue(item.Id), Vector3.zero, null);
MeshFilter filter = renderTrans.GetComponentInChildren<MeshFilter>();
mesh = filter.sharedMesh;
MeshRenderer renderer = renderTrans.GetComponentInChildren<MeshRenderer>();
renderParams = new RenderParams(renderer.material)
{
layer = renderer.gameObject.layer,
lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off,
shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off,
rendererPriority = renderer.rendererPriority,
renderingLayerMask = renderer.renderingLayerMask,
motionVectorMode = MotionVectorGenerationMode.ForceNoMotion
};
data = new SimpleMeshTransformData(renderer.localToWorldMatrix, mesh.bounds);
}
public override void Update()
{
if (GameManager.IsDedicatedServer)
{
return;
}
foreach (var set in dict_fired_projectiles.Values)
{
foreach (var projectile in set)
{
Graphics.RenderMesh(in projectile.renderParams, mesh, 0, projectile.finalMat);
}
}
}
protected override PHSimpleMesh Create(ProjectileParams par)
{
return new PHSimpleMesh(par, data, in renderParams);
}
}

View File

@@ -0,0 +1,32 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public abstract class ParameterHolderAbs
{
public ProjectileParams Params => par;
protected readonly ProjectileParams par;
protected ParameterHolderAbs(ProjectileParams par)
{
this.par = par;
}
public override int GetHashCode()
{
return par.ProjectileID;
}
public abstract void UpdatePosition();
public virtual void Fire()
{
}
}
}

View File

@@ -0,0 +1,121 @@
using System.Collections.Generic;
using UnityEngine;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public interface IProjectileItemGroup
{
void Pool(int count);
ProjectileParams Fire(int entityID, ProjectileParams.ItemInfo info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f);
void Update();
void FixedUpdate();
void Cleanup();
}
public abstract class ProjectileItemGroupAbs<T> : IProjectileItemGroup where T : ParameterHolderAbs
{
protected readonly Queue<T> queue_pool = new Queue<T>();
protected readonly Dictionary<int, HashSet<T>> dict_fired_projectiles = new Dictionary<int, HashSet<T>>();
protected readonly ItemClass item;
protected readonly int maxPoolCount = 1000;
private int nextID;
private int NextID { get => nextID++; }
public ProjectileItemGroupAbs(ItemClass item)
{
this.item = item;
}
protected abstract T Create(ProjectileParams par);
public virtual void Cleanup()
{
}
public void Pool(int count)
{
count = Mathf.Min(maxPoolCount - queue_pool.Count, count - queue_pool.Count);
for (int i = 0; i < count; i++)
{
queue_pool.Enqueue(Create(new ProjectileParams(NextID)));
}
}
public virtual void Pool(T par)
{
if (maxPoolCount > queue_pool.Count)
{
par.Params.bOnIdealPosition = false;
queue_pool.Enqueue(par);
}
}
public ProjectileParams Fire(int entityID, ProjectileParams.ItemInfo info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f)
{
T par = queue_pool.Count == 0 ? Create(new ProjectileParams(NextID)) : queue_pool.Dequeue();
if(!dict_fired_projectiles.TryGetValue(entityID, out HashSet<T> set))
{
set = new HashSet<T>();
dict_fired_projectiles.Add(entityID, set);
}
set.Add(par);
par.Params.Fire(info, _idealStartPosition, _realStartPosition, _flyDirection, _firingEntity, _hmOverride, _radius);
par.Fire();
return par.Params;
}
public abstract void Update();
private List<T> list_remove = new List<T>();
public void FixedUpdate()
{
if (GameManager.Instance == null || GameManager.Instance.IsPaused() || GameManager.Instance.World == null)
return;
foreach (var pair in dict_fired_projectiles)
{
EntityAlive entityAlive = GameManager.Instance.World.GetEntity(pair.Key) as EntityAlive;
list_remove.Clear();
foreach (var projectile in pair.Value)
{
if (projectile.Params.UpdatePosition())
{
list_remove.Add(projectile);
}
else
{
projectile.UpdatePosition();
}
}
if (entityAlive != null)
{
int prevLayer = 0;
if (entityAlive.emodel != null)
{
prevLayer = entityAlive.GetModelLayer();
entityAlive.SetModelLayer(2, false, null);
}
foreach (var projectile in pair.Value)
{
if (projectile.Params.CheckCollision(entityAlive))
{
list_remove.Add(projectile);
}
}
if (entityAlive.emodel != null)
{
entityAlive.SetModelLayer(prevLayer, false, null);
}
}
foreach (var remove in list_remove)
{
pair.Value.Remove(remove);
Pool(remove);
}
list_remove.Clear();
}
}
}
}

View File

@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
namespace FullautoLauncher.Scripts.ProjectileManager
{
public class ProjectileParams
{
public int ProjectileID;
public ItemInfo info;
public Vector3 flyDirection;
public Vector3 renderPosition;
public Vector3 velocity;
public Vector3 previousPosition;
public Vector3 currentPosition;
public Vector3 gravity;
public Vector3 moveDir;
public float timeShotStarted;
public int hmOverride;
public float radius;
public bool bOnIdealPosition = false;
public CollisionParticleController waterCollisionParticles = new CollisionParticleController();
public ProjectileParams(int projectileID)
{
ProjectileID = projectileID;
}
public void Fire(ItemInfo _info, Vector3 _idealStartPosition, Vector3 _realStartPosition, Vector3 _flyDirection, Entity _firingEntity, int _hmOverride = 0, float _radius = 0f)
{
info = _info;
flyDirection = _flyDirection.normalized;
moveDir = flyDirection;
previousPosition = currentPosition = _idealStartPosition;
renderPosition = _realStartPosition;
velocity = flyDirection.normalized * EffectManager.GetValue(PassiveEffects.ProjectileVelocity, info.itemValueLauncher, info.itemActionProjectile.Velocity, _firingEntity as EntityAlive);
hmOverride = _hmOverride;
radius = _radius;
waterCollisionParticles.Init(_firingEntity.entityId, info.itemProjectile.MadeOfMaterial.SurfaceCategory, "water", 16);
gravity = Vector3.up * EffectManager.GetValue(PassiveEffects.ProjectileGravity, info.itemValueLauncher, info.itemActionProjectile.Gravity, _firingEntity as EntityAlive);
timeShotStarted = Time.time;
}
public override int GetHashCode()
{
return ProjectileID;
}
public bool UpdatePosition()
{
float flyTime = Time.time - timeShotStarted;
if (flyTime >= info.itemActionProjectile.LifeTime)
{
return true;
}
if (flyTime >= info.itemActionProjectile.FlyTime)
{
velocity += gravity * Time.fixedDeltaTime;
}
moveDir = velocity * Time.fixedDeltaTime;
previousPosition = currentPosition;
currentPosition += moveDir;
renderPosition += moveDir;
if (!bOnIdealPosition)
{
bOnIdealPosition = flyTime > 0.5f;
}
if(!bOnIdealPosition)
{
renderPosition = Vector3.Lerp(renderPosition, currentPosition, flyTime * 2f);
}
//Log.Out($"projectile {ProjectileID} position {currentPosition} entity position {info.actionData.invData.holdingEntity.position}");
return false;
}
public bool CheckCollision(EntityAlive entityAlive)
{
//Already checked in ItemActionBetterLauncher.ItemActionEffects, projectiles are not created on dedi if fired from remote entity.
//if(entityAlive.isEntityRemote && GameManager.IsDedicatedServer)
// return true;
World world = GameManager.Instance.World;
Vector3 dir = currentPosition - previousPosition;
Vector3 dirNorm = dir.normalized;
float magnitude = dir.magnitude;
if (magnitude < 0.04f)
{
return false;
}
Ray ray = new Ray(previousPosition, dir);
waterCollisionParticles.CheckCollision(ray.origin, ray.direction, magnitude, (entityAlive != null) ? entityAlive.entityId : (-1));
int hitmask = ((hmOverride == 0) ? 80 : hmOverride);
bool bHit = Voxel.Raycast(world, ray, magnitude, -538750997, hitmask, radius);
if (bHit && (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag) || Voxel.voxelRayHitInfo.tag.StartsWith("E_")))
{
if (entityAlive != null && !entityAlive.isEntityRemote)
{
entityAlive.MinEventContext.Other = ItemActionAttack.FindHitEntity(Voxel.voxelRayHitInfo) as EntityAlive;
ItemActionAttack.AttackHitInfo attackHitInfo = new ItemActionAttack.AttackHitInfo
{
WeaponTypeTag = ItemActionAttack.RangedTag
};
ItemActionAttack.Hit(Voxel.voxelRayHitInfo,
entityAlive.entityId,
EnumDamageTypes.Piercing,
info.itemActionProjectile.GetDamageBlock(info.itemValueLauncher, ItemActionAttack.GetBlockHit(world, Voxel.voxelRayHitInfo), entityAlive, info.actionData.indexInEntityOfAction),
info.itemActionProjectile.GetDamageEntity(info.itemValueLauncher, entityAlive, info.actionData.indexInEntityOfAction),
1f,
1f,
EffectManager.GetValue(PassiveEffects.CriticalChance, info.itemValueLauncher, info.itemProjectile.CritChance.Value, entityAlive, null, info.itemProjectile.ItemTags),
ItemAction.GetDismemberChance(info.actionData, Voxel.voxelRayHitInfo),
info.itemProjectile.MadeOfMaterial.SurfaceCategory,
info.itemActionProjectile.GetDamageMultiplier(),
info.itemActionProjectile.BuffActions,
attackHitInfo,
1,
info.itemActionProjectile.ActionExp,
info.itemActionProjectile.ActionExpBonusMultiplier,
null,
null,
ItemActionAttack.EnumAttackMode.RealNoHarvesting,
null,
-1,
info.itemValueLauncher);
if (entityAlive.MinEventContext.Other == null)
{
entityAlive.FireEvent(MinEventTypes.onSelfPrimaryActionMissEntity, true);
}
entityAlive.FireEvent(MinEventTypes.onProjectileImpact, false);
MinEventParams.CachedEventParam.Self = entityAlive;
MinEventParams.CachedEventParam.Position = Voxel.voxelRayHitInfo.hit.pos;
MinEventParams.CachedEventParam.ItemValue = info.itemValueProjectile;
MinEventParams.CachedEventParam.Other = entityAlive.MinEventContext.Other;
info.itemProjectile.FireEvent(MinEventTypes.onProjectileImpact, MinEventParams.CachedEventParam);
if (info.itemActionProjectile.Explosion.ParticleIndex > 0)
{
Vector3 vector3 = Voxel.voxelRayHitInfo.hit.pos - dirNorm * 0.1f;
Vector3i vector3i = World.worldToBlockPos(vector3);
if (!world.GetBlock(vector3i).isair)
{
BlockFace blockFace;
vector3i = Voxel.OneVoxelStep(vector3i, vector3, -dirNorm, out vector3, out blockFace);
}
GameManager.Instance.ExplosionServer(Voxel.voxelRayHitInfo.hit.clrIdx, vector3, vector3i, Quaternion.identity, info.itemActionProjectile.Explosion, entityAlive.entityId, 0f, false, info.itemValueProjectile);
}
else if (info.itemProjectile.IsSticky)
{
GameRandom gameRandom = world.GetGameRandom();
if (GameUtils.IsBlockOrTerrain(Voxel.voxelRayHitInfo.tag))
{
if (gameRandom.RandomFloat < EffectManager.GetValue(PassiveEffects.ProjectileStickChance, info.itemValueLauncher, 0.5f, entityAlive, null, info.itemProjectile.ItemTags | FastTags<TagGroup.Global>.Parse(Voxel.voxelRayHitInfo.fmcHit.blockValue.Block.blockMaterial.SurfaceCategory)))
{
global::ProjectileManager.AddProjectileItem(null, -1, Voxel.voxelRayHitInfo.hit.pos, dirNorm, info.itemValueProjectile.type);
}
else
{
GameManager.Instance.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, info.itemProjectile.MadeOfMaterial.SurfaceCategory), null), entityAlive.entityId, false, false);
}
}
else if (gameRandom.RandomFloat < EffectManager.GetValue(PassiveEffects.ProjectileStickChance, info.itemValueLauncher, 0.5f, entityAlive, null, info.itemProjectile.ItemTags))
{
int id = global::ProjectileManager.AddProjectileItem(null, -1, Voxel.voxelRayHitInfo.hit.pos, dirNorm, info.itemValueProjectile.type);
Utils.SetLayerRecursively(global::ProjectileManager.GetProjectile(id).gameObject, 14, null);
}
else
{
GameManager.Instance.SpawnParticleEffectServer(new ParticleEffect("impact_metal_on_wood", Voxel.voxelRayHitInfo.hit.pos, Utils.BlockFaceToRotation(Voxel.voxelRayHitInfo.fmcHit.blockFace), 1f, Color.white, "bullethitwood", null), entityAlive.entityId, false, false);
}
}
}
return true;
}
return false;
}
public class ItemInfo
{
public ItemActionProjectile itemActionProjectile;
public ItemClass itemProjectile;
public ItemValue itemValueProjectile;
public ItemValue itemValueLauncher;
public ItemActionData actionData;
}
}
}