655 lines
17 KiB
Plaintext
655 lines
17 KiB
Plaintext
using Sirenix.OdinInspector;
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public delegate void OnAfterPHomeUnitHitHandler(PHomeUnit attacker, PHomeUnit victim);
|
|
|
|
public enum NodeType
|
|
{
|
|
Strong,
|
|
Health,
|
|
Pet,
|
|
Lottery,
|
|
Dialogue,
|
|
}
|
|
|
|
|
|
public class PHomeGameMode : MonoBehaviour
|
|
{
|
|
public static PHomeGameMode main;
|
|
|
|
public PHomeGameConfig GameConfig;
|
|
|
|
public float MysticalScentTime;
|
|
public Transform m_mysticalScentPos;
|
|
|
|
[SerializeField]
|
|
private SceneTransition m_transition;
|
|
|
|
[SerializeField]
|
|
public PHomePlayer PlayerUnit;
|
|
[SerializeField]
|
|
private PHomeWorldBall m_worldBallMainWorld;
|
|
|
|
[SerializeField]
|
|
private Transform m_playerStart;
|
|
|
|
[SerializeField]
|
|
private GameObject m_playerPrefab;
|
|
|
|
[SerializeField]
|
|
private GameObject m_portalPrefab;
|
|
|
|
[SerializeField]
|
|
private bool m_transiting;
|
|
|
|
[SerializeField]
|
|
private bool m_mysticalScentDes;
|
|
|
|
[SerializeField]
|
|
private PHomeSpawner m_spawner;
|
|
[SerializeField]
|
|
private PHomePush m_push;
|
|
|
|
[SerializeField]
|
|
private GameObject m_dropGemPrefab;
|
|
|
|
public PHomePush Push
|
|
{
|
|
get
|
|
{
|
|
return m_push;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private List<WeaponUpdate> m_weaponUpdateConfig;
|
|
[SerializeField]
|
|
private AnimationCurve m_levelDifficultyCurve;
|
|
[SerializeField]
|
|
public VirtualJoystick m_joystick;
|
|
[SerializeField]
|
|
public List<PHomeEventCard> m_eventCards;
|
|
private PHomePortal m_portal;
|
|
|
|
public void DoUnitAttack(PHomeUnit source, PHomeUnit target, PHomeProjectile projectile = null)
|
|
{
|
|
float curHealth = target.Health;
|
|
float attack = PHomeGameMode.CalcTotalDamage(source, target, 0);
|
|
if (source is PHomeMonster)
|
|
{
|
|
attack *= GetMultiWithType(EventTargetUnitType.Enemy, EventCardEffectType.Damage);
|
|
}
|
|
else if (source is PHomePet)
|
|
{
|
|
attack *= GetMultiWithType(EventTargetUnitType.Pet, EventCardEffectType.Damage);
|
|
}
|
|
|
|
source.OnAfterHit(source, target);
|
|
target.OnAfterHitted(source, target);
|
|
|
|
DealDamage(source, target, projectile, attack);
|
|
source.EnterCooldown();
|
|
}
|
|
|
|
public void DoProjectileAttack(PHomeUnit source, PHomeUnit target, PHomeProjectile projectile, float attack)
|
|
{
|
|
attack = PHomeGameMode.CalcTotalDamage(source, target, attack);
|
|
|
|
if (source is PHomeMonster)
|
|
{
|
|
attack *= GetMultiWithType(EventTargetUnitType.Enemy, EventCardEffectType.Damage);
|
|
}
|
|
else if (source is PHomePet)
|
|
{
|
|
attack *= GetMultiWithType(EventTargetUnitType.Pet, EventCardEffectType.Damage);
|
|
}
|
|
|
|
source.OnAfterHit(source, target);
|
|
target.OnAfterHitted(source, target);
|
|
|
|
DealDamage(source, target, projectile, attack);
|
|
}
|
|
|
|
public void DealDamage(PHomeUnit source, PHomeUnit target, PHomeProjectile projectile, float attack)
|
|
{
|
|
float curHealth = target.Health;
|
|
curHealth -= attack;
|
|
bool dead = false;
|
|
if (curHealth <= 0)
|
|
{
|
|
curHealth = 0;
|
|
dead = true;
|
|
}
|
|
target.Health = curHealth;
|
|
target.Attacked();
|
|
|
|
if (dead)
|
|
{
|
|
target.IsDead = true;
|
|
|
|
if (target is PHomeMonster monster)
|
|
{
|
|
m_spawner.OnMonsterKilled(monster);
|
|
DropGem(monster.transform.position, monster.GemDropMin, monster.GemDropMax);
|
|
Destroy(target.gameObject);
|
|
}
|
|
else if (target is PHomePlayer)
|
|
{
|
|
RestartPlayer();
|
|
}
|
|
}
|
|
}
|
|
|
|
public static float CalcTotalDamage(PHomeUnit attacker, PHomeUnit victim, float additionalDmg = 0)
|
|
{
|
|
float result = additionalDmg;
|
|
result += attacker.Attack;
|
|
|
|
PHomePet pet = attacker as PHomePet;
|
|
if (pet != null)
|
|
{
|
|
result += pet.Player.Attack;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public PHomeProjectile SpawnProjectile(PHomeUnit instigator, ShootData data, GameObject projectilePrefab)
|
|
{
|
|
GameObject projectile = Instantiate(projectilePrefab, Vector3.zero, Quaternion.identity);
|
|
PHomeProjectile result = projectile.GetComponent<PHomeProjectile>();
|
|
result.Instigator = instigator;
|
|
result.Init(data);
|
|
return projectile.GetComponent<PHomeProjectile>();
|
|
}
|
|
|
|
public PHomeUnit SpawnUnit(Vector3 at, GameObject unitPrefab)
|
|
{
|
|
GameObject monster = Instantiate(unitPrefab, at, Quaternion.identity);
|
|
PHomeUnit result = monster.GetComponent<PHomeUnit>();
|
|
result.Health = result.MaxHealth;
|
|
|
|
foreach (var item in result.StartBuffs)
|
|
{
|
|
AddBuff(result, item);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public PHomeMonster SpawnMonster(Vector3 at, GameObject monsterPrefab)
|
|
{
|
|
PHomeMonster result = SpawnUnit(at, monsterPrefab) as PHomeMonster;
|
|
return result;
|
|
}
|
|
|
|
public PHomePlayer SpawnPlayer(Vector3 at, GameObject playerPrefab)
|
|
{
|
|
PHomePlayer player = SpawnUnit(at, playerPrefab) as PHomePlayer;
|
|
foreach (var item in player.StartPets)
|
|
{
|
|
AddPet(player, item);
|
|
}
|
|
return player;
|
|
}
|
|
|
|
public PHomeWeapon SpawnWeapon(Vector3 at, GameObject weaponPrefab, Transform parent = null)
|
|
{
|
|
GameObject weaponGo = Instantiate(weaponPrefab, at, Quaternion.identity, parent);
|
|
PHomeWeapon result = weaponGo.GetComponent<PHomeWeapon>();
|
|
return result;
|
|
}
|
|
|
|
public PHomePet SpawnPet(Vector3 at, GameObject petPrefab)
|
|
{
|
|
GameObject petGo = Instantiate(petPrefab, at, Quaternion.identity);
|
|
PHomePet result = petGo.GetComponent<PHomePet>();
|
|
return result;
|
|
}
|
|
|
|
public void DropGem(Vector3 position, int min, int max)
|
|
{
|
|
float randRadius = 0.6f;
|
|
int randCount = UnityEngine.Random.Range(min, max);
|
|
for (int i = 0; i < randCount; i++)
|
|
{
|
|
Vector3 randOffset = Vector3.right * UnityEngine.Random.Range(-randRadius, randRadius) + Vector3.up * UnityEngine.Random.Range(-randRadius, randRadius);
|
|
GameObject go = Instantiate(m_dropGemPrefab, position + randOffset, Quaternion.identity);
|
|
PGem gem = go.GetComponent<PGem>();
|
|
Destroy(go, gem.GemSpawnLifeTime);
|
|
}
|
|
}
|
|
|
|
public PHomeWorldBall SpawnWorldBall(GameObject worldBallPrefab, PHomeInventory toInventory)
|
|
{
|
|
GameObject worldBallGo = Instantiate(worldBallPrefab, Vector3.zero, Quaternion.identity);
|
|
PHomeWorldBall result = worldBallGo.GetComponent<PHomeWorldBall>();
|
|
result.transform.parent = toInventory.transform;
|
|
result.Inventory = toInventory;
|
|
toInventory.Items.Add(result);
|
|
return result;
|
|
}
|
|
|
|
public PHomePortal SpawnPortal(PHomeWorldBall worldBall, Vector3 portalPos, bool spawnInPlayerProtal = false, Action beforeEnterAction = null)
|
|
{
|
|
if (m_portal == null)
|
|
{
|
|
GameObject worldBallGo = Instantiate(m_portalPrefab, portalPos, Quaternion.identity);
|
|
m_portal = worldBallGo.GetComponent<PHomePortal>();
|
|
}
|
|
m_portal.transform.position = spawnInPlayerProtal ? PlayerUnit.m_portalPos.position : portalPos;
|
|
if (worldBall == null)
|
|
{
|
|
m_portal.Init(m_worldBallMainWorld, beforeEnterAction);
|
|
}
|
|
else
|
|
{
|
|
m_portal.Init(worldBall, beforeEnterAction);
|
|
}
|
|
return m_portal;
|
|
}
|
|
|
|
public void AddGem(PHomePlayer player, int gemCount)
|
|
{
|
|
player.GemCount += gemCount;
|
|
}
|
|
|
|
public void AddWeapon(PHomeUnit unit, PHomeWeapon weapon)
|
|
{
|
|
PHomeWeapon weaponScript = SpawnWeapon(Vector3.zero, weapon.gameObject, unit.transform);
|
|
unit.AddWeapon(weaponScript);
|
|
weaponScript.Init(unit);
|
|
for (int j = 0; j < m_weaponUpdateConfig.Count; j++)
|
|
{
|
|
List<string> matchNames = m_weaponUpdateConfig[j].MatchNames;
|
|
bool isContain = false;
|
|
for (int k = 0; k < matchNames.Count; k++)
|
|
{
|
|
if (matchNames[k] == weapon.WeaponName)
|
|
{
|
|
isContain = true;
|
|
}
|
|
}
|
|
if (isContain)
|
|
{
|
|
bool canUpdate = true;
|
|
for (int k = 0; k < matchNames.Count; k++)
|
|
{
|
|
if (!unit.HaveWeapon(matchNames[k]))
|
|
{
|
|
canUpdate = false;
|
|
break;
|
|
}
|
|
}
|
|
if (canUpdate)
|
|
{
|
|
for (int k = 0; k < matchNames.Count; k++)
|
|
{
|
|
RemoveWeapon(unit, matchNames[k]);
|
|
}
|
|
AddWeapon(unit, m_weaponUpdateConfig[j].SpawnWeaponPrefabs);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void RemoveWeapon(PHomeUnit unit, string weaponName)
|
|
{
|
|
if (unit.HaveWeapon(weaponName))
|
|
{
|
|
unit.RemoveWeapon(weaponName);
|
|
}
|
|
}
|
|
|
|
public void StartMyticalScent(float time)
|
|
{
|
|
m_mysticalScentDes = false;
|
|
MysticalScentTime = time;
|
|
}
|
|
|
|
public void RestartPlayer()
|
|
{
|
|
PlayerUnit.Health = PlayerUnit.MaxHealth;
|
|
PlayerUnit.transform.position = m_playerStart.position;
|
|
PlayerUnit.IsDead = false;
|
|
}
|
|
|
|
public void StartPlayer()
|
|
{
|
|
PlayerUnit = SpawnPlayer(m_playerStart.position, m_playerPrefab);
|
|
m_joystick.SetPlayer(PlayerUnit);
|
|
}
|
|
|
|
public void FinishGame()
|
|
{
|
|
PHomeInventory.instance.GemCount += PlayerUnit.GemCount;
|
|
}
|
|
|
|
#region Utils
|
|
|
|
public IEnumerable<PHomeUnit> FindMonsters(Vector3 at, float range, PHomeUnit refUnit = null)
|
|
{
|
|
Vector2 point = new Vector2()
|
|
{
|
|
x = at.x,
|
|
y = at.y,
|
|
};
|
|
Collider2D[] colliders = Physics2D.OverlapCircleAll(point, range);
|
|
if (colliders.Length == 0)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
for (int i = 0; i < colliders.Length; i++)
|
|
{
|
|
if (colliders[i].attachedRigidbody == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
PHomeUnit script = colliders[i].attachedRigidbody.GetComponent<PHomeUnit>();
|
|
|
|
if (refUnit != null)
|
|
{
|
|
if (!IsEnemy(refUnit, script))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
yield return script;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public PHomeMonster FindClosestMonster(Vector3 at, float range, PHomeUnit refUnit = null)
|
|
{
|
|
Vector2 point = new Vector2()
|
|
{
|
|
x = at.x,
|
|
y = at.y,
|
|
};
|
|
Collider2D[] colliders = Physics2D.OverlapCircleAll(point, range);
|
|
if (colliders.Length == 0)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Collider2D result = null;
|
|
float length = float.MaxValue;
|
|
for (int i = 0; i < colliders.Length; i++)
|
|
{
|
|
if (colliders[i].attachedRigidbody == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (refUnit != null)
|
|
{
|
|
if (!IsEnemy(refUnit, colliders[i].attachedRigidbody.GetComponent<PHomeUnit>()))
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
float len = Vector3.Distance(at, colliders[i].transform.position);
|
|
if (len < length)
|
|
{
|
|
length = len;
|
|
result = colliders[i];
|
|
}
|
|
}
|
|
|
|
if (result == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return result.attachedRigidbody.GetComponent<PHomeMonster>();
|
|
}
|
|
|
|
public static bool IsEnemy(PHomeUnit unit, PHomeUnit other)
|
|
{
|
|
if (unit == other)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (unit == null || other == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return (unit is PHomePet && other is PHomeMonster) || (unit is PHomeMonster && other is PHomePet);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Pet
|
|
|
|
[Button]
|
|
public void AddPet(PHomePlayer player, PHomePet pet)
|
|
{
|
|
PHomePet samePet = player.GetHavePet(pet);
|
|
if (samePet != null)
|
|
{
|
|
samePet.LevelUp();
|
|
}
|
|
else
|
|
{
|
|
samePet = SpawnPet(Vector3.zero, pet.gameObject);
|
|
samePet.FromPet = pet;
|
|
player.AddPet(samePet);
|
|
}
|
|
float getCountAdd = GetMultiWithType(EventTargetUnitType.Pet, EventCardEffectType.Number) - 1;
|
|
for (int i = 0; i < getCountAdd; i++)
|
|
{
|
|
samePet.LevelUp();
|
|
}
|
|
samePet.MaxHealth *= GetMultiWithType(EventTargetUnitType.Pet, EventCardEffectType.Hp);
|
|
samePet.Health = samePet.MaxHealth;
|
|
}
|
|
|
|
public void PetLevelUp()
|
|
{
|
|
PlayerUnit.RandomPetLevelUp();
|
|
}
|
|
|
|
[Button]
|
|
public void ClearPets()
|
|
{
|
|
PlayerUnit.ClearPets();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Buff
|
|
|
|
public PHomeBuff AddBuff(PHomeUnit unit, PHomeBuff buffPrefab)
|
|
{
|
|
return unit.AddBuff(buffPrefab);
|
|
}
|
|
|
|
#endregion
|
|
|
|
|
|
public void KillAllMonster()
|
|
{
|
|
m_spawner.KillAllMonster();
|
|
}
|
|
|
|
List<PHomeEventCard> selectEventCard;
|
|
public List<PHomeEventCard> SelectEventCard()
|
|
{
|
|
if (selectEventCard == null) selectEventCard = new List<PHomeEventCard>();
|
|
List<PHomeEventCard> currCards = new List<PHomeEventCard>();
|
|
currCards.AddRange(m_eventCards);
|
|
for (int i = 0; i < 2; i++)
|
|
{
|
|
int currIndex = UnityEngine.Random.Range(0, currCards.Count);
|
|
selectEventCard.Add(currCards[currIndex]);
|
|
currCards.RemoveAt(currIndex);
|
|
}
|
|
if (GetMultiWithType(EventTargetUnitType.Pet, EventCardEffectType.Hp) != 1)
|
|
{
|
|
PlayerUnit.ResetPet();
|
|
}
|
|
for (int i = 0; i < selectEventCard.Count; i++)
|
|
{
|
|
if (selectEventCard[i].EventCardType == EventCardType.CoinMonster)
|
|
{
|
|
// TODO iii
|
|
//m_spawner.TrySpawnCoinMonster(selectEventCard[i]);
|
|
}
|
|
}
|
|
return selectEventCard;
|
|
}
|
|
|
|
|
|
public float GetMultiWithType(EventTargetUnitType unitType, EventCardEffectType effectType)
|
|
{
|
|
float multiCount = 1;
|
|
if (selectEventCard == null)
|
|
{
|
|
return multiCount;
|
|
}
|
|
for (int i = 0; i < selectEventCard.Count; i++)
|
|
{
|
|
List<EventEffectData> effctData = selectEventCard[i].EventEffectDatas;
|
|
for (int j = 0; j < effctData.Count; j++)
|
|
{
|
|
if (effctData[j].EventTargetUnitType == unitType && effctData[j].EventCardEffectType == effectType)
|
|
{
|
|
multiCount *= effctData[j].EventCardEffectNumber;
|
|
}
|
|
}
|
|
}
|
|
return multiCount;
|
|
}
|
|
|
|
|
|
void UpdateMysticalScent()
|
|
{
|
|
if (MysticalScentTime > 0)
|
|
{
|
|
MysticalScentTime -= Time.deltaTime;
|
|
}
|
|
else if (!m_mysticalScentDes)
|
|
{
|
|
KillAllMonster();
|
|
m_mysticalScentDes = true;
|
|
}
|
|
}
|
|
|
|
int m_currLevel = -1;
|
|
int m_finalLevel;
|
|
|
|
void UpdateLevelDifficulty()
|
|
{
|
|
m_finalLevel = m_push.FinishNodeCount;
|
|
if (m_finalLevel != m_currLevel)
|
|
{
|
|
m_currLevel = m_finalLevel;
|
|
m_spawner.MonsterHpBoost = m_levelDifficultyCurve.Evaluate(m_push.FinishNodeCount);
|
|
}
|
|
}
|
|
|
|
#region Cart Pushing
|
|
|
|
private bool m_isInPreStage;
|
|
public PuzzleGameMode.VoidDelegate OnAfterIsInPreStageChanged;
|
|
public bool IsInPreStage
|
|
{
|
|
get
|
|
{
|
|
return m_isInPreStage;
|
|
}
|
|
|
|
set
|
|
{
|
|
if (m_isInPreStage != value)
|
|
{
|
|
m_isInPreStage = value;
|
|
OnAfterIsInPreStageChanged?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
private float m_preStageStartTime;
|
|
public float PreStageStartTime
|
|
{
|
|
get
|
|
{
|
|
return m_preStageStartTime;
|
|
}
|
|
}
|
|
|
|
[SerializeField]
|
|
private float m_preStageDuration;
|
|
public float PreStageDuration
|
|
{
|
|
get
|
|
{
|
|
return m_preStageDuration;
|
|
}
|
|
}
|
|
|
|
public void StartPreStage(PHomeWorldBall wordBall)
|
|
{
|
|
if (IsInPreStage)
|
|
{
|
|
Debug.LogWarning($"Already in prestage");
|
|
return;
|
|
}
|
|
|
|
IsInPreStage = true;
|
|
m_preStageStartTime = Time.time;
|
|
Push.SelectWorldBall(wordBall);
|
|
}
|
|
|
|
private void Update_PreStage()
|
|
{
|
|
if (!IsInPreStage)
|
|
{
|
|
return;
|
|
}
|
|
|
|
float timePassed = Time.time - m_preStageStartTime;
|
|
if (timePassed > m_preStageDuration)
|
|
{
|
|
IsInPreStage = false;
|
|
Push.StartPush();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void Awake()
|
|
{
|
|
main = this;
|
|
StartPlayer();
|
|
}
|
|
|
|
// Update is called once per frame
|
|
void Update()
|
|
{
|
|
UpdateMysticalScent();
|
|
UpdateLevelDifficulty();
|
|
Update_PreStage();
|
|
}
|
|
}
|
|
|
|
[System.Serializable]
|
|
public class WeaponUpdate
|
|
{
|
|
public List<string> MatchNames;
|
|
public PHomeWeapon SpawnWeaponPrefabs;
|
|
}
|
|
|