using System; using System.Collections.Generic; using System.Linq; using System.Text; using VBXSE; using Microsoft.Xna.Framework; namespace DIGIEVO { public static class Combat { public const int COMBAT_TEXT_BEGIN_TOP = 405; public const int COMBAT_TEXT_BEGIN_LEFT = 20; public const int FIRST_ROW_BEGIN_LEFT = 70; public const int SECOND_ROW_BEGIN_LEFT = 430; public const int CHOICE_SPACING = 45; public const int BUTTON_TEXT_X = 10; public const int BUTTON_TEXT_Y = 5; public const int CREATURE_ONE_X = 155; public const int CREATURE_ONE_Y = 125; public const int CREATURE_TWO_X = 515; public const int CREATURE_TWO_Y = 125; public const int HP_BAR_ONE = 280; public const int HP_BAR_TWO = 280; public const float BACKGROUND_MOVE_SPEED = 90; //-------- public static Creature enemy; public static int selectedAttack = 0; public static Random random; public static bool combatOver = false; public static bool combatFullyOver = false; public static int yourStoredAttack = -1; public static int enemystoredAttack = -1; public static int enemyAttack = -1; public static bool playerBarrier = false; public static bool enemyBarrier = false; public static bool friendly = false; public static bool auto = false; public static int turn = 1; //-------- public static List attackButtons; public static List attackTexts; public static Sprite combatBar; public static Sprite creatureOneSprite; public static Sprite creatureTwoSprite; public static GText combatText; public static float lettersDisplayed = 0; public static string targetCombatText = ""; public static List backLog = new List(); public static int textwait = 0; public static int maxTextwait = 1000; public static List displayedLines = new List(); public static Sprite background1; public static Sprite background2; public static Sprite HPBar1; public static Sprite HPBar2; public static void StartCombat() { enemy = new Creature(Creature.EnemyType.TrainingDummy); StartCombat(enemy); } public static void StartCombat(Creature enemy) { turn = 1; Main.currentMenu = Main.Menus.Combat; SpriteEngine.Nuke(); Combat.enemy = enemy; enemy.ApplyTraits(); enemy.FullHeal(); combatOver = false; combatFullyOver = false; random = new Random(); ResetDisplayed(); yourStoredAttack = -1; enemystoredAttack = -1; playerBarrier = false; enemyBarrier = false; SpriteEngine.EraseGObject(background1); SpriteEngine.EraseGObject(background2); background1 = SpriteEngine.CreateSprite("backgroundCombat", 0, 0, 1f); background2 = SpriteEngine.CreateSprite("backgroundCombat", 800, 0, 1f); background1.depth = 500; background2.depth = 501; combatBar = SpriteEngine.CreateMultiSprite(new string[] { "CombatBarPassive", "CombatBarActive" }, 0, 0, 1f); combatBar.depth = 5; string[] hpbars = new string[] { "hp_0", "hp_1", "hp_2", "hp_3", "hp_4", "hp_5", "hp_6", "hp_7", "hp_8", "hp_9", "hp_10" }; HPBar1 = SpriteEngine.CreateMultiSprite(hpbars, CREATURE_ONE_X, HP_BAR_ONE, 1f); HPBar2 = SpriteEngine.CreateMultiSprite(hpbars, CREATURE_TWO_X, HP_BAR_TWO, 1f); combatText = SpriteEngine.CreateGText("", COMBAT_TEXT_BEGIN_LEFT, COMBAT_TEXT_BEGIN_TOP); creatureOneSprite = Main.creature.CreateSprite(CREATURE_ONE_X, CREATURE_ONE_Y); creatureTwoSprite = enemy.CreateSprite(CREATURE_TWO_X, CREATURE_TWO_Y); SpriteEngine.CreateSprite("scanline1", CREATURE_ONE_X, CREATURE_ONE_Y, 1f).depth = -15f; SpriteEngine.CreateSprite("scanline1", CREATURE_TWO_X, CREATURE_TWO_Y, 1f).depth = -15f; SpriteEngine.CreateGText(Main.creature.name, CREATURE_ONE_X, CREATURE_ONE_Y - 50); SpriteEngine.CreateGText(enemy.name, CREATURE_TWO_X, CREATURE_TWO_Y - 50); SpriteEngine.CreateGText("POWER RATING: " + Main.creature.totalEXP, CREATURE_ONE_X - 50, HP_BAR_ONE + 50); SpriteEngine.CreateGText("POWER RATING: " + enemy.totalEXP, CREATURE_TWO_X - 50, HP_BAR_TWO + 50); UpdateHP(); ShowAttacks(); Audio.PlayMusic("EVOLUTION BATTLE"); } public static void ShowAttacks() { selectedAttack = 0; combatBar.currentImage = 1; attackButtons = new List(); attackTexts = new List(); //SpriteEngine.CreateSprite("CombatBarActive", 0, 0, 1f).depth = 60; for (int i = 0; i < Main.creature.moves.Count; i++) { int left = FIRST_ROW_BEGIN_LEFT; if (i > 3) left = SECOND_ROW_BEGIN_LEFT; int top = COMBAT_TEXT_BEGIN_TOP + CHOICE_SPACING * i; if (i > 3) top -= CHOICE_SPACING * 4; attackButtons.Add(SpriteEngine.CreateMultiSprite(new string[] { "AttackButtonPassive", "AttackButtonActive" }, left, top, 1f)); if(Main.creature.moves[i].pp <= 0) attackButtons[attackButtons.Count - 1].color = Microsoft.Xna.Framework.Color.DarkGray; //int buttonX = left + BUTTON_TEXT_X; string toPrint = Main.creature.moves[i].name + " (" + Main.creature.moves[i].pp + "/" + Main.creature.moves[i].maxPP + ")"; int buttonX = left + 150 - (int)Math.Floor((float)toPrint.Length * 6.5f); attackTexts.Add(SpriteEngine.CreateGText(toPrint, buttonX, top + BUTTON_TEXT_Y)); } UpdateSelectedAttack(); int totalPP = 0; for (int i = 0; i < Main.creature.moves.Count; i++) totalPP += Main.creature.moves[i].pp; if (totalPP > 0) { if (yourStoredAttack == -1) { if (!auto) { Input.inputMode = Input.InputMode.Combat; Main.currentMenu = Main.Menus.Combat; } else { bool acceptable = false; while (!acceptable) { selectedAttack = random.Next(Main.creature.moves.Count); if (Main.creature.moves[selectedAttack].pp > 0) acceptable = true; } HideAttacks(); Input.inputMode = Input.InputMode.CombatWait; PlayRound(); } } else { HideAttacks(); Input.inputMode = Input.InputMode.CombatWait; //selectedAttack = yourStoredAttack; PlayRound(); } } else { HideAttacks(); Input.inputMode = Input.InputMode.CombatWait; selectedAttack = -1; PlayRound(); } } public static void ResetDisplayed() { backLog = new List(); displayedLines = new List(); for (int i = 0; i < 4; i++) displayedLines.Add(""); } public static void HideAttacks() { combatBar.currentImage = 0; for (int i = 0; i < attackButtons.Count; i++) { SpriteEngine.EraseGObject(attackButtons[i]); SpriteEngine.EraseGObject(attackTexts[i]); } } public static void UpdateSelectedAttack() { for (int i = 0; i < attackButtons.Count; i++) { attackButtons[i].currentImage = 0; if (selectedAttack == i) attackButtons[i].currentImage = 1; } } public static void UpdateHP() { HPBar1.currentImage = (int)Math.Ceiling((double)((float)Main.creature.HP / (float)Main.creature.GetMaxHP() * 10)); HPBar2.currentImage = (int)Math.Ceiling((double)((float)enemy.HP / (float)enemy.GetMaxHP() * 10)); } public static void UpdateHP(int HP1, int HP2) { HPBar1.currentImage = (int)Math.Ceiling((double)((float)HP1 / (float)Main.creature.GetMaxHP() * 10)); HPBar2.currentImage = (int)Math.Ceiling((double)((float)HP2 / (float)enemy.GetMaxHP() * 10)); } public static void MoveSelection(int deltaX, int deltaY) { Audio.PlaySound("cursor"); if (deltaX > 0 || deltaX < 0) { if (selectedAttack < 4) selectedAttack += 4; else selectedAttack -= 4; } selectedAttack += deltaY; if (deltaY > 0 && selectedAttack == 4) selectedAttack--; if (deltaY < 0 && selectedAttack == 3) selectedAttack++; while (selectedAttack < 0) selectedAttack++; while (selectedAttack > Main.creature.moves.Count - 1) selectedAttack--; UpdateSelectedAttack(); } public static void ConfirmAttack() { if (Main.creature.moves[selectedAttack].pp > 0) { HideAttacks(); Input.inputMode = Input.InputMode.CombatWait; Audio.PlaySound("confirm"); PlayRound(); } } public static void PlayRound() { EnemyMove(); //Enemy picks move //Probabistically pick a starting player based on AGI bool playerStarts = (random.Next(Main.creature.GetAGI() + enemy.GetAGI()) <= Main.creature.GetAGI()); //11 - Barrier if (yourStoredAttack == -1) { if (selectedAttack > -1 && selectedAttack < Main.creature.moves.Count) { if (Main.creature.moves[selectedAttack].type == 11) playerStarts = true; //Barrier takes precedence } } if (enemyAttack == 11 && enemystoredAttack == -1) playerStarts = false; DisplayText("[Turn " + turn + "]"); if (!playerStarts) /*EnemyMove();*/ PerformAttack(enemy, Main.creature, Move.allMoves[enemyAttack]); if (!combatOver) { if (selectedAttack != -1) { if (yourStoredAttack != -1) { PerformAttack(Main.creature, enemy, Move.allMoves[yourStoredAttack]); } else { PerformAttack(Main.creature, enemy, Main.creature.moves[selectedAttack]); } } else PerformAttack(Main.creature, enemy, Move.allMoves[10]); //Out of PP, focus } if (!combatOver) { if (playerStarts) /*EnemyMove();*/ PerformAttack(enemy, Main.creature, Move.allMoves[enemyAttack]); } playerBarrier = false; enemyBarrier = false; if (!combatOver) turn++; DisplayText("(Press a key)"); } public static void EnemyMove() { int totalPP = 0; for (int i = 0; i < enemy.moves.Count; i++) { totalPP += enemy.moves[i].pp; } if (totalPP == 0) { //PerformAttack(enemy, Main.creature, Move.allMoves[10]); //FOCUS enemyAttack = 10; } else { if (enemystoredAttack == -1) { Move theMove = new Move(); bool acceptable = false; while (!acceptable) { theMove = enemy.moves[random.Next(enemy.moves.Count)]; if (theMove.pp > 0) acceptable = true; } //PerformAttack(enemy, Main.creature, theMove); enemyAttack = theMove.type; } else { //PerformAttack(enemy, Main.creature, Move.allMoves[enemystoredAttack]); enemyAttack = enemystoredAttack; } } } public static void PerformAttack(Creature user, Creature target, Move move) { bool playerAttacking = false; if (user.pass == Main.creature.pass) playerAttacking = true; DisplayText("/sfx:attack"); switch (move.type) { case 12: case 13: case 14: case 15: if ((playerAttacking && yourStoredAttack == -1) || (!playerAttacking && enemystoredAttack == -1)) { } else { DisplayText(user.name + " uses " + move.name + "!"); } break; default: DisplayText(user.name + " uses " + move.name + "!"); break; } bool failure = false; if (move.type != 10 || (playerAttacking && yourStoredAttack != -1) || (!playerAttacking && enemystoredAttack != -1)) { if (move.pp > 0) { move.pp--; for (int i = 0; i < user.moves.Count; i++) { if (user.moves[i].type == move.type) user.moves[i] = move; } } else failure = true; } if (failure) { DisplayText("...out of PP!"); } else { //Check for miss bool missed = false; if (random.NextDouble() < move.dodgechance * (target.GetAGI() / user.GetAGI())) { missed = true; } switch (move.type) { //Elemental delayed attacks can't miss the preparation case 12: case 13: case 14: case 15: if((yourStoredAttack == -1 && playerAttacking) || (enemystoredAttack == -1 && !playerAttacking)) { missed = false; } break; } if (missed) { DisplayText("/sfx:dodge"); if (move.damageType != DamageType.None) DisplayText("But " + target.name + " avoided the attack!"); else DisplayText("But nothing happened!"); } if (!missed) { //Calculate damage float powerfactor = 1f; if (move.element == user.element && user.element != Element.NORMAL) powerfactor += 0.25f; if (Main.ElementalAdvantage(move.element, target.element)) powerfactor *= 1.5f; if (Main.ElementalResistance(move.element, target.element)) powerfactor *= 0.66f; float statFactor = user.GetATK(); if (move.damageType == DamageType.Special) statFactor = user.GetSPC(); int damage = (int)Math.Round(move.damage * powerfactor * statFactor * 0.00625 * (1 + move.variation / 2 - random.NextDouble() * move.variation)); //Apply defense float defFactor = 0f; if (move.damageType == DamageType.Physical) defFactor = target.GetDEF(); else if (move.damageType == DamageType.Special) defFactor = (target.GetDEF() + target.GetSPC()) / 2; else defFactor = 0f; //Defense-penetrating attacks damage = (int)Math.Round(damage * 1f - (float)Math.Sqrt(80f * defFactor) * 0.1f); if (damage < 0) damage = 0; bool charging = false; switch (move.type) { case 12: case 13: case 14: case 15: if (yourStoredAttack == -1 && playerAttacking) { yourStoredAttack = move.type; damage = 0; charging = true; } else if (enemystoredAttack == -1 && !playerAttacking) { enemystoredAttack = move.type; damage = 0; charging = true; } break; } if (move.damageType != DamageType.None) { if ((playerAttacking && enemyBarrier) || (!playerAttacking && playerBarrier)) { damage = 0; DisplayText(target.name + "'s BARRIER blocks the attack!"); } } target.HP -= damage; DisplayText("/updatehp:" + Main.creature.HP + ":" + enemy.HP); if (damage != 0) { DisplayText("/sfx:hit"); DisplayText(Convert.ToString(damage) + " damage dealt to " + target.name + "!"); } else if (damage == 0 && (move.damageType == DamageType.Physical || move.damageType == DamageType.Special)) { if (charging) { DisplayText(user.name + " is charging an attack!"); } else { DisplayText("No damage was dealt!"); } } //Other effects switch (move.type) { case 8: //HEAL user.HP = user.GetMaxHP(); DisplayText("/updatehp:" + Main.creature.HP + ":" + enemy.HP); DisplayText(user.name + " was fully healed!"); break; case 9: //DISABLE int disabledMove = random.Next(target.moves.Count); Move theMove = target.moves[disabledMove]; theMove.pp = 0; target.moves[disabledMove] = theMove; DisplayText(target.name + "'s " + theMove.name + " was disabled!"); break; case 10: //FOCUS int recoveredMove = random.Next(user.moves.Count); Move reMo = user.moves[recoveredMove]; reMo.pp += 1; user.moves[recoveredMove] = reMo; DisplayText(user.name + " tries to focus!"); break; case 11: //BARRIER if (playerAttacking) playerBarrier = true; else enemyBarrier = true; DisplayText(user.name + " has set up a BARRIER!"); break; case 12: case 13: case 14: case 15: if (!charging) { if (playerAttacking) yourStoredAttack = -1; else enemystoredAttack = -1; } break; } //UpdateHP(); if (target.HP <= 0) { target.HP = 0; CombatOver(user == Main.creature); } } } } public static void DisplayText(string text) { if (targetCombatText != "") { backLog.Add(text); } else { if (text.StartsWith("/")) HandleCommand(text); else { ResetDisplayed(); combatText.text = ""; targetCombatText = text; lettersDisplayed = 0; textwait = 0; } } } public static void Confirm() { if (!combatFullyOver) NextText(); else { Input.inputMode = Input.InputMode.Ignore; SpriteEngine.Nuke(); targetCombatText = ""; textwait = 0; lettersDisplayed = 0; Menu.OpenMenu(Main.Menus.Main); } } public static void NextText() { if (backLog.Count == 0) { ResetDisplayed(); targetCombatText = ""; lettersDisplayed = 0; ShowAttacks(); } else { //combatText.text = ""; string text = backLog[0]; backLog.RemoveAt(0); if (text.StartsWith("/")) HandleCommand(text); else { displayedLines.Add(targetCombatText); if (displayedLines.Count > 4) displayedLines.RemoveAt(0); targetCombatText = text; lettersDisplayed = 0; textwait = 0; } } } public static void HandleCommand(string command) { string[] parts = command.Split(new string[] { ":" }, StringSplitOptions.None); switch (parts[0]) { case "/sfx": Audio.PlaySound(parts[1]); break; case "/music": Audio.PlayMusic(parts[1]); break; case "/updatehp": int part1 = Convert.ToInt32(parts[1]); int part2 = Convert.ToInt32(parts[2]); if (part1 < 0) part1 = 0; if (part2 < 0) part2 = 0; UpdateHP(part1, part2); break; case "/combatover": if (parts[1] == "yes") creatureTwoSprite.enabled = false; else creatureOneSprite.enabled = false; combatFullyOver = true; break; } if(backLog.Count > 0) NextText(); } public static void CombatOver(bool winner) { combatOver = true; if (winner) { DisplayText("/combatover:" + "yes"); DisplayText("/music:EVOLUTION VICTORY"); DisplayText(enemy.name + " was defeated!"); DisplayText("[Battle ended after " + turn + " turns.]"); if (!friendly) { int expGained = Main.creature.GainExperience(enemy.totalEXP); DisplayText(Main.creature.name + " gained " + expGained + " EXP!"); } else { DisplayText("No experience was gained in this friendly match."); } } else { DisplayText("/combatover:" + "no"); DisplayText("/music:EVOLUTION LOSS"); DisplayText(Main.creature.name + " was defeated!"); DisplayText("[Battle ended after " + turn + " turns.]"); } Main.SendCreature(); } private static float remainder1 = 0; private static float remainder2 = 0; public static void Update(GameTime gameTime) { lettersDisplayed += Main.textSpeed * (float)gameTime.ElapsedGameTime.TotalSeconds; if (lettersDisplayed > targetCombatText.Length) lettersDisplayed = targetCombatText.Length; { textwait += (int)gameTime.ElapsedGameTime.TotalMilliseconds; if (backLog.Count > 0 && textwait > maxTextwait) { NextText(); } } combatText.text = ""; for (int i = 0; i < displayedLines.Count; i++) { combatText.text += displayedLines[i] + "\n"; } combatText.text += targetCombatText.Substring(0, (int)Math.Floor(lettersDisplayed)); if (background1 != null && background2 != null) { float tempChange1 = (float)gameTime.ElapsedGameTime.TotalSeconds * (float)BACKGROUND_MOVE_SPEED + remainder1; float realChange1 = (float)Math.Floor(tempChange1); remainder1 = tempChange1 - realChange1; float tempChange2 = (float)gameTime.ElapsedGameTime.TotalSeconds * (float)BACKGROUND_MOVE_SPEED + remainder2; float realChange2 = (float)Math.Floor(tempChange2); remainder2 = tempChange2 - realChange2; background1.SetX((float)background1.position.X - realChange1); background2.SetX((float)background2.position.X - realChange2); if (background1.position.X <= -800) background1.SetX(background1.position.X + 1600); if (background2.position.X <= -800) background2.SetX(background2.position.X + 1600); } } } }