diff --git a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java
index af6789fab..5384d3146 100644
--- a/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java
+++ b/bukkit/src/main/java/net/momirealms/craftengine/bukkit/plugin/user/BukkitServerPlayer.java
@@ -35,10 +35,7 @@ import net.momirealms.craftengine.core.plugin.locale.TranslationManager;
import net.momirealms.craftengine.core.plugin.network.ConnectionState;
import net.momirealms.craftengine.core.plugin.network.EntityPacketHandler;
import net.momirealms.craftengine.core.sound.SoundSource;
-import net.momirealms.craftengine.core.util.Direction;
-import net.momirealms.craftengine.core.util.IntIdentityList;
-import net.momirealms.craftengine.core.util.Key;
-import net.momirealms.craftengine.core.util.VersionHelper;
+import net.momirealms.craftengine.core.util.*;
import net.momirealms.craftengine.core.world.*;
import net.momirealms.craftengine.core.world.World;
import net.momirealms.craftengine.core.world.chunk.ChunkStatus;
@@ -1253,4 +1250,32 @@ public class BukkitServerPlayer extends Player {
platformPlayer().getPersistentDataContainer().remove(KeyUtils.toNamespacedKey(SELECTED_LOCALE_KEY));
}
}
+
+ @Override
+ public void giveExperiencePoints(int xpPoints) {
+ platformPlayer().giveExp(xpPoints);
+ }
+
+ @Override
+ public void giveExperienceLevels(int levels) {
+ platformPlayer().giveExpLevels(levels);
+ }
+
+ @Override
+ public int getXpNeededForNextLevel() {
+ return platformPlayer().getExperiencePointsNeededForNextLevel();
+ }
+
+ @Override
+ public void setExperiencePoints(int experiencePoints) {
+ float xpNeededForNextLevel = this.getXpNeededForNextLevel();
+ float maxProgressThreshold = (xpNeededForNextLevel - 1.0F) / xpNeededForNextLevel;
+ float experienceProgress = MiscUtils.clamp(experiencePoints / xpNeededForNextLevel, 0.0F, maxProgressThreshold);
+ platformPlayer().setExp(experienceProgress);
+ }
+
+ @Override
+ public void setExperienceLevels(int level) {
+ platformPlayer().setLevel(level);
+ }
}
diff --git a/common-files/src/main/resources/translations/en.yml b/common-files/src/main/resources/translations/en.yml
index 761e46f38..e8fbebaaf 100644
--- a/common-files/src/main/resources/translations/en.yml
+++ b/common-files/src/main/resources/translations/en.yml
@@ -492,6 +492,7 @@ warning.config.function.if_else.missing_rules: "Issue found in file Issue found in file - The config '' is missing the required 'properties' argument for 'update_block_property' function."
warning.config.function.transform_block.missing_block: "Issue found in file - The config '' is missing the required 'block' argument for 'transform_block' function."
warning.config.function.cycle_block_property.missing_property: "Issue found in file - The config '' is missing the required 'property' argument for 'cycle_block_property' function."
+warning.config.function.exp.missing_count: "Issue found in file - The config '' is missing the required 'count' argument for 'exp' function."
warning.config.selector.missing_type: "Issue found in file - The config '' is missing the required 'type' argument for selector."
warning.config.selector.invalid_type: "Issue found in file - The config '' is using an invalid selector type ''."
warning.config.selector.invalid_target: "Issue found in file - The config '' is using an invalid selector target ''."
diff --git a/common-files/src/main/resources/translations/zh_cn.yml b/common-files/src/main/resources/translations/zh_cn.yml
index 3de669d54..2264fc523 100644
--- a/common-files/src/main/resources/translations/zh_cn.yml
+++ b/common-files/src/main/resources/translations/zh_cn.yml
@@ -388,6 +388,7 @@ warning.config.loot_table.function.apply_bonus.missing_enchantment: "在
warning.config.loot_table.function.apply_bonus.missing_formula: "在文件 发现问题 - '' 的战利品表配置错误 'apply_bonus' 函数缺少必需的 'formula' 参数"
warning.config.loot_table.function.drop_exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'drop_exp' 函数缺少必需的 'count' 参数"
warning.config.loot_table.function.set_count.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'set_count' 函数缺少必需的 'count' 参数"
+warning.config.loot_table.function.apply_data.missing_data: "在文件 发现问题 - '' 的战利品表配置错误 'apply_data' 函数缺少必需的 'data' 参数"
warning.config.loot_table.entry.missing_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目缺少必需的 'type' 参数"
warning.config.loot_table.entry.invalid_type: "在文件 发现问题 - '' 的战利品表配置错误 某个条目使用了无效的条目类型 ''"
warning.config.loot_table.entry.exp.missing_count: "在文件 发现问题 - '' 的战利品表配置错误 'exp' 条目缺少必需的 'count' 参数"
@@ -490,6 +491,7 @@ warning.config.function.when.missing_source: "在文件 发现
warning.config.function.if_else.missing_rules: "在文件 发现问题 - 配置项 '' 缺少 'if_else' 函数所需的 'rules' 参数"
warning.config.function.update_block_property.missing_properties: "在文件 发现问题 - 配置项 '' 缺少 'update_block_property' 函数所需的 'properties' 参数"
warning.config.function.transform_block.missing_block: "在文件 发现问题 - 配置项 '' 缺少 'transform_block' 函数所需的 'block' 参数"
+warning.config.function.exp.missing_count: "在文件 发现问题 - 配置项 '' 缺少 'exp' 函数所需的 'count' 参数"
warning.config.function.cycle_block_property.missing_property: "在文件 发现问题 - 配置项 '' 缺少 'cycle_block_property' 函数所需的 'property' 参数"
warning.config.selector.missing_type: "在文件 发现问题 - 配置项 '' 缺少选择器必需的 'type' 参数"
warning.config.selector.invalid_type: "在文件 发现问题 - 配置项 '' 使用了无效的选择器类型 ''"
@@ -509,4 +511,4 @@ warning.config.resource_pack.invalid_overlay_format: "在 config.yml 的
warning.config.equipment.duplicate: "在文件 发现问题 - 重复的装备配置 ''. 请检查其他文件中是否存在相同配置"
warning.config.equipment.missing_type: "在文件 发现问题 - 装备 '' 缺少必需的 'type' 参数"
warning.config.equipment.invalid_type: "在文件 发现问题 - 装备 '' 使用了无效的 'type' 参数"
-warning.config.equipment.invalid_sacrificed_armor: "在 config.yml 的 'equipment.sacrificed-vanilla-armor' 处发现问题 - 无效的原版盔甲类型 ''"
+warning.config.equipment.invalid_sacrificed_armor: "在 config.yml 的 'equipment.sacrificed-vanilla-armor' 处发现问题 - 无效的原版盔甲类型 ''"
\ No newline at end of file
diff --git a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java
index c8088ef4f..507def94d 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/entity/player/Player.java
@@ -184,6 +184,16 @@ public abstract class Player extends AbstractEntity implements NetWorkUser {
public abstract void setSelectedLocale(@Nullable Locale locale);
+ public abstract void giveExperiencePoints(int xpPoints);
+
+ public abstract void giveExperienceLevels(int levels);
+
+ public abstract int getXpNeededForNextLevel();
+
+ public abstract void setExperiencePoints(int experiencePoints);
+
+ public abstract void setExperienceLevels(int level);
+
@Override
public void remove() {
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
index 447ce05f0..15a23bcd1 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/event/EventFunctions.java
@@ -55,6 +55,7 @@ public class EventFunctions {
register(CommonFunctions.WHEN, new WhenFunction.FactoryImpl<>(EventConditions::fromMap, EventFunctions::fromMap));
register(CommonFunctions.DAMAGE_ITEM, new DamageItemFunction.FactoryImpl<>(EventConditions::fromMap));
register(CommonFunctions.CYCLE_BLOCK_PROPERTY, new CycleBlockPropertyFunction.FactoryImpl<>(EventConditions::fromMap));
+ register(CommonFunctions.EXP, new ExpFunction.FactoryImpl<>(EventConditions::fromMap));
}
public static void register(Key key, FunctionFactory factory) {
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
index bc638a23d..a8e0ee201 100644
--- a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/CommonFunctions.java
@@ -46,4 +46,5 @@ public final class CommonFunctions {
public static final Key DUMMY = Key.of("craftengine:dummy");
public static final Key DAMAGE_ITEM = Key.of("craftengine:damage_item");
public static final Key CYCLE_BLOCK_PROPERTY = Key.of("craftengine:cycle_block_property");
+ public static final Key EXP = Key.of("craftengine:exp");
}
diff --git a/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java
new file mode 100644
index 000000000..663d4618f
--- /dev/null
+++ b/core/src/main/java/net/momirealms/craftengine/core/plugin/context/function/ExpFunction.java
@@ -0,0 +1,65 @@
+package net.momirealms.craftengine.core.plugin.context.function;
+
+import net.momirealms.craftengine.core.entity.player.Player;
+import net.momirealms.craftengine.core.plugin.context.Condition;
+import net.momirealms.craftengine.core.plugin.context.Context;
+import net.momirealms.craftengine.core.plugin.context.number.NumberProvider;
+import net.momirealms.craftengine.core.plugin.context.number.NumberProviders;
+import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelector;
+import net.momirealms.craftengine.core.plugin.context.selector.PlayerSelectors;
+import net.momirealms.craftengine.core.util.Key;
+import net.momirealms.craftengine.core.util.ResourceConfigUtils;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiConsumer;
+
+public class ExpFunction extends AbstractConditionalFunction {
+ private final PlayerSelector selector;
+ private final NumberProvider count;
+ private final BiConsumer operation;
+
+ public ExpFunction(List> predicates, PlayerSelector selector, NumberProvider count, BiConsumer operation) {
+ super(predicates);
+ this.selector = selector;
+ this.count = count;
+ this.operation = operation;
+ }
+
+ @Override
+ protected void runInternal(CTX ctx) {
+ for (Player player : this.selector.get(ctx)) {
+ this.operation.accept(player, this.count.getInt(ctx));
+ }
+ }
+
+ @Override
+ public Key type() {
+ return CommonFunctions.EXP;
+ }
+
+ public static class FactoryImpl extends AbstractFactory {
+ private static final BiConsumer ADD_POINTS = Player::giveExperiencePoints;
+ private static final BiConsumer ADD_LEVELS = Player::giveExperienceLevels;
+ private static final BiConsumer SET_POINTS = (player, experience) -> {
+ if (experience < player.getXpNeededForNextLevel()) {
+ player.setExperiencePoints(experience);
+ }
+ };
+ private static final BiConsumer SET_LEVELS = Player::setExperienceLevels;
+
+ public FactoryImpl(java.util.function.Function