aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore5
-rw-r--r--src/config.yml747
-rw-r--r--src/me/fatpigsarefat/quests/Quests.java426
-rw-r--r--src/me/fatpigsarefat/quests/bstats/Metrics.java661
-rw-r--r--src/me/fatpigsarefat/quests/commands/CommandQuests.java222
-rw-r--r--src/me/fatpigsarefat/quests/events/EventInventory.java73
-rw-r--r--src/me/fatpigsarefat/quests/events/EventPlayerJoin.java21
-rw-r--r--src/me/fatpigsarefat/quests/events/EventPlayerLeave.java25
-rw-r--r--src/me/fatpigsarefat/quests/obj/Items.java23
-rw-r--r--src/me/fatpigsarefat/quests/obj/Messages.java45
-rw-r--r--src/me/fatpigsarefat/quests/obj/Options.java48
-rw-r--r--src/me/fatpigsarefat/quests/obj/misc/QItemStack.java108
-rw-r--r--src/me/fatpigsarefat/quests/obj/misc/QMenu.java12
-rw-r--r--src/me/fatpigsarefat/quests/obj/misc/QMenuCategory.java92
-rw-r--r--src/me/fatpigsarefat/quests/obj/misc/QMenuQuest.java177
-rw-r--r--src/me/fatpigsarefat/quests/player/QPlayer.java101
-rw-r--r--src/me/fatpigsarefat/quests/player/QPlayerManager.java86
-rw-r--r--src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgress.java96
-rw-r--r--src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgressFile.java234
-rw-r--r--src/me/fatpigsarefat/quests/player/questprogressfile/TaskProgress.java41
-rw-r--r--src/me/fatpigsarefat/quests/quests/Category.java42
-rw-r--r--src/me/fatpigsarefat/quests/quests/Quest.java96
-rw-r--r--src/me/fatpigsarefat/quests/quests/QuestManager.java38
-rw-r--r--src/me/fatpigsarefat/quests/quests/Task.java38
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/ASkyBlockLevelType.java52
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/BuildingCertainTaskType.java72
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/BuildingTaskType.java55
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/FishingTaskType.java61
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/MilkingTaskType.java64
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/MiningCertainTaskType.java72
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/MiningTaskType.java56
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingCertainTaskType.java91
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingTaskType.java87
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/PlayerkillingTaskType.java70
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/ShearingTaskType.java63
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/TamingTaskType.java62
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/TaskType.java52
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/TaskTypeManager.java40
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/WalkingTaskType.java62
-rw-r--r--src/me/fatpigsarefat/quests/quests/tasktypes/uSkyBlockLevelType.java52
-rw-r--r--src/me/fatpigsarefat/quests/title/Title.java8
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_Other.java19
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_10_R1.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_11_R1.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_12_R1.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_8_R1.java23
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_8_R2.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_8_R3.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_9_R1.java22
-rw-r--r--src/me/fatpigsarefat/quests/title/Title_v1_9_R2.java22
-rw-r--r--src/me/fatpigsarefat/quests/updater/Updater.java59
-rw-r--r--src/plugin.yml17
52 files changed, 4748 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..4e6ce0ef
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+out/
+.idea/
+test/
+*.iml
+*.xml
diff --git a/src/config.yml b/src/config.yml
new file mode 100644
index 00000000..948a415b
--- /dev/null
+++ b/src/config.yml
@@ -0,0 +1,747 @@
+# | =================================================== |
+# | Thank you for downloading and trying out my plugin: |
+# | Quests |
+# | https://www.spigotmc.org/resources/23696/ |
+# | Created by fatpigsarefat |
+# | |
+# | =x= |
+# | |
+# | File comments should help |
+# | you with the new config |
+# | and guide you with making |
+# | quests |
+# | |
+# | =x= |
+# | |
+# | Have Discord & need help? |
+# | https://www.discord.gg/8amrJnX |
+# | =================================================== |
+
+# !! READ ME !!
+#
+# A quest is a series of tasks which players must complete for a reward and may require a previous quest to start.
+# A task is an objective such as breaking blocks or obtaining items.
+# A reward is a command executed by the SERVER. Use {player} to get the players name.
+#
+# A quest can have a 'rewardstring' (this is optional). They will be sent to the player when they complete the quest.
+# An example of the rewardstring in use can be seen in the quest example4.
+#
+# Each quest will have ONE "display" item, this is the item shown to the player in the GUI.
+# The display item will have a "name", a "type" and TWO lores.
+# The name is the name of the item, the type is the material and the lore is the text underneath the item (when mouse-over-ing).
+# The first lore you must give is called 'lore-normal'. This is the lore seen if the player has not started the quest.
+# The second lore you must give is 'lore-started'. This will be appended to the first lore IF the player has started the quest - useful for putting progression.
+# Within the lores you can get the players" progress for each task. Use {TASKID:progress} (replace TASKID with the ID of the task).
+#
+# Quests can be put inside a category. When a player does /quests they will first see a menu of categories. They can click one and another menu of quests
+# under that category will show up. Categories can be disabled.
+#
+# !! READ ME !!
+
+# Everything inside of this section is a quest
+quests:
+ # This is the quest ID ("example"). This MUST be unique against all other quest IDs.
+ example1:
+ # Everything inside of this section defines tasks the player must complete to progress.
+ tasks:
+ # This is the task ID ("mining"). This can share the same name as the quest ID but MUST be unique with all other task IDs in the same quest.
+ mining:
+ # This defines what type of task this is. In this instance, it is "blockbreak" (breaking blocks)
+ # NOTE: guides to set up each type of task is on the plugin page!
+ type: "blockbreak"
+ # This defines the amount of blocks which need to be broken
+ amount: 30
+ # You can have multiple tasks for each quest (example further down).
+ # Everything inside of this section defines the display item.
+ display:
+ # This is the name of the item. This allows color codes.
+ name: "&cExample I (Single Task)"
+ # This is the lore of the item if the player has not started the quest. This allows color codes and task/player placeholders.
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Break 30 blocks."
+ - ""
+ - "&7Rewards:"
+ - "&7 - 10 diamonds."
+ # This lore will be appended to the bottom of the above lore when the player starts their quest.
+ # To get the players progress through a task, use {TASKID:progress} and replace TASKID with the ID of the task.
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mining:progress}/30 blocks broken."
+ # This is the material of the item. It is recommended to stick to bukkit names.
+ type: "WOOD_PICKAXE"
+ # List all commands to be executed by the server when the player completes the quest. Use {player} to get the players name.
+ rewards:
+ - "give {player} diamond 10"
+ # Everything inside this section define quest-specific options
+ options:
+ # This is the category for the quest, it will appear under the "examples" category. Categories can be disabled.
+ category: "examples"
+ # Set the quest IDs of required quests here, leave empty if none.
+ requires:
+ - ""
+ # Set if the quest can be repeated after being completed for the first time.
+ repeatable: false
+ # Define the cooldown on quests. The above (repeatable) must be true for this to take effect.
+ cooldown:
+ # If true, players will have to wait between repeating quests.
+ enabled: true
+ # Time (in minutes)
+ time: 1440
+
+ # This is a quest which requires the previous quest to be complete to start.
+ example2:
+ tasks:
+ # Unlike the previous quest, this quest has multiple tasks.
+ mining:
+ type: "blockbreak"
+ amount: 100
+ building:
+ type: "blockplace"
+ amount: 100
+ display:
+ name: "&cExample II (Multiple Tasks)"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Break 100 blocks."
+ - "&7 - Place 100 blocks."
+ - ""
+ - "&7Rewards:"
+ - "&7 - 15 diamonds."
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mining:progress}/100 blocks broken."
+ - "&7 - {building:progress}/100 blocks placed."
+ type: "GRASS"
+ rewards:
+ - "give {player} diamond 15"
+ - "eco give {player} 50"
+ options:
+ category: "examples"
+ # Unlike the previous quest, this quest has "example1" as a required quest. You cannot start this quest without "example1" quest complete.
+ requires:
+ - "example1"
+ repeatable: false
+ cooldown:
+ enabled: true
+ time: 1440
+
+ # This is a quest which requires the previous quest to be complete to start.
+ # Unlike the previous quest, this one can be re-done but it has a 10 minute cooldown.
+ example3:
+ tasks:
+ # Unlike the previous two quests, this quest specifies a specific block to be broken.
+ mining:
+ type: "blockbreakcertain"
+ amount: 81
+ block: 14 # (gold ore)
+ building:
+ type: "blockplacecertain"
+ amount: 9
+ block: 41 # (gold blocks)
+ display:
+ name: "&cExample III (Repeatable, 10 minute cooldown)"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Break 81 gold ore."
+ - "&7 - Place 9 gold blocks."
+ - ""
+ - "&7Rewards:"
+ - "&7 - 30 diamonds."
+ - "&7 - $10 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mining:progress}/81 gold ore broken."
+ - "&7 - {building:progress}/9 gold blocks placed."
+ type: "GOLD_ORE"
+ rewards:
+ - "give {player} diamond 30"
+ - "eco give {player} 10"
+ options:
+ category: "examples"
+ requires:
+ - "example2"
+ # This quest is repeatable, it has cooldowns enabled (meaning the player must wait before repeating it) and the time set to 10 (minutes).
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 10
+
+ # This is a quest which requires the previous quest to be complete to start.
+ # Unlike the previous quests, this quest has a reward string.
+ example4:
+ tasks:
+ mobkilling:
+ type: "mobkilling"
+ amount: 3
+ display:
+ name: "&cExample IV (Reward String)"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Kill 3 mobs."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mobkilling:progress}/3 mobs killed."
+ type: "STRING"
+ rewards:
+ - "eco give {player} 50"
+ # Here you can list messages which will be sent to the player (if they are online) upon completion.
+ rewardstring:
+ - ' &8* &c$10 &7was added to your in-game balance.'
+ - ' &8* &c30 diamonds &7was added to your inventory.'
+ options:
+ category: "examples"
+ requires:
+ - "example3"
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ # This is the end of the config example quests.
+ # Hopefully you should be able to understand the quest config from this.
+ # --------------------------------------------------------------------------------------
+ # Below are some basic quests. You should be able to understand what to do from reading these.
+ # These quests show off the other task types this plugin has to offer by default.
+ # Developers can add their own (look at the wiki on GitHub for details).
+
+ mining1:
+ tasks:
+ mining:
+ type: "blockbreak"
+ amount: 100
+ display:
+ name: "&cNovice Miner"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Break 100 blocks."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mining:progress}/100 blocks broken."
+ type: "WOOD_PICKAXE"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "easy"
+ requires:
+ - ""
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ building1:
+ tasks:
+ building:
+ type: "blockplace"
+ amount: 100
+ display:
+ name: "&cNovice Builder"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Place 100 blocks."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {building:progress}/100 blocks placed."
+ type: "STONE"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "easy"
+ requires:
+ - ""
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ youmonster:
+ tasks:
+ mobkilling:
+ type: "mobkilling"
+ amount: 10
+ hostile: false
+ display:
+ name: "&cYou Monster"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Kill 10 non-hostile animals."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $1 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mobkilling:progress}/10 non-hostile animals."
+ type: "PORK"
+ rewards:
+ - "eco give {player} 1"
+ options:
+ category: "easy"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ mobkiller:
+ tasks:
+ mobkilling:
+ type: "mobkilling"
+ amount: 10
+ hostile: true
+ display:
+ name: "&cMonster Slayer"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Kill 10 hostile monsters."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mobkilling:progress}/10 hostile monsters killed."
+ type: "WOOD_SWORD"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "easy"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ walking1:
+ tasks:
+ walking:
+ type: "walking"
+ distance: 1000
+ display:
+ name: "&cAdventurer"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Walk 1km."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {walking:progress}m/1000m walked."
+ type: "LEATHER_BOOTS"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "easy"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ playerkiller:
+ tasks:
+ playerkilling:
+ type: "playerkilling"
+ amount: 10
+ display:
+ name: "&cMurderer"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Kill 10 players."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $10 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {playerkilling:progress}/10 players killed."
+ type: "IRON_SWORD"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "medium"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ fisher:
+ tasks:
+ fishing:
+ type: "fishing"
+ amount: 10
+ display:
+ name: "&cProfessional Fisher"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Catch 10 items from the sea."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $30 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {fishing:progress}/10 items caught."
+ type: "FISHING_ROD"
+ rewards:
+ - "eco give {player} 30"
+ options:
+ category: "medium"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ animals1:
+ tasks:
+ milking:
+ type: "milking"
+ amount: 10
+ shearing:
+ type: "shearing"
+ amount: 10
+ taming:
+ type: "taming"
+ amount: 3
+ display:
+ name: "&cAnimal Keeper"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Milk 10 cows."
+ - "&7 - Shear 10 sheep."
+ - "&7 - Tame 3 animals as pets."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $50 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {milking:progress}/10 cows milked."
+ - "&7 - {shearing:progress}/10 sheep sheared."
+ - "&7 - {taming:progress}/3 animals tamed."
+ type: "MILK_BUCKET"
+ rewards:
+ - "eco give {player} 50"
+ options:
+ category: "medium"
+ requires:
+ - ''
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ mining2:
+ tasks:
+ mining:
+ type: "blockbreak"
+ amount: 350
+ ironmining:
+ type: "blockbreakcertain"
+ block: IRON_ORE
+ amount: 20
+ display:
+ name: "&cSkilled Miner"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Break 350 blocks."
+ - "&7 - Break 20 iron ore."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $150 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {mining:progress}/350 blocks broken."
+ - "&7 - {ironmining:progress}/20 iron ore broken."
+ type: "IRON_PICKAXE"
+ rewards:
+ - "eco give {player} 150"
+ options:
+ category: "medium"
+ requires:
+ - "mining1"
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ building2:
+ tasks:
+ building:
+ type: "blockplace"
+ amount: 350
+ woodbuilding:
+ type: "blockplacecertain"
+ block: 5
+ amount: 20
+ bricksbuilding:
+ type: "blockplacecertain"
+ block: 45
+ amount: 20
+ redwoolbuilding:
+ type: "blockplacecertain"
+ block: 35
+ amount: 20
+ data: 14
+ display:
+ name: "&cVariety Builder"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Place 350 blocks."
+ - "&7 - Place 20 oak wood."
+ - "&7 - Place 20 bricks."
+ - "&7 - Place 20 red wool."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $150 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {building:progress}/350 blocks placed."
+ - "&7 - {woodbuilding:progress}/20 oak wood placed."
+ - "&7 - {bricksbuilding:progress}/20 bricks placed."
+ - "&7 - {redwoolbuilding:progress}/20 red wool placed."
+ type: "WOOL"
+ rewards:
+ - "eco give {player} 150"
+ options:
+ category: "medium"
+ requires:
+ - "building1"
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ walking2:
+ tasks:
+ walking:
+ type: "walking"
+ distance: 10000
+ display:
+ name: "&cMountaineer"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Walk 10km."
+ - ""
+ - "&7Rewards:"
+ - "&7 - $500 added to your in-game balance."
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {walking:progress}m/10000m walked."
+ type: "LEATHER_BOOTS"
+ rewards:
+ - "eco give {player} 500"
+ options:
+ category: "medium"
+ requires:
+ - 'walking1'
+ repeatable: true
+ cooldown:
+ enabled: true
+ time: 1440
+
+ askyblock:
+ tasks:
+ islandlevel:
+ type: "askyblock_level"
+ level: 50
+ display:
+ name: "&cIslander (ASkyBlock)"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Reach island level 50"
+ - ""
+ - "&7Rewards:"
+ - "&7 - $30 added to your in-game balance."
+ - ''
+ - '&cRequires plugin ASkyBlock!'
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {islandlevel:progress}/50 island level."
+ type: "GRASS"
+ rewards:
+ - "eco give {player} 30"
+ options:
+ category: "dependent"
+ requires:
+ - ''
+ repeatable: false
+ cooldown:
+ enabled: true
+ time: 1440
+
+ uskyblock:
+ tasks:
+ islandlevel:
+ type: "uskyblock_level"
+ level: 50
+ display:
+ name: "&cIslander (uSkyBlock)"
+ lore-normal:
+ - "&7This quest requires you to:"
+ - "&7 - Reach island level 50"
+ - ""
+ - "&7Rewards:"
+ - "&7 - $30 added to your in-game balance."
+ - ''
+ - '&cRequires plugin uSkyBlock!'
+ lore-started:
+ - ""
+ - "&7Your current progression:"
+ - "&7 - {islandlevel:progress}/50 island level."
+ type: "GRASS"
+ rewards:
+ - "eco give {player} 30"
+ options:
+ category: "dependent"
+ requires:
+ - ''
+ repeatable: false
+ cooldown:
+ enabled: true
+ time: 1440
+
+# Categories are a way of grouping up quests.
+# When a player uses /quests, a menu of categories will be presented to them.
+# When a player clicks ona category, a list of quests under that category will show.
+# If categories are disabled, all quests will be shown under one big GUI.
+# Players can access specific categories by command using /quests c [category].
+# If a quest does not have a category, it will not be shown.
+categories:
+ examples:
+ display:
+ name: "&cExample Quests"
+ lore:
+ - "&7This category contains example quests"
+ - "&7which are commented in the config."
+ - "&7The comments should guide you with"
+ - "&7how the config works."
+ - ''
+ - '&cIt is highly recommended you read this'
+ - '&csection and all the comments so you can'
+ - '&cmake the most of this plugin.'
+ type: "327"
+ easy:
+ display:
+ name: "&cEasy Difficulty Quests"
+ lore:
+ - "&7This category contains easy quests."
+ - "&7They do not require you to do much but"
+ - "&7rewards are low."
+ type: "260"
+ medium:
+ display:
+ name: "&cIntermediate Difficulty Quests"
+ lore:
+ - "&7This category contains intermediate quests."
+ - "&7These quests are more challenging than the"
+ - "&7previous with greater rewards."
+ type: "264"
+ dependent:
+ display:
+ name: "&cDependent Quests"
+ lore:
+ - "&7This category contains quests which are dependent"
+ - "&7on other plugins being installed such as &cASkyBlock,"
+ - "&cuSkyBlock &7and &cCitizens&7."
+ type: "GRASS"
+
+# The items listed below are placeholder items for quests which the player cannot start.
+gui:
+ back-button:
+ name: "&cReturn"
+ lore:
+ - "&7Return to the categories menu."
+ type: "ARROW"
+ quest-locked-display:
+ name: "&c&lQuest Locked"
+ lore:
+ - "&7You have not completed the requirements"
+ - "&7for this quest (&c{quest}&7)."
+ - ''
+ - '&7Requires: &c{requirements}'
+ - '&7to be completed to unlock.'
+ type: "160:14"
+ quest-cooldown-display:
+ name: "&e&lQuest On Cooldown"
+ lore:
+ - "&7You have recently completed this quest"
+ - "&7(&e{quest}&7) and you must"
+ - "&7wait another &e{time} &7to unlock again."
+ type: "160:1"
+ quest-completed-display:
+ name: "&a&lQuest Complete"
+ lore:
+ - "&7You have completed this quest"
+ - "&7(&a{quest}&7) and cannot."
+ - '&7repeat it.'
+ type: "160:5"
+
+options:
+ # If categories are disabled, quests will be put into one big gui.
+ categories-enabled: true
+ # If true, the gui size will automatically change based on the amount of quests inside it.
+ trim-gui-size: true
+ titles-enabled: true
+ # Players cannot start any more quests than this at a single time
+ quest-started-limit: 2
+
+titles:
+ quest-start:
+ title: "&cQuest Started"
+ subtitle: "&7{quest}"
+ quest-complete:
+ title: "&cQuest Complete"
+ subtitle: "&7{quest}"
+
+messages:
+ quest-start: "&7Quest &c{quest} &7started!"
+ quest-complete: "&7Quest &c{quest} &7completed!"
+ quest-start-limit: "&7Players are limited to &c{limit} &7started quests at a time."
+ quest-start-disabled: "&7You cannot repeat this quest."
+ quest-start-locked: "&7You have not unlocked this quest yet."
+ quest-start-cooldown: "&7You have recently completed this quest. You have to wait &c{time} &7until you are able to restart it."
+ quest-updater: "&cQuests > &7A new version &c{newver} &7was found on Spigot (your version: &c{oldver}&7). Please update me! <3 - Link: {link}"
+ command-quest-start-doesntexist: "&7The specified quest '&c{quest}&7' does not exist."
+ command-category-open-disabled: "&7Categories are disabled."
+ command-category-open-doesntexist: "&7The specified category '&c{category}&7' does not exist."
+ command-quest-admin-playernotfound: "&7Player '&c{player}&7' could not be found."
+ command-quest-openquests-admin-success: "&7Opened Quest GUI for player &c{player}&7."
+ command-quest-opencategory-admin-success: "&7Opened category &c{category} &7for player &c{player}&7."
+ command-quest-start-admin-success: "&7Successfully started quest '&c{quest}&7' for player &c{player}&7."
+ command-quest-complete-admin-success: "&7Successfully completed quest '&c{quest}&7' for player &c{player}&7."
+ command-quest-reset-admin-success: "&7Successfully reset quest '&c{quest}&7' for player &c{player}&7."
+ command-quest-fullreset-admin-success: "&7Successfully reset player &c{player}&7."
+ command-quest-start-admin-fail: "&7Quest '&c{quest}&7' could not be started for player &c{player}&7. They may not have unlocked it, on cooldown, etc."
+ command-quest-complete-admin-fail: "&7Failed to complete '&c{quest}&7' for player &c{player}&7. They may have not started it."
+ command-taskview-admin-fail: "&7Task type '&c{task}&7' does not exist."
diff --git a/src/me/fatpigsarefat/quests/Quests.java b/src/me/fatpigsarefat/quests/Quests.java
new file mode 100644
index 00000000..2c3a7880
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/Quests.java
@@ -0,0 +1,426 @@
+package me.fatpigsarefat.quests;
+
+import com.google.common.io.ByteStreams;
+import me.fatpigsarefat.quests.bstats.Metrics;
+import me.fatpigsarefat.quests.commands.CommandQuests;
+import me.fatpigsarefat.quests.events.EventInventory;
+import me.fatpigsarefat.quests.events.EventPlayerJoin;
+import me.fatpigsarefat.quests.events.EventPlayerLeave;
+import me.fatpigsarefat.quests.obj.misc.QItemStack;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.QPlayerManager;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Category;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.QuestManager;
+import me.fatpigsarefat.quests.quests.Task;
+import me.fatpigsarefat.quests.quests.tasktypes.*;
+import me.fatpigsarefat.quests.title.*;
+import me.fatpigsarefat.quests.updater.Updater;
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.Material;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.io.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.logging.Level;
+
+public class Quests extends JavaPlugin {
+
+ private static Quests instance;
+ private static QuestManager questManager;
+ private static QPlayerManager qPlayerManager;
+ private static TaskTypeManager taskTypeManager;
+ private static Updater updater;
+ private static Title title;
+ private boolean brokenConfig = false;
+
+ public static Quests getInstance() {
+ return instance;
+ }
+
+ public static QuestManager getQuestManager() {
+ return questManager;
+ }
+
+ public static QPlayerManager getPlayerManager() {
+ return qPlayerManager;
+ }
+
+ public static TaskTypeManager getTaskTypeManager() {
+ return taskTypeManager;
+ }
+
+ public boolean isBrokenConfig() {
+ return brokenConfig;
+ }
+
+ public static Title getTitle() {
+ return title;
+ }
+
+ public static Updater getUpdater() {
+ return updater;
+ }
+
+ public static String convertToFormat(long m) {
+ long hours = m / 60;
+ long minutesLeft = m - hours * 60;
+
+ String formattedTime = "";
+
+ if (hours < 10)
+ formattedTime = formattedTime + "0";
+ formattedTime = formattedTime + hours + "h";
+
+ formattedTime = formattedTime + " ";
+
+ if (minutesLeft < 10)
+ formattedTime = formattedTime + "0";
+ formattedTime = formattedTime + minutesLeft + "m";
+
+ return formattedTime;
+ }
+
+ @Override
+ public void onEnable() {
+ instance = this;
+ taskTypeManager = new TaskTypeManager();
+ questManager = new QuestManager();
+ qPlayerManager = new QPlayerManager();
+
+ dataGenerator();
+ setupTitle();
+
+ taskTypeManager.registerTaskType(new MiningTaskType());
+ taskTypeManager.registerTaskType(new MiningCertainTaskType());
+ taskTypeManager.registerTaskType(new BuildingTaskType());
+ taskTypeManager.registerTaskType(new BuildingCertainTaskType());
+ taskTypeManager.registerTaskType(new MobkillingTaskType());
+ taskTypeManager.registerTaskType(new MobkillingCertainTaskType());
+ taskTypeManager.registerTaskType(new PlayerkillingTaskType());
+ taskTypeManager.registerTaskType(new FishingTaskType());
+ taskTypeManager.registerTaskType(new WalkingTaskType());
+ taskTypeManager.registerTaskType(new TamingTaskType());
+ taskTypeManager.registerTaskType(new MilkingTaskType());
+ taskTypeManager.registerTaskType(new ShearingTaskType());
+ if (Bukkit.getPluginManager().isPluginEnabled("ASkyBlock")) {
+ taskTypeManager.registerTaskType(new ASkyBlockLevelType());
+ }
+ if (Bukkit.getPluginManager().isPluginEnabled("uSkyBlock")) {
+ taskTypeManager.registerTaskType(new uSkyBlockLevelType());
+ }
+
+ Bukkit.getPluginCommand("quests").setExecutor(new CommandQuests());
+ Bukkit.getPluginManager().registerEvents(new EventPlayerJoin(), this);
+ Bukkit.getPluginManager().registerEvents(new EventInventory(), this);
+ Bukkit.getPluginManager().registerEvents(new EventPlayerLeave(), this);
+
+ Metrics metrics = new Metrics(this);
+ this.getLogger().log(Level.INFO, "Metrics started. This can be disabled at /plugins/bStats/config.yml.");
+
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ reloadQuests();
+
+ for (Player player : Bukkit.getOnlinePlayers()) {
+ qPlayerManager.loadPlayer(player.getUniqueId());
+ }
+ }
+ }.runTask(this);
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ for (QPlayer qPlayer : qPlayerManager.getQPlayers()) {
+ qPlayer.getQuestProgressFile().saveToDisk();
+ }
+ }
+ }.runTaskTimerAsynchronously(this, 12000L, 12000L);
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ for (QPlayer qPlayer : qPlayerManager.getQPlayers()) {
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+ for (Map.Entry<String, Quest> entry : Quests.getQuestManager().getQuests().entrySet()) {
+ String id = entry.getKey();
+ Quest quest = entry.getValue();
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+ if (questProgress != null && questProgress.isStarted()) {
+ boolean complete = true;
+ for (TaskProgress taskProgress : questProgress.getTaskProgress()) {
+ if (!taskProgress.isCompleted()) {
+ complete = false;
+ break;
+ }
+ }
+ if (complete) {
+ questProgressFile.completeQuest(quest);
+ }
+ }
+ }
+ }
+ }
+ }.runTaskTimerAsynchronously(this, 20L, 20L);
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ updater = new Updater(Quests.this);
+ updater.check();
+ }
+ }.runTaskAsynchronously(this);
+ }
+
+ @Override
+ public void onDisable() {
+ for (QPlayer qPlayer : qPlayerManager.getQPlayers()) {
+ qPlayer.getQuestProgressFile().saveToDisk();
+ }
+ }
+
+ public void reloadQuests() {
+ questManager.getQuests().clear();
+ questManager.getCategories().clear();
+ taskTypeManager.resetTaskTypes();
+
+ // test file integrity
+ try {
+ YamlConfiguration config = new YamlConfiguration();
+ config.load(new File(String.valueOf(Quests.this.getDataFolder() + File.separator + "config.yml")));
+ } catch (Exception ex) {
+ Quests.this.getLogger().log(Level.SEVERE, "You have a YAML error in your Quests config. If this is your first time using Quests, please remove the Quests folder and RESTART (not reload!) the server and try again.");
+ brokenConfig = true;
+ }
+
+ for (String id : getConfig().getConfigurationSection("categories").getKeys(false)) {
+ ItemStack displayItem = getItemStack("categories." + id + ".display");
+ Category category = new Category(id, displayItem);
+ questManager.registerCategory(category);
+ }
+
+ for (String id : getConfig().getConfigurationSection("quests").getKeys(false)) {
+ String root = "quests." + id;
+
+ QItemStack displayItem = getQItemStack(root + ".display");
+ List<String> rewards = getConfig().getStringList(root + ".rewards");
+ List<String> requirements = getConfig().getStringList(root + ".options.requires");
+ List<String> rewardString = getConfig().getStringList(root + ".options.rewardstring");
+ boolean repeatable = getConfig().getBoolean(root + ".options.repeatable", false);
+ boolean cooldown = getConfig().getBoolean(root + ".options.cooldown.enabled", false);
+ int cooldownTime = getConfig().getInt(root + ".options.cooldown.time", 10);
+ String category = getConfig().getString(root + ".options.category");
+
+ if (rewardString == null) {
+ rewardString = new ArrayList<>();
+ }
+ if (requirements == null) {
+ rewardString = new ArrayList<>();
+ }
+ if (rewards == null) {
+ rewardString = new ArrayList<>();
+ }
+ if (category == null) {
+ category = "";
+ }
+
+
+ Quest quest;
+ if (category.equals("")) {
+ quest = new Quest(id, displayItem, rewards, requirements, repeatable, cooldown, cooldownTime, rewardString);
+ } else {
+ quest = new Quest(id, displayItem, rewards, requirements, repeatable, cooldown, cooldownTime, rewardString, category);
+ Category c = questManager.getCategoryById(category);
+ if (c != null) {
+ c.registerQuestId(id);
+ }
+ }
+
+ for (String taskId : getConfig().getConfigurationSection(root + ".tasks").getKeys(false)) {
+ String taskRoot = root + ".tasks." + taskId;
+ String taskType = getConfig().getString(taskRoot + ".type");
+
+ Task task = new Task(taskId, taskType);
+
+ for (String key : getConfig().getConfigurationSection(taskRoot).getKeys(false)) {
+ task.addConfigValue(key, getConfig().get(taskRoot + "." + key));
+ }
+
+ quest.registerTask(task);
+ }
+
+ this.getLogger().log(Level.INFO, "Registering quest " + quest.getId() + " with " + quest.getTasks().size() + " tasks.");
+ questManager.registerQuest(quest);
+ taskTypeManager.registerQuestTasksWithTaskTypes(quest);
+ }
+ }
+
+ private QItemStack getQItemStack(String path) {
+ String cName = this.getConfig().getString(path + ".name", path + ".name");
+ String cType = this.getConfig().getString(path + ".type", path + ".type");
+ List<String> cLoreNormal = this.getConfig().getStringList(path + ".lore-normal");
+ List<String> cLoreStarted = this.getConfig().getStringList(path + ".lore-started");
+
+ String name;
+ Material type = null;
+ int data = 0;
+ List<String> loreNormal = new ArrayList<>();
+ if (cLoreNormal != null) {
+ for (String s : cLoreNormal) {
+ loreNormal.add(ChatColor.translateAlternateColorCodes('&', s));
+ }
+ }
+ List<String> loreStarted = new ArrayList<>();
+ if (cLoreStarted != null) {
+ for (String s : cLoreStarted) {
+ loreStarted.add(ChatColor.translateAlternateColorCodes('&', s));
+ }
+ }
+ name = ChatColor.translateAlternateColorCodes('&', cName);
+
+ if (StringUtils.isNumeric(cType)) {
+ type = Material.getMaterial(Integer.parseInt(cType));
+ } else if (Material.getMaterial(cType) != null) {
+ type = Material.getMaterial(cType);
+ } else if (cType.contains(":")) {
+ String[] parts = cType.split(":");
+ if (parts.length > 1) {
+ if (StringUtils.isNumeric(parts[0])) {
+ type = Material.getMaterial(Integer.parseInt(parts[0]));
+ } else if (Material.getMaterial(parts[0]) != null) {
+ type = Material.getMaterial(parts[0]);
+ }
+ if (StringUtils.isNumeric(parts[1])) {
+ data = Integer.parseInt(parts[1]);
+ }
+ }
+ }
+
+ if (type == null) {
+ type = Material.STONE;
+ }
+
+ QItemStack is = new QItemStack(name, loreNormal, loreStarted, type, data);
+
+ return is;
+ }
+
+ public ItemStack getItemStack(String path) {
+ String cName = this.getConfig().getString(path + ".name", path + ".name");
+ String cType = this.getConfig().getString(path + ".type", path + ".type");
+ List<String> cLore = this.getConfig().getStringList(path + ".lore");
+
+ String name;
+ Material type = null;
+ int data = 0;
+ List<String> lore = new ArrayList<>();
+ if (cLore != null) {
+ for (String s : cLore) {
+ lore.add(ChatColor.translateAlternateColorCodes('&', s));
+ }
+ }
+ name = ChatColor.translateAlternateColorCodes('&', cName);
+
+ if (StringUtils.isNumeric(cType)) {
+ type = Material.getMaterial(Integer.parseInt(cType));
+ } else if (Material.getMaterial(cType) != null) {
+ type = Material.getMaterial(cType);
+ } else if (cType.contains(":")) {
+ String[] parts = cType.split(":");
+ if (parts.length > 1) {
+ if (StringUtils.isNumeric(parts[0])) {
+ type = Material.getMaterial(Integer.parseInt(parts[0]));
+ } else if (Material.getMaterial(parts[0]) != null) {
+ type = Material.getMaterial(parts[0]);
+ }
+ if (StringUtils.isNumeric(parts[1])) {
+ data = Integer.parseInt(parts[1]);
+ }
+ }
+ }
+
+ if (type == null) {
+ type = Material.STONE;
+ }
+
+ ItemStack is = new ItemStack(type, 1, (short) data);
+ ItemMeta ism = is.getItemMeta();
+ ism.setLore(lore);
+ ism.setDisplayName(name);
+ is.setItemMeta(ism);
+
+ return is;
+ }
+
+ private boolean setupTitle() {
+ String version;
+ try {
+ version = Bukkit.getServer().getClass().getPackage().getName().replace(".", ",").split(",")[3];
+ } catch (ArrayIndexOutOfBoundsException e) {
+ return false;
+ }
+ boolean success = false;
+ getLogger().info("Your server is running version " + version);
+ if (version.equals("v1_8_R3")) {
+ title = new Title_v1_8_R3();
+ success = true;
+ } else if (version.equals("v1_8_R2")) {
+ title = new Title_v1_8_R2();
+ success = true;
+ } else if (version.equals("v1_8_R1")) {
+ title = new Title_v1_8_R1();
+ success = true;
+ } else if (version.equals("v1_9_R2")) {
+ title = new Title_v1_9_R2();
+ success = true;
+ } else if (version.equals("v1_9_R1")) {
+ title = new Title_v1_9_R1();
+ success = true;
+ } else if (version.equals("v1_10_R1")) {
+ title = new Title_v1_10_R1();
+ success = true;
+ } else if (version.equals("v1_11_R1")) {
+ title = new Title_v1_11_R1();
+ success = true;
+ } else if (version.equals("v1_12_R1")) {
+ title = new Title_v1_12_R1();
+ success = true;
+ } else {
+ title = new Title_Other();
+ }
+ return success;
+ }
+
+ private void dataGenerator() {
+ File directory = new File(String.valueOf(this.getDataFolder()));
+ if (!directory.exists() && !directory.isDirectory()) {
+ directory.mkdir();
+ }
+
+ File config = new File(this.getDataFolder() + File.separator + "config.yml");
+ if (!config.exists()) {
+ try {
+ config.createNewFile();
+ try (InputStream in = Quests.class.getClassLoader().getResourceAsStream("config.yml")) {
+ OutputStream out = new FileOutputStream(config);
+ ByteStreams.copy(in, out);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ return;
+ }
+ }
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/bstats/Metrics.java b/src/me/fatpigsarefat/quests/bstats/Metrics.java
new file mode 100644
index 00000000..5a8a21b3
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/bstats/Metrics.java
@@ -0,0 +1,661 @@
+package me.fatpigsarefat.quests.bstats;
+
+import org.bukkit.Bukkit;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+import org.bukkit.plugin.RegisteredServiceProvider;
+import org.bukkit.plugin.ServicePriority;
+import org.bukkit.plugin.java.JavaPlugin;
+import org.json.simple.JSONArray;
+import org.json.simple.JSONObject;
+
+import javax.net.ssl.HttpsURLConnection;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.logging.Level;
+import java.util.zip.GZIPOutputStream;
+
+/**
+ * bStats collects some data for plugin authors.
+ *
+ * Check out https://bStats.org/ to learn more about bStats!
+ */
+public class Metrics {
+
+ static {
+ // You can use the property to disable the check in your test environment
+ if (System.getProperty("bstats.relocatecheck") == null || !System.getProperty("bstats.relocatecheck").equals("false")) {
+ // Maven's Relocate is clever and changes strings, too. So we have to use this little "trick" ... :D
+ final String defaultPackage = new String(
+ new byte[]{'o', 'r', 'g', '.', 'b', 's', 't', 'a', 't', 's', '.', 'b', 'u', 'k', 'k', 'i', 't'});
+ final String examplePackage = new String(new byte[]{'y', 'o', 'u', 'r', '.', 'p', 'a', 'c', 'k', 'a', 'g', 'e'});
+ // We want to make sure nobody just copy & pastes the example and use the wrong package names
+ if (Metrics.class.getPackage().getName().equals(defaultPackage) || Metrics.class.getPackage().getName().equals(examplePackage)) {
+ throw new IllegalStateException("bStats Metrics class has not been relocated correctly!");
+ }
+ }
+ }
+
+ // The version of this bStats class
+ public static final int B_STATS_VERSION = 1;
+
+ // The url to which the data is sent
+ private static final String URL = "https://bStats.org/submitData/bukkit";
+
+ // Should failed requests be logged?
+ private static boolean logFailedRequests;
+
+ // The uuid of the server
+ private static String serverUUID;
+
+ // The plugin
+ private final JavaPlugin plugin;
+
+ // A list with all custom charts
+ private final List<CustomChart> charts = new ArrayList<>();
+
+ /**
+ * Class constructor.
+ *
+ * @param plugin The plugin which stats should be submitted.
+ */
+ public Metrics(JavaPlugin plugin) {
+ if (plugin == null) {
+ throw new IllegalArgumentException("Plugin cannot be null!");
+ }
+ this.plugin = plugin;
+
+ // Get the config file
+ File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
+ File configFile = new File(bStatsFolder, "config.yml");
+ YamlConfiguration config = YamlConfiguration.loadConfiguration(configFile);
+
+ // Check if the config file exists
+ if (!config.isSet("serverUuid")) {
+
+ // Add default values
+ config.addDefault("enabled", true);
+ // Every server gets it's unique random id.
+ config.addDefault("serverUuid", UUID.randomUUID().toString());
+ // Should failed request be logged?
+ config.addDefault("logFailedRequests", false);
+
+ // Inform the server owners about bStats
+ config.options().header(
+ "bStats collects some data for plugin authors like how many servers are using their plugins.\n" +
+ "To honor their work, you should not disable it.\n" +
+ "This has nearly no effect on the server performance!\n" +
+ "Check out https://bStats.org/ to learn more :)"
+ ).copyDefaults(true);
+ try {
+ config.save(configFile);
+ } catch (IOException ignored) { }
+ }
+
+ // Load the data
+ serverUUID = config.getString("serverUuid");
+ logFailedRequests = config.getBoolean("logFailedRequests", false);
+ if (config.getBoolean("enabled", true)) {
+ boolean found = false;
+ // Search for all other bStats Metrics classes to see if we are the first one
+ for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
+ try {
+ service.getField("B_STATS_VERSION"); // Our identifier :)
+ found = true; // We aren't the first
+ break;
+ } catch (NoSuchFieldException ignored) { }
+ }
+ // Register our service
+ Bukkit.getServicesManager().register(Metrics.class, this, plugin, ServicePriority.Normal);
+ if (!found) {
+ // We are the first!
+ startSubmitting();
+ }
+ }
+ }
+
+ /**
+ * Adds a custom chart.
+ *
+ * @param chart The chart to add.
+ */
+ public void addCustomChart(CustomChart chart) {
+ if (chart == null) {
+ throw new IllegalArgumentException("Chart cannot be null!");
+ }
+ charts.add(chart);
+ }
+
+ /**
+ * Starts the Scheduler which submits our data every 30 minutes.
+ */
+ private void startSubmitting() {
+ final Timer timer = new Timer(true); // We use a timer cause the Bukkit scheduler is affected by server lags
+ timer.scheduleAtFixedRate(new TimerTask() {
+ @Override
+ public void run() {
+ if (!plugin.isEnabled()) { // Plugin was disabled
+ timer.cancel();
+ return;
+ }
+ // Nevertheless we want our code to run in the Bukkit main thread, so we have to use the Bukkit scheduler
+ // Don't be afraid! The connection to the bStats server is still async, only the stats collection is sync ;)
+ Bukkit.getScheduler().runTask(plugin, new Runnable() {
+ @Override
+ public void run() {
+ submitData();
+ }
+ });
+ }
+ }, 1000*60*5, 1000*60*30);
+ // Submit the data every 30 minutes, first time after 5 minutes to give other plugins enough time to start
+ // WARNING: Changing the frequency has no effect but your plugin WILL be blocked/deleted!
+ // WARNING: Just don't do it!
+ }
+
+ /**
+ * Gets the plugin specific data.
+ * This method is called using Reflection.
+ *
+ * @return The plugin specific data.
+ */
+ public JSONObject getPluginData() {
+ JSONObject data = new JSONObject();
+
+ String pluginName = plugin.getDescription().getName();
+ String pluginVersion = plugin.getDescription().getVersion();
+
+ data.put("pluginName", pluginName); // Append the name of the plugin
+ data.put("pluginVersion", pluginVersion); // Append the version of the plugin
+ JSONArray customCharts = new JSONArray();
+ for (CustomChart customChart : charts) {
+ // Add the data of the custom charts
+ JSONObject chart = customChart.getRequestJsonObject();
+ if (chart == null) { // If the chart is null, we skip it
+ continue;
+ }
+ customCharts.add(chart);
+ }
+ data.put("customCharts", customCharts);
+
+ return data;
+ }
+
+ /**
+ * Gets the server specific data.
+ *
+ * @return The server specific data.
+ */
+ private JSONObject getServerData() {
+ // Minecraft specific data
+ int playerAmount;
+ try {
+ // Around MC 1.8 the return type was changed to a collection from an array,
+ // This fixes java.lang.NoSuchMethodError: org.bukkit.Bukkit.getOnlinePlayers()Ljava/util/Collection;
+ Method onlinePlayersMethod = Class.forName("org.bukkit.Server").getMethod("getOnlinePlayers");
+ playerAmount = onlinePlayersMethod.getReturnType().equals(Collection.class)
+ ? ((Collection<?>) onlinePlayersMethod.invoke(Bukkit.getServer())).size()
+ : ((Player[]) onlinePlayersMethod.invoke(Bukkit.getServer())).length;
+ } catch (Exception e) {
+ playerAmount = Bukkit.getOnlinePlayers().size(); // Just use the new method if the Reflection failed
+ }
+ int onlineMode = Bukkit.getOnlineMode() ? 1 : 0;
+ String bukkitVersion = org.bukkit.Bukkit.getVersion();
+ bukkitVersion = bukkitVersion.substring(bukkitVersion.indexOf("MC: ") + 4, bukkitVersion.length() - 1);
+
+ // OS/Java specific data
+ String javaVersion = System.getProperty("java.version");
+ String osName = System.getProperty("os.name");
+ String osArch = System.getProperty("os.arch");
+ String osVersion = System.getProperty("os.version");
+ int coreCount = Runtime.getRuntime().availableProcessors();
+
+ JSONObject data = new JSONObject();
+
+ data.put("serverUUID", serverUUID);
+
+ data.put("playerAmount", playerAmount);
+ data.put("onlineMode", onlineMode);
+ data.put("bukkitVersion", bukkitVersion);
+
+ data.put("javaVersion", javaVersion);
+ data.put("osName", osName);
+ data.put("osArch", osArch);
+ data.put("osVersion", osVersion);
+ data.put("coreCount", coreCount);
+
+ return data;
+ }
+
+ /**
+ * Collects the data and sends it afterwards.
+ */
+ private void submitData() {
+ final JSONObject data = getServerData();
+
+ JSONArray pluginData = new JSONArray();
+ // Search for all other bStats Metrics classes to get their plugin data
+ for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
+ try {
+ service.getField("B_STATS_VERSION"); // Our identifier :)
+
+ for (RegisteredServiceProvider<?> provider : Bukkit.getServicesManager().getRegistrations(service)) {
+ try {
+ pluginData.add(provider.getService().getMethod("getPluginData").invoke(provider.getProvider()));
+ } catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
+ }
+ } catch (NoSuchFieldException ignored) { }
+ }
+
+ data.put("plugins", pluginData);
+
+ // Create a new thread for the connection to the bStats server
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ // Send the data
+ sendData(data);
+ } catch (Exception e) {
+ // Something went wrong! :(
+ if (logFailedRequests) {
+ plugin.getLogger().log(Level.WARNING, "Could not submit plugin stats of " + plugin.getName(), e);
+ }
+ }
+ }
+ }).start();
+ }
+
+ /**
+ * Sends the data to the bStats server.
+ *
+ * @param data The data to send.
+ * @throws Exception If the request failed.
+ */
+ private static void sendData(JSONObject data) throws Exception {
+ if (data == null) {
+ throw new IllegalArgumentException("Data cannot be null!");
+ }
+ if (Bukkit.isPrimaryThread()) {
+ throw new IllegalAccessException("This method must not be called from the main thread!");
+ }
+ HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
+
+ // Compress the data to save bandwidth
+ byte[] compressedData = compress(data.toString());
+
+ // Add headers
+ connection.setRequestMethod("POST");
+ connection.addRequestProperty("Accept", "application/json");
+ connection.addRequestProperty("Connection", "close");
+ connection.addRequestProperty("Content-Encoding", "gzip"); // We gzip our request
+ connection.addRequestProperty("Content-Length", String.valueOf(compressedData.length));
+ connection.setRequestProperty("Content-Type", "application/json"); // We send our data in JSON format
+ connection.setRequestProperty("User-Agent", "MC-Server/" + B_STATS_VERSION);
+
+ // Send data
+ connection.setDoOutput(true);
+ DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
+ outputStream.write(compressedData);
+ outputStream.flush();
+ outputStream.close();
+
+ connection.getInputStream().close(); // We don't care about the response - Just send our data :)
+ }
+
+ /**
+ * Gzips the given String.
+ *
+ * @param str The string to gzip.
+ * @return The gzipped String.
+ * @throws IOException If the compression failed.
+ */
+ private static byte[] compress(final String str) throws IOException {
+ if (str == null) {
+ return null;
+ }
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+ GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
+ gzip.write(str.getBytes("UTF-8"));
+ gzip.close();
+ return outputStream.toByteArray();
+ }
+
+ /**
+ * Represents a custom chart.
+ */
+ public static abstract class CustomChart {
+
+ // The id of the chart
+ final String chartId;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ */
+ CustomChart(String chartId) {
+ if (chartId == null || chartId.isEmpty()) {
+ throw new IllegalArgumentException("ChartId cannot be null or empty!");
+ }
+ this.chartId = chartId;
+ }
+
+ private JSONObject getRequestJsonObject() {
+ JSONObject chart = new JSONObject();
+ chart.put("chartId", chartId);
+ try {
+ JSONObject data = getChartData();
+ if (data == null) {
+ // If the data is null we don't send the chart.
+ return null;
+ }
+ chart.put("data", data);
+ } catch (Throwable t) {
+ if (logFailedRequests) {
+ Bukkit.getLogger().log(Level.WARNING, "Failed to get data for custom chart with id " + chartId, t);
+ }
+ return null;
+ }
+ return chart;
+ }
+
+ protected abstract JSONObject getChartData() throws Exception;
+
+ }
+
+ /**
+ * Represents a custom simple pie.
+ */
+ public static class SimplePie extends CustomChart {
+
+ private final Callable<String> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public SimplePie(String chartId, Callable<String> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ String value = callable.call();
+ if (value == null || value.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("value", value);
+ return data;
+ }
+ }
+
+ /**
+ * Represents a custom advanced pie.
+ */
+ public static class AdvancedPie extends CustomChart {
+
+ private final Callable<Map<String, Integer>> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public AdvancedPie(String chartId, Callable<Map<String, Integer>> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ Map<String, Integer> map = callable.call();
+ if (map == null || map.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ boolean allSkipped = true;
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ if (entry.getValue() == 0) {
+ continue; // Skip this invalid
+ }
+ allSkipped = false;
+ values.put(entry.getKey(), entry.getValue());
+ }
+ if (allSkipped) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("values", values);
+ return data;
+ }
+ }
+
+ /**
+ * Represents a custom drilldown pie.
+ */
+ public static class DrilldownPie extends CustomChart {
+
+ private final Callable<Map<String, Map<String, Integer>>> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public DrilldownPie(String chartId, Callable<Map<String, Map<String, Integer>>> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ public JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ Map<String, Map<String, Integer>> map = callable.call();
+ if (map == null || map.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ boolean reallyAllSkipped = true;
+ for (Map.Entry<String, Map<String, Integer>> entryValues : map.entrySet()) {
+ JSONObject value = new JSONObject();
+ boolean allSkipped = true;
+ for (Map.Entry<String, Integer> valueEntry : map.get(entryValues.getKey()).entrySet()) {
+ value.put(valueEntry.getKey(), valueEntry.getValue());
+ allSkipped = false;
+ }
+ if (!allSkipped) {
+ reallyAllSkipped = false;
+ values.put(entryValues.getKey(), value);
+ }
+ }
+ if (reallyAllSkipped) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("values", values);
+ return data;
+ }
+ }
+
+ /**
+ * Represents a custom single line chart.
+ */
+ public static class SingleLineChart extends CustomChart {
+
+ private final Callable<Integer> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public SingleLineChart(String chartId, Callable<Integer> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ int value = callable.call();
+ if (value == 0) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("value", value);
+ return data;
+ }
+
+ }
+
+ /**
+ * Represents a custom multi line chart.
+ */
+ public static class MultiLineChart extends CustomChart {
+
+ private final Callable<Map<String, Integer>> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public MultiLineChart(String chartId, Callable<Map<String, Integer>> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ Map<String, Integer> map = callable.call();
+ if (map == null || map.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ boolean allSkipped = true;
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ if (entry.getValue() == 0) {
+ continue; // Skip this invalid
+ }
+ allSkipped = false;
+ values.put(entry.getKey(), entry.getValue());
+ }
+ if (allSkipped) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("values", values);
+ return data;
+ }
+
+ }
+
+ /**
+ * Represents a custom simple bar chart.
+ */
+ public static class SimpleBarChart extends CustomChart {
+
+ private final Callable<Map<String, Integer>> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public SimpleBarChart(String chartId, Callable<Map<String, Integer>> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ Map<String, Integer> map = callable.call();
+ if (map == null || map.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ for (Map.Entry<String, Integer> entry : map.entrySet()) {
+ JSONArray categoryValues = new JSONArray();
+ categoryValues.add(entry.getValue());
+ values.put(entry.getKey(), categoryValues);
+ }
+ data.put("values", values);
+ return data;
+ }
+
+ }
+
+ /**
+ * Represents a custom advanced bar chart.
+ */
+ public static class AdvancedBarChart extends CustomChart {
+
+ private final Callable<Map<String, int[]>> callable;
+
+ /**
+ * Class constructor.
+ *
+ * @param chartId The id of the chart.
+ * @param callable The callable which is used to request the chart data.
+ */
+ public AdvancedBarChart(String chartId, Callable<Map<String, int[]>> callable) {
+ super(chartId);
+ this.callable = callable;
+ }
+
+ @Override
+ protected JSONObject getChartData() throws Exception {
+ JSONObject data = new JSONObject();
+ JSONObject values = new JSONObject();
+ Map<String, int[]> map = callable.call();
+ if (map == null || map.isEmpty()) {
+ // Null = skip the chart
+ return null;
+ }
+ boolean allSkipped = true;
+ for (Map.Entry<String, int[]> entry : map.entrySet()) {
+ if (entry.getValue().length == 0) {
+ continue; // Skip this invalid
+ }
+ allSkipped = false;
+ JSONArray categoryValues = new JSONArray();
+ for (int categoryValue : entry.getValue()) {
+ categoryValues.add(categoryValue);
+ }
+ values.put(entry.getKey(), categoryValues);
+ }
+ if (allSkipped) {
+ // Null = skip the chart
+ return null;
+ }
+ data.put("values", values);
+ return data;
+ }
+
+ }
+} \ No newline at end of file
diff --git a/src/me/fatpigsarefat/quests/commands/CommandQuests.java b/src/me/fatpigsarefat/quests/commands/CommandQuests.java
new file mode 100644
index 00000000..c6ffe540
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/commands/CommandQuests.java
@@ -0,0 +1,222 @@
+package me.fatpigsarefat.quests.commands;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.obj.Messages;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.quests.Category;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.tasktypes.TaskType;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.command.Command;
+import org.bukkit.command.CommandExecutor;
+import org.bukkit.command.CommandSender;
+import org.bukkit.entity.Player;
+
+public class CommandQuests implements CommandExecutor {
+
+ public boolean onCommand(CommandSender sender, Command cmd, String label, String[] args) {
+ if (Quests.getInstance().isBrokenConfig()) {
+ sender.sendMessage(ChatColor.RED + "You have a YAML error in your config and Quests cannot load. If this is your first time using Quests, please " +
+ "delete the Quests folder and RESTART (not reload!) the server. If you have modified the config, check for errors in a YAML parser.");
+ return true;
+ }
+
+ if (args.length >= 1 && args[0].equalsIgnoreCase("help")) {
+ showHelp(sender);
+ return true;
+ }
+
+ if (args.length == 0 && sender instanceof Player) {
+ Player player = (Player) sender;
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ qPlayer.openQuests();
+ return true;
+ } else if (args.length >= 1) {
+ if (args[0].equalsIgnoreCase("a") || args[0].equalsIgnoreCase("admin") && sender.hasPermission("quests.admin")) {
+ if (args.length == 2) {
+ if (args[1].equalsIgnoreCase("opengui")) {
+ showAdminHelp(sender, "opengui");
+ return true;
+ } else if (args[1].equalsIgnoreCase("moddata")) {
+ showAdminHelp(sender, "moddata");
+ return true;
+ } else if (args[1].equalsIgnoreCase("reload")) {
+ Quests.getInstance().reloadConfig();
+ Quests.getInstance().reloadQuests();
+ sender.sendMessage(ChatColor.GRAY + "Quests was reloaded.");
+ return true;
+ } else if (args[1].equalsIgnoreCase("types")) {
+ sender.sendMessage(ChatColor.GRAY + "Registered task types:");
+ for (TaskType taskType : Quests.getTaskTypeManager().getTaskTypes()) {
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + taskType.getType());
+ }
+ sender.sendMessage(ChatColor.DARK_GRAY + "View info using /q a types [type].");
+ return true;
+ }
+ } else if (args.length == 3) {
+ if (args[1].equalsIgnoreCase("opengui")) {
+ showAdminHelp(sender, "opengui");
+ return true;
+ } else if (args[1].equalsIgnoreCase("moddata")) {
+ showAdminHelp(sender, "moddata");
+ return true;
+ } else if (args[1].equalsIgnoreCase("types")) {
+ TaskType taskType = null;
+ for (TaskType task : Quests.getTaskTypeManager().getTaskTypes()) {
+ if (task.getType().equals(args[2])) {
+ taskType = task;
+ }
+ }
+ if (taskType == null) {
+ sender.sendMessage(Messages.COMMAND_TASKVIEW_ADMIN_FAIL.getMessage().replace("{task}", args[2]));
+ } else {
+ sender.sendMessage(ChatColor.RED + "Task type: " + ChatColor.GRAY + taskType.getType());
+ sender.sendMessage(ChatColor.RED + "Author: " + ChatColor.GRAY + taskType.getAuthor());
+ sender.sendMessage(ChatColor.RED + "Description: " + ChatColor.GRAY + taskType.getDescription());
+ }
+ return true;
+ }
+ } else if (args.length == 4) {
+ if (args[1].equalsIgnoreCase("opengui")) {
+ if (args[2].equalsIgnoreCase("q") || args[2].equalsIgnoreCase("quests")) {
+ Player player = Bukkit.getPlayer(args[3]);
+ if (player != null) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ if (qPlayer != null) {
+ qPlayer.openQuests();
+ sender.sendMessage(Messages.COMMAND_QUEST_OPENQUESTS_ADMIN_SUCCESS.getMessage().replace("{player}", player.getName()));
+ return true;
+ }
+ }
+ sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_PLAYERNOTFOUND.getMessage().replace("{player}", args[3]));
+ return true;
+ }
+ showAdminHelp(sender, "opengui");
+ return true;
+ } else if (args[1].equalsIgnoreCase("moddata")) {
+ showAdminHelp(sender, "moddata");
+ return true;
+ }
+ } else if (args.length == 5) {
+ if (args[2].equalsIgnoreCase("c") || args[2].equalsIgnoreCase("category")) {
+ if (!Options.CATEGORIES_ENABLED.getBooleanValue()) {
+ sender.sendMessage(Messages.COMMAND_CATEGORY_OPEN_DISABLED.getMessage());
+ return true;
+ }
+ Category category = Quests.getQuestManager().getCategoryById(args[4]);
+ if (category == null) {
+ sender.sendMessage(Messages.COMMAND_CATEGORY_OPEN_DOESNTEXIST.getMessage().replace("{category}", args[4]));
+ return true;
+ }
+ Player player = Bukkit.getPlayer(args[3]);
+ if (player != null) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ if (qPlayer != null) {
+ qPlayer.openCategory(category);
+ sender.sendMessage(Messages.COMMAND_QUEST_OPENCATEGORY_ADMIN_SUCCESS.getMessage().replace("{player}", player.getName())
+ .replace("{category}", category.getId()));
+ return true;
+ }
+ }
+ sender.sendMessage(Messages.COMMAND_QUEST_ADMIN_PLAYERNOTFOUND.getMessage().replace("{player}", args[3]));
+ return true;
+ }
+ }
+
+ showAdminHelp(sender, null);
+ return true;
+ }
+ if (sender instanceof Player && (args[0].equalsIgnoreCase("q") || args[0].equalsIgnoreCase("quests"))) {
+ Player player = (Player) sender;
+ if (args.length >= 2) {
+ Quest quest = Quests.getQuestManager().getQuestById(args[1]);
+ if (quest == null) {
+ sender.sendMessage(Messages.COMMAND_QUEST_START_DOESNTEXIST.getMessage().replace("{quest}", args[1]));
+ } else {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ if (qPlayer == null) {
+ // shit + fan
+ sender.sendMessage(ChatColor.RED + "An error occurred finding your player.");
+ } else {
+ qPlayer.getQuestProgressFile().startQuest(quest);
+ }
+ }
+ return true;
+ }
+ } else if (sender instanceof Player && (args[0].equalsIgnoreCase("c") || args[0].equalsIgnoreCase("category"))) {
+ if (!Options.CATEGORIES_ENABLED.getBooleanValue()) {
+ sender.sendMessage(Messages.COMMAND_CATEGORY_OPEN_DISABLED.getMessage());
+ return true;
+ }
+ Player player = (Player) sender;
+ if (args.length >= 2) {
+ Category category = Quests.getQuestManager().getCategoryById(args[1]);
+ if (category == null) {
+ sender.sendMessage(Messages.COMMAND_CATEGORY_OPEN_DOESNTEXIST.getMessage().replace("{category}", args[1]));
+ } else {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ qPlayer.openCategory(category);
+ return true;
+ }
+ return true;
+ }
+ }
+ showHelp(sender);
+ return true;
+ } else {
+ sender.sendMessage(ChatColor.RED + "Only admin commands are available to non-player senders.");
+ }
+ return true;
+ }
+
+ private void showHelp(CommandSender sender) {
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "------------=[" + ChatColor.RED + " Quests v" + Quests.getInstance()
+ .getDescription().getVersion() + " " + ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "]=------------");
+ sender.sendMessage(ChatColor.GRAY + "The following commands are available: ");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests " + ChatColor.DARK_GRAY + ": show quests");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests c/category <categoryid> " + ChatColor.DARK_GRAY + ": open category by ID");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests q/quest <questid> " + ChatColor.DARK_GRAY + ": start quest by ID");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a/admin " + ChatColor.DARK_GRAY + ": view help for admins");
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "-----=[" + ChatColor.RED + " made with <3 by fatpigsarefat " + ChatColor
+ .GRAY.toString() + ChatColor.STRIKETHROUGH + "]=-----");
+ }
+
+ private void showAdminHelp(CommandSender sender, String command) {
+ if (command != null && command.equalsIgnoreCase("opengui")) {
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "------------=[" + ChatColor.RED + " Quests Admin: opengui " + ChatColor
+ .GRAY.toString() + ChatColor.STRIKETHROUGH + "]=------------");
+ sender.sendMessage(ChatColor.GRAY + "The following commands are available: ");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a opengui q/quest <player> " + ChatColor.DARK_GRAY + ": forcefully show" +
+ " quests for player");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a opengui c/category <player> <category> " + ChatColor.DARK_GRAY + ": " +
+ "forcefully " +
+ "open category by ID for player");
+ sender.sendMessage(ChatColor.GRAY + "These commands are useful for command NPCs. These will bypass the usual quests.command permission.");
+ } else if (command != null && command.equalsIgnoreCase("moddata")) {
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "------------=[" + ChatColor.RED + " Quests Admin: moddata " + ChatColor
+ .GRAY.toString() + ChatColor.STRIKETHROUGH + "]=------------");
+ sender.sendMessage(ChatColor.GRAY + "The following commands are available: ");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a moddata fullreset <player> " + ChatColor.DARK_GRAY + ": clear a " +
+ "players quest data file");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a moddata reset <player> <questid>" + ChatColor.DARK_GRAY + ": clear a " +
+ "players data for specifc quest");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a moddata start <player> <questid>" + ChatColor.DARK_GRAY + ": start a " +
+ "quest for a player");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a moddata complete <player> <questid>" + ChatColor.DARK_GRAY + ": " +
+ "complete a quest for a player");
+ sender.sendMessage(ChatColor.GRAY + "These commands modify quest progress for players. Use them cautiously. Changes are irreversible.");
+ } else {
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "------------=[" + ChatColor.RED + " Quests Admin " + ChatColor.GRAY
+ .toString() + ChatColor.STRIKETHROUGH + "]=------------");
+ sender.sendMessage(ChatColor.GRAY + "The following commands are available: ");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a opengui " + ChatColor.DARK_GRAY + ": view help for opengui");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a moddata " + ChatColor.DARK_GRAY + ": view help for quest progression");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a types [type]" + ChatColor.DARK_GRAY + ": view registered task types");
+ sender.sendMessage(ChatColor.DARK_GRAY + " * " + ChatColor.RED + "/quests a reload " + ChatColor.DARK_GRAY + ": reload Quests configuration");
+ }
+ sender.sendMessage(ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "-----=[" + ChatColor.RED + " requires permission: quests.admin " +
+ ChatColor.GRAY.toString() + ChatColor.STRIKETHROUGH + "]=-----");
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/events/EventInventory.java b/src/me/fatpigsarefat/quests/events/EventInventory.java
new file mode 100644
index 00000000..fae87623
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/events/EventInventory.java
@@ -0,0 +1,73 @@
+package me.fatpigsarefat.quests.events;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.obj.misc.QMenu;
+import me.fatpigsarefat.quests.obj.misc.QMenuCategory;
+import me.fatpigsarefat.quests.obj.misc.QMenuQuest;
+import me.fatpigsarefat.quests.quests.Quest;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.inventory.InventoryClickEvent;
+import org.bukkit.event.inventory.InventoryCloseEvent;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.UUID;
+
+public class EventInventory implements Listener {
+
+ private static HashMap<UUID, QMenu> tracker = new HashMap<>();
+ private ArrayList<UUID> buffer = new ArrayList<>();
+
+ public static void track(UUID uuid, QMenu qMenu) {
+ tracker.put(uuid, qMenu);
+ }
+
+ @EventHandler
+ public void onEvent(InventoryClickEvent event) {
+ if (tracker.containsKey(event.getWhoClicked().getUniqueId())) {
+ event.setCancelled(true);
+ QMenu qMenu = tracker.get(event.getWhoClicked().getUniqueId());
+
+ if (qMenu instanceof QMenuQuest) {
+ QMenuQuest qMenuQuest = (QMenuQuest) qMenu;
+
+ //TODO check page clicks
+
+ if (Options.CATEGORIES_ENABLED.getBooleanValue() && qMenuQuest.getBackButtonLocation() == event.getSlot()) {
+ QMenuCategory qMenuCategory = qMenuQuest.getSuperMenu();
+ buffer.add(event.getWhoClicked().getUniqueId());
+ event.getWhoClicked().openInventory(qMenuCategory.toInventory(1));
+ tracker.put(event.getWhoClicked().getUniqueId(), qMenuCategory);
+
+ } else if (qMenuQuest.getSlotsToMenu().containsKey(event.getSlot())) {
+ String questid = qMenuQuest.getSlotsToMenu().get(event.getSlot());
+ Quest quest = Quests.getQuestManager().getQuestById(questid);
+ if (qMenuQuest.getOwner().getQuestProgressFile().startQuest(quest)) {
+ event.getWhoClicked().closeInventory();
+ }
+ }
+ } else if (qMenu instanceof QMenuCategory) {
+ QMenuCategory qMenuCategory = (QMenuCategory) qMenu;
+
+ if (qMenuCategory.getSlotsToMenu().containsKey(event.getSlot())) {
+ QMenuQuest qMenuQuest = qMenuCategory.getSlotsToMenu().get(event.getSlot());
+ buffer.add(event.getWhoClicked().getUniqueId());
+ event.getWhoClicked().openInventory(qMenuQuest.toInventory(1));
+ tracker.put(event.getWhoClicked().getUniqueId(), qMenuQuest);
+ }
+ }
+ }
+ }
+
+ @EventHandler
+ public void onEvent(InventoryCloseEvent event) {
+ if (buffer.contains(event.getPlayer().getUniqueId())) {
+ buffer.remove(event.getPlayer().getUniqueId());
+ } else if (tracker.containsKey(event.getPlayer().getUniqueId())) {
+ tracker.remove(event.getPlayer().getUniqueId());
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/events/EventPlayerJoin.java b/src/me/fatpigsarefat/quests/events/EventPlayerJoin.java
new file mode 100644
index 00000000..26805748
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/events/EventPlayerJoin.java
@@ -0,0 +1,21 @@
+package me.fatpigsarefat.quests.events;
+
+import me.fatpigsarefat.quests.Quests;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerJoinEvent;
+
+import java.util.UUID;
+
+public class EventPlayerJoin implements Listener {
+
+ @EventHandler
+ public void onEvent(PlayerJoinEvent event) {
+ UUID playerUuid = event.getPlayer().getUniqueId();
+ Quests.getPlayerManager().loadPlayer(playerUuid);
+ if (Quests.getUpdater().isUpdateReady() && event.getPlayer().hasPermission("quests.admin")) {
+ event.getPlayer().sendMessage(Quests.getUpdater().getMessage());
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/events/EventPlayerLeave.java b/src/me/fatpigsarefat/quests/events/EventPlayerLeave.java
new file mode 100644
index 00000000..15d09a8e
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/events/EventPlayerLeave.java
@@ -0,0 +1,25 @@
+package me.fatpigsarefat.quests.events;
+
+import me.fatpigsarefat.quests.Quests;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.Listener;
+import org.bukkit.event.player.PlayerQuitEvent;
+import org.bukkit.scheduler.BukkitRunnable;
+
+import java.util.UUID;
+
+public class EventPlayerLeave implements Listener {
+
+ @EventHandler
+ public void onEvent(PlayerQuitEvent event) {
+ UUID playerUuid = event.getPlayer().getUniqueId();
+ new BukkitRunnable() {
+ @Override
+ public void run() {
+ Quests.getPlayerManager().getPlayer(playerUuid).getQuestProgressFile().saveToDisk();
+ Quests.getPlayerManager().removePlayer(playerUuid);
+ }
+ }.runTaskAsynchronously(Quests.getInstance());
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/obj/Items.java b/src/me/fatpigsarefat/quests/obj/Items.java
new file mode 100644
index 00000000..c8f8b7b8
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/Items.java
@@ -0,0 +1,23 @@
+package me.fatpigsarefat.quests.obj;
+
+import me.fatpigsarefat.quests.Quests;
+import org.bukkit.inventory.ItemStack;
+
+public enum Items {
+
+ BACK_BUTTON("gui.back-button"),
+ QUEST_LOCKED("gui.quest-locked-display"),
+ QUEST_COOLDOWN("gui.quest-cooldown-display"),
+ QUEST_COMPLETED("gui.quest-completed-display");
+
+ String path;
+
+ Items(String path) {
+ this.path = path;
+ }
+
+ public ItemStack getItem() {
+ return Quests.getInstance().getItemStack(path);
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/obj/Messages.java b/src/me/fatpigsarefat/quests/obj/Messages.java
new file mode 100644
index 00000000..60eac35d
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/Messages.java
@@ -0,0 +1,45 @@
+package me.fatpigsarefat.quests.obj;
+
+import me.fatpigsarefat.quests.Quests;
+import org.bukkit.ChatColor;
+
+public enum Messages {
+
+ QUEST_START("messages.quest-start"),
+ QUEST_COMPLETE("messages.quest-complete"),
+ QUEST_START_LIMIT("messages.quest-start-limit"),
+ QUEST_START_DISABLED("messages.quest-start-disabled"),
+ QUEST_START_LOCKED("messages.quest-start-locked"),
+ QUEST_START_COOLDOWN("messages.quest-start-cooldown"),
+ QUEST_UPDATER("messages.quest-updater"),
+ COMMAND_QUEST_START_DOESNTEXIST("messages.command-quest-start-doesntexist"),
+ COMMAND_QUEST_OPENCATEGORY_ADMIN_SUCCESS("messages.command-quest-opencategory-admin-success"),
+ COMMAND_QUEST_OPENQUESTS_ADMIN_SUCCESS("messages.command-quest-openquests-admin-success"),
+ COMMAND_QUEST_ADMIN_PLAYERNOTFOUND("messages.command-quest-admin-playernotfound"),
+ COMMAND_CATEGORY_OPEN_DOESNTEXIST("messages.command-category-open-doesntexist"),
+ COMMAND_CATEGORY_OPEN_DISABLED("messages.command-category-open-disabled"),
+ COMMAND_QUEST_START_ADMIN_SUCCESS("messages.command-quest-start-admin-success"),
+ COMMAND_TASKVIEW_ADMIN_FAIL("messages.command-taskview-admin-fail"),
+ COMMAND_QUEST_START_ADMIN_FAIL("messages.command-quest-start-admin-fail"),
+ TITLE_QUEST_START_TITLE("titles.quest-start.title"),
+ TITLE_QUEST_START_SUBTITLE("titles.quest-start.subtitle"),
+ TITLE_QUEST_COMPLETE_TITLE("titles.quest-complete.title"),
+ TITLE_QUEST_COMPLETE_SUBTITLE("titles.quest-complete.subtitle");
+
+ private String path;
+
+ Messages(String path) {
+ this.path = path;
+ }
+
+ public String getMessage() {
+ if (Quests.getInstance().getConfig().contains(path)) {
+ String message = Quests.getInstance().getConfig().getString(path);
+ if (message != null) {
+ return path;
+ }
+ }
+ return path;
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/obj/Options.java b/src/me/fatpigsarefat/quests/obj/Options.java
new file mode 100644
index 00000000..d4b63499
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/Options.java
@@ -0,0 +1,48 @@
+package me.fatpigsarefat.quests.obj;
+
+import me.fatpigsarefat.quests.Quests;
+import org.bukkit.ChatColor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public enum Options {
+
+ CATEGORIES_ENABLED("options.categories-enabled"),
+ TRIM_GUI_SIZE("options.trim-gui-size"),
+ QUESTS_START_LIMIT("options.quest-started-limit"),
+ TITLES_ENABLED("options.titles-enabled");
+
+ private String path;
+
+ Options(String path) {
+ this.path = path;
+ }
+
+ public int getIntValue() {
+ return Quests.getInstance().getConfig().getInt(path);
+ }
+
+ public String getStringValue() {
+ return Quests.getInstance().getConfig().getString(path);
+ }
+
+ public boolean getBooleanValue() {
+ return Quests.getInstance().getConfig().getBoolean(path);
+ }
+
+ public List<String> getStringListValue() {
+ return Quests.getInstance().getConfig().getStringList(path);
+ }
+
+ public static String color(String s) {
+ return ChatColor.translateAlternateColorCodes('&', s);
+ }
+ public static List<String> color(List<String> s) {
+ List<String> colored = new ArrayList<>();
+ for (String line : s) {
+ colored.add(ChatColor.translateAlternateColorCodes('&', line));
+ }
+ return colored;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/obj/misc/QItemStack.java b/src/me/fatpigsarefat/quests/obj/misc/QItemStack.java
new file mode 100644
index 00000000..efde9f51
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/misc/QItemStack.java
@@ -0,0 +1,108 @@
+package me.fatpigsarefat.quests.obj.misc;
+
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import org.bukkit.Material;
+import org.bukkit.enchantments.Enchantment;
+import org.bukkit.inventory.ItemFlag;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class QItemStack {
+
+ private String name;
+ private List<String> loreNormal;
+ private List<String> loreStarted;
+ private Material type;
+ private int data;
+
+ public QItemStack(String name, List<String> loreNormal, List<String> loreStarted, Material type, int data) {
+ this.name = name;
+ this.loreNormal = loreNormal;
+ this.loreStarted = loreStarted;
+ this.type = type;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List<String> getLoreNormal() {
+ return loreNormal;
+ }
+
+ public void setLoreNormal(List<String> loreNormal) {
+ this.loreNormal = loreNormal;
+ }
+
+ public List<String> getLoreStarted() {
+ return loreStarted;
+ }
+
+ public void setLoreStarted(List<String> loreStarted) {
+ this.loreStarted = loreStarted;
+ }
+
+ public Material getType() {
+ return type;
+ }
+
+ public void setType(Material type) {
+ this.type = type;
+ }
+
+ public int getData() {
+ return data;
+ }
+
+ public void setData(int data) {
+ this.data = data;
+ }
+
+ public ItemStack toItemStack(QuestProgress questProgress) {
+ ItemStack is = new ItemStack(type, 1, (short) data);
+ ItemMeta ism = is.getItemMeta();
+ ism.setDisplayName(name);
+ List<String> formattedLore = new ArrayList<>();
+ List<String> tempLore = new ArrayList<>();
+ tempLore.addAll(loreNormal);
+ if (questProgress != null && questProgress.isStarted()) {
+ tempLore.addAll(loreStarted);
+ ism.addEnchant(Enchantment.ARROW_INFINITE, 1, true);
+ try {
+ is.getItemMeta().addItemFlags(ItemFlag.HIDE_ENCHANTS);
+ } catch (Exception ignored) {
+
+ }
+ }
+ if (questProgress != null) {
+ for (String s : tempLore) {
+ Matcher m = Pattern.compile("\\{([^}]+)\\}").matcher(s);
+ while (m.find()) {
+ String[] parts = m.group(1).split(":");
+ if (parts.length > 1) {
+ if (questProgress.getTaskProgress(parts[0]) == null) {
+ continue;
+ }
+ if (parts[1].equals("progress")) {
+ String str = String.valueOf(questProgress.getTaskProgress(parts[0]).getProgress());
+ s = s.replace("{" + m.group(1) + "}", (str.equals("null") ? String.valueOf(0) : str));
+ }
+ }
+ }
+ formattedLore.add(s);
+ }
+ }
+ ism.setLore(formattedLore);
+ is.setItemMeta(ism);
+ return is;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/obj/misc/QMenu.java b/src/me/fatpigsarefat/quests/obj/misc/QMenu.java
new file mode 100644
index 00000000..13629600
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/misc/QMenu.java
@@ -0,0 +1,12 @@
+package me.fatpigsarefat.quests.obj.misc;
+
+import me.fatpigsarefat.quests.player.QPlayer;
+
+import java.util.HashMap;
+
+public interface QMenu {
+
+ QPlayer getOwner();
+ HashMap<?, ?> getSlotsToMenu();
+
+}
diff --git a/src/me/fatpigsarefat/quests/obj/misc/QMenuCategory.java b/src/me/fatpigsarefat/quests/obj/misc/QMenuCategory.java
new file mode 100644
index 00000000..449f81d6
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/misc/QMenuCategory.java
@@ -0,0 +1,92 @@
+package me.fatpigsarefat.quests.obj.misc;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.quests.Category;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+
+import java.util.HashMap;
+import java.util.List;
+
+public class QMenuCategory implements QMenu {
+
+ private final int pageSize = 45;
+ private HashMap<Integer, QMenuQuest> slotsToMenuQuest = new HashMap<>();
+ private QPlayer owner;
+
+ public QMenuCategory(QPlayer owner) {
+ this.owner = owner;
+ }
+
+ public void populate(List<QMenuQuest> menuQuests) {
+ int slot = 0;
+ for (QMenuQuest qMenuQuest : menuQuests) {
+ slotsToMenuQuest.put(slot, qMenuQuest);
+ slot++;
+ }
+ }
+
+ @Override
+ public HashMap<Integer, QMenuQuest> getSlotsToMenu() {
+ return slotsToMenuQuest;
+ }
+
+ @Override
+ public QPlayer getOwner() {
+ return owner;
+ }
+
+ public Inventory toInventory(int page) {
+ int pageMin = pageSize * (page - 1);
+ int pageMax = pageSize * page;
+ String title = "Quest Categories";
+
+ ItemStack pageIs = new ItemStack(Material.DIRT);
+
+ Inventory inventory = Bukkit.createInventory(null, 54, title); //TODO make configurable title
+
+ for (int pointer = pageMin; pointer < pageMax; pointer++) {
+ if (slotsToMenuQuest.containsKey(pointer)) {
+ Category category = Quests.getQuestManager().getCategoryById(slotsToMenuQuest.get(pointer).getCategoryName());
+ if (category != null) {
+ inventory.setItem(pointer, category.getDisplayItem());
+ }
+ }
+ }
+
+ inventory.setItem(49, pageIs);
+
+ if (Options.TRIM_GUI_SIZE.getBooleanValue() && page == 1) {
+ int slotsUsed = 0;
+ for (int pointer = 0; pointer < pageMax; pointer++) {
+ if (inventory.getItem(pointer) != null) {
+ slotsUsed++;
+ }
+ }
+
+ int inventorySize = (slotsUsed >= 54) ? 54 : slotsUsed + (9 - slotsUsed % 9) * Math.min(1, slotsUsed % 9);
+ inventorySize = inventorySize <= 0 ? 9 : inventorySize;
+ if (inventorySize == 54) {
+ return inventory;
+ }
+
+ Inventory trimmedInventory = Bukkit.createInventory(null, inventorySize, title);
+
+ for (int slot = 0; slot < pageMax; slot++) {
+ if (slot >= trimmedInventory.getSize()){
+ break;
+ }
+ trimmedInventory.setItem(slot, inventory.getItem(slot));
+ }
+ return trimmedInventory;
+ } else {
+ return inventory;
+ }
+
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/obj/misc/QMenuQuest.java b/src/me/fatpigsarefat/quests/obj/misc/QMenuQuest.java
new file mode 100644
index 00000000..4df9df89
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/obj/misc/QMenuQuest.java
@@ -0,0 +1,177 @@
+package me.fatpigsarefat.quests.obj.misc;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.obj.Items;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.inventory.Inventory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+
+public class QMenuQuest implements QMenu {
+
+ private HashMap<Integer, String> slotsToQuestIds = new HashMap<>();
+ private int backButtonLocation = -1;
+ private boolean backButtonEnabled = true;
+ private QMenuCategory superMenu;
+ private String categoryName;
+ private final int pageSize = 45;
+ private QPlayer owner;
+
+ public QMenuQuest(QPlayer owner, String categoryName, QMenuCategory superMenu) {
+ this.owner = owner;
+ this.categoryName = categoryName;
+ this.superMenu = superMenu;
+ }
+
+ public void populate(List<Quest> quests) {
+ int slot = 0;
+ for (Quest quest : quests) {
+ slotsToQuestIds.put(slot, quest.getId());
+ slot++;
+ }
+ }
+
+ @Override
+ public HashMap<Integer, String> getSlotsToMenu() {
+ return slotsToQuestIds;
+ }
+
+ @Override
+ public QPlayer getOwner() {
+ return owner;
+ }
+
+ public String getCategoryName() {
+ return categoryName;
+ }
+
+ public Inventory toInventory(int page) {
+ int pageMin = pageSize * (page - 1);
+ int pageMax = pageSize * page;
+ String title = "Quests";
+
+ ItemStack pageIs = new ItemStack(Material.DIRT);
+ ItemStack back = Items.BACK_BUTTON.getItem();
+
+ Inventory inventory = Bukkit.createInventory(null, 54, title); //TODO make configurable title
+
+ int invSlot = 0;
+ for (int pointer = pageMin; pointer < pageMax; pointer++) {
+ if (slotsToQuestIds.containsKey(pointer)) {
+ Quest quest = Quests.getQuestManager().getQuestById(slotsToQuestIds.get(pointer));
+ QuestProgress questProgress = owner.getQuestProgressFile().getQuestProgress(quest);
+ long cooldown = owner.getQuestProgressFile().getCooldownFor(quest);
+ if (!owner.getQuestProgressFile().hasMetRequirements(quest)) {
+ List<String> quests = new ArrayList<>();
+ for (String requirement : quest.getRequirements()) {
+ quests.add(Quests.getQuestManager().getQuestById(requirement).getDisplayNameStripped());
+ }
+ Map<String, String> placeholders = new HashMap<>();
+ placeholders.put("{quest}", quest.getDisplayNameStripped());
+ placeholders.put("{requirements}", String.join(", ", quests));
+ ItemStack is = replaceItemStack(Items.QUEST_LOCKED.getItem(), placeholders);
+ inventory.setItem(invSlot, is);
+ } else if (!quest.isRepeatable() && questProgress.isCompletedBefore()) {
+ Map<String, String> placeholders = new HashMap<>();
+ placeholders.put("{quest}", quest.getDisplayNameStripped());
+ ItemStack is = replaceItemStack(Items.QUEST_COMPLETED.getItem(), placeholders);
+ inventory.setItem(invSlot, is);
+ } else if (cooldown > 0) {
+ Map<String, String> placeholders = new HashMap<>();
+ placeholders.put("{time}", Quests.convertToFormat(TimeUnit.MINUTES.convert(cooldown, TimeUnit.MILLISECONDS)));
+ placeholders.put("{quest}", quest.getDisplayNameStripped());
+ ItemStack is = replaceItemStack(Items.QUEST_COOLDOWN.getItem(), placeholders);
+ inventory.setItem(invSlot, is);
+ } else {
+ inventory.setItem(invSlot, Quests.getQuestManager().getQuestById(quest.getId()).getDisplayItem().toItemStack(questProgress));
+ }
+ }
+ invSlot++;
+ }
+
+ inventory.setItem(49, pageIs);
+
+ if (Options.CATEGORIES_ENABLED.getBooleanValue() && backButtonEnabled) {
+ inventory.setItem(45, back);
+ backButtonLocation = 45;
+ }
+
+ if (Options.TRIM_GUI_SIZE.getBooleanValue() && page == 1) {
+ int slotsUsed = 0;
+ for (int pointer = 0; pointer < pageMax; pointer++) {
+ if (inventory.getItem(pointer) != null) {
+ slotsUsed++;
+ }
+ }
+
+ int inventorySize = (slotsUsed >= 54) ? 54 : slotsUsed + (9 - slotsUsed % 9) * Math.min(1, slotsUsed % 9);
+ inventorySize = inventorySize <= 0 ? 9 : inventorySize;
+ if (inventorySize == 54) {
+ return inventory;
+ } else if (Options.CATEGORIES_ENABLED.getBooleanValue() && backButtonEnabled) {
+ inventorySize += 9;
+ }
+
+ Inventory trimmedInventory = Bukkit.createInventory(null, inventorySize, title);
+
+ for (int slot = 0; slot < trimmedInventory.getSize(); slot++) {
+ if (slot >= (trimmedInventory.getSize() - 9) && backButtonEnabled){
+ if (Options.CATEGORIES_ENABLED.getBooleanValue()) {
+ trimmedInventory.setItem(slot, back);
+ backButtonLocation = slot;
+ }
+ break;
+ }
+ trimmedInventory.setItem(slot, inventory.getItem(slot));
+ }
+ return trimmedInventory;
+ } else {
+ return inventory;
+ }
+
+ //TODO add page controls
+ }
+
+ public boolean isBackButtonEnabled() {
+ return backButtonEnabled;
+ }
+
+ public void setBackButtonEnabled(boolean backButtonEnabled) {
+ this.backButtonEnabled = backButtonEnabled;
+ }
+
+ public int getBackButtonLocation() {
+ return backButtonLocation;
+ }
+
+ public QMenuCategory getSuperMenu() {
+ return superMenu;
+ }
+
+ public ItemStack replaceItemStack(ItemStack is, Map<String, String> placeholders) {
+ ItemStack newItemStack = is.clone();
+ List<String> lore = newItemStack.getItemMeta().getLore();
+ List<String> newLore = new ArrayList<>();
+ for (String s : lore) {
+ for (Map.Entry<String, String> entry : placeholders.entrySet()) {
+ s = s.replace(entry.getKey(), entry.getValue());
+ }
+ newLore.add(s);
+ }
+ ItemMeta ism = newItemStack.getItemMeta();
+ ism.setLore(newLore);
+ newItemStack.setItemMeta(ism);
+ return newItemStack;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/player/QPlayer.java b/src/me/fatpigsarefat/quests/player/QPlayer.java
new file mode 100644
index 00000000..27e9283d
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/player/QPlayer.java
@@ -0,0 +1,101 @@
+package me.fatpigsarefat.quests.player;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.events.EventInventory;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.obj.misc.QMenu;
+import me.fatpigsarefat.quests.obj.misc.QMenuCategory;
+import me.fatpigsarefat.quests.obj.misc.QMenuQuest;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.quests.Category;
+import me.fatpigsarefat.quests.quests.Quest;
+import org.bukkit.Bukkit;
+import org.bukkit.entity.Player;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
+
+public class QPlayer {
+
+ private UUID uuid;
+ private QuestProgressFile questProgressFile;
+
+ public QPlayer(UUID uuid, QuestProgressFile questProgressFile) {
+ this.uuid = uuid;
+ this.questProgressFile = questProgressFile;
+ }
+
+ public UUID getUuid() {
+ return uuid;
+ }
+
+ public void openCategory(Category category) {
+ Player player = Bukkit.getPlayer(uuid);
+ if (player == null) {
+ return;
+ }
+
+ QMenuQuest qMenuQuest = new QMenuQuest(Quests.getPlayerManager().getPlayer(player.getUniqueId()), category.getId(), null);
+ List<Quest> quests = new ArrayList<>();
+ for (String questid : category.getRegisteredQuestIds()) {
+ Quest quest = Quests.getQuestManager().getQuestById(questid);
+ if (quest != null) {
+ quests.add(quest);
+ }
+ }
+ qMenuQuest.populate(quests);
+ qMenuQuest.setBackButtonEnabled(false);
+
+ player.openInventory(qMenuQuest.toInventory(1));
+ EventInventory.track(player.getUniqueId(), qMenuQuest);
+ }
+
+ public void openQuests() {
+ Player player = Bukkit.getPlayer(uuid);
+ if (player == null) {
+ return;
+ }
+
+ if (Options.CATEGORIES_ENABLED.getBooleanValue()) {
+ QMenuCategory qMenuCategory = new QMenuCategory(Quests.getPlayerManager().getPlayer(player.getUniqueId()));
+ List<QMenuQuest> questMenus = new ArrayList<>();
+ for (Category category : Quests.getQuestManager().getCategories()) {
+ QMenuQuest qMenuQuest = new QMenuQuest(Quests.getPlayerManager().getPlayer(player.getUniqueId()), category.getId(), qMenuCategory);
+ List<Quest> quests = new ArrayList<>();
+ for (String questid : category.getRegisteredQuestIds()) {
+ Quest quest = Quests.getQuestManager().getQuestById(questid);
+ if (quest != null) {
+ quests.add(quest);
+ }
+ }
+ qMenuQuest.populate(quests);
+ questMenus.add(qMenuQuest);
+ }
+ qMenuCategory.populate(questMenus);
+
+ player.openInventory(qMenuCategory.toInventory(1));
+ EventInventory.track(player.getUniqueId(), qMenuCategory);
+ } else {
+ QMenuQuest qMenuQuest = new QMenuQuest(Quests.getPlayerManager().getPlayer(player.getUniqueId()), "", null);
+ List<Quest> quests = new ArrayList<>();
+ for (Map.Entry<String, Quest> entry : Quests.getQuestManager().getQuests().entrySet()) {
+ quests.add(entry.getValue());
+ }
+ qMenuQuest.populate(quests);
+ qMenuQuest.setBackButtonEnabled(false);
+
+ player.openInventory(qMenuQuest.toInventory(1));
+ EventInventory.track(player.getUniqueId(), qMenuQuest);
+ }
+ }
+
+ public QuestProgressFile getQuestProgressFile() {
+ return questProgressFile;
+ }
+
+ public QuestProgressFile setQuestProgressFile() {
+ return questProgressFile;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/player/QPlayerManager.java b/src/me/fatpigsarefat/quests/player/QPlayerManager.java
new file mode 100644
index 00000000..2d18bcf3
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/player/QPlayerManager.java
@@ -0,0 +1,86 @@
+package me.fatpigsarefat.quests.player;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import org.bukkit.configuration.file.YamlConfiguration;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class QPlayerManager {
+
+ private List<QPlayer> qPlayers = new ArrayList<>();
+
+ public void addPlayer(QPlayer qPlayer) {
+ qPlayers.add(qPlayer);
+ }
+
+ public QPlayer getPlayer(UUID uuid) {
+ for (QPlayer qPlayer : qPlayers) {
+ if (qPlayer.getUuid().equals(uuid)) {
+ return qPlayer;
+ }
+ }
+ return null;
+ }
+
+ public void removePlayer(UUID uuid) {
+ QPlayer toRemove = null;
+ for (QPlayer qPlayer : qPlayers) {
+ if (qPlayer.getUuid().equals(uuid)) {
+ toRemove = qPlayer;
+ break;
+ }
+ }
+ if (toRemove != null) {
+ qPlayers.remove(toRemove);
+ }
+ }
+
+ public List<QPlayer> getQPlayers() {
+ return qPlayers;
+ }
+
+ public void loadPlayer(UUID uuid) {
+ if (getPlayer(uuid) == null) {
+ QuestProgressFile questProgressFile = new QuestProgressFile(uuid);
+
+ File directory = new File(Quests.getInstance().getDataFolder() + File.separator + "playerdata");
+ if (directory.exists() && directory.isDirectory()) {
+ File file = new File(Quests.getInstance().getDataFolder() + File.separator + "playerdata" + File.separator + uuid.toString() + ".yml");
+ if (file.exists()) {
+ YamlConfiguration data = YamlConfiguration.loadConfiguration(file);
+ if (data.contains("quest-progress")) {
+ for (String id : data.getConfigurationSection("quest-progress").getKeys(false)) {
+ boolean started = data.getBoolean("quest-progress." + id + ".started");
+ boolean completed = data.getBoolean("quest-progress." + id + ".completed");
+ boolean completedBefore = data.getBoolean("quest-progress." + id + ".completed-before");
+ long completionDate = data.getLong("quest-progress." + id + ".completion-date");
+
+ QuestProgress questProgress = new QuestProgress(id, completed, completedBefore, completionDate, uuid, started, true);
+
+ for (String taskid : data.getConfigurationSection("quest-progress." + id + ".task-progress").getKeys(false)) {
+ boolean taskCompleted = data.getBoolean("quest-progress." + id + ".task-progress." + taskid + ".completed");
+ Object taskProgression = data.get("quest-progress." + id + ".task-progress." + taskid + ".progress");
+
+ TaskProgress taskProgress = new TaskProgress(taskid, taskProgression, uuid, taskCompleted);
+ questProgress.addTaskProgress(taskProgress);
+ }
+
+ questProgressFile.addQuestProgress(questProgress);
+ }
+ }
+ }
+ }
+
+ QPlayer qPlayer = new QPlayer(uuid, questProgressFile);
+
+ Quests.getPlayerManager().addPlayer(qPlayer);
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgress.java b/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgress.java
new file mode 100644
index 00000000..c4fe87b2
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgress.java
@@ -0,0 +1,96 @@
+package me.fatpigsarefat.quests.player.questprogressfile;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+public class QuestProgress {
+
+ private List<TaskProgress> taskProgress = new ArrayList<>();
+ private String questid;
+ private boolean started;
+ private boolean completed;
+ private boolean completedBefore;
+ private long completionDate;
+ private UUID player;
+ private boolean modified;
+
+ public QuestProgress(String questid, boolean completed, boolean completedBefore, long completionDate, UUID player, boolean started) {
+ this.questid = questid;
+ this.completed = completed;
+ this.completedBefore = completedBefore;
+ this.completionDate = completionDate;
+ this.player = player;
+ this.started = started;
+ }
+
+ public QuestProgress(String questid, boolean completed, boolean completedBefore, long completionDate, UUID player, boolean started, boolean modified) {
+ this(questid, completed, completedBefore, completionDate, player, started);
+ this.modified = modified;
+ }
+
+ public String getQuestId() {
+ return questid;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setStarted(boolean started) {
+ this.started = started;
+ this.modified = true;
+ }
+
+ public boolean isStarted() {
+ return started;
+ }
+
+ public void setCompleted(boolean completed) {
+ this.completed = completed;
+ this.modified = true;
+ }
+
+ public long getCompletionDate() {
+ return completionDate;
+ }
+
+ public void setCompletionDate(long completionDate) {
+ this.completionDate = completionDate;
+ this.modified = true;
+ }
+
+ public UUID getPlayer() {
+ return player;
+ }
+
+ public boolean isCompletedBefore() {
+ return completedBefore;
+ }
+
+ public void setCompletedBefore(boolean completedBefore) {
+ this.completedBefore = completedBefore;
+ this.modified = true;
+ }
+
+ public void addTaskProgress(TaskProgress taskProgress) {
+ this.taskProgress.add(taskProgress);
+ }
+
+ public List<TaskProgress> getTaskProgress() {
+ return taskProgress;
+ }
+
+ public TaskProgress getTaskProgress(String taskId) {
+ for (TaskProgress taskProgress : this.taskProgress) {
+ if (taskProgress.getTaskId().equals(taskId)) {
+ return taskProgress;
+ }
+ }
+ return null;
+ }
+
+ public boolean isWorthSaving() {
+ return modified;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgressFile.java b/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgressFile.java
new file mode 100644
index 00000000..bfc80ad3
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/player/questprogressfile/QuestProgressFile.java
@@ -0,0 +1,234 @@
+package me.fatpigsarefat.quests.player.questprogressfile;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.obj.Messages;
+import me.fatpigsarefat.quests.obj.Options;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.Bukkit;
+import org.bukkit.ChatColor;
+import org.bukkit.configuration.file.YamlConfiguration;
+import org.bukkit.entity.Player;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.TimeUnit;
+
+public class QuestProgressFile {
+
+ private List<QuestProgress> questProgress = new ArrayList<>();
+ private UUID player;
+
+ public QuestProgressFile(UUID player) {
+ this.player = player;
+ }
+
+ //TODO change back to quest id to save performance
+
+ public boolean completeQuest(Quest quest) {
+ QuestProgress questProgress = getQuestProgress(quest);
+ questProgress.setStarted(false);
+ questProgress.setCompleted(true);
+ questProgress.setCompletedBefore(true);
+ questProgress.setCompletionDate(System.currentTimeMillis());
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(this.player);
+ for (String s : quest.getRewards()) {
+ Bukkit.getServer().dispatchCommand(Bukkit.getConsoleSender(), s.replace("{player}", player.getName()));
+ }
+ player.sendMessage(Messages.QUEST_COMPLETE.getMessage().replace("{quest}", quest.getDisplayNameStripped()));
+ if (Options.TITLES_ENABLED.getBooleanValue()) {
+ Quests.getTitle().sendTitle(player, Messages.TITLE_QUEST_COMPLETE_TITLE.getMessage().replace("{quest}", quest
+ .getDisplayNameStripped()), Messages.TITLE_QUEST_COMPLETE_SUBTITLE.getMessage().replace("{quest}", quest
+ .getDisplayNameStripped()));
+ }
+ for (String s : quest.getRewardString()) {
+ player.sendMessage(ChatColor.translateAlternateColorCodes('&', s));
+ }
+ }
+ return true;
+ }
+
+ public boolean startQuest(Quest quest) {
+ if (getStartedQuests().size() >= Options.QUESTS_START_LIMIT.getIntValue()) {
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(getPlayer());
+ player.sendMessage(Messages.QUEST_START_LIMIT.getMessage().replace("{limit}", String.valueOf(Options.QUESTS_START_LIMIT.getIntValue())));
+ }
+ return false;
+ }
+ QuestProgress questProgress = getQuestProgress(quest);
+ if (!quest.isRepeatable() && questProgress.isCompletedBefore()) {
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(getPlayer());
+ player.sendMessage(Messages.QUEST_START_DISABLED.getMessage());
+ }
+ return false;
+ }
+ long cooldown = getCooldownFor(quest);
+ if (cooldown > 0) {
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(getPlayer());
+ player.sendMessage(Messages.QUEST_START_COOLDOWN.getMessage().replace("{time}", String.valueOf(Quests.convertToFormat(TimeUnit.MINUTES.convert(cooldown, TimeUnit.MILLISECONDS)))));
+ }
+ return false;
+ }
+ if (!hasMetRequirements(quest)) {
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(getPlayer());
+ player.sendMessage(Messages.QUEST_START_LOCKED.getMessage());
+ }
+ return false;
+ }
+ questProgress.setStarted(true);
+ for (TaskProgress taskProgress : questProgress.getTaskProgress()) {
+ taskProgress.setCompleted(false);
+ taskProgress.setProgress(null);
+ }
+ questProgress.setCompleted(false);
+ if (Bukkit.getPlayer(player) != null) {
+ Player player = Bukkit.getPlayer(getPlayer());
+ player.sendMessage(Messages.QUEST_START.getMessage().replace("{quest}", quest.getDisplayNameStripped()));
+ if (Options.TITLES_ENABLED.getBooleanValue()) {
+ Quests.getTitle().sendTitle(player, Messages.TITLE_QUEST_START_TITLE.getMessage().replace("{quest}", quest
+ .getDisplayNameStripped()), Messages.TITLE_QUEST_START_SUBTITLE.getMessage().replace("{quest}", quest
+ .getDisplayNameStripped()));
+ }
+ }
+ return true;
+ }
+
+ public void addQuestProgress(QuestProgress questProgress) {
+ this.questProgress.add(questProgress);
+ }
+
+ public List<Quest> getStartedQuests() {
+ List<Quest> startedQuests = new ArrayList<>();
+ for (QuestProgress questProgress : questProgress) {
+ if (questProgress.isStarted()) {
+ startedQuests.add(Quests.getQuestManager().getQuestById(questProgress.getQuestId()));
+ }
+ }
+ return startedQuests;
+ }
+
+ public boolean hasQuestProgress(Quest quest) {
+ for (QuestProgress questProgress : this.questProgress) {
+ if (questProgress.getQuestId().equals(quest.getId())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean hasStartedQuest(Quest quest) {
+ //TODO always return true if the need for starting quests is disabled & requirements are met
+ if (hasQuestProgress(quest)) {
+ if (getQuestProgress(quest).isStarted()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public long getCooldownFor(Quest quest) {
+ QuestProgress questProgress = getQuestProgress(quest);
+ if (quest.isCooldownEnabled() && questProgress.isCompleted()) {
+ if (questProgress.getCompletionDate() > 0) {
+ long date = questProgress.getCompletionDate();
+ return (date + TimeUnit.MILLISECONDS.convert(quest.getCooldown(), TimeUnit.MINUTES)) - System.currentTimeMillis();
+ }
+ }
+ return 0;
+ }
+
+ public boolean hasMetRequirements(Quest quest) {
+ for (String id : quest.getRequirements()) {
+ Quest q = Quests.getQuestManager().getQuestById(id);
+ if (q == null) {
+ continue;
+ }
+ if (hasQuestProgress(q) && !getQuestProgress(q).isCompletedBefore()) {
+ return false;
+ } else if (!hasQuestProgress(q)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public UUID getPlayer() {
+ return player;
+ }
+
+ public QuestProgress getQuestProgress(Quest quest) {
+ for (QuestProgress questProgress : this.questProgress) {
+ if (questProgress.getQuestId().equals(quest.getId())) {
+ return questProgress;
+ }
+ }
+ if (generateBlankQuestProgress(quest.getId())) {
+ return getQuestProgress(quest);
+ }
+ return null;
+ }
+
+ public boolean generateBlankQuestProgress(String questid) {
+ if (Quests.getQuestManager().getQuestById(questid) != null) {
+ Quest quest = Quests.getQuestManager().getQuestById(questid);
+ QuestProgress questProgress = new QuestProgress(quest.getId(), false, false, 0, player, false, false);
+ for (Task task : quest.getTasks()) {
+ TaskProgress taskProgress = new TaskProgress(task.getId(), null, player, false);
+ questProgress.addTaskProgress(taskProgress);
+ }
+
+ this.questProgress.add(questProgress);
+ return true;
+ }
+ return false;
+ }
+
+ public void saveToDisk() {
+ File directory = new File(Quests.getInstance().getDataFolder() + File.separator + "playerdata");
+ if (!directory.exists() && !directory.isDirectory()) {
+ directory.mkdirs();
+ }
+ File file = new File(Quests.getInstance().getDataFolder() + File.separator + "playerdata" + File.separator + player.toString() + ".yml");
+ if (!file.exists()) {
+ try {
+ file.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ YamlConfiguration data = YamlConfiguration.loadConfiguration(file);
+ data.set("quest-progress", null);
+ for (QuestProgress questProgress : this.questProgress) {
+ if (!questProgress.isWorthSaving()) {
+ continue;
+ }
+ data.set("quest-progress." + questProgress.getQuestId() + ".started", questProgress.isStarted());
+ data.set("quest-progress." + questProgress.getQuestId() + ".completed", questProgress.isCompleted());
+ data.set("quest-progress." + questProgress.getQuestId() + ".completed-before", questProgress.isCompletedBefore());
+ data.set("quest-progress." + questProgress.getQuestId() + ".completion-date", questProgress.getCompletionDate());
+ for (TaskProgress taskProgress : questProgress.getTaskProgress()) {
+ data.set("quest-progress." + questProgress.getQuestId() + ".task-progress." + taskProgress.getTaskId() + ".completed", taskProgress
+ .isCompleted());
+ data.set("quest-progress." + questProgress.getQuestId() + ".task-progress." + taskProgress.getTaskId() + ".progress", taskProgress
+ .getProgress());
+ }
+ }
+
+ try {
+ data.save(file);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+}
+
diff --git a/src/me/fatpigsarefat/quests/player/questprogressfile/TaskProgress.java b/src/me/fatpigsarefat/quests/player/questprogressfile/TaskProgress.java
new file mode 100644
index 00000000..3b896334
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/player/questprogressfile/TaskProgress.java
@@ -0,0 +1,41 @@
+package me.fatpigsarefat.quests.player.questprogressfile;
+
+import java.util.UUID;
+
+public class TaskProgress {
+
+ private String taskid;
+ private Object progress;
+ private UUID player;
+ private boolean completed;
+
+ public TaskProgress(String taskid, Object progress, UUID player, boolean completed) {
+ this.taskid = taskid;
+ this.progress = progress;
+ this.completed = completed;
+ }
+
+ public String getTaskId() {
+ return taskid;
+ }
+
+ public Object getProgress() {
+ return progress;
+ }
+
+ public void setProgress(Object progress) {
+ this.progress = progress;
+ }
+
+ public UUID getPlayer() {
+ return player;
+ }
+
+ public boolean isCompleted() {
+ return completed;
+ }
+
+ public void setCompleted(boolean complete) {
+ this.completed = complete;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/quests/Category.java b/src/me/fatpigsarefat/quests/quests/Category.java
new file mode 100644
index 00000000..b17f6db7
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/Category.java
@@ -0,0 +1,42 @@
+package me.fatpigsarefat.quests.quests;
+
+import org.bukkit.inventory.ItemStack;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Category {
+
+ private String id;
+ private ItemStack displayItem;
+ private List<String> registeredQuestIds = new ArrayList<>();
+
+ public Category(String id, ItemStack displayItem) {
+ this.id = id;
+ this.displayItem = displayItem;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public ItemStack getDisplayItem() {
+ return displayItem;
+ }
+
+ public void setDisplayItem(ItemStack displayItem) {
+ this.displayItem = displayItem;
+ }
+
+ public void registerQuestId(String questid) {
+ registeredQuestIds.add(questid);
+ }
+
+ public List<String> getRegisteredQuestIds() {
+ return registeredQuestIds;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/quests/Quest.java b/src/me/fatpigsarefat/quests/quests/Quest.java
new file mode 100644
index 00000000..239cd4fe
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/Quest.java
@@ -0,0 +1,96 @@
+package me.fatpigsarefat.quests.quests;
+
+import me.fatpigsarefat.quests.obj.misc.QItemStack;
+import org.bukkit.ChatColor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Quest {
+
+ private List<Task> tasks = new ArrayList<>();
+ private String id;
+ private QItemStack displayItem;
+ private List<String> rewards;
+ private List<String> requirements;
+ private List<String> rewardString;
+ private boolean repeatable;
+ private boolean cooldownEnabled;
+ private int cooldown;
+ private String categoryid;
+
+
+ public Quest(String id, QItemStack displayItem, List<String> rewards, List<String> requirements, boolean repeatable, boolean cooldownEnabled, int cooldown, List<String> rewardString, String categoryid) {
+ this(id, displayItem, rewards, requirements, repeatable, cooldownEnabled, cooldown, rewardString);
+ this.categoryid = categoryid;
+ }
+
+ public Quest(String id, QItemStack displayItem, List<String> rewards, List<String> requirements, boolean repeatable, boolean cooldownEnabled, int cooldown, List<String> rewardString) {
+ this.id = id;
+ this.displayItem = displayItem;
+ this.rewards = rewards;
+ this.requirements = requirements;
+ this.repeatable = repeatable;
+ this.cooldownEnabled = cooldownEnabled;
+ this.cooldown = cooldown;
+ this.rewardString = rewardString;
+ }
+
+ public void registerTask(Task task) {
+ tasks.add(task);
+ }
+
+ public List<Task> getTasks() {
+ return tasks;
+ }
+
+ public List<Task> getTasksOfType(String type) {
+ List<Task> tasks = new ArrayList<>();
+ for (Task task : this.tasks) {
+ if (task.getType().equals(type)) {
+ tasks.add(task);
+ }
+ }
+ return tasks;
+ }
+
+ public List<String> getRewardString() {
+ return rewardString;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public QItemStack getDisplayItem() {
+ return displayItem;
+ }
+
+ public List<String> getRewards() {
+ return rewards;
+ }
+
+ public List<String> getRequirements() {
+ return requirements;
+ }
+
+ public boolean isRepeatable() {
+ return repeatable;
+ }
+
+ public boolean isCooldownEnabled() {
+ return cooldownEnabled;
+ }
+
+ public int getCooldown() {
+ return cooldown;
+ }
+
+ public String getCategoryId() {
+ return categoryid;
+ }
+
+ public String getDisplayNameStripped() {
+ return ChatColor.stripColor(this.displayItem.getName());
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/quests/QuestManager.java b/src/me/fatpigsarefat/quests/quests/QuestManager.java
new file mode 100644
index 00000000..1ec6ea63
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/QuestManager.java
@@ -0,0 +1,38 @@
+package me.fatpigsarefat.quests.quests;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+public class QuestManager {
+
+ private Map<String, Quest> quests = new LinkedHashMap<>();
+ private List<Category> categories = new ArrayList<>();
+
+ public void registerQuest(Quest quest) {
+ quests.put(quest.getId(), quest);
+ }
+
+ public Quest getQuestById(String id) {
+ return quests.get(id);
+ }
+
+ public Map<String, Quest> getQuests() {
+ return quests;
+ }
+
+ public void registerCategory(Category category) { categories.add(category); }
+
+ public List<Category> getCategories() {
+ return categories;
+ }
+
+ public Category getCategoryById(String id) {
+ for (Category category : categories) {
+ if (category.getId().equals(id)) return category;
+ }
+ return null;
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/Task.java b/src/me/fatpigsarefat/quests/quests/Task.java
new file mode 100644
index 00000000..fb209145
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/Task.java
@@ -0,0 +1,38 @@
+package me.fatpigsarefat.quests.quests;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Task {
+
+ private Map<String, Object> configValues = new HashMap<>();
+
+ private String id;
+ private String type;
+
+ public Task(String id, String type) {
+ this.id = id;
+ this.type = type;
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public Object getConfigValue(String key) {
+ return configValues.getOrDefault(key, null);
+ }
+
+ public Map<String, Object> getConfigValues() {
+ return configValues;
+ }
+
+ public void addConfigValue(String key, Object value) {
+ configValues.put(key, value);
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/ASkyBlockLevelType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/ASkyBlockLevelType.java
new file mode 100644
index 00000000..e86b5a0b
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/ASkyBlockLevelType.java
@@ -0,0 +1,52 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import com.wasteofplastic.askyblock.events.IslandPostLevelEvent;
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+
+public final class ASkyBlockLevelType extends TaskType {
+
+ public ASkyBlockLevelType() {
+ super("askyblock_level", "fatpigsarefat", "Reach a certain island level for ASkyBlock.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onIslandLevel(IslandPostLevelEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer());
+ if (qPlayer == null) {
+ return;
+ }
+
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ long islandLevelNeeded = (long) (int) task.getConfigValue("level");
+
+ taskProgress.setProgress(event.getLongLevel());
+
+ if (((long) taskProgress.getProgress()) >= islandLevelNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingCertainTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingCertainTaskType.java
new file mode 100644
index 00000000..8a2add5d
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingCertainTaskType.java
@@ -0,0 +1,72 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.block.BlockPlaceEvent;
+
+public final class BuildingCertainTaskType extends TaskType {
+
+ public BuildingCertainTaskType() {
+ super("blockplacecertain", "fatpigsarefat", "Place a set amount of a specific block.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onBlockPlace(BlockPlaceEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer().getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ Material material;
+ Object configBlock = task.getConfigValue("block");
+ Object configData = task.getConfigValue("data");
+
+ if (StringUtils.isNumeric(String.valueOf(configBlock))) {
+ material = Material.getMaterial((int) configBlock);
+ } else {
+ material = Material.getMaterial(String.valueOf(configBlock));
+ }
+
+ if (material != null && event.getBlock().getType().equals(material)) {
+ if (configData != null && (((int) event.getBlock().getData()) != ((int) configData))) {
+ continue;
+ }
+ int brokenBlocksNeeded = (int) task.getConfigValue("amount");
+
+ int progressBlocksBroken;
+ if (taskProgress.getProgress() == null) {
+ progressBlocksBroken = 0;
+ } else {
+ progressBlocksBroken = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressBlocksBroken + 1);
+
+ if (((int) taskProgress.getProgress()) >= brokenBlocksNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingTaskType.java
new file mode 100644
index 00000000..17d8247b
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/BuildingTaskType.java
@@ -0,0 +1,55 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.block.BlockPlaceEvent;
+
+public final class BuildingTaskType extends TaskType {
+
+ public BuildingTaskType() {
+ super("blockplace", "fatpigsarefat", "Place a set amount of blocks.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onBlockPlace(BlockPlaceEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer().getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int brokenBlocksNeeded = (int) task.getConfigValue("amount");
+
+ int progressBlocksBroken;
+ if (taskProgress.getProgress() == null) {
+ progressBlocksBroken = 0;
+ } else {
+ progressBlocksBroken = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressBlocksBroken + 1);
+
+ if (((int) taskProgress.getProgress()) >= brokenBlocksNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/FishingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/FishingTaskType.java
new file mode 100644
index 00000000..fac0bbda
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/FishingTaskType.java
@@ -0,0 +1,61 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.PlayerFishEvent;
+
+public final class FishingTaskType extends TaskType {
+
+ public FishingTaskType() {
+ super("fishing", "fatpigsarefat", "Catch a set amount of items from the sea.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onFishCaught(PlayerFishEvent event) {
+ if (event.getState() == PlayerFishEvent.State.CAUGHT_FISH) {
+ return;
+ }
+ Player player = event.getPlayer();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int catchesNeeded = (int) task.getConfigValue("amount");
+
+ int progressCatches;
+ if (taskProgress.getProgress() == null) {
+ progressCatches = 0;
+ } else {
+ progressCatches = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressCatches + 1);
+
+ if (((int) taskProgress.getProgress()) >= catchesNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/MilkingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/MilkingTaskType.java
new file mode 100644
index 00000000..81a08dca
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/MilkingTaskType.java
@@ -0,0 +1,64 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.Material;
+import org.bukkit.entity.Cow;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.PlayerInteractEntityEvent;
+
+public final class MilkingTaskType extends TaskType {
+
+ public MilkingTaskType() {
+ super("milking", "fatpigsarefat", "Milk a set amount of cows.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onMilk(PlayerInteractEntityEvent event) {
+ if (!(event.getRightClicked() instanceof Cow) || (event.getPlayer().getItemInHand().getType() != Material.BUCKET)) {
+ return;
+ }
+
+ Player player = event.getPlayer();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int cowsNeeded = (int) task.getConfigValue("amount");
+
+ int progressMilked;
+ if (taskProgress.getProgress() == null) {
+ progressMilked = 0;
+ } else {
+ progressMilked = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressMilked + 1);
+
+ if (((int) taskProgress.getProgress()) >= cowsNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/MiningCertainTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/MiningCertainTaskType.java
new file mode 100644
index 00000000..a53d2373
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/MiningCertainTaskType.java
@@ -0,0 +1,72 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.apache.commons.lang.StringUtils;
+import org.bukkit.Material;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.block.BlockBreakEvent;
+
+public final class MiningCertainTaskType extends TaskType {
+
+ public MiningCertainTaskType() {
+ super("blockbreakcertain", "fatpigsarefat", "Break a set amount of a specific block.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onBlockPlace(BlockBreakEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer().getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ Material material;
+ Object configBlock = task.getConfigValue("block");
+ Object configData = task.getConfigValue("data");
+
+ if (StringUtils.isNumeric(String.valueOf(configBlock))) {
+ material = Material.getMaterial((int) configBlock);
+ } else {
+ material = Material.getMaterial(String.valueOf(configBlock));
+ }
+
+ if (material != null && event.getBlock().getType().equals(material)) {
+ if (configData != null && (((int) event.getBlock().getData()) != ((int) configData))) {
+ continue;
+ }
+ int brokenBlocksNeeded = (int) task.getConfigValue("amount");
+
+ int progressBlocksBroken;
+ if (taskProgress.getProgress() == null) {
+ progressBlocksBroken = 0;
+ } else {
+ progressBlocksBroken = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressBlocksBroken + 1);
+
+ if (((int) taskProgress.getProgress()) >= brokenBlocksNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/MiningTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/MiningTaskType.java
new file mode 100644
index 00000000..a0bcb4bf
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/MiningTaskType.java
@@ -0,0 +1,56 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.block.BlockBreakEvent;
+
+public final class MiningTaskType extends TaskType {
+
+ public MiningTaskType() {
+ // type, author, description
+ super("blockbreak", "fatpigsarefat", "Break a set amount of blocks.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onBlockBreak(BlockBreakEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer().getUniqueId()); // get the qplayer so you can get their progress
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile(); // the quest progress file stores progress about all quests and tasks
+
+ for (Quest quest : super.getRegisteredQuests()) { // iterate through all quests which are registered to use this task type
+ if (questProgressFile.hasStartedQuest(quest)) { // check if the player has actually started the quest before progressing it
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest); // get their progress for the specific quest
+
+ for (Task task : quest.getTasksOfType(super.getType())) { // get all tasks of this type
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId()); // get the task progress and increment progress by 1
+
+ if (taskProgress.isCompleted()) { // dont need to increment a completed task
+ continue;
+ }
+
+ int brokenBlocksNeeded = (int) task.getConfigValue("amount"); // this will retrieve a value from the config under the key "value"
+
+ int progressBlocksBroken;
+ if (taskProgress.getProgress() == null) { // note: if the player has never progressed before, getProgress() will return null
+ progressBlocksBroken = 0;
+ } else {
+ progressBlocksBroken = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressBlocksBroken + 1); // the progress does not have to be an int, although must be serializable by the yaml provider
+
+ if (((int) taskProgress.getProgress()) >= brokenBlocksNeeded) { // completion statement, if true the task is complete
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingCertainTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingCertainTaskType.java
new file mode 100644
index 00000000..11848918
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingCertainTaskType.java
@@ -0,0 +1,91 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.ChatColor;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.EntityType;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.entity.EntityDeathEvent;
+
+public final class MobkillingCertainTaskType extends TaskType {
+
+ public MobkillingCertainTaskType() {
+ super("mobkillingcertain", "fatpigsarefat", "Kill a set amount of a specific entity type.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onMobKill(EntityDeathEvent event) {
+ Entity killer = event.getEntity().getKiller();
+ Entity mob = event.getEntity();
+
+ if (mob instanceof Player) {
+ return;
+ }
+
+ if (killer == null) {
+ return;
+ }
+
+ Player player = event.getEntity().getKiller();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ String configEntity = (String) task.getConfigValue("mob");
+ String configName = (String) task.getConfigValue("name");
+
+ EntityType entity = EntityType.fromName(configEntity);
+ if (entity == null) {
+ continue;
+ }
+
+ if (configName != null) {
+ configName = ChatColor.translateAlternateColorCodes('&', configName);
+ if (!mob.getCustomName().equals(configName)) {
+ return;
+ }
+ }
+
+ if (mob.getType() != entity) {
+ continue;
+ }
+
+ int mobKillsNeeded = (int) task.getConfigValue("amount");
+
+ int progressKills;
+ if (taskProgress.getProgress() == null) {
+ progressKills = 0;
+ } else {
+ progressKills = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressKills + 1);
+
+ if (((int) taskProgress.getProgress()) >= mobKillsNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingTaskType.java
new file mode 100644
index 00000000..cdc3449d
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/MobkillingTaskType.java
@@ -0,0 +1,87 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Animals;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Monster;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.entity.EntityDeathEvent;
+
+public final class MobkillingTaskType extends TaskType {
+
+ public MobkillingTaskType() {
+ super("mobkilling", "fatpigsarefat", "Kill a set amount of entities.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onMobKill(EntityDeathEvent event) {
+ Entity killer = event.getEntity().getKiller();
+ Entity mob = event.getEntity();
+
+ if (mob instanceof Player) {
+ return;
+ }
+
+ if (killer == null) {
+ return;
+ }
+
+ Player player = event.getEntity().getKiller();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ boolean hostilitySpecified = false;
+ boolean hostile = false;
+ if (task.getConfigValue("hostile") != null) {
+ hostilitySpecified = true;
+ hostile = (boolean) task.getConfigValue("hostile");
+ }
+
+ if (hostilitySpecified) {
+ if (!hostile && !(mob instanceof Animals)) {
+ continue;
+ } else if (hostile && !(mob instanceof Monster)) {
+ continue;
+ }
+ }
+
+ int mobKillsNeeded = (int) task.getConfigValue("amount");
+
+ int progressKills;
+ if (taskProgress.getProgress() == null) {
+ progressKills = 0;
+ } else {
+ progressKills = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressKills + 1);
+
+ if (((int) taskProgress.getProgress()) >= mobKillsNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/PlayerkillingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/PlayerkillingTaskType.java
new file mode 100644
index 00000000..2edf5282
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/PlayerkillingTaskType.java
@@ -0,0 +1,70 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Entity;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.entity.EntityDeathEvent;
+
+public final class PlayerkillingTaskType extends TaskType {
+
+ public PlayerkillingTaskType() {
+ super("playerkilling", "fatpigsarefat", "Kill a set amount of players.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onMobKill(EntityDeathEvent event) {
+ Entity killer = event.getEntity().getKiller();
+ Entity mob = event.getEntity();
+
+ if (!(mob instanceof Player)) {
+ return;
+ }
+
+ if (killer == null) {
+ return;
+ }
+
+ Player player = event.getEntity().getKiller();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int playerKillsNeeded = (int) task.getConfigValue("amount");
+
+ int progressKills;
+ if (taskProgress.getProgress() == null) {
+ progressKills = 0;
+ } else {
+ progressKills = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressKills + 1);
+
+ if (((int) taskProgress.getProgress()) >= playerKillsNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/ShearingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/ShearingTaskType.java
new file mode 100644
index 00000000..57e36980
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/ShearingTaskType.java
@@ -0,0 +1,63 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Player;
+import org.bukkit.entity.Sheep;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.PlayerShearEntityEvent;
+
+public final class ShearingTaskType extends TaskType {
+
+ public ShearingTaskType() {
+ super("shearing", "fatpigsarefat", "Shear a set amount of sheep.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onShear(PlayerShearEntityEvent event) {
+ if (!(event.getEntity() instanceof Sheep)) {
+ return;
+ }
+
+ Player player = event.getPlayer();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int sheepNeeded = (int) task.getConfigValue("amount");
+
+ int progressSheared;
+ if (taskProgress.getProgress() == null) {
+ progressSheared = 0;
+ } else {
+ progressSheared = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressSheared + 1);
+
+ if (((int) taskProgress.getProgress()) >= sheepNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/TamingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/TamingTaskType.java
new file mode 100644
index 00000000..5227d6fe
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/TamingTaskType.java
@@ -0,0 +1,62 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.entity.EntityTameEvent;
+
+public final class TamingTaskType extends TaskType {
+
+ public TamingTaskType() {
+ super("taming", "fatpigsarefat", "Tame a set amount of animals.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onTame(EntityTameEvent event) {
+ if (!(event.getOwner() instanceof Player)) {
+ return;
+ }
+
+ Player player = (Player) event.getOwner();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int tamesNeeded = (int) task.getConfigValue("amount");
+
+ int progressTamed;
+ if (taskProgress.getProgress() == null) {
+ progressTamed = 0;
+ } else {
+ progressTamed = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressTamed + 1);
+
+ if (((int) taskProgress.getProgress()) >= tamesNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/TaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/TaskType.java
new file mode 100644
index 00000000..d84d13ba
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/TaskType.java
@@ -0,0 +1,52 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.quests.Quest;
+import org.bukkit.event.Listener;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class TaskType implements Listener {
+
+ private List<Quest> quests = new ArrayList<>();
+ private String type;
+ private String author;
+ private String description;
+
+ public TaskType(String type, String author, String description) {
+ this.type = type;
+ this.author = author;
+ this.description = description;
+ }
+
+ public TaskType(String type) {
+ this.type = type;
+ }
+
+ public final void registerQuest(Quest quest) {
+ if (!quests.contains(quest)) {
+ quests.add(quest);
+ }
+ }
+
+ public final void unregisterAll() {
+ quests.clear();
+ }
+
+ public final List<Quest> getRegisteredQuests() {
+ return quests;
+ }
+
+
+ public final String getType() {
+ return type;
+ }
+
+ public String getAuthor() {
+ return author;
+ }
+
+ public String getDescription() {
+ return description;
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/TaskTypeManager.java b/src/me/fatpigsarefat/quests/quests/tasktypes/TaskTypeManager.java
new file mode 100644
index 00000000..163e128c
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/TaskTypeManager.java
@@ -0,0 +1,40 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.Bukkit;
+
+import java.util.ArrayList;
+import java.util.logging.Level;
+
+public class TaskTypeManager {
+
+ private ArrayList<TaskType> taskTypes = new ArrayList<>();
+
+ public ArrayList<TaskType> getTaskTypes() {
+ return taskTypes;
+ }
+
+ public void resetTaskTypes() {
+ for (TaskType taskType : taskTypes) {
+ taskType.getRegisteredQuests().clear();
+ }
+ }
+
+ public void registerTaskType(TaskType taskType) {
+ Bukkit.getPluginManager().registerEvents(taskType, Quests.getInstance());
+ Quests.getInstance().getLogger().log(Level.INFO, "Task type " + taskType.getType() + " has been registered.");
+ taskTypes.add(taskType);
+ }
+
+ public void registerQuestTasksWithTaskTypes(Quest quest) {
+ for (Task task : quest.getTasks()) {
+ for (TaskType taskType : taskTypes) {
+ if (taskType.getType().equalsIgnoreCase(task.getType())) {
+ taskType.registerQuest(quest);
+ }
+ }
+ }
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/WalkingTaskType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/WalkingTaskType.java
new file mode 100644
index 00000000..0256096e
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/WalkingTaskType.java
@@ -0,0 +1,62 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.entity.Player;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import org.bukkit.event.player.PlayerMoveEvent;
+
+public final class WalkingTaskType extends TaskType {
+
+ public WalkingTaskType() {
+ super("walking", "fatpigsarefat", "Walk a set distance.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onMove(PlayerMoveEvent event) {
+ if (event.getFrom().getBlockX() == event.getTo().getBlockX() && event.getFrom().getBlockZ() == event.getTo().getBlockZ()) {
+ return;
+ }
+
+ Player player = event.getPlayer();
+
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(player.getUniqueId());
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ int distanceNeeded = (int) task.getConfigValue("distance");
+
+ int progressDistance;
+ if (taskProgress.getProgress() == null) {
+ progressDistance = 0;
+ } else {
+ progressDistance = (int) taskProgress.getProgress();
+ }
+
+ taskProgress.setProgress(progressDistance + 1);
+
+ if (((int) taskProgress.getProgress()) >= distanceNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/quests/tasktypes/uSkyBlockLevelType.java b/src/me/fatpigsarefat/quests/quests/tasktypes/uSkyBlockLevelType.java
new file mode 100644
index 00000000..89950c25
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/quests/tasktypes/uSkyBlockLevelType.java
@@ -0,0 +1,52 @@
+package me.fatpigsarefat.quests.quests.tasktypes;
+
+import me.fatpigsarefat.quests.Quests;
+import me.fatpigsarefat.quests.player.QPlayer;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgress;
+import me.fatpigsarefat.quests.player.questprogressfile.QuestProgressFile;
+import me.fatpigsarefat.quests.player.questprogressfile.TaskProgress;
+import me.fatpigsarefat.quests.quests.Quest;
+import me.fatpigsarefat.quests.quests.Task;
+import org.bukkit.event.EventHandler;
+import org.bukkit.event.EventPriority;
+import us.talabrek.ultimateskyblock.api.event.uSkyBlockScoreChangedEvent;
+
+public final class uSkyBlockLevelType extends TaskType {
+
+ public uSkyBlockLevelType() {
+ super("uskyblock_level", "fatpigsarefat", "Reach a certain island level for uSkyBlock.");
+ }
+
+ @EventHandler(priority = EventPriority.MONITOR, ignoreCancelled = true)
+ public void onIslandLevel(uSkyBlockScoreChangedEvent event) {
+ QPlayer qPlayer = Quests.getPlayerManager().getPlayer(event.getPlayer().getUniqueId());
+ if (qPlayer == null) {
+ return;
+ }
+
+ QuestProgressFile questProgressFile = qPlayer.getQuestProgressFile();
+
+ for (Quest quest : super.getRegisteredQuests()) {
+ if (questProgressFile.hasStartedQuest(quest)) {
+ QuestProgress questProgress = questProgressFile.getQuestProgress(quest);
+
+ for (Task task : quest.getTasksOfType(super.getType())) {
+ TaskProgress taskProgress = questProgress.getTaskProgress(task.getId());
+
+ if (taskProgress.isCompleted()) {
+ continue;
+ }
+
+ double islandLevelNeeded = (double) (int) task.getConfigValue("level");
+
+ taskProgress.setProgress(event.getScore().getScore());
+
+ if (((double) taskProgress.getProgress()) >= islandLevelNeeded) {
+ taskProgress.setCompleted(true);
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title.java b/src/me/fatpigsarefat/quests/title/Title.java
new file mode 100644
index 00000000..4851440b
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title.java
@@ -0,0 +1,8 @@
+package me.fatpigsarefat.quests.title;
+
+import org.bukkit.entity.Player;
+
+public interface Title {
+
+ public void sendTitle(Player player, String message, String submessage);
+} \ No newline at end of file
diff --git a/src/me/fatpigsarefat/quests/title/Title_Other.java b/src/me/fatpigsarefat/quests/title/Title_Other.java
new file mode 100644
index 00000000..eef4bc07
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_Other.java
@@ -0,0 +1,19 @@
+package me.fatpigsarefat.quests.title;
+
+import org.bukkit.entity.Player;
+
+public class Title_Other implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ /*
+ I'm too lazy to do a null check each time I want to send a title, so here.
+ Anyway, hello there programmer! I see you're rummaging through my source code.
+ Try not to leave a mess please. If you see anything dodgy or looks like it
+ could do with a little improvement make a pull request.
+ Also join my discord: https://discord.gg/8amrJnX
+ Have a great day!
+ - fatpigsarefat
+ */
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_10_R1.java b/src/me/fatpigsarefat/quests/title/Title_v1_10_R1.java
new file mode 100644
index 00000000..b91cdbfc
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_10_R1.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_10_R1.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_10_R1.PacketPlayOutTitle;
+import net.minecraft.server.v1_10_R1.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_10_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_10_R1 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_11_R1.java b/src/me/fatpigsarefat/quests/title/Title_v1_11_R1.java
new file mode 100644
index 00000000..1cb522e1
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_11_R1.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_11_R1.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_11_R1.PacketPlayOutTitle;
+import net.minecraft.server.v1_11_R1.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_11_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_11_R1 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_12_R1.java b/src/me/fatpigsarefat/quests/title/Title_v1_12_R1.java
new file mode 100644
index 00000000..61a221fa
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_12_R1.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_12_R1.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_12_R1.PacketPlayOutTitle;
+import net.minecraft.server.v1_12_R1.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_12_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_12_R1 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_8_R1.java b/src/me/fatpigsarefat/quests/title/Title_v1_8_R1.java
new file mode 100644
index 00000000..e6c7dbcb
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_8_R1.java
@@ -0,0 +1,23 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_8_R1.ChatSerializer;
+import net.minecraft.server.v1_8_R1.EnumTitleAction;
+import net.minecraft.server.v1_8_R1.PacketPlayOutTitle;
+import org.bukkit.craftbukkit.v1_8_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_8_R1 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+
+} \ No newline at end of file
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_8_R2.java b/src/me/fatpigsarefat/quests/title/Title_v1_8_R2.java
new file mode 100644
index 00000000..3e76e001
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_8_R2.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_8_R2.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_8_R2.PacketPlayOutTitle;
+import net.minecraft.server.v1_8_R2.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_8_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_8_R2 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+} \ No newline at end of file
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_8_R3.java b/src/me/fatpigsarefat/quests/title/Title_v1_8_R3.java
new file mode 100644
index 00000000..933bd098
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_8_R3.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_8_R3.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_8_R3.PacketPlayOutTitle;
+import net.minecraft.server.v1_8_R3.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_8_R3 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_9_R1.java b/src/me/fatpigsarefat/quests/title/Title_v1_9_R1.java
new file mode 100644
index 00000000..b42b4920
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_9_R1.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_9_R1.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_9_R1.PacketPlayOutTitle;
+import net.minecraft.server.v1_9_R1.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_9_R1.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_9_R1 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/title/Title_v1_9_R2.java b/src/me/fatpigsarefat/quests/title/Title_v1_9_R2.java
new file mode 100644
index 00000000..5f69ccfd
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/title/Title_v1_9_R2.java
@@ -0,0 +1,22 @@
+package me.fatpigsarefat.quests.title;
+
+import net.minecraft.server.v1_9_R2.IChatBaseComponent.ChatSerializer;
+import net.minecraft.server.v1_9_R2.PacketPlayOutTitle;
+import net.minecraft.server.v1_9_R2.PacketPlayOutTitle.EnumTitleAction;
+import org.bukkit.craftbukkit.v1_9_R2.entity.CraftPlayer;
+import org.bukkit.entity.Player;
+
+public class Title_v1_9_R2 implements Title {
+
+ @Override
+ public void sendTitle(Player player, String message, String submessage) {
+ message = "{\"text\":\"" + message + "\"}";
+ PacketPlayOutTitle title = new PacketPlayOutTitle(EnumTitleAction.TITLE, ChatSerializer.a(message), 10, 100,
+ 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(title);
+ submessage = "{\"text\":\"" + submessage + "\"}";
+ PacketPlayOutTitle subtitle = new PacketPlayOutTitle(EnumTitleAction.SUBTITLE, ChatSerializer.a(submessage), 10,
+ 100, 10);
+ ((CraftPlayer) player).getHandle().playerConnection.sendPacket(subtitle);
+ }
+}
diff --git a/src/me/fatpigsarefat/quests/updater/Updater.java b/src/me/fatpigsarefat/quests/updater/Updater.java
new file mode 100644
index 00000000..0dca333d
--- /dev/null
+++ b/src/me/fatpigsarefat/quests/updater/Updater.java
@@ -0,0 +1,59 @@
+package me.fatpigsarefat.quests.updater;
+
+import me.fatpigsarefat.quests.obj.Messages;
+import org.bukkit.plugin.Plugin;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.logging.Level;
+
+public class Updater {
+
+ private static final int PROJECT_ID = 23696;
+ private String installedVersion;
+ private String returnedVersion;
+ private URL api;
+ private Plugin plugin;
+ private boolean updateReady;
+
+ public Updater(Plugin plugin) {
+ this.plugin = plugin;
+ this.installedVersion = plugin.getDescription().getVersion();
+ try {
+ this.api = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + PROJECT_ID);
+ } catch (MalformedURLException ignored) {
+ // shit + fan
+ }
+ }
+
+ public String getLink() {
+ return "https://www.spigotmc.org/resources/" + PROJECT_ID;
+ }
+
+ public boolean check() {
+ try {
+ URLConnection con = api.openConnection();
+ returnedVersion = new BufferedReader(new InputStreamReader(con.getInputStream())).readLine();
+ if (!returnedVersion.equals(installedVersion)) {
+ plugin.getLogger().log(Level.INFO, "A new version " + returnedVersion + " was found on Spigot (your version: " + installedVersion + "). Please update me! <3 - Link: " + getLink());
+ updateReady = true;
+ }
+ } catch (IOException e) {
+ plugin.getLogger().log(Level.WARNING, "Failed to check for updates. You can check manually at " + getLink());
+ // probably offline
+ }
+ return false;
+ }
+
+ public boolean isUpdateReady() {
+ return updateReady;
+ }
+
+ public String getMessage() {
+ return Messages.QUEST_UPDATER.getMessage().replace("{newver}", returnedVersion).replace("{oldver}", installedVersion).replace("{link}", getLink());
+ }
+} \ No newline at end of file
diff --git a/src/plugin.yml b/src/plugin.yml
new file mode 100644
index 00000000..16409321
--- /dev/null
+++ b/src/plugin.yml
@@ -0,0 +1,17 @@
+name: Quests
+version: 2.0.0
+main: me.fatpigsarefat.quests.Quests
+author: fatpigsarefat
+softdepend: [ASkyBlock, uSkyBlock]
+prefix: Quests
+
+commands:
+ quests:
+ description: Description
+ usage: /quests
+ permission: quests.command
+ aliases: [q, quest]
+
+permissions:
+ quests.command:
+ description: Quests permission \ No newline at end of file