コード例 #1
0
ファイル: Battle.cs プロジェクト: Metasynic/PkmnEngine
        /* This function applies the correct formulae to detemine whether a move hits or not.
         * The attacking Pokemon's accuracy stage and the defending Pokemon's evasion stage are combined
         * The calculated stage is then sent to the PokemonInBattle.ApplyAccStage() function, which returns a percentage change of the attack hitting.
         * Finally, the percentage is compared to a random number between 0 and 99 to determine if the move actually hits. */
        public static bool AccuracyCheck(byte moveAccuracy, sbyte attackerAccStage, sbyte defenderEvaStage)
        {
            sbyte stage    = (sbyte)MathHelper.Clamp((attackerAccStage - defenderEvaStage), -6, 6);
            byte  accuracy = (byte)MathHelper.Clamp((PokemonInBattle.ApplyAccStage(moveAccuracy, stage)), 0, 100);

            return(PkmnUtils.RandomInclusive(99) < accuracy);
        }
コード例 #2
0
        /* This function is triggered when the player chooses a saved game from the ListBox to be loaded.
         * Focus is returned from the loadListBox to the loadLinkLabel (although it doesn't really matter at this point - the state is about to change).
         * The ControlManager is allowed to accept input again so the user will be able to control the player.
         * Loading the selected save data is accomplished by converting the sender back into a ListBox,
         * and loading the save file corresponding to the ListBox's selected item.
         * The CreatePlayer() and CreateWorld() functions are called to initialize the world and player.
         * Properties from the loaded data are passed into the functions, thus meaning the data is used to create the player and world.
         * Finally, the state is changed to the WorldScreen. */
        void loadListBox_Selected(object sender, EventArgs e)
        {
            loadLinkLabel.HasFocus      = true;
            ControlManager.AcceptsInput = true;
            ListBox  box  = sender as ListBox;
            SaveData load = PkmnUtils.BinaryLoad <SaveData>(@"../../../../../Saves/" + box.SelectedItem + ".pks");

            CreateWorld(load.LevelID);
            CreatePlayer(load.Gender, load.TileX, load.TileY, load.Team);
            Change(ChangeType.Change, GameRef.WorldScreen);
        }
コード例 #3
0
ファイル: TurnRun.cs プロジェクト: Metasynic/PkmnEngine
        /* When a TurnRun is executed, the engine will work out whether the user can run away. */
        public override bool Execute()
        {
            /* A byte representing the probability of a successful escape is generated using this formula.
             * It is then compared to a random integer between 0 and 255 to determine whether the escape can happen. */
            byte escapeChance = (byte)((((user.Speed * 128) / target.Speed) + (30 * user.RunCount)) % 256);
            bool canRun       = (PkmnUtils.RandomInclusive(255) < escapeChance);

            /* If the player Pokemon has tried to run away, and they have managed it, then it is written to the log.
             * The battle's EmptyLog flag is set to true so that the log is displayed to the screen, and the function returns true.
             * This means that the battle will end after this turn, since the user has run away.
             * However, if the player cannot run, this is also written to the log, EmptyLog is set to true and the function returns false since the battle will continue. */
            if (user == battleRef.CurrentPlayer)
            {
                if (canRun)
                {
                    battleRef.WriteToLog("You fled from the battle!");
                    battleRef.EmptyLog = true;
                    return(true);
                }
                else
                {
                    battleRef.WriteToLog("Couldn't escape!");
                    battleRef.EmptyLog = true;
                    return(false);
                }
            }

            /* Otherwise, the opponent Pokemon is the one trying to run away.
             * If the run has succeeded, then it's written to the log, EmptyLog is set to true, and the function returns true.
             * In the other case, the run failed, and this is written to the log. EmptyLog is again set to true and false is returned. */
            else
            {
                if (canRun)
                {
                    battleRef.WriteToLog(target.Nickname + " fled from the battle!");
                    battleRef.EmptyLog = true;
                    return(true);
                }
                else
                {
                    battleRef.WriteToLog(target.Nickname + " tried to run but couldn't escape!");
                    battleRef.EmptyLog = true;
                    return(false);
                }
            }
        }
