/
InteractScriptContainer.java
329 lines (304 loc) · 13.8 KB
/
InteractScriptContainer.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
package net.aufdemrand.denizen.scripts.containers.core;
import net.aufdemrand.denizen.BukkitScriptEntryData;
import net.aufdemrand.denizen.objects.dNPC;
import net.aufdemrand.denizen.objects.dPlayer;
import net.aufdemrand.denizen.scripts.triggers.AbstractTrigger;
import net.aufdemrand.denizen.utilities.DenizenAPI;
import net.aufdemrand.denizen.utilities.debugging.dB;
import net.aufdemrand.denizencore.scripts.ScriptEntry;
import net.aufdemrand.denizencore.scripts.containers.ScriptContainer;
import net.aufdemrand.denizencore.utilities.YamlConfiguration;
import net.aufdemrand.denizencore.utilities.text.StringHolder;
import java.util.*;
public class InteractScriptContainer extends ScriptContainer {
// <--[language]
// @name Interact Script Containers
// @group Script Container System
// @description
// Interact script containers are used to handle NPC triggers.
//
// Interact scripts must be referenced from an assignment script container to be of any use.
// See <@link language assignment script containers>.
//
// The only required key on a task script container is the 'steps:' key.
//
// Within the steps key is a list of steps,
// where the first step is '1', 'default', or any step that contains a '*' symbol.
// After that, any steps must be 'zapped' to via the zap command: <@link command zap>.
//
// Each step contains a list of trigger types that it handles, and the relevant handling that the given
// trigger makes available.
//
// Refer to <@link language interact script triggers> for documentation about the triggers available.
// Any triggers used must be enabled in <@link action assignment> by <@link command trigger>.
//
// Note that script commands ran in interact scripts by default have a delay between each command.
// To override this delay, put a '^' in front of each command name, or set 'speed: 0' on the container.
//
// <code>
// Interact_Script_Name:
//
// type: interact
//
// steps:
//
// # The first step
// 1:
// # Any trigger type here
// click trigger:
// script:
// # Handle what happens when the NPC is clicked during step 1
// - some commands
// # Other triggers here
// # other steps here
//
// </code>
//
// -->
// <--[language]
// @name Interact Script Triggers
// @group NPC Interact Scripts
// @description
// Interact script triggers are the most basic components of standard NPC scripting.
// They're very useful for NPCs that give quests or have other very basic interactions with players.
// While less powerful that other tools that Denizen provides, they can be very straightforward and clear to use in many simpler cases.
//
// Note that triggers have a default cooldown system built in to prevent users from clicking too rapidly.
// However these are very short cooldowns by default - when you need a longer cooldown, use
// <@link command cooldown> or <@link command engage>.
//
// Triggers go in <@link language interact script containers>.
//
// The available default trigger types are <@link language click triggers>,
// <@link language damage triggers>, <@link language chat triggers>, and <@link language proximity triggers>.
// -->
public InteractScriptContainer(YamlConfiguration configurationSection, String scriptContainerName) {
super(configurationSection, scriptContainerName);
try {
// Find steps/default step in the script
Set<StringHolder> keys;
keys = getConfigurationSection("STEPS").getKeys(false);
// TODO: Throw a warning if 'requirements' section exists
if (contains("REQUIREMENTS")) {
dB.echoError("Interact script '" + getName() + "' is outdated: 'requirements' do not exist in modern Denizen!");
}
if (keys.isEmpty()) {
throw new ExceptionInInitializerError("Could not find any STEPS in " + getName() + "! Is the type on this script correct?");
}
for (StringHolder step1 : keys) {
String step = step1.str;
if (step.contains("*")) {
YamlConfiguration defaultStepSection = getConfigurationSection("STEPS." + step);
step = step.replace("*", "");
set("STEPS." + step, defaultStepSection);
set("STEPS." + step + "*", null);
defaultStep = step;
}
if (step.equalsIgnoreCase("1")) {
defaultStep = step;
}
if (step.equalsIgnoreCase("DEFAULT")) {
defaultStep = step;
}
steps.add(step);
}
}
catch (Exception e) {
dB.echoError(e);
}
// Make default step the only step if there is only one step
if (defaultStep == null && steps.size() == 1) {
defaultStep = steps.get(0);
}
if (defaultStep == null) {
throw new ExceptionInInitializerError("Must specify a default step in '" + getName() + "'!");
}
}
private String defaultStep = null;
private List<String> steps = new ArrayList<>();
public List<String> getStepNames() {
return steps;
}
/**
* <p>Gets the name of the default step for this interact script container. Default step
* is specified by a '*' character on the end of the step name.</p>
* <p/>
* <b>Example:</b>
* <tt>
* Example Interact Script: <br/>
* Type: interact <br/>
* Steps: <br/>
* Step Name*: <--- Default step for this interact script <br/>
* ... <br/>
* <br/>
* Another Step Name: <--- Not the default step, must use ZAP <br/>
* ... <br/>
* </tt>
* <p/>
* <p>Note: For the sake of compatibility with v0.76, a step named '1' can also
* be used to specify a default step.</p>
*
* @return name of the default step
*/
public String getDefaultStepName() {
return defaultStep;
}
/**
* Checks if the step in this script contains an entry for the specified trigger.
*
* @param step the name of the step to check
* @param trigger the trigger to check for
* @return true if the trigger is present in the step, false otherwise
*/
public boolean containsTriggerInStep(String step, Class<? extends AbstractTrigger> trigger) {
String triggerName = DenizenAPI.getCurrentInstance()
.getTriggerRegistry().get(trigger).getName().toUpperCase();
return contains("STEPS." + step.toUpperCase() + "." + triggerName + " TRIGGER");
}
/**
* <p>Gets a list of ScriptEntries from a Trigger's script key. In order to get the correct
* entries, pass along the id of the exact script for the Trigger, which should be identified
* by using getPossibleTriggersFor(). If no id is specified by passing null, the
* base Script key will be used for the Trigger. If no script matches the trigger or
* trigger/id combination, an empty list is returned.</p>
* <p/>
* <b>Example:</b>
* <tt>
* Example Interact Script: <br/>
* Type: interact <br/>
* Steps: <br/>
* Current Step: <--- checked with Player object <br/>
* Click Trigger: <--- obtained with the Trigger class <br/>
* <br/>
* id: <--- id of the specific trigger script/script options <br/>
* Script: <br/>
* - ... <--- entries obtained if id matches <br/>
* - ... <br/>
* <br/>
* Script: <--- script that is referenced if NO id is specified <br/>
* - ... <--- entries returned <br/>
* - ... <br/>
* </tt>
* <p/>
* <p>Note: This is handled internally with the parse() method in AbstractTrigger, so for
* basic triggers, you probably don't need to even call this.</p>
*
* @param trigger the class of the Trigger to use
* @param player the Player involved
* @param npc the NPC involved
* @param id the id of the Trigger Script, optional
* @return a list of ScriptEntries from the script or an empty list if no script was found
*/
public List<ScriptEntry> getEntriesFor(Class<? extends AbstractTrigger> trigger,
dPlayer player, dNPC npc, String id) {
return getEntriesFor(trigger, player, npc, id, false);
}
public List<ScriptEntry> getEntriesFor(Class<? extends AbstractTrigger> trigger,
dPlayer player, dNPC npc, String id, boolean quiet) {
// Get the trigger name
String triggerName = DenizenAPI.getCurrentInstance()
.getTriggerRegistry().get(trigger).getName().toUpperCase();
// Check for entries
if (contains("STEPS." + InteractScriptHelper.getCurrentStep(player, getName()) + "."
+ triggerName + " TRIGGER."
+ (id == null ? "SCRIPT" : id.toUpperCase() + ".SCRIPT"))) {
// Entries exist, so get them and return the list of ScriptEntries
return getEntries(new BukkitScriptEntryData(player, npc),
"STEPS." + InteractScriptHelper.getCurrentStep(player, getName()) + "."
+ triggerName + " TRIGGER."
+ (id == null ? "SCRIPT" : id.toUpperCase() + ".SCRIPT"));
// No entries, so just return an empty list to avoid NPEs
}
else {
if (!quiet) {
dB.echoDebug(this, "No entries in script for " +
("STEPS." + InteractScriptHelper.getCurrentStep(player, getName()) + "."
+ triggerName + " TRIGGER."
+ (id == null ? "SCRIPT" : id.toUpperCase() + ".SCRIPT")));
}
return Collections.emptyList();
}
}
/**
* Gets the available IDs with its trigger value in the form of a Map. The 'key' is
* the name of the ID and the value is the value of the 'Trigger' key that is owned
* by the ID key.
* <p/>
* <b>Example:</b>
* <tt>
* Example Interact Script: <br/>
* Type: interact <br/>
* Steps: <br/>
* Current Step:
* Click Trigger: <--- obtained with the Trigger class <br/>
* <br/>
* id: <--- id of the specific trigger script/script options <br/>
* Trigger: iron_sword <--- value of the id key <br/>
* Script: <br/>
* - ...
* - ... <br/>
* <br/>
* Script: <--- since this is an id-less entry for the click trigger, <br/>
* - ... it will be ignored and not in the Map. <br/>
* - ... <br/>
* </tt>
* <p/>
* <p>Note: This is handled internally with the parse() method in AbstractTrigger, so for
* basic triggers, you probably don't need to even call this.</p>
*
* @param trigger The trigger involved
* @param player The Denizen Player object for the player who triggered it
* @return A map of options in the trigger's script, excluding a plain 'script'
*/
public Map<String, String> getIdMapFor(Class<? extends AbstractTrigger> trigger,
dPlayer player) {
// Get the trigger name
String triggerName = DenizenAPI.getCurrentInstance()
.getTriggerRegistry().get(trigger).getName().toUpperCase();
// Get the step
String step = InteractScriptHelper.getCurrentStep(player, getName());
// Check for entries
if (contains("STEPS." + step + "." + triggerName + " TRIGGER")) {
// Trigger exists in Player's current step, get ids.
Map<String, String> idMap = new HashMap<>();
// Iterate through IDs to build the idMap
try {
for (StringHolder id : getConfigurationSection("STEPS." + step + "."
+ triggerName + " TRIGGER").getKeys(false)) {
if (!id.str.equalsIgnoreCase("SCRIPT")) {
idMap.put(id.str, getString("STEPS." + step + "."
+ triggerName + " TRIGGER." + id.str + ".TRIGGER", ""));
}
}
}
catch (Exception ex) {
dB.echoError("Warning: improperly defined " + trigger.getName() + " trigger for script '" + getName() + "'!");
}
return idMap;
}
// No entries, so just return an empty list to avoid NPEs
else {
return Collections.emptyMap();
}
}
public String getTriggerOptionFor(Class<? extends AbstractTrigger> trigger,
dPlayer player, String id, String option) {
// Get the trigger name
String triggerName = DenizenAPI.getCurrentInstance()
.getTriggerRegistry().get(trigger).getName().toUpperCase();
// Get the step
String step = InteractScriptHelper.getCurrentStep(player, getName());
return getString("STEPS." + step + "." + triggerName + " TRIGGER"
+ (id == null ? "" : "." + id.toUpperCase()) + "." + option.toUpperCase());
}
public boolean hasTriggerOptionFor(Class<? extends AbstractTrigger> trigger,
dPlayer player, String id, String option) {
// Get the trigger name
String triggerName = DenizenAPI.getCurrentInstance()
.getTriggerRegistry().get(trigger).getName().toUpperCase();
// Get the step
String step = InteractScriptHelper.getCurrentStep(player, getName());
return contains("STEPS." + step + "." + triggerName + " TRIGGER"
+ (id == null ? "" : "." + id.toUpperCase()) + "." + option.toUpperCase());
}
}