Exemple #1
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();
        }
Exemple #2
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];
        }
Exemple #3
0
        /* 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);
        }