コード例 #4
0
        /* The PokemonEngine() constructor initializes all of the important parts of the engine, including managers and screens. */
        public PokemonEngine()
        {
            /* The GraphicsDeviceManager is initialized, and the PreferredBackBuffer properties are set to the constants screenWidth and screenHeight.
             * This means that the engine window will have these dimensions, if possible.
             * The ScreenRectangle is also initialized with a position of zero and the screen width and height. */
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferWidth  = screenWidth;
            graphics.PreferredBackBufferHeight = screenHeight;
            ScreenRectangle = new Rectangle(0, 0, screenWidth, screenHeight);

            /* The two vital components of the engine, the InputHandler and the GameStateManager, are initialized and added to the engine's master component list.
             * Whenever base.Update() and base.Draw() are called from this class, everything on the component list is updated and drawn.
             * Since these classes contain all code for handling input and for drawing the game states, everything will now be updated and drawn properly. */
            Components.Add(new InputHandler(this));
            stateManager = new GameStateManager(this);
            Components.Add(stateManager);

            /* Every screen is initialized. Each has a reference to the engine and the state manager passed in. */
            SplashScreen          = new SplashScreen(this, stateManager);
            StartScreen           = new StartScreen(this, stateManager);
            WorldScreen           = new WorldScreen(this, stateManager);
            CharacterSelectScreen = new CharacterSelectScreen(this, stateManager);
            LoadGameScreen        = new LoadGameScreen(this, stateManager);
            StarterSelectScreen   = new StarterSelectScreen(this, stateManager);
            InventoryScreen       = new InventoryScreen(this, stateManager);
            BattleScreen          = new BattleScreen(this, stateManager);
            SwitchScreen          = new SwitchScreen(this, stateManager);

            /* The state stack is reset and the SplashScreen is pushed on. This means the first thing to appear when the engine is run will be the SplashScreen. */
            stateManager.ChangeState(SplashScreen);

            /* The content root directory is set to the "/Content/" folder, meaning every filename string passed to Content.Load<T>() will start in this folder. */
            Content.RootDirectory = "Content";

            /* Type matchups are initialized in the PkmnUtils class, which creates the type advantage lookup table. */
            PkmnUtils.InitTypeMatchups();

            /* These two settings make the game run as many times per second as it can.
            * They could be changed later depending on how well the engine performs. */
            IsFixedTimeStep = false;
            graphics.SynchronizeWithVerticalRetrace = false;
        }
コード例 #5
0
        /* The Draw() function is in charge of rendering the BattleLog to the screen. It takes a reference to the game's SpriteBatch. */
        public override void Draw(SpriteBatch spriteBatch)
        {
            /* A Vector2 variable called drawTo is used to hold the current location of the drawing "pen".
             * First, the background is drawn to the BattleLog's position, with no tint colour.
             * Then, the "pen" is moved down and right by 10 pixels. */
            Vector2 drawTo = position;

            spriteBatch.Draw(background, drawTo, Color.White);
            drawTo.X += 10;
            drawTo.Y += 10;

            /* If there's a valid string in currentString, then it will be split into an IEnumerable of lines using PkmnUtils.SplitString(), passing in the string and background width, as well as the width of one character.
             * Then, each of the lines is drawn using the BattleLog's spriteFont in black.
             * Each time a new line is drawn, the "pen" is moved down by the font's line spacing plus three pixels. */
            if (currentString != null)
            {
                IEnumerable <string> lines = PkmnUtils.SplitString(currentString, background.Width / (int)(spriteFont.MeasureString(" ").X));
                foreach (string s in lines)
                {
                    spriteBatch.DrawString(spriteFont, s, drawTo, Color.Black);
                    drawTo.Y += spriteFont.LineSpacing + 3f;
                }
            }
        }
