UnityTowerDefense/Assets/_Project/Scripts/Gameplay/TowerPlacementSettings.cs

101 lines
No EOL
5.2 KiB
C#

// Assets/_Project/Scripts/Gameplay/TowerPlacementSettings.cs
using UnityEngine;
namespace TD.Gameplay
{
/// <summary>
/// Project-wide settings for the tower placement system. One asset shared across all
/// tower types — assign it to <see cref="TowerPlacementController"/> in the scene.
/// </summary>
/// <remarks>
/// <para><b>Why not on TowerDefinition?</b> Ghost materials and rejection messages are
/// identical for every tower type. Putting them on TowerDefinition would mean 100+
/// assets each holding two redundant material references that must be kept in sync.
/// A single shared settings asset is easier to maintain and impossible to accidentally
/// de-sync.</para>
///
/// <para><b>Rejection messages.</b> These are the strings shown on screen when the
/// server rejects a placement. Kept here so designers can tune the wording without
/// touching code.</para>
///
/// <para><b>Note on build-site visuals.</b> The green queued-ghost material and the
/// constructing-stage material live on the <c>BuildSiteVisual</c> prefab, not here.
/// Those materials are prefab-local (the build-site visual prefab references them
/// directly), so promoting them to a shared settings asset would just add an
/// indirection without simplifying anything.</para>
/// </remarks>
[CreateAssetMenu(fileName = "TowerPlacementSettings",
menuName = "TD/Tower Placement Settings",
order = 3)]
public class TowerPlacementSettings : ScriptableObject
{
// ----- Cursor ghost visuals ---------------------------------------
[Header("Cursor Ghost Materials")]
[Tooltip("Material applied to the cursor placement ghost when placement is valid. " +
"Should be a transparent/unlit white material so the tower mesh is " +
"recognizable but clearly distinct from a placed tower.")]
public Material GhostValidMaterial;
[Tooltip("Material applied to the cursor placement ghost when placement is invalid. " +
"Should be a transparent/unlit red material.")]
public Material GhostInvalidMaterial;
// ----- Rejection messages -----------------------------------------
[Header("Rejection Messages")]
[Tooltip("Shown when the server rejects a placement because tiles belong to " +
"another player's zone.")]
public string MessageWrongOwner = "You can only place towers in your own zone.";
[Tooltip("Shown when the server rejects because one or more tiles are " +
"Restricted or Outside the map.")]
public string MessageTileNotBuildable = "That location is not buildable.";
[Tooltip("Shown when the server rejects because a tile is already occupied " +
"by an existing tower or by a queued/constructing build job.")]
public string MessageTileOccupied = "A tower is already there or queued there.";
[Tooltip("Shown when the server rejects because the player cannot afford " +
"the tower.")]
public string MessageInsufficientGold = "Not enough gold.";
[Tooltip("Shown when the server rejects because the builder is too far away " +
"from the requested location.")]
public string MessageOutOfRange = "Your builder is too far away.";
[Tooltip("Shown when the server rejects because placing the tower would block " +
"all valid paths through the player's zone.")]
public string MessageBlocksPath = "That placement would block the path.";
[Tooltip("Shown when the server rejects because the builder's queue is full. " +
"Player must cancel pending jobs or wait for one to complete.")]
public string MessageJobLimitReached = "Builder queue is full.";
[Tooltip("Shown for unexpected server-side errors (invalid tower type, etc.). " +
"Should rarely appear in normal play.")]
public string MessageServerError = "Placement failed. Please try again.";
// ----- Helpers -----------------------------------------------------
/// <summary>
/// Returns the human-readable rejection message for the given reason.
/// </summary>
public string GetRejectionMessage(PlacementRejectionReason reason)
{
switch (reason)
{
case PlacementRejectionReason.WrongOwner: return MessageWrongOwner;
case PlacementRejectionReason.TileNotBuildable: return MessageTileNotBuildable;
case PlacementRejectionReason.TileOccupied: return MessageTileOccupied;
case PlacementRejectionReason.InsufficientGold: return MessageInsufficientGold;
case PlacementRejectionReason.OutOfRange: return MessageOutOfRange;
case PlacementRejectionReason.BlocksPath: return MessageBlocksPath;
case PlacementRejectionReason.JobLimitReached: return MessageJobLimitReached;
case PlacementRejectionReason.InvalidTowerType:
case PlacementRejectionReason.ServerError:
default: return MessageServerError;
}
}
}
}