Resource Making of an RPG plugin [A copy and paste tutorial]

Discussion in 'Plugin Help/Development/Requests' started by terturl890, Jan 3, 2015.

?

What class spell should I post first?

Poll closed Jan 10, 2015.
  1. Archer

    0 vote(s)
    0.0%
  2. Mage

    50.0%
  3. Warrior

    0 vote(s)
    0.0%
  4. Tank

    0 vote(s)
    0.0%
  5. Healer

    50.0%
Thread Status:
Not open for further replies.
  1. Offline

    terturl890

    So there are many plugin devs I see lately trying to make an RPG in minecraft. I also found most of them are having troubles and don't know how to do some things. So this will be a copy-and-paste guide and tutorial to making an RPG with custom items (items that do different damage, different names, and abilities), Quests, Mounts, Pets that can help in battle (Well all that can be used), Level system, as well as a player targeting system for the entities to hit the player doing the most threat! Check back for updates and be sure to comment what you want to be added and how I am doing!

    This tutorial is how I WOULD DO THINGS. This is just my opinion on how it should be coded. There are many ways and even more advanced ways todo this things. But I aim for a simple and understandable way of making a basic RPG. You can Edit, Use, and Share all code in this.

    WORD OF WARNING!
    These codes are NOT for people new to coding AT ALL. This code should be used by people who can troubleshoot errors, know what I mean when I say "put in a listener class", and know that if some code is wrong they can do a quick fix.

    First off, the basics of a character creation:
    Character Creation (Updated with the new Money system) (open)

    Code:
    // Inside a Listener Class
    @EventHandler
        public void onJoin(PlayerJoinEvent e) {
           Player p = e.getPlayer();
           if(!p.hasPlayedBefore()) {
               p.sendMessage(ChatColor.BLUE + "Welcome new player, to the world of [NAME]!" + "\n This is a world of fantasy and team work." + "\n team up with other players to work on quests and dungeons!" + "\n to start your journey, do /letsbegin");
           }
        }
    
    // Inside the main class or a command class
    
    public Map<UUID, Boolean> hasPicked = new HashMap<UUID, Boolean>();
    
    public Location yourSpawn = new Location(Bukkit.getServer().getWorlds().get(0), Integer.valueOf(X CORD), Integer.valueOf(Y CORD), Integer.valueOf(Z CORD)); //Sorry I will make config for the spawn and commands to set the spawn later. (This spawn is different from the world spawn)
    
    public boolean playerHasPicked(Player p) {
           UUID uuid = p.getUniqueId();
           if(hasPicked.get(uuid) == true)
               return true;
           return false;
        }
      
        public boolean onCommand(CommandSender cs, Command cmd, String label, String[] args) {
           Player p = (Player)cs;
           if(label.equalsIgnoreCase("letsbegin")) {
               p.sendMessage(ChatColor.GREEN + "Great!" + ChatColor.BLUE + "\n now lets pick your class!" + "\n do /class list for a list of classes" + "\n and /class pick {class} to pick your class!");
           }
           if(label.equalsIgnoreCase("class")) {
               if(args.length == 1)
                   p.sendMessage(ChatColor.RED + "Invalid arguments!");
               if(args.length == 2) {
                   if(args[1].equalsIgnoreCase("list")) {
                   p.sendMessage(ChatColor.GOLD + "Class List:" + ChatColor.RED + "\n Warrior" + ChatColor.BLUE + "\n Mage" + ChatColor.GREEN + "Tank" + ChatColor.RED + "Archer" + ChatColor.LIGHT_PURPLE + "Healer");
                   }
               }
               if(args.length == 3) {
                   if(args[1].equalsIgnoreCase("pick")) {
                       p.sendMessage(ChatColor.RED + "Invalid arguments!");
                       if(!playerHasPicked(p)) {
                           if(args[2].equalsIgnoreCase("Warrior")) {
                               File f = new File(getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
                               YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
                               config.set(p.getName() + ".Class", Integer.valueOf(1)); //Integers are easier for later on
                               config.set(p.getName() + ".Type", Integer.valueOf(1)); //1 = physical 2 = magical
                               config.set(p.getName() + ".Health", Integer.valueOf(30)); //15 hearts
                               config.set(p.getName() + ".BasicDamage", Integer.valueOf(2)); // By itself, this class with do 1 heart(s) of damage (integers of 1 will add 1)
                               config.set(p.getName() + ".Level", Integer.valueOf(1));
                               config.set(p.getName() + ".Exp", Integer.valueOf(0));
                               config.set(p.getName() + ".Money", Integer.valueOf(100)); //Set to what you want them to start with
                               try {
                                   config.save(f);
                               } catch (Exception exc) {
                                   exc.printStackTrace();
                               }
                               p.sendMessage(ChatColor.GREEN + "You picked the Warrior class!");
                               p.setMaxHealth(config.getInt(p.getName() + ".Health"));
                               p.setHealth(p.getMaxHealth());
                               p.teleport(yourSpawn);
                               p.setLevel(config.getInt(p.getName() + ".Level"));
                           }
                           if(args[2].equalsIgnoreCase("Tank")) {
                               File f = new File(getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
                               YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
                               config.set(p.getName() + ".Class", Integer.valueOf(2)); //Integers are easier for later on
                               config.set(p.getName() + ".Type", Integer.valueOf(1)); //1 = physical 2 = magical
                               config.set(p.getName() + ".Health", Integer.valueOf(50)); //25 hearts
                               config.set(p.getName() + ".BasicDamage", Integer.valueOf(1)); // By itself, this class with do .5 heart(s) of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".Level", Integer.valueOf(1));
                               config.set(p.getName() + ".Exp", Integer.valueOf(0));
                               config.set(p.getName() + ".Money", Integer.valueOf(100)); //Set to what you want them to start with
                               try {
                                   config.save(f);
                               } catch (Exception exc) {
                                   exc.printStackTrace();
                               }
                               p.sendMessage(ChatColor.GREEN + "You picked the Tank class!");
                               p.setMaxHealth(config.getInt(p.getName() + ".Health"));
                               p.setHealth(p.getMaxHealth());
                               p.teleport(yourSpawn);
                               p.setLevel(config.getInt(p.getName() + ".Level"));
                           }
                           if(args[2].equalsIgnoreCase("Mage")) {
                               File f = new File(getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
                               YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
                               config.set(p.getName() + ".Class", Integer.valueOf(3)); //Integers are easier for later on
                               config.set(p.getName() + ".Type", Integer.valueOf(2)); //1 = physical 2 = magical
                               config.set(p.getName() + ".Health", Integer.valueOf(20)); //10 hearts
                               config.set(p.getName() + ".BasicDamage", Integer.valueOf(1)); // By itself, this class with do .5 heart(s) of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".BasicRangedDamage", Integer.valueOf(3)); //When doing ranged attack, this class does 1.5 hearts of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".Level", Integer.valueOf(1));
                               config.set(p.getName() + ".Exp", Integer.valueOf(0));
                               config.set(p.getName() + ".Money", Integer.valueOf(100)); //Set to what you want them to start with
                               try {
                                   config.save(f);
                               } catch (Exception exc) {
                                   exc.printStackTrace();
                               }
                               p.sendMessage(ChatColor.GREEN + "You picked the Mage class!");
                               p.setMaxHealth(config.getInt(p.getName() + ".Health"));
                               p.setHealth(p.getMaxHealth());
                               p.teleport(yourSpawn);
                               p.setExp(config.getInt(p.getName() + ".Level"));
                           }
                           if(args[2].equalsIgnoreCase("Archer")) {
                               File f = new File(getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
                               YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
                               config.set(p.getName() + ".Class", Integer.valueOf(4)); //Integers are easier for later on
                               config.set(p.getName() + ".Type", Integer.valueOf(1)); //1 = physical 2 = magical
                               config.set(p.getName() + ".Health", Integer.valueOf(30)); //15 hearts
                               //Class is RANGED only!
                               config.set(p.getName() + ".BasicRangedDamage", Integer.valueOf(3)); //When doing ranged attack, this class does 1.5 hearts of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".Level", Integer.valueOf(1));
                               config.set(p.getName() + ".Exp", Integer.valueOf(0));
                               config.set(p.getName() + ".Money", Integer.valueOf(100)); //Set to what you want them to start with
                               try {
                                   config.save(f);
                               } catch (Exception exc) {
                                   exc.printStackTrace();
                               }
                               p.sendMessage(ChatColor.GREEN + "You picked the Archer class!");
                               p.setMaxHealth(config.getInt(p.getName() + ".Health"));
                               p.setHealth(p.getMaxHealth());
                               p.teleport(yourSpawn);
                               p.setExp(config.getInt(p.getName() + ".Level"));
                           }
                           if(args[2].equalsIgnoreCase("Healer")) {
                               File f = new File(getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
                               YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
                               config.set(p.getName() + ".Class", Integer.valueOf(5)); //Integers are easier for later on
                               config.set(p.getName() + ".Type", Integer.valueOf(2)); //1 = physical 2 = magical
                               config.set(p.getName() + ".Health", Integer.valueOf(40)); //20 hearts
                               config.set(p.getName() + ".BasicDamage", Integer.valueOf(1)); // By itself, this class with do .5 heart(s) of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".BasicRangedDamage", Integer.valueOf(2)); //When doing ranged attack, this class does 1 hearts of damage (integers of 1 will add .5)
                               config.set(p.getName() + ".BasicHeal", Integer.valueOf(4)); //Basic heal when they use there healing spells. Heals 2 hearts (integers of 1 will add .5)
                               config.set(p.getName() + ".Level", Integer.valueOf(1));
                               config.set(p.getName() + ".Exp", Integer.valueOf(0));
                               config.set(p.getName() + ".Money", Integer.valueOf(100)); //Set to what you want them to start with
                               try {
                                   config.save(f);
                               } catch (Exception exc) {
                                   exc.printStackTrace();
                               }
                               p.sendMessage(ChatColor.GREEN + "You picked the Healer class!");
                               p.setMaxHealth(config.getInt(p.getName() + ".Health"));
                               p.setHealth(p.getMaxHealth());
                               p.teleport(yourSpawn);
                               p.setExp(config.getInt(p.getName() + ".Level"));
                           }
                       }
                   }
               }
           }
            return false;
        }
    


    Leveling is a very small system of changing the level of a player and getting it but is a very reasonable one at that.

    Level System (open)

    Code:
    //add to a listener class (A.K.A PlayerListeners.java)
    @EventHandler
        public void onLevelChange(PlayerExpChangeEvent e) {
           e.setAmount(0); // I dont want players Level to change when the naturally get Exp points from killing mobs (This is an optional step but I am going to add ways to get Exp in a bit.
        }
    
    //Add to a Util class (A.K.A LevelingUtil.java)
    
    public void updatePlayerLevel(Player p) {
           File f = new File(plugin.getDataFolder() + File.separator + "Players" + File.separator + p.getName() + ".yml");
           YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
           p.setLevel(config.getInt(p.getName() + ".Level"));
           p.setExp(config.getInt(p.getName() + ".Exp"));
           if(p.getExpToLevel() == 0) { //So that when they have enough Exp to level up, they do
               addLevel(p, 1);
               p.setLevel(config.getInt(p.getName() + ".Level"));
               p.setExp(config.getInt(p.getName() + ".Exp"));
           }
           //this MUST be called EVERYTIME you use addExp or addExpLevel
        }
    
        public void addExp(Player p, int amount) {
           p.setExp(p.getExp() + amount);
        }
    
        public void addLevel(Player p, int amount) {
           p.setLevel(p.getLevel() + amount); //For those dungeons or rare quests ;)
        }
    
    That is all, we will be using these in the quest part


    Next we will have an ItemUtil.java class to hold all the info on items. This one will be LONG and look like its repetitive, but each "block" of code will do something different.

    Item System (open)

    Code:
    
    @SuppressWarnings("deprecation")
        public void giveItem(Player p, ItemStack m, String name, int rarity, String lore) {
           ItemStack name1 = new ItemStack(m);
           ItemMeta meta = name1.getItemMeta();
           if(rarity == 1) {
               meta.setDisplayName(ChatColor.GRAY + name);
               List<String> lore1 = new ArrayList<String>();
               lore1.add("Common");
               lore1.add(lore);
               meta.setLore(lore1);
               name1.setItemMeta(meta);
              
               p.getInventory().addItem(m);
               p.updateInventory();
           }
           if(rarity == 2) {
               meta.setDisplayName(ChatColor.GREEN + name);
               List<String> lore1 = new ArrayList<String>();
               lore1.add("Uncommon");
               lore1.add(lore);
               meta.setLore(lore1);
               name1.setItemMeta(meta);
              
               p.getInventory().addItem(m);
               p.updateInventory();
           }
           if(rarity == 3) {
               meta.setDisplayName(ChatColor.GOLD + name);
               List<String> lore1 = new ArrayList<String>();
               lore1.add("Rare");
               lore1.add(lore);
               meta.setLore(lore1);
               name1.setItemMeta(meta);
              
               p.getInventory().addItem(m);
               p.updateInventory();
           }
           if(rarity == 4) {
               meta.setDisplayName(ChatColor.DARK_PURPLE + name);
               List<String> lore1 = new ArrayList<String>();
               lore1.add("Legendary");
               lore1.add(lore);
               meta.setLore(lore1);
               name1.setItemMeta(meta);
              
               p.getInventory().addItem(m);
               p.updateInventory();
           }
        }
    
        @SuppressWarnings("deprecation")
        public void giveItem(Player p, ItemStack m, String name, ChatColor g, List<String> lore) {
           ItemStack name1 = new ItemStack(m);
           ItemMeta meta = name1.getItemMeta();
           meta.setDisplayName(g + "name");
           meta.setLore(lore);
           name1.setItemMeta(meta);
    
           p.getInventory().addItem(m);
           p.updateInventory();
        }
    
    //I will add more methods later on
    
    


    Questing will give players rewards like: Mounts, Pets, Exp, Money, and Items or Armor.
    More will be added but for now it is only going to be getting rewards setup.

    Questing (Updated with the new money system and item system) (open)

    Code:
    //put this in a util class. Like QuestingUtil.java or QuestingRewards.java
    
    //For this one, you need to hook into the leveling util we made
        public LevelingUtils lu;
        public ItemUtil IU;
        public Main plugin;
      
        public void giveExp(Player p, int amount) {
           lu.addExp(p, amount);
           lu.updatePlayerLevel(p);
        }
    
        public void giveExpLevel(Player p, int amount) {
           lu.addLevel(p, amount);
           lu.updatePlayerLevel(p);
        }
    
        public void giveMoney(Player p, int amount) {
           File f = new File(plugin.getDataFolder() + File.separator + "players" + File.separator + p.getName() + ".yml");
           YamlConfiguration config = YamlConfiguration.loadConfiguration(f);
           config.set(p.getName() + ".Money", Integer.valueOf(config.getInt(p.getName() + ".Money") + amount));
           try {
               config.save(f);
           } catch (Exception e) {
               e.printStackTrace();
           }
        }
    
        //Hook into the ItemUtil
        public void rewardBasicItem(Player p, Material m, ChatColor g, String lore) {
           IU.giveItem(p, new ItemStack(m), "a name", g, lore); //IU.giveItem(p, Material.IRON_SWORD, "Behomth", ChatColor.GREEN, "An epic item of bigness");
        }
    
    //Will add more later
    
    


    Achievements! (open)

    This one will be the LONGEST one so I have split up the Spoiler into MANY MANY MANY spoilers to show all the achievements I want to add, of course, you can make your own.

    To start making these achievements you will need to hook into the plugin "CustomAchievements", by teej107. Failure to do things, your achievements will not work.
    For the achievements I will be showing AN ENTIRE class of how it is made. except for all the imports.

    Killing 10 Zombies (open)

    make a achievements package in your plugin (a.k.a me.terturl.RPG.achievements)
    and add the class, KillTenZombies.java
    Code:
       
    public ItemUtil IU;
        private Map<UUID, Integer> zombiesKilled; // a map to store data with players UUID and the amount of zombies they killed
    
        public KillTenZombies() { //public {your class name}() to add the super() stanza and other needed components.
            super("Kill 10 zombies", Material.WOOD_SWORD); //This makes the name and what the achievement's looks like.
            setColor(ChatColor.GREEN); //sets the color of the name
            getLore().add("Kill 10 zombies!");
            setDisplayUnachieved(true); //false means you will ONLY see it if you have got it. True means ANYONE can see it
    
            zombiesKilled = new HashMap<UUID, Integer>();
        }
    
        @EventHandler
        public void onZombieKill(EntityDeathEvent e) {
            Player p = e.getEntity().getKiller();
            UUID uuid = p.getUniqueId();
    
            if(e.getEntity() instanceof Zombie) { //Checking if the entity is a zombie
    
                if(!zombiesKilled.containsKey(uuid)) { //Checking if the player is NOT put in the system yet
                    zombiesKilled.put(uuid, 0);
                }
    
                zombiesKilled.put(uuid, zombiesKilled.get(uuid) + 1); //Adding 1 if they killed a zombie
    
                if(zombiesKilled.get(uuid) > 10) { //checks if the player killed 10 zombies
                    giveAchievement(p); //awards the achievement
                    //(optional below)
                    IU.giveItem(p, new ItemStack(Material.STONE_SWORD), "Zombie Killer", ChatColor.GOLD, "You are a killer of zombies");
                }
            }
        }
        

    Getting rare loot (open)

    This will be an achievement when a player receives an item with the Rare Loot lore in the items lore. (A new item update has happened as well)
    Code:
       
    public RareLootCheck() {
            super("Rare Loot!", Material.DIAMOND);
            setColor(ChatColor.GOLD);
            getLore().add("Acquire a rare item.");
            setDisplayUnachieved(false); //Only people who got it, can see it
        }
    
        @EventHandler
        public void onPickUp(PlayerPickupItemEvent e) {
            if(e.getItem().getItemStack().getItemMeta().getLore().contains("Rare")) {
                giveAchievement(e.getPlayer());
            }
        }
        

    Check back later for more achievements and other goodies!


    Haha that is it for right now, I will work on ways to make quests, items, pets, mounts, and spells a bit later. SPELLS codes WILL BE PUT INTO SPOILERS and INTO THEIR OWN SPOILERS IN THE SPOILERS to make sure the page is not so big.
     
    Last edited: Jan 4, 2015
  2. Offline

    teej107

    • Unchecked casting.
    • Using command label
    • Not using encapsulation
    • Not using @Override (highly recommended)
    • Code:
      new File(p.getDataFolder() + "players" + File.seperator + p.getName() + ".yml");
    I would fix the above.
     
    ZodiacTheories likes this.
  3. Offline

    terturl890

    1) I said its a WIP :p (not using those words of course)
    2) Command label works JUST the same as cmd.label or something like that
    3) Why would I pack everything? This is a way for players to look for specific things they want
    4) That was an honest mistake. All was fixed and put (decently) right.

    This is not meant for noobs to start coding, this is meant for people who know how to code bukkit plugins, or at least know the basics, to get a broader understanding of making a RPG plugin. Now yes, I will give you I did put p.getDataFolder() which refers to the player by accident, I fixed that. But this was all coded without a editor since I dont have one on my tablet. When I get home from vacation, well winter break, I will start on fixing everything and tidying it up. Most players though should be able to troubleshoot if things go wrong or simple ask question on bukkit or even me if they needed help :)

    If this was meant for new coders I would have a lot more indepth like the entire class of something. (A.K.A create a class called [clever name here] and put these lines of code into it {array of strings A.K.A lines of codes} and you should get the result of (what was intended))

    But thank you for your input :) this will help me to make this post more professional and appealing to coders.

    Thanks to teej107, I added a new achievements option so now we can start making some really cool and custom achievements ;)

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: Oct 31, 2016
  4. Offline

    terturl890

  5. Offline

    terturl890

Thread Status:
Not open for further replies.

Share This Page