コード例 #6
0
        /* The main constructor for a Pokemon takes its species (PokemonData), Gender, moveset, level, and a TrainerRank.
         * The TrainerRank is nullable so that null can be passed in for a wild Pokemon. */
        public Pokemon(PokemonData pokemonData, Gender gender, Move[] moves, byte level, TrainerRank?rank)
        {
            /* The nickname, type and base stats for the Pokemon are copied from the PokemonData for its species. */
            Nickname           = pokemonData.PokemonName;
            pokemonID          = pokemonData.ID;
            baseHP             = pokemonData.BaseHP;
            baseAttack         = pokemonData.BaseAttack;
            baseDefence        = pokemonData.BaseDefence;
            baseSpecialAttack  = pokemonData.BaseSpecialAttack;
            baseSpecialDefence = pokemonData.BaseSpecialDefence;
            baseSpeed          = pokemonData.BaseSpeed;
            type = new List <PkmnType>();
            type = pokemonData.Type;

            /* Gender and Level are set according to what was passed in. */
            this.gender = gender;
            this.level  = level;

            /* Fainted is false by default, as a Pokemon is initially conscious. */
            fainted = false;

            /* The moveset is copied using Array.Copy(). */
            Array.Copy(moves, this.moves, 4);

            /* The IVs for the Pokemon are randomly generated between 0 and 31. */
            iv_HP             = (byte)PkmnUtils.RandomInclusive(31);
            iv_Attack         = (byte)PkmnUtils.RandomInclusive(31);
            iv_Defence        = (byte)PkmnUtils.RandomInclusive(31);
            iv_SpecialAttack  = (byte)PkmnUtils.RandomInclusive(31);
            iv_SpecialDefence = (byte)PkmnUtils.RandomInclusive(31);
            iv_Speed          = (byte)PkmnUtils.RandomInclusive(31);

            /* The EVs of the Pokemon are set according to the TrainerRank passed in.
             * This will only happen if the Pokemon is owned by an NPC trainer.
             * The TrainerRank will be null for player Pokemon (since they earn EVs through battle and start at 0).
             * It will also be null for wild Pokemon since they have EVs of 0. */
            if (rank.HasValue)
            {
                if (rank.Value == TrainerRank.Normal)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 0;
                }
                else if (rank.Value == TrainerRank.Grunt)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 10;
                }
                else if (rank.Value == TrainerRank.Elite)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 20;
                }
                else if (rank.Value == TrainerRank.GymLeader)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 40;
                }
                else if (rank.Value == TrainerRank.EliteFour)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 60;
                }
                else if (rank.Value == TrainerRank.Champion)
                {
                    ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 80;
                }
            }
            else
            {
                /* This block executes if the TrainerRank passed in is null.
                 * This means newly created wild Pokemon or player Pokemon have EVs of 0. */
                ev_HP = ev_Attack = ev_Defence = ev_SpecialAttack = ev_SpecialDefence = ev_Speed = 0;
            }

            /* Finally, set the health of the Pokemon using the new HP stats. */
            health = new AttributePair(HP);
        }
