小更新,实现了动作传参,现在创建一个阵型起码50行json TvT

This commit is contained in:
2026-02-17 15:42:38 +08:00
parent 81be418af6
commit 7e1218b68b
27 changed files with 414 additions and 174 deletions

View File

@@ -1,6 +1,7 @@
TODO List: TODO List:
- [x] 修复已知bugnoclear不生效萤石不足的提示会出现两次 - [x] 修复已知bugnoclear不生效萤石不足的提示会出现两次
- [ ] 优化(匹配结构的方式) - [ ] 优化(匹配结构的方式)
- [ ] 为action的执行添加参数(ActionParam) - [x] 为action的执行添加参数(ActionParam)
- [ ] 增加更多好看的东西好不好玩另说awa - [ ] 增加更多好看的东西好不好玩另说awa
- [ ] 法术吟唱! - [ ] 法术吟唱!
- [ ] 尽可能不要动不动抛个错误出来

View File

@@ -1,21 +0,0 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.entity.Player;
/**
* 打雷动作
*/
public class ActionLightning implements IActionBase{
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller) {
location.getWorld().strikeLightning(location);
return true;
}
}

View File

@@ -0,0 +1,79 @@
package top.sunsetlab.actions;
import top.sunsetlab.PluginMain;
import java.util.ArrayList;
import java.util.HashMap;
public class ActionParam {
private HashMap<String, Object> data;
public ActionParam(HashMap<String, Object> data) {
this.data = data;
}
public Object get(String key) {
if (!hasKey(key)) {
return null;
}
return data.get(key);
}
public int getInt(String key) {
return getNumber(key).intValue();
}
public Number getNumber(String key) {
if (!(get(key) instanceof Number)) {
return -1;
}
return (Number) get(key);
}
public String getString(String key) {
if (!(get(key) instanceof String)) {
return "";
}
return (String) data.get(key);
}
public boolean getBoolean(String key) {
if (!(get(key) instanceof Boolean)) {
return false;
}
return (boolean) data.get(key);
}
public ArrayList<?> getList(String key) {
if (!(get(key) instanceof ArrayList)) {
return new ArrayList<>();
}
return (ArrayList<?>) data.get(key);
}
/**
* 获取一个整数列表
* 类型安全
* @param key 键
* @return 列表
*/
public ArrayList<Integer> getIntList(String key) {
ArrayList<?> list = getList(key);
ArrayList<Integer> result = new ArrayList<>();
// PluginMain.LOGGER.info("Parsing list " + list);
if (list.isEmpty()) {
return new ArrayList<>();
}
for (Object o : list) {
if (!(o instanceof Number)) {
return new ArrayList<>();
}
result.add(((Number) o).intValue());
}
return result;
}
public boolean hasKey(String key) {
return data.containsKey(key);
}
}

View File

