MagicSpells
MagicSpells copied to clipboard
Dynamically adjust spell configuration nodes via api.
Copying conversation from discord server channel:
NeumimTo 19.12.2020
Hey, I would like to dynamically adjust spell numerical values depending on ingame conditions/player state. So far it seems nearly impossible to achieve such thing.
For example
dodge:
spell-class: ".buff.DodgeSpell"
toggle: true
duration: <value that is unique depending on a player that casts the spell>
distance: 2
spells:
- particleProjectileSpellName
Any chance to make this thing more accessible, or provide necessary hooks that other plugins could use? I looked at the code, and only thing that came to my mind is to somehow make MobConfig to have an unique spell definition per player. eg:
dodge_player1:
duration: 1000
...
dodge_player2:
duration: 2000
which is a sollution i dont really like
The easiest and least destructive way to allow such thing seems to be editing Spell#getConfigInt/Long/... methods pseudocode
protected int getConfigInt(String key, int defaultValue, Player caster) {
return thirdPartyHookEnabled && hook.supportsNode(key) ? callhook(key, caster) : config.getInt("spells." + internalName + '.' + key, defaultValue);
}
JasperLorelai 19.12.2020 This was something that was planned. We wanted to have MS variable replacement supported in configuration options. I looked into it before and I agree that one of the easiest ways to do that is through the getConfig methods. We'd need to add a player argument and make it read the values every time the spell is casted.
I could help to complete this task if theres someone willing to guide me thru codebase
From discord:
JasperLorelai:
You could elaborate on how you plan to tackle this. Point is, nobody has any objections.
General Stuff
My approach would be to create an interface with methods for reading data from Spell.config.
interface SpellDataReader {
int getConfigInt(LivingEntity, key, defVal);
long getConfigLong(LivingEntity, key, defVal)
... you get the idea
}
Spell.config would also become datatype SpellDataReader
Then provide a default implementation, pretty close to what is in MS only add reference to the caster entity.
XX implements SpellDataReader {
protected MagicConfig config;
protected int getConfigInt(LivingEntity caster, String key, int defaultValue) {
return config.getInt("spells." + internalName + '.' + key, defaultValue);
}
protected long getConfigLong(LivingEntity caster, String key, long defaultValue) {
return config.getLong("spells." + internalName + '.' + key, defaultValue);
}
protected boolean getConfigBoolean(String key, boolean defaultValue) {
return config.getBoolean("spells." + internalName + '.' + key, defaultValue);
}
Thirdparty plugins could eventually override these values as needed
YY extends XX {
protected boolean getConfigBoolean(String key, boolean defaultValue) {
if (thirdPartyPluginSupportsKey("spells." + internalName + '.' + key) {
return X;
}
return super.getConfigBoolean(key, defaultValue);
}
Since MagicConfig class is no longer within Spell constructor lookup needs to be edited as well.
try {
constructor = spellClass.getConstructor(MagicConfig.class, String.class);
} catch (NoSuchMethodException e) {
continue;
}
An api that third party plugins could use to register its own spell data reader into ms would be also required.
Firing with an event, that other plugins could use to pass custom SpellDataReader seems to be sufficient
try {
spell = constructor.newInstance(event.getReader == null? default: event.getReader, spellName);
} catch (Exception e) {
Spells
Lots of spells/listeners that i looked on seems to be trivial to refactor into such system such as AgeSpell that would only need trivial changes
private void applyAgeChanges(Ageable a, LivingEntity caster) {
if (setMaturity) {
int rawAge = getConfigInt(caster, "age", 0);
a.setAge(rawAge);
}
if (applyAgeLock) a.setAgeLock(true);
}
In this approach one of main problems that i can think of now is validation you do after loading the spell from config.
For example within StunSpell#initialize there is
if (!stunBody && !stunMonitor) {
MagicSpells.error("StunSpell '" + internalName + "' is not attempting to stun the body or the monitor.");
return;
}
These validations, would no longer work since other plugins may eventually edit each time the spell is executed. These must be moved into spell execution, or abandoned.
This approach would also add some overhead. The spell config values would be read more often from MemoryConfiguration.