コード例 #7
0
ファイル: WorldScreen.cs プロジェクト: Metasynic/PkmnEngine
        /* The CheckSpawn() function checks if a wild Pokemon battle will be triggered on this frame.
         * It is only called when the player's StepCheck bit is true, so it is only called once every step. */
        private void CheckSpawn()
        {
            /* This Random object will be needed for a lot of things later. */
            Random r = new Random();

            /* Each MapLayer in the map needs to be checked for a Tile with its Spawn bit set to true. */
            foreach (MapLayer layer in world.CurrentMap.MapLayers)
            {
                /* If tile that the player is standing on in the layer has its Spawn bit set to true, a battle may happen. */
                if (layer.GetTile(player.Sprite.TileLocation.X, player.Sprite.TileLocation.Y).Spawn)
                {
                    /* This condition determines whether an actual battle happens or not.
                     * If a random double between 0 and 187.5 is less than the level's grass encounter rate, then a battle will happen.
                     * The most likely a battle can be to happen is a VeryCommon encounter rate, which is 10.0.
                     * With a VeryCommon encounter rate, the chance of a battle occurring is 10/187.5.
                     * The least likely a battle can be to happen is with a VeryRare encounter rate, which has a chance of 1.25/187.5.
                     * Note the the encounter rate is not how likely it is for a certain Pokemon to be battled - it is how likely that a battle will happen at all. */
                    if ((r.NextDouble() * 187.5) < World.CurrentLevel.GrassEncounterRate)
                    {
                        /* A new array of ProportionValue<PokemonSpawn> objects is created with the length of the GrassSpawns in the current level.
                         * GrassSpawns is a list of PokemonSpawn objects for the level's grass, stating what Pokemon at what level can be encountered.
                         * The spawns array is filled with ProportionValues created using the PokemonSpawn's Percentage property, and the PokemonSpawn object itself. */
                        ProportionValue <PokemonSpawn>[] spawns = new ProportionValue <PokemonSpawn> [World.CurrentLevel.GrassSpawns.Count];
                        for (int i = 0; i < spawns.Length; i++) // NOTE TO SELF: IT WOULD BE MORE EFFICIENT TO CREATE THE ARRAY IN A HIGHER SCOPE, SINCE IT IS PART OF THE LEVEL, BUT THIS WILL DO FOR NOW.
                        {
                            spawns[i] = ProportionValue.Create(World.CurrentLevel.GrassSpawns[i].Percentage, World.CurrentLevel.GrassSpawns[i]);
                        }

                        /* The encounter for the spawn is found proportionally from the array using ChooseByRandom().
                         * A new Pokemon object is created using the encounter data's ID, gender, and minimum/maximum levels.
                         * The wild Pokemon's level is chosen randomly between the minimum and maximum level in the selected PokemonSpawn.
                         * The moveset is currently just "Scratch". Although dynamic moveset generation is not part of the user requirements,
                         * I could add it if I have time by choosing the last four moves that the species can learn before it reaches its level. */
                        PokemonSpawn encounter = spawns.ChooseByRandom();
                        Pokemon      p         = new Pokemon(DataManager.PokemonSpecies[encounter.ID], Pokemon.GetGender(DataManager.PokemonSpecies[encounter.ID].GenderRatio), new[] { DataManager.Moves["Scratch"], null, null, null }, (byte)PkmnUtils.RandomInclusive(encounter.MinLevel, encounter.MaxLevel), null);

                        /* A wild battle is essentially a fight against an enemy team of one Pokemon.
                         * The BattleScreen.InitBattle() is called, which sends the player and enemy to the screen and sets the battle background index to zero.
                         * Finally, the BattleScreen is pushed on to the state stack. */
                        GameRef.BattleScreen.InitBattle(player.Team, p, 0);
                        Change(ChangeType.Push, GameRef.BattleScreen);
                    }
                }
            }
        }
コード例 #8
0
        /* This function is called when the saveLabel is selected.
         * A new SaveData object is created using the current game, and saved externally using the PkmnUtils.BinarySave<T>() function.
         * The save file is named after the date and time when it was saved. */
        void saveLabel_Selected(object sender, EventArgs e)
        {
            SaveData saveData = new SaveData(WorldScreen.World.CurrentLevelIndex, WorldScreen.Player.TilePosition, (Pokemon[])WorldScreen.Player.Team.Clone(), WorldScreen.Player.Gender);

            PkmnUtils.BinarySave(saveData, DateTime.Now.ToString("dd-MM-yyyy_hh-mm-ss"));
        }
