private int AttemptCapture(MonsterObj monster) { // Algo is from g3/4. float a = (3 * monster.MaxHp - 2 * monster.CurrentHp) * monster.Base.CatchRate * ConditionDB.GetStatusBonus(monster.Status) / (3 * monster.MaxHp); if (a >= 255) { return(4); } float b = 1048560 / Mathf.Sqrt(Mathf.Sqrt(16711680 / a)); var beamCount = 0; while (beamCount < 4) { if (UnityEngine.Random.Range(0, 65535) >= b) { break; } ++beamCount; } return(beamCount); }
private IEnumerator SwitchMonster(MonsterObj newMonster) { if (_playerMonster.Monster.CurrentHp > 0) { yield return(_dialogBox.TypeDialog($"{_playerMonster.Monster.Base.Name}, fall back!")); _playerMonster.PlayDownedAnimation(); //TODO - create animation for returning to party yield return(YieldHelper.TwoSeconds); } _playerMonster.Setup(newMonster); _dialogBox.SetMoveList(newMonster.Moves); yield return(_dialogBox.TypeDialog($"It's your turn now, {newMonster.Base.Name}!")); // Allow player to switch monsters after enemy replaces a downed monster. if (_prevState == null) { _state = BattleState.ExecutingTurn; } else if (_prevState == BattleState.ChoiceSelection) { _prevState = null; StartCoroutine(SwitchEnemyMonster()); } }
private IEnumerator UseMoveEffects(MoveEffects effects, MonsterObj attackingMonster, MonsterObj defendingMonster, MoveTarget moveTarget) { // Handle any stat changes. if (effects.StatChanges != null) { if (moveTarget == MoveTarget.Self) { attackingMonster.ApplyStatChanges(effects.StatChanges); } else { defendingMonster.ApplyStatChanges(effects.StatChanges); } } // Handle any status conditions. if (effects.Status != ConditionID.None) { defendingMonster.SetStatus(effects.Status); } // Handle any volatile status conditions. if (effects.VolatileStatus != ConditionID.None) { defendingMonster.SetVolatileStatus(effects.VolatileStatus); } yield return(ShowStatusChanges(attackingMonster)); yield return(ShowStatusChanges(defendingMonster)); }
private void CheckIfBattleIsOver(BattleMonster downedMonster) { if (downedMonster.IsPlayerMonster) { MonsterObj nextMonster = _playerParty.GetHealthyMonster(); if (nextMonster != null) { OpenPartyScreen(); } else { BattleOver(BattleResult.Lost); } } else { if (!_isCharBattle) { BattleOver(BattleResult.Won); } else { MonsterObj nextEnemyMonster = _battlerParty.GetHealthyMonster(); if (nextEnemyMonster != null) { StartCoroutine(ChoiceSelection(nextEnemyMonster)); } else { BattleOver(BattleResult.Won); } } } }
private IEnumerator ShowStatusChanges(MonsterObj monster) { while (monster.StatusChanges.Count > 0) { string message = monster.StatusChanges.Dequeue(); yield return(_dialogBox.TypeDialog(message)); } }
private IEnumerator ChoiceSelection(MonsterObj nextMonster) { _state = BattleState.Busy; yield return(_dialogBox.TypeDialog($"{_battler.Name} is about to deploy {nextMonster.Base.Name}! Do you want to switch your Battokuri too?")); _state = BattleState.ChoiceSelection; _dialogBox.EnableChoiceSelector(true); }
/// <summary> /// Get a random monster from the available pool. /// </summary> /// <returns>Monster to encounter.</returns> public MonsterObj GetRandomMonster() { //TODO - refactor this for monster rarity MonsterObj wildMonster = _wildMonsters[Random.Range(0, _wildMonsters.Count)]; wildMonster.Init(); return(wildMonster); }
/// <summary> /// Starts a battle with a wild monster. /// </summary> /// <param name="playerParty">Player's party of monsters.</param> /// <param name="wildMonster">Copy of the wild monster generated from the MapLayer.</param> public void StartWildBattle(MonsterParty playerParty, MonsterObj wildMonster) { _playerParty = playerParty; _wildMonster = wildMonster; _isCharBattle = false; _player = playerParty.GetComponent <PlayerController>(); StartCoroutine(SetupBattle()); }
private IEnumerator ForgetMoveSelection(MonsterObj monster, LearnableMove newMove) { _prevState = BattleState.ForgetSelection; _state = BattleState.Busy; yield return(_dialogBox.TypeDialog($"{monster.Base.Name} can learn {newMove.Base.Name}, but it's move list is full. Forget a move to learn {newMove.Base.Name}?")); _state = BattleState.ChoiceSelection; _dialogBox.EnableChoiceSelector(true); }
private IEnumerator SwitchEnemyMonster() { _state = BattleState.Busy; MonsterObj nextMonster = _battlerParty.GetHealthyMonster(); _enemyMonster.Setup(nextMonster); yield return(_dialogBox.TypeDialog($"{_battler.Name} has deployed {nextMonster.Base.Name} to the battle!")); _state = BattleState.ExecutingTurn; }
/// <summary> /// Starts a battle with a wild monster after Encounter collider is triggered. /// </summary> public void StartWildBattle() { _state = GameState.Battle; _battleSystem.gameObject.SetActive(true); _worldCamera.gameObject.SetActive(false); var playerParty = _playerController.GetComponent <MonsterParty>(); MonsterObj wildMonster = FindObjectOfType <MapArea>().GetComponent <MapArea>().GetRandomMonster(); var enemyMonster = new MonsterObj(wildMonster.Base, wildMonster.Level); _battleSystem.StartWildBattle(playerParty, enemyMonster); }
private IEnumerator ForgetMove(MonsterObj monster, MoveObj oldMove) { //TODO - fix bug where new move is lost if monster gains more than one level LearnableMove newMove = _playerMonster.Monster.GetLearnableMove(); monster.ForgetMove(oldMove); monster.LearnMove(newMove); _dialogBox.SetMoveList(monster.Moves); yield return(_dialogBox.TypeDialog($"{_playerMonster.Monster.Base.Name} has forgotten {oldMove.Base.Name}!")); yield return(_dialogBox.TypeDialog($"{_playerMonster.Monster.Base.Name} has learned {newMove.Base.Name}!")); _state = BattleState.ExecutingTurn; }
/// <summary> /// Creates a new save game data. /// </summary> /// <param name="playerName">New player's name.</param> /// <param name="monsterChoice">New player's choice of starting monster.</param> public static void NewGameData(string playerName, string monsterChoice) { // Create a new player and place it in the starting spot. _playerData = new PlayerData(playerName, "World.Fornwest.Main", new[] { 1, 1 }); // Create the starter monster and add it to the party. var monsterBase = Resources.Load <MonsterBase>($"Monsters/{monsterChoice}"); var starterMonster = new MonsterObj(monsterBase, 5); _partyMonsters.Clear(); _partyMonsters.Add(starterMonster.SaveMonsterData()); // Data is ready so now we signal the new game. GameSignals.GAME_NEW.Dispatch(_playerData.id); Debug.Log($"New game: {_playerData.id} created."); }
/// <summary> /// Set the data in the HUD. /// </summary> /// <param name="monster">Monster to set data for.</param> public void SetData(MonsterObj monster) { _monster = monster; _nameText.text = monster.Base.Name; SetLevel(); SetExp(); _hpBar.SetHp((float)monster.CurrentHp / monster.MaxHp); _statusColors = new Dictionary <ConditionID, Color> { { ConditionID.Poison, _psnColor }, { ConditionID.Burn, _brnColor }, { ConditionID.Sleep, _slpColor }, { ConditionID.Paralyze, _parColor }, { ConditionID.Freeze, _frzColor } }; SetStatusText(); _monster.OnStatusChange += SetStatusText; }
private IEnumerator SetupBattle() { //TODO - handle case with no healthy monsters _playerMonster.HideHud(); _enemyMonster.HideHud(); if (!_isCharBattle) { _playerMonster.Setup(_playerParty.GetHealthyMonster()); _enemyMonster.Setup(_wildMonster); _dialogBox.SetMoveList(_playerMonster.Monster.Moves); yield return(_dialogBox.TypeDialog($"You have encountered an enemy {_enemyMonster.Monster.Base.Name}!")); } else { ShowCharacterSprites(); yield return(_dialogBox.TypeDialog($"{_battler.Name} has challenged you to a battle!")); // Deploy enemy monster. _battlerImage.gameObject.SetActive(false); //TODO - animate this _enemyMonster.gameObject.SetActive(true); MonsterObj enemyLeadMonster = _battlerParty.GetHealthyMonster(); _enemyMonster.Setup(enemyLeadMonster); yield return(_dialogBox.TypeDialog($"{_battler.Name} has deployed {enemyLeadMonster.Base.Name} to the battle!")); // Deploy player monster. _playerImage.gameObject.SetActive(false); //TODO - animate this too _playerMonster.gameObject.SetActive(true); MonsterObj playerLeadMonster = _playerParty.GetHealthyMonster(); _playerMonster.Setup(playerLeadMonster); _dialogBox.SetMoveList(_playerMonster.Monster.Moves); yield return(_dialogBox.TypeDialog($"You have deployed {playerLeadMonster.Base.Name} to the battle!")); } _escapeAttempts = 0; _partyScreen.Init(); ActionSelection(); }
private bool CheckIfMoveHits(MoveObj move, MonsterObj attackingMonster, MonsterObj defendingMonster) { if (move.Base.AlwaysHits) { return(true); } // Stat changes based on original game's formula. float moveAccuracy = move.Base.Accuracy; int accuracy = attackingMonster.StatsChanged[MonsterStat.Accuracy]; int evasion = defendingMonster.StatsChanged[MonsterStat.Evasion]; float[] changeVals = { 1f, 4f / 3f, 5f / 3f, 2f, 7f / 3f, 8f / 3f, 3f }; if (accuracy > 0) { moveAccuracy *= changeVals[accuracy]; } else if (accuracy < 0) { moveAccuracy /= changeVals[-accuracy]; } if (evasion > 0) { moveAccuracy /= changeVals[evasion]; } else if (evasion < 0) { moveAccuracy *= changeVals[evasion]; } int rng = UnityEngine.Random.Range(1, 101); return(rng <= moveAccuracy); }
/// <summary> /// Populate the UI with Monster data. /// </summary> /// <param name="monster">Monster in party.</param> public void SetData(MonsterObj monster) { _nameText.text = monster.Base.Name; _levelText.text = "Lvl " + monster.Level; _hpBar.SetHp((float)monster.CurrentHp / monster.MaxHp); }
private void HandlePartySelection() { if (Keyboard.current.rightArrowKey.wasPressedThisFrame) { ++_currentMember; } else if (Keyboard.current.leftArrowKey.wasPressedThisFrame) { --_currentMember; } else if (Keyboard.current.downArrowKey.wasPressedThisFrame) { _currentMember += 3; } else if (Keyboard.current.upArrowKey.wasPressedThisFrame) { _currentMember -= 3; } _currentMember = Mathf.Clamp(_currentMember, 0, _playerParty.Monsters.Count - 1); _partyScreen.UpdateMemberSelection(_currentMember); if (Keyboard.current.zKey.wasPressedThisFrame) { MonsterObj selectedMember = _playerParty.Monsters[_currentMember]; if (selectedMember.CurrentHp <= 0) { _partyScreen.SetMessageText("That Battokuri is downed and cannot be used!"); return; } if (selectedMember == _playerMonster.Monster) { _partyScreen.SetMessageText("That Battokuri is already being used!"); return; } _partyScreen.gameObject.SetActive(false); // If player switched monster voluntarily it should count as a turn move // If monster was downed and forced switch then it should trigger a new turn if (_prevState == BattleState.ActionSelection) { _prevState = null; StartCoroutine(ExecuteTurn(BattleAction.SwitchMonster)); } else { _state = BattleState.Busy; StartCoroutine(SwitchMonster(selectedMember)); } } else if (Keyboard.current.xKey.wasPressedThisFrame) { if (_playerMonster.Monster.CurrentHp <= 0) { _partyScreen.SetMessageText("You must select a Battokuri!"); return; } _partyScreen.gameObject.SetActive(false); if (_prevState == BattleState.ChoiceSelection) { _prevState = null; StartCoroutine(SwitchEnemyMonster()); } else { ActionSelection(); } } }
private IEnumerator ExecuteTurn(BattleAction playerAction) { _state = BattleState.ExecutingTurn; if (playerAction == BattleAction.Move) { // Get the monster moves. _playerMonster.Monster.CurrentMove = _playerMonster.Monster.Moves[_currentMove]; _enemyMonster.Monster.CurrentMove = _enemyMonster.Monster.GetRandomMove(); if (_playerMonster.Monster.CurrentMove == null || _enemyMonster.Monster.CurrentMove == null) { BattleOver(BattleResult.Error); yield break; } int playerPriority = _playerMonster.Monster.CurrentMove.Base.Priority; int enemyPriority = _enemyMonster.Monster.CurrentMove.Base.Priority; // Check who goes first. var isPlayerFirst = true; if (enemyPriority > playerPriority) { isPlayerFirst = false; } else if (playerPriority == enemyPriority) { isPlayerFirst = _playerMonster.Monster.Speed >= _enemyMonster.Monster.Speed; } BattleMonster firstMonster = (isPlayerFirst) ? _playerMonster : _enemyMonster; BattleMonster secondMonster = (isPlayerFirst) ? _enemyMonster : _playerMonster; // Store in case it gets downed and switched out before its move. MonsterObj lastMonster = secondMonster.Monster; // Execute the first move. yield return(UseMove(firstMonster, secondMonster, firstMonster.Monster.CurrentMove)); yield return(CleanUpTurn(firstMonster)); if (_state == BattleState.BattleOver) { yield break; } // Execute the second move. if (lastMonster.CurrentHp > 0) { yield return(UseMove(secondMonster, firstMonster, secondMonster.Monster.CurrentMove)); yield return(CleanUpTurn(secondMonster)); if (_state == BattleState.BattleOver) { yield break; } } } else if (playerAction == BattleAction.SwitchMonster) { // Switch the monster. MonsterObj selectedMember = _playerParty.Monsters[_currentMember]; _state = BattleState.Busy; yield return(SwitchMonster(selectedMember)); // Now it's the enemy's turn. MoveObj enemyMove = _enemyMonster.Monster.GetRandomMove(); if (enemyMove == null) { BattleOver(BattleResult.Error); yield break; } yield return(UseMove(_enemyMonster, _playerMonster, enemyMove)); yield return(CleanUpTurn(_enemyMonster)); if (_state == BattleState.BattleOver) { yield break; } } else if (playerAction == BattleAction.UseItem) { //TODO - refactor this when item system is implemented. // Use the item. _dialogBox.EnableActionSelector(false); yield return(ActivateCrystal()); } else if (playerAction == BattleAction.Run) { // Run from the battle. yield return(AttemptRun()); } // Return to ActionSelection. if (_state != BattleState.BattleOver) { ActionSelection(); } }
/// <summary> /// 查找目标。 /// </summary> private void FindTarget() { PlayerObj player = CoreEntry.gActorMgr.MainPlayer; if (player == null) { return; } //有任务在身时,判断任务位置 Vector3 selfpos = player.transform.position; int mid = 0; LogMgr.LogAI("AutoAIFindTarget.FindTarget"); if (TaskMgr.RunTaskType != 0 && !IsInDungeon()) { mid = TaskMgr.Instance.GetCurTargetMonsterId(); if (mid != 0) { const float RESET_DISTANCE = 5; //重新回到任务点距离 Vector3 pos = TaskMgr.Instance.goPos; float x = selfpos.x - pos.x; float z = selfpos.z - pos.z; if (x * x + z * z >= RESET_DISTANCE * RESET_DISTANCE) { LogMgr.LogAI("AutoAIFindTarget.FindTarget 离开任务目标位置太远,重新回去"); m_TargetPosition = pos; m_IsHaveTarget = true; return; } } } int mapid = MapMgr.Instance.EnterMapId; LuaTable cfg = ConfigManager.Instance.Map.GetMapConfig(mapid); ActorObj actor = player.FindTarget(cfg.Get <int>("autoFightRange"), mapid); if (actor != null) { //如果选择的不是当前怪物则忽略 if (mid != 0 && actor is MonsterObj) { MonsterObj monster = actor as MonsterObj; if (monster.ConfigID != mid) { return; } } m_TargetPosition = actor.transform.position; m_IsHaveTarget = true; return; } if (MapMgr.Instance.CurMapType == MapMgr.MapType.Map_CrossServer) { return; } //向服务器查询位置 if (WildQuery || IsInDungeon()) { //加载查询列表 if (m_QueryList.Count <= 0) { m_QueryList = GetSortedEntList(player.transform.position); m_QueryIndex = 0; } //开始查询 if (m_QueryList.Count > 0) { Vector3 pos = m_QueryList[m_QueryIndex]; m_QueryIndex = (m_QueryIndex + 1) % m_QueryList.Count; m_IsRequestPositon = true; m_QueryWaitCount = 5; NetLogicGame.Instance.SendQueryMonsterByPosition((int)(pos.x * 1000), (int)(pos.z * 1000)); LogMgr.LogAI("AutoAIFindTarget.FindTarget RequestPositon:{0}", pos); } else { LogMgr.LogAI("AutoAIFindTarget.FindTarget The QueryList is empty!"); } } else { LogMgr.LogAI("AutoAIFindTarget.FindTarget Can not find target."); } }