Grid/Destruction/WallColumnDamage.cs
using Sandbox;
using Sandbox.Sboku;
using SWB.Base;
using System;
using System.Numerics;
public sealed class WallColumnDamage : Component
{
//public GameObject SnowCap { get; set; }
[Property] public int GridIndex { get; set; }
[Property] public int MaxHealth { get; set; } = 62;
[Property] public DebrisVisualPool DebrisPool { get; set; }
[Property] public GameObject DustPrefab { get; set; }
[Property] public Material WallMaterial { get; set; }
[Property] public int ColumnHeight { get; set; }
[Property] public float GridSize { get; set; }
[Property] public bool IsIndestructible { get; set; }
Model BoxModel;
private int health;
private bool destroyed;
private bool isSegmented;
public int DestroyedSegmentCount { get; private set; }
public bool FullyDestroyed { get; private set; }
protected override void OnStart()
{
health = MaxHealth;
BoxModel = Model.Load("models/dev/box.vmdl");
}
public void OnSegmentDestroyed(Vector3 hitWorldPos)
{
if (FullyDestroyed)
return;
DestroyedSegmentCount++;
// =========================
// YLIN SEGMENTTI → POISTA LUMI + PIENI DUST
// =========================
/*float columnWorldHeight = GridSize * ColumnHeight;
float columnBottomZ = Transform.Position.z - columnWorldHeight * 0.5f;
float topSegmentMinZ = columnBottomZ + (ColumnHeight - 1) * GridSize;
if (hitWorldPos.z >= topSegmentMinZ)
{
if (SnowCap.IsValid())
{
SnowCap.Destroy();
SnowCap = null;
}
}*/
// 💨 PIENI SAVUPUFFI YLÄOSASSA
if (DustPrefab.IsValid())
{
var dust = DustPrefab.Clone(
new Vector3(
Transform.Position.x,
Transform.Position.y,
hitWorldPos.z
)
);
dust.DestroyAsync(1.5f);
}
// =========================
// KOKO PYLVÄS TUHOUTUU
// =========================
if (DestroyedSegmentCount >= 2)
{
FullyDestroyed = true;
DestroyWholeColumn(hitWorldPos);
RpcDestroyWholeColumn(hitWorldPos);
}
}
// =========================
// CLIENT → HOST
// =========================
[Rpc.Host]
public void RpcHit(int damage, Vector3 hitWorldPos)
{
if (destroyed || IsIndestructible)
return;
if (!isSegmented)
{
ConvertToSegmented();
RpcConvertToSegmented();
}
ApplyDamage(damage, hitWorldPos);
}
[Rpc.Broadcast]
private void RpcConvertToSegmented()
{
if (isSegmented)
return;
ConvertToSegmented();
}
private void ConvertToSegmented()
{
isSegmented = true;
var worldRot = Transform.Rotation;
var worldScale = Transform.Scale;
float columnWorldHeight = GridSize * ColumnHeight;
// 🔑 PYLVAAN ALAPINTA
Vector3 columnBottom =
Transform.Position - Vector3.Up * (columnWorldHeight * 0.5f);
// Poista vanha pylväs
Components.Get<ModelRenderer>()?.Destroy();
Components.Get<BoxCollider>()?.Destroy();
Components.Get<ModelPhysics>()?.Destroy();
// 🔑 Parent ei enää skaalaa
Transform.Scale = Vector3.One;
for (int i = 0; i < ColumnHeight; i++)
{
var seg = new GameObject($"WallSeg_{i}");
seg.SetParent(GameObject, false);
// 🔥 OIKEA WORLD-POSITION
seg.Transform.Position =
columnBottom + Vector3.Up * ((i + 0.5f) * GridSize);
seg.Transform.Rotation = worldRot;
// 🔑 Skaala lapselle
seg.Transform.Scale = new Vector3(
worldScale.x,
worldScale.y,
worldScale.z / ColumnHeight
);
var renderer = seg.Components.Create<ModelRenderer>();
renderer.Model = BoxModel;
var wallMaterial = WallMaterial.CreateCopy();
// 🔑 segmentin z-skaala määrittää tekstuurin pystytoiston,
// jotta UV ei veny/puristu kun pylväs pilkotaan osiin
float segmentScaleZ = worldScale.z / ColumnHeight;
wallMaterial.Set(
"g_vTexCoordScale",
new Vector2(1f, segmentScaleZ)
);
// 🔑 offset jatkaa UV:tä ylöspäin segmentin koon mukaan
wallMaterial.Set(
"g_vTexCoordOffset",
new Vector2(0f, i * segmentScaleZ)
);
renderer.MaterialOverride = wallMaterial;
var collider = seg.Components.Create<BoxCollider>();
collider.Static = true;
var dmg = seg.Components.Create<WallSegmentDamage>();
dmg.ParentColumn = this;
dmg.MaxHealth = MaxHealth / ColumnHeight;
}
}
// =========================
// HOST LOGIC
// =========================
private void ApplyDamage(int damage, Vector3 hitWorldPos)
{
if (!Networking.IsHost)
return;
health -= damage;
if (health > 0)
return;
destroyed = true;
// 🔥 tee hostilla heti
DestroyVisual(hitWorldPos);
// 🔑 kerro kaikille muille
RpcDestroyVisual(hitWorldPos);
}
// =========================
// HOST → ALL
// =========================
[Rpc.Broadcast]
private void RpcDestroyWholeColumn(Vector3 hitWorldPos)
{
if (Networking.IsHost)
return;
DestroyWholeColumn(hitWorldPos);
}
[Rpc.Broadcast]
private void RpcDestroyVisual(Vector3 hitWorldPos)
{
if (destroyed && Networking.IsHost)
return;
destroyed = true;
DestroyVisual(hitWorldPos);
}
// =========================
// VISUAL
// =========================
private void DestroyWholeColumn(Vector3 hitWorldPos)
{
/*if (SnowCap.IsValid())
{
SnowCap.Destroy();
SnowCap = null;
}*/
if (DustPrefab.IsValid())
{
var dust = DustPrefab.Clone(hitWorldPos + Vector3.Up * 16f);
dust.DestroyAsync(3f);
}
DebrisPool.SpawnColumnDebris(
Transform.Position,
ColumnHeight,
GridSize,
0,
WallMaterial
);
GameObject.Destroy();
}
private void DestroyVisual(Vector3 hitWorldPos)
{
/*if (SnowCap.IsValid())
{
SnowCap.Destroy();
SnowCap = null;
}*/
if (DustPrefab.IsValid())
{
var dust = DustPrefab.Clone(hitWorldPos + Vector3.Up * 16f);
dust.DestroyAsync(1.5f);
}
DebrisPool.SpawnColumnDebris(
Transform.Position,
ColumnHeight,
GridSize,
0,
WallMaterial
);
GameObject.Destroy();
}
}