コード例 #9
0
        /* This function is attached to the pokemonSelector.SelectionChanged event handler.
         * Therefore it will be triggered when the user scrolls from one Pokemon to another in the list. */
        void pokemonSelector_SelectionChanged(object sender, EventArgs e)
        {
            /* Firstly, the Pokemon sprite is changed so that it displays the sprite for the newly selected Pokemon.
             * It does this by getting the new sprite from the DataManager's PkmnFrontSprites dictionary. */
            pkmnSprite.Image = DataManager.PkmnFrontSprites[WorldScreen.Player.Team[SelectedPokemon].PokemonID];

            /* Next, the Text property of each label is changed to reflect the stats of the new Pokemon. */
            level.Text = "Lv " + WorldScreen.Player.Team[SelectedPokemon].Level;
            hp.Text    = "HP: " + WorldScreen.Player.Team[SelectedPokemon].Health.CurrentValue + "/" + WorldScreen.Player.Team[SelectedPokemon].Health.MaximumValue;
            atk.Text   = "ATK: " + WorldScreen.Player.Team[SelectedPokemon].Attack;
            def.Text   = "DEF: " + WorldScreen.Player.Team[SelectedPokemon].Defence;
            spatk.Text = "SP. ATK: " + WorldScreen.Player.Team[SelectedPokemon].SpecialAttack;
            spdef.Text = "SP. DEF: " + WorldScreen.Player.Team[SelectedPokemon].SpecialDefence;
            spd.Text   = "SPD: " + WorldScreen.Player.Team[SelectedPokemon].Speed;
            xp.Text    = "XP: " + WorldScreen.Player.Team[SelectedPokemon].XP + "/" + PkmnUtils.NextLevel(DataManager.PokemonSpecies[WorldScreen.Player.Team[SelectedPokemon].PokemonID].GrowthRate, WorldScreen.Player.Team[SelectedPokemon].Level);

            /* Finally, the source rectangles of the type and gender sprites are changed to the rectangles for the new Pokemon's type(s) and gender.
             * If the Pokemon is dual type, the second type sprite is updated correctly.
             * If it's single type, the source rectangle of the second type sprite is changed to null so it doesn't draw anything. */
            typeSprite.SourceRectangle = typeRects[(int)WorldScreen.Player.Team[SelectedPokemon].Type[0]];
            if (WorldScreen.Player.Team[SelectedPokemon].Type.Count > 1)
            {
                secondTypeSprite.SourceRectangle = typeRects[(int)WorldScreen.Player.Team[SelectedPokemon].Type[1]];
            }
            else
            {
                secondTypeSprite.SourceRectangle = new Rectangle();
            }
            genderSprite.SourceRectangle = genderRects[(int)WorldScreen.Player.Team[SelectedPokemon].PokemonGender];
        }
