| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- using TMPro;
- using UnityEngine;
- public enum ActionDecisionState
- {
- NoAction,
- ActionSelected,
- ActionExecuted,
- EnemyAction
- }
- [System.Serializable]
- public class CharacterActionData
- {
- public ActionDecisionState state = ActionDecisionState.NoAction;
- public Vector3 targetPosition;
- public GameObject targetEnemy;
- public bool hasTarget => targetEnemy != null || targetPosition != Vector3.zero;
- public void Reset()
- {
- state = ActionDecisionState.NoAction;
- targetPosition = Vector3.zero;
- targetEnemy = null;
- }
- public void SetMoveTarget(Vector3 position)
- {
- targetPosition = position;
- targetEnemy = null;
- state = ActionDecisionState.ActionSelected;
- }
- public void SetAttackTarget(GameObject enemy)
- {
- targetEnemy = enemy;
- targetPosition = Vector3.zero;
- state = ActionDecisionState.ActionSelected;
- }
- }
- public abstract class Character : MonoBehaviour
- {
- private string characterName;
- private int maxHealth;
- private int currentHealth;
- private int attack;
- private int constitution;
- private int dexterity;
- private int wisdom;
- private Sprite characterSprite;
- private int level;
- private int experiencePoints;
- private int experienceToLevelUp;
- private int initModifier;
- private int damageModifier;
- private int spellModifier;
- private int movementSpeed;
- private int armorClass;
- private bool isDead = false;
- private bool isTargetable = true;
- private Bank bank;
- private Inventory inventory;
- private TextMeshProUGUI nameText;
- // Health bar components
- private UnityEngine.UI.Slider healthSlider;
- private Canvas healthBarCanvas;
- // Add action system components
- [Header("Action System")]
- public CharacterActionData actionData = new CharacterActionData();
- [Header("Visual Feedback Settings")]
- public Color noActionColor = Color.yellow;
- public Color actionSelectedColor = Color.green;
- public Color normalColor = Color.white;
- public float outlineWidth = 0.1f;
- // Visual feedback components
- private Renderer characterRenderer;
- private Material characterMaterial;
- private Color originalColor;
- private GameObject outlineObject;
- public string CharacterName
- {
- get => characterName; set
- {
- characterName = value;
- UpdateNameDisplay();
- }
- }
- public int MaxHealth { get => maxHealth; set => maxHealth = value; }
- public int CurrentHealth { get => currentHealth; set => currentHealth = value; }
- public int Attack { get => attack; set => attack = value; }
- public int Constitution { get => constitution; set => constitution = value; }
- public int Dexterity { get => dexterity; set => dexterity = value; }
- public int Wisdom { get => wisdom; set => wisdom = value; }
- public Sprite CharacterSprite { get => characterSprite; set => characterSprite = value; }
- public int Level { get => level; set => level = value; }
- public int ExperiencePoints { get => experiencePoints; set => experiencePoints = value; }
- public int ExperienceToLevelUp { get => experienceToLevelUp; set => experienceToLevelUp = value; }
- public int InitModifier { get => initModifier; set => initModifier = value; }
- public int DamageModifier { get => damageModifier; set => damageModifier = value; }
- public int SpellModifier { get => spellModifier; set => spellModifier = value; }
- public int MovementSpeed { get => movementSpeed; set => movementSpeed = value; }
- public int ArmorClass { get => armorClass; set => armorClass = value; }
- public bool IsDead { get => isDead; }
- public bool IsTargetable { get => isTargetable; }
- protected Weapon spawnedWeapon;
- public Weapon Weapon { get => spawnedWeapon; set => spawnedWeapon = value; }
- public abstract Character Spawn(int count);
- public abstract Weapon GetWeapon();
- public Character()
- {
- }
- void Start()
- {
- InitializeStats();
- // SpawnAndEquipWeapon();
- InitializeActionSystem();
- Debug.Log($"Character Start() completed for {CharacterName}");
- healthSlider = GetComponentInChildren<UnityEngine.UI.Slider>();
- }
- protected virtual void InitializeStats()
- {
- // Override in subclasses to set character stats
- }
- public Weapon GetEquippedWeapon()
- {
- return spawnedWeapon;
- }
- public virtual void SpawnAndEquipWeapon()
- {
- Debug.Log($"SpawnAndEquipWeapon called for {CharacterName}");
- Weapon weaponPrefab = GetWeapon();
- Debug.Log($"GetWeapon returned: {(weaponPrefab != null ? weaponPrefab.name : "null")}");
- if (weaponPrefab != null)
- {
- GameObject weaponInstance = Instantiate(weaponPrefab.gameObject);
- weaponInstance.transform.SetParent(this.transform, false);
- Debug.Log($"Instantiated weapon: {weaponInstance.name}");
- spawnedWeapon = weaponInstance.GetComponent<Weapon>();
- Debug.Log($"spawnedWeapon component: {(spawnedWeapon != null ? spawnedWeapon.GetType().Name : "null")}");
- if (spawnedWeapon != null)
- {
- // Set the wielder reference directly to avoid timing issues
- spawnedWeapon.SetWielder(this);
- Debug.Log($"{CharacterName} equipped {spawnedWeapon.weaponName}");
- // Position the weapon appropriately (you may want to customize this)
- PositionWeapon(weaponInstance);
- }
- else
- {
- Debug.LogError($"Weapon prefab for {CharacterName} doesn't have a Weapon component!");
- Destroy(weaponInstance);
- }
- }
- else
- {
- Debug.Log($"{CharacterName} no weapon prefab, trying direct creation...");
- CreateDirectWeapon();
- }
- }
- protected virtual Weapon CreateDirectWeapon()
- {
- Debug.LogWarning($"{CharacterName} - CreateDirectWeapon not implemented in subclass!");
- return null;
- }
- // Virtual method to position the weapon - can be overridden by specific character types
- protected virtual void PositionWeapon(GameObject weaponInstance)
- {
- // Default positioning - you can customize this based on your character setup
- weaponInstance.transform.localPosition = Vector3.zero;
- weaponInstance.transform.localRotation = Quaternion.identity;
- weaponInstance.transform.localScale = Vector3.one; // Ensure proper scale
- // Optional: Look for specific attachment points
- Transform weaponAttachPoint = transform.Find("WeaponAttachPoint");
- if (weaponAttachPoint != null)
- {
- weaponInstance.transform.SetParent(weaponAttachPoint, false);
- weaponInstance.transform.localPosition = Vector3.zero;
- weaponInstance.transform.localRotation = Quaternion.identity;
- Debug.Log($"Weapon attached to {weaponAttachPoint.name}");
- }
- else
- {
- // If no specific attach point, just position relative to character
- weaponInstance.transform.localPosition = new Vector3(0.5f, 0, 0); // Slightly to the right
- Debug.Log($"Weapon positioned at default location relative to {CharacterName}");
- }
- }
- void Awake()
- {
- nameText = GetComponentInChildren<TextMeshProUGUI>();
- if (nameText == null)
- {
- Debug.LogWarning($"No TextMeshProUGUI component found in children of {gameObject.name}");
- }
- // Initialize visual feedback system
- InitializeVisualFeedback();
- UpdateNameDisplay();
- }
- // Initialize visual feedback components
- private void InitializeVisualFeedback()
- {
- characterRenderer = GetComponent<Renderer>();
- if (characterRenderer != null)
- {
- // Create a unique material instance to avoid modifying shared materials
- characterMaterial = new Material(characterRenderer.material);
- characterRenderer.material = characterMaterial;
- originalColor = characterMaterial.color;
- }
- else
- {
- Debug.LogWarning($"No Renderer found on {gameObject.name}. Visual feedback will not work.");
- }
- // Ensure we have a collider for mouse detection
- Collider col = GetComponent<Collider>();
- if (col == null)
- {
- col = gameObject.AddComponent<CapsuleCollider>();
- Debug.Log($"Added CapsuleCollider to {gameObject.name} for mouse detection");
- }
- }
- private void UpdateNameDisplay()
- {
- if (nameText != null && !string.IsNullOrEmpty(characterName))
- {
- nameText.text = characterName;
- }
- }
- public void RefreshNameDisplay()
- {
- UpdateNameDisplay();
- }
- public virtual float GetAttackRange()
- {
- return spawnedWeapon != null ? spawnedWeapon.Range : 1.0f; // Default range if no weapon is equipped
- }
- private void InitializeActionSystem()
- {
- actionData.Reset();
- SetVisualState(ActionDecisionState.NoAction);
- }
- public void SetVisualState(ActionDecisionState state)
- {
- if (characterRenderer == null) return;
- switch (state)
- {
- case ActionDecisionState.NoAction:
- ShowNoActionState();
- break;
- case ActionDecisionState.ActionSelected:
- ShowActionSelectedState();
- break;
- default:
- ShowNormalState();
- break;
- }
- }
- private void ShowNoActionState()
- {
- if (characterMaterial != null)
- characterMaterial.color = noActionColor;
- CreateOutline();
- }
- private void ShowActionSelectedState()
- {
- if (characterMaterial != null)
- characterMaterial.color = actionSelectedColor;
- RemoveOutline();
- }
- private void ShowNormalState()
- {
- if (characterMaterial != null)
- characterMaterial.color = originalColor;
- RemoveOutline();
- }
- private void CreateOutline()
- {
- if (outlineObject != null) return;
- outlineObject = new GameObject("Outline");
- outlineObject.transform.SetParent(transform);
- outlineObject.transform.localPosition = Vector3.zero;
- outlineObject.transform.localScale = Vector3.one * (1f + outlineWidth);
- // For 3D characters, we'll create a simple colored outline using a MeshRenderer
- MeshFilter meshFilter = outlineObject.AddComponent<MeshFilter>();
- MeshRenderer outlineRenderer = outlineObject.AddComponent<MeshRenderer>();
- // Copy the mesh from the character
- MeshFilter characterMesh = GetComponent<MeshFilter>();
- if (characterMesh != null)
- {
- meshFilter.mesh = characterMesh.mesh;
- }
- // Create a simple black material for the outline
- Material outlineMaterial = new Material(Shader.Find("Standard"));
- outlineMaterial.color = Color.black;
- outlineRenderer.material = outlineMaterial;
- // Set rendering order to appear behind the character
- outlineRenderer.sortingOrder = -1;
- }
- private void RemoveOutline()
- {
- if (outlineObject != null)
- {
- DestroyImmediate(outlineObject);
- outlineObject = null;
- }
- }
- public void ExecuteAction()
- {
- Debug.Log($"=== ExecuteAction called for {CharacterName} ===");
- Debug.Log($"Action state: {actionData.state}");
- Debug.Log($"Has target enemy: {actionData.targetEnemy != null}");
- Debug.Log($"Target enemy name: {(actionData.targetEnemy != null ? actionData.targetEnemy.name : "none")}");
- if (actionData.state != ActionDecisionState.ActionSelected)
- {
- Debug.Log($"Action not selected, returning. State: {actionData.state}");
- return;
- }
- if (actionData.targetEnemy != null)
- {
- Debug.Log($"{CharacterName} executing attack on {actionData.targetEnemy.name}");
- // Get equipped weapon and perform attack
- Debug.Log($"Getting equipped weapon for {CharacterName}...");
- Debug.Log($"spawnedWeapon is: {(spawnedWeapon != null ? spawnedWeapon.GetType().Name : "null")}");
- Weapon equippedWeapon = GetEquippedWeapon();
- Debug.Log($"GetEquippedWeapon returned: {(equippedWeapon != null ? equippedWeapon.GetType().Name : "null")}");
- if (equippedWeapon != null)
- {
- Debug.Log($"Using weapon: {equippedWeapon.weaponName}");
- equippedWeapon.Attack(actionData.targetEnemy);
- Character targetCharacter = actionData.targetEnemy.GetComponent<Character>();
- if (targetCharacter != null && targetCharacter.IsDead)
- {
- actionData.state = ActionDecisionState.ActionExecuted;
- SetVisualState(actionData.state);
- }
- }
- else
- {
- Debug.LogError($"{CharacterName} has no weapon equipped for attack!");
- Debug.LogError($"spawnedWeapon field is: {(spawnedWeapon != null ? "not null" : "null")}");
- Debug.LogError($"Weapon property is: {(Weapon != null ? "not null" : "null")}");
- }
- }
- else if (actionData.targetPosition != Vector3.zero)
- {
- Debug.Log($"{CharacterName} moves to {actionData.targetPosition}");
- // Add your movement logic here
- actionData.state = ActionDecisionState.ActionExecuted;
- SetVisualState(actionData.state);
- }
- }
- public bool HasActionSelected()
- {
- return actionData.state == ActionDecisionState.ActionSelected;
- }
- public bool IsActionComplete()
- {
- // If action is already executed or no action, it's complete
- if (actionData.state != ActionDecisionState.ActionSelected) return true;
- if (actionData.targetEnemy != null)
- {
- // Check if we're within attack range of the target
- float distance = Vector3.Distance(transform.position, actionData.targetEnemy.transform.position);
- float attackRange = GetAttackRange();
- return distance <= attackRange;
- }
- else if (actionData.targetPosition != Vector3.zero)
- {
- // Check if we've reached the movement target using horizontal distance
- Vector3 currentPos = transform.position;
- Vector3 targetPos = actionData.targetPosition;
- Vector2 currentPos2D = new Vector2(currentPos.x, currentPos.z);
- Vector2 targetPos2D = new Vector2(targetPos.x, targetPos.z);
- float horizontalDistance = Vector2.Distance(currentPos2D, targetPos2D);
- return horizontalDistance <= 1.2f; // Arrival threshold
- }
- return true; // No target means completed
- }
- public void ResetAction()
- {
- actionData.Reset();
- SetVisualState(actionData.state);
- }
- public void TakeDamage(int damage)
- {
- if (isDead) return;
- currentHealth -= damage;
- Debug.Log($"{CharacterName} takes {damage} damage. Health: {currentHealth}/{maxHealth}");
- // Update health bar
- UpdateHealthBar();
- if (currentHealth <= 0)
- {
- Die();
- }
- }
- // Debug method to test attacks manually
- public void DebugAttack(GameObject target)
- {
- Weapon equippedWeapon = GetEquippedWeapon();
- if (equippedWeapon != null)
- {
- Debug.Log($"DEBUG: {CharacterName} manually attacking {target.name}");
- equippedWeapon.Attack(target);
- }
- else
- {
- Debug.LogError($"DEBUG: {CharacterName} has no weapon equipped!");
- }
- }
- private void Die()
- {
- isDead = true;
- isTargetable = false;
- currentHealth = 0;
- gameObject.SetActive(false); // Disable the character GameObject
- characterRenderer.enabled = false;
- // Make character gray
- if (characterMaterial != null)
- {
- characterMaterial.color = Color.gray;
- }
- // Update health bar to show death
- UpdateHealthBar();
- Debug.Log($"{CharacterName} has died!");
- }
- private void UpdateHealthBar()
- {
- if (healthSlider != null)
- {
- healthSlider.value = currentHealth;
- healthSlider.maxValue = maxHealth;
- // Change color based on health percentage
- UnityEngine.UI.Image fillImage = healthSlider.fillRect.GetComponent<UnityEngine.UI.Image>();
- if (fillImage != null)
- {
- float healthPercent = (float)currentHealth / maxHealth;
- if (healthPercent > 0.6f)
- fillImage.color = Color.green;
- else if (healthPercent > 0.3f)
- fillImage.color = Color.yellow;
- else
- fillImage.color = Color.red;
- }
- Debug.Log($"Updated health bar for {CharacterName}: {currentHealth}/{maxHealth}");
- }
- }
- }
|