Skip to content

Commit

Permalink
v0.7 - Item Fetching Quests (#100)
Browse files Browse the repository at this point in the history
* chore: update version 0.6 -> 0.7

* Add gather item action (#99)

* add bare implementation of GatherItem + quest action dev docs

* add mock-up functionality for ITEMS action option

meaning, generally if using this you can see the workflow, but there's no real data.

* add data/serialisation for GatherItem action

* implement pickup item gathering listen and check

* implement GatherItem crafting listener

* implement GatherItem inventory movement listener

* fix not checking inventory at beginning of GatherItem listen

this means potentially having the items already gathered, but needing to pick up and place them again for it to register

* remove redundant onCraftItem listen for GatherItem

* add quest auto-save on item list exit

* add onRemove for GatherItem list

* (untested) add finish message option to GatherItem

* add defining JAVA_HOME in dev shell

* add missing javadoc return value for QuestAction.setFinishMessage

* Add take item action (#102)

**uncaught case warning; will break item on TakeItem: failing/items not being available to take.**

* fix TakeItem serialisation

* simplify allActionTypes list func

* add TakeItem action

undecided what to do if items can't be taken

* fix taking all matching ItemStacks in TakeItem

* Add reward item action and quest inventory (#104)

* fix next action starting before game code finishes

* SelectBlock -> SelectMaterial

can add a boolean to restrict to only blocks

* add RewardItem action

* fix overuse of mutable lists

* add 'Quest Inventory' w/ restocking

* add 'Out of Stock' indication for required items

* add 'Not Enough Stock' indicator if 'requiredInventory' unmet

* remove unused 'player' variable

* fix null inventory var in getInventory

* use QuestRegistry+Database for Quest Inventories

* fix Dynamicitemlist adding item after deleting one

kinda weird solution, the Back button won't go back on it's own. It requires onFinish to be set to whatever the next GUI screen is. This was basically because i needed to set onFinish for the screen it was going to, and when the UpdateScreen was happening in the item editor it had no onFinish so it was getting lost. Could potentially steal the Consumer from the Director state, but really the whole onFinish vanishing is bung, maybe UpdateScreen needs to preserve the screen onFinish along with the screen name so you can go to a million screens and eventually you'll get back to the origin and the behaviour won't have disappeared.

* fix unable to sort inventory that doesn't exist

* fix realtime responsiveness of quest inventory

* fix incorrect variable in QuestRegistry.setInventory

* add javadoc for getPredictiveInventory

* add TakeItem contributing to 'Quest Inventory'

* updated pq guide: action support list

RequestItem and RewardItem is supported now

* fix GatherItem incorrect initOptions body

* never return null from QuestRegistry.getInventory

* force into survival when using /playerquests

* fix QuestBuilder.build always overwriting current

* move 'Quest Inventory' to 'myquest' screen (before editing)

* Create RewardItem and TakeItem listeners (#106)

* refactor: require listener for each QuestAction

* remove quest inventory object in QuestBuilder

* fix not waiting for RewardItem to finish before TakeItem

* Fix current quest not showing (#108)

* add missing javadoc

* add onPickupItem event for TakeItem completion

* add waiting/implement 'current' actions in quests
  • Loading branch information
sammypanda authored Sep 27, 2024
1 parent 8762343 commit 7b55a3e
Show file tree
Hide file tree
Showing 62 changed files with 2,454 additions and 254 deletions.
4 changes: 2 additions & 2 deletions branding/docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ I'm always planning to add more quest actions, but so far:
| Action | Description | Supported |
| --------------------------------------- | ------------------------------------------------------ | --------- |
| Speak | Makes an NPC say something to the player ||
| Request Item | Waits for the player to return with an item | |
| Reward Item | Gives the player an item | |
| Request Item | Waits for the player to return with an item | |
| Reward Item | Gives the player an item | |
| None | Ends the quest (default) ||

<br>
Expand Down
23 changes: 19 additions & 4 deletions branding/docs/developers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Realistically 'Quest Actions' won't ever have to be called by their function nam
| UpdateScreen | 1: the dynamic GUI name | Changes the current GUI screen to a different GUI |
| ChatPrompt | 1: the prompt to show to the user<br>2: key of the value to set (options: "none", "gui.title", "quest.title") | Prompts the user to sets a text value (by typing in the chat box) |
| Save | 1. key of instance to save (options: "quest") | Calls the defined save processes for instances |
| SelectBlock | 1. the prompt to show the user<br>2. list of denied blocks<br>3. list of denied methods | Prompts the user to select a block (by hitting or selecting in inventory) |
| SelectMaterial | 1. the prompt to show the user<br>2. list of denied materials<br>3. list of denied methods<br>4. if has to be a block | Prompts the user to select a block (by hitting or selecting in inventory) |
| SelectLocation | 1. the prompt to show the user | Prompts the user to place a block to set it as the location |

###### Quest Actions (Actions)
Expand All @@ -23,8 +23,7 @@ TODO: Each <ins>quest</ins> is a <ins>container of stages</ins>. Each <ins>stage
|------------------------|-------------------------------|-------------------------------------------------|
| None | N/A | Nothing; ignored |
| Speak | 1: Text<br>2: NPC ID | Makes an NPC say things |
| RequestItem | 1: Material ENUM<br>2: Count | Generic item + amount the quest wants |
| ChangeQuestEntry | 1: stage ID or action ID | Changes what stage or action the quest opens to |
| GatherItem | 1: Material ENUM<br>2: Count | Generic item + amount the quest wants |

# How To Get Functionality: 'Templates'
###### We have Meta and Quest Actions, but how do we actually use them?
Expand All @@ -35,6 +34,9 @@ Usually you would never need this, but this is what makes it all tick. When you
"entry": String, // Path: (as in 'entry point') where the quest starts
"creator": UUID, // the player who created this quest
"id": String, // the id, composed of: [Quest Title]_[Creator UUID]
"inventory": {
"[a minecraft material]": Integer // for instance: "BIRCH_LOG" : 1
},
"npcs": { // directory of all the quest npcs
"npc_0": { // NPC ID (automatically generated)
"name": String, // the name of the NPC
Expand All @@ -61,7 +63,10 @@ Usually you would never need this, but this is what makes it all tick. When you
"type": String, // Quest Action type
"id": String, // Quest Action ID
"npc": String, // NPC ID (if applicable)
"dialogue": String Array // Dialogue lines (if applicable)
"dialogue": String Array, // Dialogue lines (if applicable)
"items": {
"MATERIAL": Integer // a map of items
},
"connections": { // defining where the action is in the stage
"next": @Nullable String, // where to go if the action succeeds
"curr": @Nullable String, // where to return to if the action is exited
Expand Down Expand Up @@ -92,3 +97,13 @@ Usually you would never need this, but this is what makes it all tick. When you
| client/ | Ways to control the plugin |
| utility/ | Tools for reducing repeated code |
| utility/annotation | Custom code annotations |

# How to add new quest actions
Feel free to use 'Speak' as an example to help you, alongside this brief guide:
1. Like 'None' and 'Speak' each quest action should extend the QuestAction class.
- Then add the unimplemented methods, as required, from QuestAction.
- Add an empty constructor for Jackson parsing, and one taking QuestStage.
2. Then after the new one is created, in QuestAction it needs to be added to the JsonSubTypes annotations and the allActionTypes() list.
3. Write the code to implement the action and add javadocs.
- Such as: return list of options used for this action in InitOptions, at least return an empty optional in validate (as to mean 'no error message').
- If you need to add an ActionOption just add it to the ActionOption enum and then create a case for it in the Dynamicactioneditor.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

<groupId>moe.sammypanda</groupId>
<artifactId>playerquests</artifactId>
<version>0.6</version>
<version>0.7</version>

<packaging>jar</packaging>

Expand Down
4 changes: 4 additions & 0 deletions shell.nix
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,8 @@ mkShell {
jdk22
glibc
];

shellHook = ''
export JAVA_HOME=${jdk22.outPath}
'';
}
2 changes: 1 addition & 1 deletion src/main/java/playerquests/builder/gui/GUIBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ public class GUIBuilder implements Builder {
/**
* The previous screens the user have been to.
*/
private List<String> previousScreens = new ArrayList<String>();
private List<String> previousScreens = new ArrayList<>();

/**
* Instantiate a GUIBuilder with default GUI and set as current GUIBuilder.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public String getTitle() {

/**
* Set's the number of slots in the GUI screen.
* @param size the number of slots (has to be a multiple of 9. Like 9 or 18
* @param size the number of slots (has to be a multiple of 9. Like 9 or 18. Up to 54.
*/
public void setSize(int size) {
this.size = size;
Expand Down
43 changes: 42 additions & 1 deletion src/main/java/playerquests/builder/gui/component/GUISlot.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List; // generic list type

import org.bukkit.ChatColor; // used to modify formatting of in-game chat text
import org.bukkit.Material;
import org.bukkit.entity.HumanEntity; // refers to the player

import playerquests.builder.gui.GUIBuilder; // the builder which enlists this slot
Expand Down Expand Up @@ -43,7 +44,7 @@ public class GUISlot {
/**
* List of functions associated with this slot. Functions are executed when this slot is interacted with.
*/
private List<GUIFunction> functionList = new ArrayList<GUIFunction>();
private List<GUIFunction> functionList = new ArrayList<>();

/**
* Indicates whether the slot has encountered a syntax error. Defaults to {@code false}.
Expand All @@ -60,6 +61,11 @@ public class GUISlot {
*/
private Integer stackCount = 1; // 1 default, being no number shown

/**
* If the slot item is shiny.
*/
private Boolean glinting = false;

/**
* Constructs a new {@code GUISlot} with the specified parent {@code GUIBuilder}.
* @param builder The parent GUI builder managing this slot.
Expand Down Expand Up @@ -118,6 +124,23 @@ public GUISlot setItem(String item) {
return this;
}

/**
* Sets the material to be displayed in this slot.
*
* <p>This method updates the item in the slot to be represented by the given {@link Material}.
* The provided material is converted to its string representation and stored in the internal
* item field. This method is typically used to configure the slot with a specific material
* for display or interaction purposes.</p>
*
* @param material The {@link Material} to be displayed in the slot. This is converted to a
* string and assigned to the slot's item.
* @return The current instance of {@link GUISlot}, allowing for method chaining.
*/
public GUISlot setItem(Material material) {
this.item = material.toString();
return this;
}

/**
* Sets the hover label for this slot.
* @param label The label text to be displayed when hovering over the slot.
Expand Down Expand Up @@ -251,4 +274,22 @@ public void setCount(Integer count) {
public Integer getCount() {
return this.stackCount;
}

/**
* Sets whether the item is shining/glinting.
* @param glinting if there is a glint.
* @return the state of the GUI slot.
*/
public GUISlot setGlinting(Boolean glinting) {
this.glinting = glinting;
return this;
}

/**
* Gets whether the item is shining/glinting.
* @return if there is a glint.
*/
public boolean isGlinting() {
return this.glinting;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@
import java.util.Arrays; // generic array handling
import java.util.List; // generic list type
import java.util.Map; // generic map type
import java.util.Optional; // for a value that may be null
import java.util.stream.Collectors; // summising a stream to a data type
import java.util.stream.IntStream; // functional loops

import org.bukkit.inventory.ItemStack;

import playerquests.builder.gui.component.GUISlot; // modifying gui slots
import playerquests.builder.gui.function.ChatPrompt; // prompts the user for input
import playerquests.builder.gui.function.UpdateScreen; // going to previous screen
Expand Down Expand Up @@ -74,23 +75,15 @@ protected void execute_custom() {
exitButton.setLabel("Back");
exitButton.setItem("OAK_DOOR");
exitButton.onClick(() -> {
Optional<String> validity = action.validate(); // check if action is valid

// exit and send error if action is invalid
if (!validity.isEmpty()) {
ChatUtils.message(validity.get())
.player(this.director.getPlayer())
.type(MessageType.WARN)
.send();
return;
}
// check if action is valid
action.Validate();

// update quest
QuestRegistry.getInstance().submit(this.stage.getQuest());

// switch GUI
new UpdateScreen(
new ArrayList<>(Arrays.asList("queststage")), // set the previous screen
Arrays.asList("queststage"), // set the previous screen
director // set the client director
).execute();
});
Expand All @@ -103,7 +96,7 @@ protected void execute_custom() {
this.director.setCurrentInstance(this.action.getConnections());

new UpdateScreen(
new ArrayList<>(Arrays.asList("connectioneditor")),
Arrays.asList("connectioneditor"),
director
).execute();
});
Expand All @@ -130,7 +123,7 @@ protected void execute_custom() {

// update UI
new UpdateScreen(
new ArrayList<>(Arrays.asList("queststage")),
Arrays.asList("queststage"),
director
).execute();
});
Expand All @@ -141,7 +134,7 @@ protected void execute_custom() {
typeButton.setLabel("Change Type (" + this.action.toString() + ")");
typeButton.onClick(() -> {
new UpdateScreen(
new ArrayList<>(Arrays.asList("actiontypes")),
Arrays.asList("actiontypes"),
director
).execute();
});
Expand Down Expand Up @@ -205,7 +198,7 @@ private void putOptionSlot(Integer slot, ActionOption option) {

optionSlot.onClick(() -> {
new UpdateScreen(
new ArrayList<>(Arrays.asList("selectnpc")),
Arrays.asList("selectnpc"),
director
).onFinish(function -> {
UpdateScreen functionUpdateScreen = (UpdateScreen) function;
Expand All @@ -231,7 +224,7 @@ private void putOptionSlot(Integer slot, ActionOption option) {

optionSlot.onClick(() -> {
new ChatPrompt(
new ArrayList<>(Arrays.asList("Enter the dialogue", "none")),
Arrays.asList("Enter the dialogue", "none"),
this.director
).onFinish((function) -> {
ChatPrompt prompt = (ChatPrompt) function; // cast the GUIFunction as ChatPrompt
Expand All @@ -248,6 +241,75 @@ private void putOptionSlot(Integer slot, ActionOption option) {
this.execute();
}).execute();
});
break;
case ITEMS:
// open the items list screen
optionSlot.onClick(() -> {
// fetch existing items list
ArrayList<ItemStack> items = (ArrayList<ItemStack>) this.action.getItems();

// if items list exists, set it as the current instance to use
if (items != null) {
this.director.setCurrentInstance(items);
}

new UpdateScreen(
Arrays.asList("itemslist"),
director
).onFinish((GUI) -> {
// get the itemslist gui instance
UpdateScreen updateScreen = (UpdateScreen) GUI;
Dynamicitemslist itemslistGUI = (Dynamicitemslist) updateScreen.getDynamicGUI();

itemslistGUI.onFinish((_) -> {
List<ItemStack> itemslist = itemslistGUI.getItems();

// exit if no items in the list
if (itemslist == null) {
return;
}

// set this as the list of items
this.action.setItems(itemslist);

// update quest
this.stage.getQuest().save();

// refresh to see changes
this.execute();
});
}).execute();
});
break;
case FINISH_MESSAGE:
String finishMessage = this.action.getFinishMessage();

if (finishMessage != null) {
Integer maxLength = 16;
optionSlot.setDescription(String.format("%s%s",
finishMessage.length() >= maxLength ? finishMessage.substring(0, maxLength) : finishMessage,
finishMessage.length() > maxLength ? "..." : ""
));
}

// handle clicking the option slot
optionSlot.onClick(() -> {

// create a new chat prompt to get the value
new ChatPrompt(
Arrays.asList("Enter the finish message", "none"), director
)
.onFinish((f) -> {
ChatPrompt function = (ChatPrompt) f;

// get the value
this.action.setFinishMessage(function.getResponse());

// refresh to see updated value
this.execute();
}).execute();;
});
break;
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ private void generatePage(ArrayList<String> actionTypes_remaining) {
Class<?> classRef = Class.forName("playerquests.builder.quest.action." + type);

// Then it means the action type has been implemented:
if (stage.getActions().get(stage.getActionToEdit()).getType().equals(type)) { // compare action type being modified with action type in this loop
if (stage.getActions().get(stage.getActionToEdit()).getType().equals(classRef)) { // compare action type being modified with action type in this loop
typeButton.setItem("GLOWSTONE_DUST");
typeButton.setLabel(type + " (Selected)");
} else {
Expand All @@ -151,7 +151,7 @@ private void generatePage(ArrayList<String> actionTypes_remaining) {
this.gui.clearSlots(); // clear to prevent duplicates
this.execute(); // re-run to show changes
} catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
throw new IllegalStateException("Action type " + type + " could not be instantiated.");
throw new IllegalStateException("Action type " + type + " could not be instantiated.", e);
}
});

Expand All @@ -175,7 +175,7 @@ private void generatePage(ArrayList<String> actionTypes_remaining) {
exitButton.setLabel("Back");
exitButton.setItem("OAK_DOOR");
exitButton.addFunction(new UpdateScreen( // set function as 'UpdateScreen'
new ArrayList<>(Arrays.asList(this.previousScreen)), // set the previous screen
Arrays.asList(this.previousScreen), // set the previous screen
director // set the client director
));

Expand Down
Loading

0 comments on commit 7b55a3e

Please sign in to comment.