@@ -16,11 +16,12 @@ public class CastDelay implements IActionBase{
* @return 是否成功 * @return 是否成功
*/ */
@Override @Override
public boolean call(Location location, Player caller) { public boolean call(Location location, Player caller, ActionParam data) {
int ticks = data.hasKey("ticks") ? data.getInt("ticks") : 20;
int count = 0; int count = 0;
while (count < 50) { while (count < ticks) {
try { try {
Thread.sleep(100); Thread.sleep(50);
}catch (InterruptedException ignored){} }catch (InterruptedException ignored){}
location.getWorld().spawnParticle( location.getWorld().spawnParticle(
Particle.DUST, Particle.DUST,

View File

@@ -1,7 +1,5 @@
package top.sunsetlab.actions; package top.sunsetlab.actions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextComponent;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.Material; import org.bukkit.Material;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
@@ -18,14 +16,15 @@ public class CheckGlowDust implements IActionBase {
* @return 是否成功 * @return 是否成功
*/ */
@Override @Override
public boolean call(Location location, Player caller) { public boolean call(Location location, Player caller, ActionParam data) {
if (PluginMain.DEBUG) { if (PluginMain.DEBUG) {
PluginMain.LOGGER.info("Checking amount of glow dust"); PluginMain.LOGGER.info("Checking amount of glow dust");
} }
int req_amount = data.hasKey("amount") ? data.getInt("amount") : 20;
if (caller.getInventory().getItemInMainHand().getType() == Material.GLOWSTONE_DUST if (caller.getInventory().getItemInMainHand().getType() == Material.GLOWSTONE_DUST
&& caller.getInventory().getItemInMainHand().getAmount() >= 20) { && caller.getInventory().getItemInMainHand().getAmount() >= req_amount) {
int amount = caller.getInventory().getItemInMainHand().getAmount(); int amount = caller.getInventory().getItemInMainHand().getAmount();
caller.getInventory().getItemInMainHand().setAmount(amount - 20); caller.getInventory().getItemInMainHand().setAmount(amount - req_amount);
return true; return true;
} }
caller.sendMessage(PluginMain.langUtils.translateC("starlight.message.glowdust_lack")); caller.sendMessage(PluginMain.langUtils.translateC("starlight.message.glowdust_lack"));

View File

@@ -2,6 +2,9 @@ package top.sunsetlab.actions;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain;
import java.util.logging.Level;
/** /**
* 动作基类 * 动作基类
@@ -13,5 +16,5 @@ public interface IActionBase {
* @param caller 动作执行者 * @param caller 动作执行者
* @return 是否成功 * @return 是否成功
*/ */
boolean call(Location location, Player caller); boolean call(Location location, Player caller, ActionParam data);
} }

View File

@@ -0,0 +1,53 @@
package top.sunsetlab.actions;
import org.bukkit.*;
import org.bukkit.block.BlockType;
import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain;
import java.util.ArrayList;
/**
* 放置方块(sync执行)
*/
public class PlaceBlock implements IActionBase {
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller, ActionParam data) {
if (!(data.hasKey("pos") && data.hasKey("id"))) {
PluginMain.LOGGER.warning("Failed to place block: unprovided blockpos and blockid");
return false;
}
ArrayList<Integer> pos = data.getIntList("pos");
// PluginMain.LOGGER.info(String.format("Placing block at %s", pos));
if (pos == null || pos.size() != 3) {
PluginMain.LOGGER.warning("Failed to place block: invalid blockpos");
return false;
}
Location loc = location.clone().add(
pos.get(0), pos.get(1), pos.get(2)
);
String id = data.getString("id");
if (id.isEmpty()) {
PluginMain.LOGGER.warning("Failed to place block: invalid blockid");
}
NamespacedKey key = NamespacedKey.fromString(id);
if (key == null) {
PluginMain.LOGGER.warning("Failed to place block: invalid blockid: " + id);
return false;
}
BlockType type = Registry.BLOCK.get(key);
if (type == null) {
PluginMain.LOGGER.warning("Failed to place block: unknown block: " + key);
return false;
}
location.getWorld().setBlockData(loc, type.createBlockData());
return true;
}
}

View File

@@ -1,36 +0,0 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.entity.Player;
/**
* 放置火焰
*/
public class PlaceFire implements IActionBase {
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller) {
placeFire(location.clone().add(1,0,0));
placeFire(location.clone().add(-1,0,0));
placeFire(location.clone().add(0,0,1));
placeFire(location.clone().add(0,0,-1));
placeFire(location.clone().add(0,0,0));
return true;
}
/**
* 在指定位置放置火焰
* @param location 位置
*/
private void placeFire(Location location) {
World world = location.getWorld();
world.setBlockData(location, Material.FIRE.createBlockData());
}
}

View File

@@ -1,22 +0,0 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
/**
* 向调用者播放铁砧的声音
*/
public class PlayAnvilSound implements IActionBase {
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller) {
caller.playSound(location, Sound.BLOCK_ANVIL_LAND, 1, 1);
return true;
}
}

View File

@@ -0,0 +1,43 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.Sound;
import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain;
/**
* 向调用者播放声音
*/
public class PlaySound implements IActionBase {
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller, ActionParam data) {
// caller.playSound(location, Sound.BLOCK_ANVIL_LAND, 1, 1);
if (!data.hasKey("id")) {
PluginMain.LOGGER.warning("Failed to play sound: unprovided id");
return false;
}
String id = data.getString("id");
float volume = data.hasKey("volume")?data.getNumber("volume").floatValue():1;
float pitch = data.hasKey("pitch")?data.getNumber("pitch").floatValue():1;
NamespacedKey key = NamespacedKey.fromString(id);
if (key == null) {
PluginMain.LOGGER.warning("Failed to play sound: invalid id");
return false;
}
Sound sound = Registry.SOUNDS.get(key);
if (sound == null) {
PluginMain.LOGGER.warning("Failed to play sound: unknown sound:" + id);
return false;
}
caller.playSound(location, sound, volume, pitch);
return true;
}
}

View File

@@ -0,0 +1,51 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.NamespacedKey;
import org.bukkit.Registry;
import org.bukkit.entity.EntityType;
import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain;
import java.util.ArrayList;
/**
* 生成生物
* sync动作
*/
public class SummonEntity implements IActionBase{
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller, ActionParam data) {
if (!(data.hasKey("pos") && data.hasKey("id"))) {
PluginMain.LOGGER.warning("Failed to spawn entity: pos or id undefined");
return false;
}
ArrayList<Integer> pos = data.getIntList("pos");
if (!(pos.size() ==3)) {
PluginMain.LOGGER.warning("Failed to spawn entity: invalid pos");
return false;
}
Location loc = location.clone().add(
pos.get(0),pos.get(1),pos.get(2)
);
String id = data.getString("id");
NamespacedKey key = NamespacedKey.fromString(id);
if (key == null) {
PluginMain.LOGGER.warning("Failed to spawn entity: invalid id");
return false;
}
EntityType type = Registry.ENTITY_TYPE.get(key);
if (type == null) {
PluginMain.LOGGER.warning("Failed to spawn entity: invalid entity type");
return false;
}
location.getWorld().spawnEntity(loc, type);
return true;
}
}

View File

@@ -1,35 +0,0 @@
package top.sunsetlab.actions;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.entity.Player;
/**
* 嬗变!(神奇炼金术)
*/
public class Transformation implements IActionBase {
/**
* 调用动作
* @param location 动作执行位置
* @param caller 动作执行者
* @return 是否成功
*/
@Override
public boolean call(Location location, Player caller) {
location.getWorld().setBlockData(
location.getBlockX(),
location.getBlockY(),
location.getBlockZ(),
Material.NETHERITE_BLOCK.createBlockData()
);
location.getWorld().setBlockData(
location.clone().subtract(1,0,0),
Material.AIR.createBlockData()
);
location.getWorld().setBlockData(
location.clone().add(1,0,0),
Material.AIR.createBlockData()
);
return true;
}
}

View File

@@ -10,6 +10,7 @@ import org.bukkit.event.block.Action;
import org.bukkit.event.player.PlayerInteractEvent; import org.bukkit.event.player.PlayerInteractEvent;
import top.sunsetlab.PluginMain; import top.sunsetlab.PluginMain;
import top.sunsetlab.utils.JsonStructure; import top.sunsetlab.utils.JsonStructure;
import top.sunsetlab.utils.StructureAction;
import java.util.ArrayList; import java.util.ArrayList;
@@ -53,18 +54,20 @@ public class EventListener implements Listener {
return; return;
} }
if (structure.getPreConditionTask() != null if (structure.getPreConditionTask() != null) {
&& !PluginMain.actionManager.call(structure.getPreConditionTask(), location, player)) { StructureAction structureAction = structure.getPreConditionTask();
if (!PluginMain.actionManager.call(structureAction.id, location, player, structureAction.param)) {
return; return;
} }
}
structure.breakStructure(location); structure.breakStructure(location);
// 异步执行动作 // 异步执行动作
Bukkit.getScheduler().runTaskAsynchronously(PluginMain.plugin, () -> { Bukkit.getScheduler().runTaskAsynchronously(PluginMain.plugin, () -> {
ArrayList<String> actions = structure.getActions(); ArrayList<StructureAction> actions = structure.getActions();
for (String action : actions) { for (StructureAction action : actions) {
PluginMain.actionManager.call(action, location, player); PluginMain.actionManager.call(action.id, location, player, action.param);
} }
}); });
event.setCancelled(true); event.setCancelled(true);

View File

@@ -4,6 +4,7 @@ import com.google.gson.Gson;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain; import top.sunsetlab.PluginMain;
import top.sunsetlab.actions.ActionParam;
import top.sunsetlab.actions.IActionBase; import top.sunsetlab.actions.IActionBase;
import java.io.BufferedReader; import java.io.BufferedReader;
@@ -57,12 +58,12 @@ public class ActionManager {
* @param caller 执行者 * @param caller 执行者
* @return 动作是否执行成功sync动作始终返回true * @return 动作是否执行成功sync动作始终返回true
*/ */
public boolean call(String actionId, Location location, Player caller) { public boolean call(String actionId, Location location, Player caller, ActionParam data) {
// 获取动作 // 获取动作
JsonAction action = actions.get(actionId); JsonAction action = actions.get(actionId);
if (action == null) { if (action == null) {
throw new RuntimeException("Invalid action id " + actionId); throw new RuntimeException("Invalid action id " + actionId);
} }
return action.run(location, caller); return action.run(location, caller, data);
} }
} }

View File

@@ -5,6 +5,7 @@ import org.bukkit.Bukkit;
import org.bukkit.Location; import org.bukkit.Location;
import org.bukkit.entity.Player; import org.bukkit.entity.Player;
import top.sunsetlab.PluginMain; import top.sunsetlab.PluginMain;
import top.sunsetlab.actions.ActionParam;
import top.sunsetlab.actions.IActionBase; import top.sunsetlab.actions.IActionBase;
import java.io.BufferedReader; import java.io.BufferedReader;
@@ -12,6 +13,7 @@ import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
class ActionData { class ActionData {
String id; String id;
@@ -45,7 +47,7 @@ public class JsonAction {
* @param caller 执行者 * @param caller 执行者
* @return 动作是否成功运行sync动作始终为true * @return 动作是否成功运行sync动作始终为true
*/ */
public boolean run(Location location, Player caller) { public boolean run(Location location, Player caller, ActionParam param) {
try { try {
// 通过反射加载动作类 // 通过反射加载动作类
Class<?> clazz = Class.forName(data.classname); Class<?> clazz = Class.forName(data.classname);
@@ -55,15 +57,17 @@ public class JsonAction {
// 获取构造器 // 获取构造器
Constructor<?> constructor = clazz.getConstructor(); Constructor<?> constructor = clazz.getConstructor();
IActionBase action = (IActionBase) constructor.newInstance(); IActionBase action = (IActionBase) constructor.newInstance();
ActionParam p = param == null ? new ActionParam(new HashMap<>()): param;
if (data.sync) { if (data.sync) {
// 在Minecraft线程内执行一般用于世界交互 // 在Minecraft线程内执行一般用于世界交互
Bukkit.getScheduler().runTask(PluginMain.plugin, () -> { Bukkit.getScheduler().runTask(PluginMain.plugin, () -> {
action.call(location, caller); action.call(location, caller, p);
}); });
return true; return true;
}else { }else {
// 异步执行 // 异步执行
return action.call(location, caller); return action.call(location, caller, p);
} }
}catch (ClassNotFoundException }catch (ClassNotFoundException
| NoSuchMethodException | NoSuchMethodException

View File

@@ -5,6 +5,7 @@ import org.bukkit.*;
import org.bukkit.block.BlockType; import org.bukkit.block.BlockType;
import org.jspecify.annotations.NonNull; import org.jspecify.annotations.NonNull;
import top.sunsetlab.PluginMain; import top.sunsetlab.PluginMain;
import top.sunsetlab.actions.ActionParam;
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStream;
@@ -19,8 +20,8 @@ class JsonStructureData {
HashMap<String,String> marks; HashMap<String,String> marks;
int[] center; int[] center;
String id; String id;
ArrayList<String> actions; ArrayList<StructureAction> actions;
String precondition_task; StructureAction precondition_task;
ArrayList<String> noclear; ArrayList<String> noclear;
} }
@@ -175,7 +176,7 @@ public class JsonStructure {
* 获取要执行的动作列表 * 获取要执行的动作列表
* @return 动作列表 * @return 动作列表
*/ */
public ArrayList<String> getActions() { public ArrayList<StructureAction> getActions() {
return data.actions; return data.actions;
} }
@@ -235,7 +236,7 @@ public class JsonStructure {
* 获取门控任务(仅当该任务成功时执行任务列表) * 获取门控任务(仅当该任务成功时执行任务列表)
* @return 门控任务id * @return 门控任务id
*/ */
public String getPreConditionTask() { public StructureAction getPreConditionTask() {
return data.precondition_task; return data.precondition_task;
} }
} }

View File

@@ -0,0 +1,8 @@
package top.sunsetlab.utils;
import top.sunsetlab.actions.ActionParam;
public class StructureAction {
public String id;
public ActionParam param;
}

View File

@@ -1,10 +1,9 @@
{ {
"locations": [ "locations": [
"actions/lightning.json", "actions/summon.json",
"actions/castdelay.json", "actions/castdelay.json",
"actions/checkglowdust.json", "actions/checkglowdust.json",
"actions/transformation.json", "actions/placeblock.json",
"actions/placefire.json", "actions/playsound.json"
"actions/playanvilsound.json"
] ]
} }

View File

@@ -1,5 +0,0 @@
{
"id": "lightning",
"classname": "top.sunsetlab.actions.ActionLightning",
"sync": true
}

View File

@@ -0,0 +1,5 @@
{
"id": "placeblock",
"classname": "top.sunsetlab.actions.PlaceBlock",
"sync": true
}

View File

@@ -1,5 +0,0 @@
{
"id": "placefire",
"classname": "top.sunsetlab.actions.PlaceFire",
"sync": true
}

View File

@@ -1,5 +0,0 @@
{
"id": "playanvilsound",
"classname": "top.sunsetlab.actions.PlayAnvilSound",
"sync": false
}

View File

@@ -0,0 +1,5 @@
{
"id": "playsound",
"classname": "top.sunsetlab.actions.PlaySound",
"sync": false
}

View File

@@ -0,0 +1,5 @@
{
"id": "summon",
"classname": "top.sunsetlab.actions.SummonEntity",
"sync": true
}

View File

@@ -1,5 +0,0 @@
{
"id": "transformation",
"classname": "top.sunsetlab.actions.Transformation",
"sync": true
}

View File

@@ -21,7 +21,30 @@
}, },
"center": [6,6], "center": [6,6],
"actions": [ "actions": [
"castdelay", {
"lightning" "id": "castdelay",
] "param": {
"data": {
"ticks": 60
}
}
},
{
"id": "summon",
"param": {
"data": {
"pos": [0,0,0],
"id": "minecraft:lightning_bolt"
}
}
}
],
"precondition_task": {
"id": "checkglowdust",
"param": {
"data": {
"amount": 3
}
}
}
} }

View File

@@ -17,14 +17,104 @@
}, },
"center": [3,3], "center": [3,3],
"actions": [ "actions": [
"placefire", {
"playanvilsound", "id": "placeblock",
"castdelay", "param": {
"playanvilsound", "data": {
"transformation", "id": "minecraft:fire",
"lightning" "pos": [0,0,0]
}
}
},
{
"id": "placeblock",
"param": {
"data": {
"id": "minecraft:fire",
"pos": [1,0,0]
}
}
},
{
"id": "placeblock",
"param": {
"data": {
"id": "minecraft:fire",
"pos": [-1,0,0]
}
}
},
{
"id": "placeblock",
"param": {
"data": {
"id": "minecraft:fire",
"pos": [0,0,1]
}
}
},
{
"id": "placeblock",
"param": {
"data": {
"id": "minecraft:fire",
"pos": [0,0,-1]
}
}
},
{
"id": "playsound",
"param": {
"data": {
"id": "minecraft:block.anvil.land"
}
}
},
{
"id": "castdelay",
"param": {
"data": {
"ticks": 250
}
}
},
{
"id": "playsound",
"param": {
"data": {
"id": "minecraft:block.anvil.land",
"volume": 1.0,
"pitch": 1.0
}
}
},
{
"id": "placeblock",
"param": {
"data": {
"pos": [0,0,0],
"id": "minecraft:netherite_block"
}
}
},
{
"id": "summon",
"param": {
"data": {
"pos": [0,0,0],
"id": "minecraft:lightning_bolt"
}
}
}
], ],
"precondition_task": "checkglowdust", "precondition_task": {
"id": "checkglowdust",
"param": {
"data": {
"amount": 20
}
}
},
"noclear": [ "noclear": [
"minecraft:diamond_block" "minecraft:diamond_block"
] ]