コード例 #10
0
        /* The CreateControls() method for the InventoryScreen is quite long but doesn't require much explanation. */
        private void CreateControls()
        {
            /* The three textures for the LeftRightSelector are loaded in. */
            Texture2D leftTexture  = Game.Content.Load <Texture2D>(@"GUI/leftArrowUp");
            Texture2D rightTexture = Game.Content.Load <Texture2D>(@"GUI/rightArrowUp");
            Texture2D stopTexture  = Game.Content.Load <Texture2D>(@"GUI/stopBar");

            /* The background image is loaded in with Content.Load<T>() and given the window as the destination rectangle.
             * Every Control is added to the ControlManager. */
            backgroundImage = new PictureBox(Game.Content.Load <Texture2D>(@"Backgrounds/startscreen"), GameRef.ScreenRectangle);
            ControlManager.Add(backgroundImage);

            /* Each sprite's PictureBox is constructed using the sprites applying to the first Pokemon in the Player's inventory.
             * In the cases of the Pokemon sprite, the image is obtained by finding it (by its ID) in the DataManager's master list of Pokemon sprites.
             * The type and gender sprites are found by loading in the whole sprite sheet, and setting the source rectangle using the array of source rectangles.
             * The second type sprite is not always used, since Pokemon can be single or dual type.
             * If the first Pokemon in the Player's team is single type, the second type sprite is initialized with a source rectangle of size zero.
             * Effectively, this means nothing will be drawn to the screen for the second type. */
            pkmnSprite = new PictureBox(DataManager.PkmnFrontSprites[WorldScreen.Player.Team[0].PokemonID], new Rectangle(20, 100, 288, 288));
            ControlManager.Add(pkmnSprite);
            typeSprite = new PictureBox(typeSheet, typeRects[(int)WorldScreen.Player.Team[0].Type[0]], new Rectangle(50, 400, 105, 40));
            ControlManager.Add(typeSprite);
            if (WorldScreen.Player.Team[0].Type.Count > 1)
            {
                secondTypeSprite = new PictureBox(typeSheet, typeRects[(int)WorldScreen.Player.Team[0].Type[1]], new Rectangle(175, 400, 105, 40));
            }
            else
            {
                secondTypeSprite = new PictureBox(typeSheet, new Rectangle(), new Rectangle(175, 400, 105, 40));
            }
            ControlManager.Add(secondTypeSprite);
            genderSprite = new PictureBox(genderSheet, genderRects[(int)WorldScreen.Player.Team[0].PokemonGender], new Rectangle(300, 380, 64, 64));
            ControlManager.Add(genderSprite);

            /* The pokemonSelector is created by passing in the three textures loaded in earlier.
             * The items for the pokemonSelector are set to the pokemonNames array with a maximum width of 175.
             * Its SelectionChanged event handler is wired to call pokemonSelector_SelectionChanged(). */
            pokemonSelector = new LeftRightSelector(leftTexture, rightTexture, stopTexture);
            pokemonSelector.SetItems(pokemonNames, 175);
            pokemonSelector.Position          = new Vector2(300, 100);
            pokemonSelector.SelectionChanged += new EventHandler(pokemonSelector_SelectionChanged);
            ControlManager.Add(pokemonSelector);

            /* Each of the labels on the screen are constructed in the same way.
             * After initialization, the Text property is set using the properties of the current Pokemon.
             * Size is calculated using the SpriteFont.MeasureString() method.
             * Finally, the position is set to a Vector2. The last six labels (displaying level and stats) are aligned to x = 400. */
            name          = new Label();
            name.Text     = "Inventory";
            name.Size     = name.SpriteFont.MeasureString(name.Text);
            name.Position = new Vector2((GameRef.Window.ClientBounds.Width - name.Size.X) / 2, 50);
            ControlManager.Add(name);
            level          = new Label();
            level.Text     = "Lv " + WorldScreen.Player.Team[0].Level;
            level.Size     = level.SpriteFont.MeasureString(level.Text);
            level.Position = new Vector2(400, 150);
            ControlManager.Add(level);
            hp          = new Label();
            hp.Text     = "HP: " + WorldScreen.Player.Team[0].Health.CurrentValue + "/" + WorldScreen.Player.Team[0].Health.MaximumValue;
            hp.Size     = hp.SpriteFont.MeasureString(hp.Text);
            hp.Position = new Vector2(400, 250);
            ControlManager.Add(hp);
            atk          = new Label();
            atk.Text     = "ATK: " + WorldScreen.Player.Team[0].Attack;
            atk.Size     = atk.SpriteFont.MeasureString(atk.Text);
            atk.Position = new Vector2(400, 280);
            ControlManager.Add(atk);
            def          = new Label();
            def.Text     = "DEF: " + WorldScreen.Player.Team[0].Defence;
            def.Size     = def.SpriteFont.MeasureString(def.Text);
            def.Position = new Vector2(400, 310);
            ControlManager.Add(def);
            spatk          = new Label();
            spatk.Text     = "SP. ATK: " + WorldScreen.Player.Team[0].SpecialAttack;
            spatk.Size     = spatk.SpriteFont.MeasureString(spatk.Text);
            spatk.Position = new Vector2(400, 340);
            ControlManager.Add(spatk);
            spdef          = new Label();
            spdef.Text     = "SP. DEF: " + WorldScreen.Player.Team[0].SpecialDefence;
            spdef.Size     = spdef.SpriteFont.MeasureString(spdef.Text);
            spdef.Position = new Vector2(400, 370);
            ControlManager.Add(spdef);
            spd          = new Label();
            spd.Text     = "SPD: " + WorldScreen.Player.Team[0].Speed;
            spd.Size     = spd.SpriteFont.MeasureString(spd.Text);
            spd.Position = new Vector2(400, 400);
            ControlManager.Add(spd);
            xp          = new Label();
            xp.Text     = "XP: " + WorldScreen.Player.Team[0].XP + "/" + PkmnUtils.NextLevel(DataManager.PokemonSpecies[WorldScreen.Player.Team[0].PokemonID].GrowthRate, WorldScreen.Player.Team[0].Level);
            xp.Size     = xp.SpriteFont.MeasureString(xp.Text);
            xp.Position = new Vector2(200, 430);
            ControlManager.Add(xp);

            /* The saveLabel is constructed and set with its text, size, and position. It is set with the saveLabel_Selected() function as its Selected event. */
            saveLabel           = new LinkLabel();
            saveLabel.Text      = "Save";
            saveLabel.Size      = saveLabel.SpriteFont.MeasureString(saveLabel.Text);
            saveLabel.Position  = new Vector2(400, 430);
            saveLabel.Selected += new EventHandler(saveLabel_Selected);
            ControlManager.Add(saveLabel);

            /* ControlManager.NextControl() is called to have a control already selected when the user enters the screen. */
            ControlManager.NextControl();
        }
コード例 #11
0
ファイル: TurnMove.cs プロジェクト: Metasynic/PkmnEngine
        /* Execute() is called when the move is actually used.
         * It returns a boolean dictating whether or not the battle needs to end after this turn. */
        public override bool Execute()
        {
            /* The damage field holds the amount of health that will be removed from the target Pokemon when the move is used. */
            decimal damage;

            /* The multiplier is a factor applied to the damage to reflect type advantage and STAB (Same Type Attack Bonus).
             * These two things will be explained further down the class. */
            decimal multiplier;

            /* First, the engine checks whether the move is going to hit the target or not.
             * It calls the static AccuracyCheck() function, passing in the relevant stats. */
            if (Battle.AccuracyCheck(move.Accuracy, user.AccStage, target.EvaStage))
            {
                /* This whole block executes if the user Pokemon is able to hit the target.
                 * As only damaging moves are required for this engine's user requirements, the stat stage affecting moves "Growl" and "Leer" do nothing.
                 * Hence, the engine checks if the move being used is one of these, and if it isn't, it moves on to damage calculations. */
                if (move.Name == "Growl" || move.Name == "Leer")
                {
                }

                else
                {
                    /* The same formula is used for damage calculation, regardless of whether the attack is of physical or special category.
                     * What differs is that physical moves take into account the user attack and target defence,
                     * whereas special moves use the user special attack and target special defence.
                     * In both cases, the user's level and the move's power value are also part of the final formula. */
                    if (move.Category == MoveCategory.physical)
                    {
                        damage = ((((2 * user.Level / 5 + 2) * user.Attack * move.Power / target.Defence) / 50) + 2);
                    }
                    else if (move.Category == MoveCategory.special)
                    {
                        damage = ((((2 * user.Level / 5 + 2) * user.SpecialAttack * move.Power / target.SpecialDefence) / 50) + 2);
                    }

                    /* If the move is of "Status" category (moves which burn/freeze/paralyze the target), an exception is thrown, as this category of moves is not implemented. */
                    else
                    {
                        throw new NotImplementedException("Status moves not yet implemented.");
                    }

                    /* Now that the damage is calculated, the engine must determine the multiplier.
                     * It starts with a default value of one (the empty product), which will leave the damage unchanged. */
                    multiplier = 1.0m;

                    /* The multiplier can be affected by type advantage/disadvantage.
                     * For instance, if a water-type move is used against a fire-type, the damage doubles, but if it's the other way round, it halves.
                     * This is achieved by creating a TypePair object containing the attack type and the defence type, then checking the DataManager.TypeMatchups entry for that TypePair.
                     * As the target may have two types, the check needs to happen once for each of the target's types, and the effects on the multiplier are stacked.
                     * This means the maximum possible multiplier at this point is 4.0, and the minimum is 0.0 (which only happens if one of the combinations is an immunity). */
                    if (PkmnUtils.TypeMatchups.ContainsKey(new TypePair(move.Type, target.Type[0])))
                    {
                        multiplier *= PkmnUtils.TypeMatchups[new TypePair(move.Type, target.Type[0])];
                    }
                    if (target.IsDualType && PkmnUtils.TypeMatchups.ContainsKey(new TypePair(move.Type, target.Type[1])))
                    {
                        multiplier *= PkmnUtils.TypeMatchups[new TypePair(move.Type, target.Type[1])];
                    }

                    /* The other factor affecting the multiplier is STAB (Same Type Attack Bonus).
                     * This is applied when a Pokemon uses a move of the same type as itself.
                     * So, if a psychic type Pokemon uses a psychic type move, the multiplier increases by 1.5 in addition to any type advantage modifiers. */
                    foreach (PkmnType type in user.Type)
                    {
                        if (type == move.Type)
                        {
                            multiplier *= 1.5m;
                        }
                    }

                    /* Now that we have the initial damage and multiplier, the multiplier is applied to the initial damage to get the final damage.
                     * The name of the user, move, and amount of damage is written to the battle's log. */
                    damage *= multiplier;
                    battleRef.WriteToLog(user.Nickname + " used " + move.Name + "! (" + (ushort)damage + " damage)");

                    /* If a type advantage multiplier was used, then another message will be written to the log.
                     * The engine has to check for two values per message - one with STAB applied, and one without. */
                    if (multiplier == 4.0m || multiplier == 6.0m)
                    {
                        battleRef.WriteToLog("It's super duper effective!");
                    }
                    else if (multiplier == 2.0m || multiplier == 3.0m)
                    {
                        battleRef.WriteToLog("It's super effective!");
                    }
                    else if (multiplier == 0.5m || multiplier == 0.75m)
                    {
                        battleRef.WriteToLog("It's not very effective...");
                    }
                    else if (multiplier == 0.25m || multiplier == 0.375m)
                    {
                        battleRef.WriteToLog("It's really not very effective at all...");
                    }
                    else if (multiplier == 0m)
                    {
                        battleRef.WriteToLog("It had no effect!");
                    }

                    /* Finally, the damage is subtracted from the target Pokemon's health, doing the damage. */
                    target.Health.Subtract((ushort)damage);
                }

                /* Now that the move has been executed, the engine checks if the target Pokemon has no health left.
                 * If this is the case, its fainted flag is set as true, and the information is written to the battle log. */
                if (target.Health.CurrentValue <= 0)
                {
                    target.Fainted = true;
                    battleRef.WriteToLog(target.Nickname + " Fainted!");

                    /* If the Pokemon that used the move belongs to the player, then they need to earn the correct amount of XP points. */
                    if (user == battleRef.CurrentPlayer)
                    {
                        /* The XP value to be added is calculated using the defeated Pokemon's base experience multiplied by its level, all divided by seven.
                         * This number is added to the player Pokemon's XP. Note that the Pokemon's XP field holds the amount of XP they've gained since the last level up,
                         * not the total amount of XP gained overall.
                         * The amount of earned XP is written to the log. */
                        int xp = ((Battle.PokemonSpeciesRef[target.PokemonID].BaseExp * target.Level) / 7);
                        user.XP += xp;
                        battleRef.WriteToLog(user.Nickname + " earned " + xp + " XP!");

                        /* Now the engine checks for level ups. In a loop, the engine checks whether the user's XP is greater than or equal to the amount of XP they need to level up.
                         * In addition, a level 100 Pokemon cannot level up, so it checks for that as well.
                         * The formula to calculate the next level is in the PkmnUtils class and is explained there.
                         * If the user can level up, the XP required to do so is removed from their XP pool, and their level increases by one.
                         * They might have become level 100, in which case any excess XP is stripped as it is of no use.
                         * The level up message is written to the log.
                         * Since this happens in a while loop, if there's enough XP to level up multiple times, it will happen automatically. */
                        while (user.XP >= PkmnUtils.NextLevel(Battle.PokemonSpeciesRef[user.PokemonID].GrowthRate, user.Level) && user.Level < 100)
                        {
                            user.XP -= PkmnUtils.NextLevel(Battle.PokemonSpeciesRef[user.PokemonID].GrowthRate, user.Level);
                            user.Level++;
                            if (user.Level == 100)
                            {
                                user.XP = 0;
                            }
                            battleRef.WriteToLog(user.Nickname + " levelled up to Lv " + user.Level);
                        }
                    }
                }
            }

            /* This block executes if the user misses with their move. It writes a message to the log. */
            else
            {
                battleRef.WriteToLog(user.Nickname + "'s attack missed!");
            }

            /* Lastly, since the move has been used, its PP decreases by one point.
             * The EmptyLog flag in the battle is set to true so that the log is displayed.
             * The function returns false as the battle should not end at the moment. */
            move.PP.CurrentValue--;
            battleRef.EmptyLog = true;
            return(false);
        }