Merge pull request 'feat: 产物格里的拔刀剑可以设杀敌数与锻造数' (#1) from feature/slashblade-tag into 1.7.10
This commit was merged in pull request #1.
This commit is contained in:
commit
505f8aaab7
6 files changed
+207
No files matched your search
@@ -25,3 +25,14 @@ Modpacks & license
|
||||
------------------
|
||||
|
||||
As per usual this mod is open-source, you can see the license [here](LICENSE.txt)
|
||||
|
||||
## 拔刀剑(SlashBlade)杀敌数 / 锻造数
|
||||
|
||||
在 MTRM 的产物格(0 号)放一把拔刀剑,右侧「Slot Options」里勾选 **SlashBlade data (kills / refines)**,
|
||||
用两个滑条设定杀敌数(`killCount`)和锻造数(`RepairCounter`,范围 0–999999999)。生成的脚本形如:
|
||||
|
||||
```
|
||||
recipes.addShaped(<flammpfeil.slashblade:slashblade>.withTag({killCount: 1000, RepairCounter: 7}), [[...]]);
|
||||
```
|
||||
|
||||
不装 SlashBlade 时这个选项不出现,行为和原来一致。
|
||||
@@ -53,6 +53,14 @@ processResources {
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testCompile 'junit:junit:4.13.2'
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
from "LICENSE.txt"
|
||||
from sourceSets.main.allSource
|
||||
|
||||
Whitespace-only changes.
@@ -0,0 +1,67 @@
|
||||
package net.doubledoordev.mtrm;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* 拔刀剑(SlashBlade)的杀敌数、锻造数:配方产物写成 {@code .withTag({killCount: K, RepairCounter: R})}。
|
||||
* 不引用 SlashBlade 的类(模组可以不装),只靠类名和 NBT 键名;键名与 SlashBlade 的
|
||||
* {@code ItemSlashBlade.KillCount} / {@code RepairCount} 一致。
|
||||
*/
|
||||
public final class SlashBladeTag
|
||||
{
|
||||
public static final String BLADE_CLASS = "mods.flammpfeil.slashblade.ItemSlashBlade";
|
||||
public static final String KILL_KEY = "killCount";
|
||||
public static final String REFINE_KEY = "RepairCounter";
|
||||
/** SlashBlade 的 TagPropertyIntegerWithRange 上限 */
|
||||
public static final int MAX = 999999999;
|
||||
|
||||
public static final Pattern PATTERN = Pattern.compile("\\.withTag\\(\\{ ?" + KILL_KEY + ": ?(\\d+), ?" + REFINE_KEY + ": ?(\\d+) ?\\}\\)");
|
||||
|
||||
private SlashBladeTag()
|
||||
{
|
||||
}
|
||||
|
||||
/** 这个物品类是不是拔刀剑(含所有子类,沿父类链按名字找)。 */
|
||||
public static boolean isSlashBlade(Class<?> type)
|
||||
{
|
||||
for (Class<?> c = type; c != null; c = c.getSuperclass())
|
||||
{
|
||||
if (BLADE_CLASS.equals(c.getName())) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static int clamp(int value)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX, value));
|
||||
}
|
||||
|
||||
/** 两个数一起写,超出范围的按 SlashBlade 的上下限收住。 */
|
||||
public static String build(int killCount, int refineCount)
|
||||
{
|
||||
return ".withTag({" + KILL_KEY + ": " + clamp(killCount) + ", " + REFINE_KEY + ": " + clamp(refineCount) + "})";
|
||||
}
|
||||
|
||||
/** 从 token 里读回 {杀敌数, 锻造数};没有就返回 null。 */
|
||||
public static int[] parse(String token)
|
||||
{
|
||||
if (token == null) return null;
|
||||
Matcher m = PATTERN.matcher(token);
|
||||
if (!m.find()) return null;
|
||||
try
|
||||
{
|
||||
return new int[]{clamp(Integer.parseInt(m.group(1))), clamp(Integer.parseInt(m.group(2)))};
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
return null; // 超出 int
|
||||
}
|
||||
}
|
||||
|
||||
/** 把 token 里已有的拔刀剑标签换成新的;{@code tag} 为空串就是去掉。 */
|
||||
public static String replaceIn(String token, String tag)
|
||||
{
|
||||
return PATTERN.matcher(token).replaceFirst(Matcher.quoteReplacement(tag));
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import cpw.mods.fml.client.config.GuiSlider;
|
||||
import cpw.mods.fml.common.registry.GameData;
|
||||
import cpw.mods.fml.common.registry.GameRegistry;
|
||||
import net.doubledoordev.mtrm.MineTweakerRecipeMaker;
|
||||
import net.doubledoordev.mtrm.SlashBladeTag;
|
||||
import net.doubledoordev.mtrm.network.MessageSend;
|
||||
import net.minecraft.client.gui.GuiButton;
|
||||
import net.minecraft.client.gui.GuiTextField;
|
||||
@@ -68,6 +69,7 @@ public class MTRMGui extends GuiContainer
|
||||
private static final int ID_OPTION_RETURN_OK = 29;
|
||||
private static final int ID_OPTION_ID_OPTION_TRANSFORM_REPLACE = 30;
|
||||
private static final int ID_OPTION_GIVE_BACK = 31;
|
||||
private static final int ID_OPTION_SLASHBLADE = 32;
|
||||
|
||||
private final GuiSlider.ISlider iSlider = new GuiSlider.ISlider()
|
||||
{
|
||||
@@ -98,6 +100,9 @@ public class MTRMGui extends GuiContainer
|
||||
private GuiCheckBox noReturn;
|
||||
private GuiCheckBox transformDamage;
|
||||
private GuiCheckBox transformReplace;
|
||||
private GuiCheckBox slashBlade;
|
||||
private GuiSlider killCount;
|
||||
private GuiSlider refineCount;
|
||||
private GuiCheckBox giveBack;
|
||||
private GuiButtonExt returnOk;
|
||||
private Map<GuiCheckBox, GuiSlider[]> sliders = new HashMap<>();
|
||||
@@ -141,6 +146,7 @@ public class MTRMGui extends GuiContainer
|
||||
builder.append(stackName);
|
||||
if (!oreDict && (metaWildcard || stack.getItemDamage() != 0)) builder.append(':').append(metaWildcard || stack.getItemDamage() == OreDictionary.WILDCARD_VALUE ? "*" : stack.getItemDamage());
|
||||
builder.append('>');
|
||||
if (slashBladeTagApplies(stack)) builder.append(SlashBladeTag.build(killCount.getValueInt(), refineCount.getValueInt()));
|
||||
if (stack.stackSize > 1) builder.append(" * ").append(stack.stackSize);
|
||||
if (anyDamage.isChecked()) builder.append(".anyDamage()");
|
||||
if (onlyDamaged.isChecked()) builder.append(".onlyDamaged()");
|
||||
@@ -167,6 +173,23 @@ public class MTRMGui extends GuiContainer
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
/** 只有产物格(0)里放的是拔刀剑、并且勾了「拔刀剑数据」才写标签;输入格没有"改数据"这回事。 */
|
||||
private boolean slashBladeTagApplies(ItemStack stack)
|
||||
{
|
||||
return editing == 0 && slashBlade != null && slashBlade.isChecked() && stack != null && SlashBladeTag.isSlashBlade(stack.getItem().getClass());
|
||||
}
|
||||
|
||||
private void updateSlashBladeOptions(ItemStack stack, String token)
|
||||
{
|
||||
boolean blade = editing == 0 && stack != null && SlashBladeTag.isSlashBlade(stack.getItem().getClass());
|
||||
slashBlade.visible = blade;
|
||||
int[] values = blade ? SlashBladeTag.parse(token) : null;
|
||||
slashBlade.setIsChecked(values != null);
|
||||
killCount.setValue(values == null ? 0 : values[0]);
|
||||
refineCount.setValue(values == null ? 0 : values[1]);
|
||||
killCount.visible = refineCount.visible = blade && values != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initGui()
|
||||
{
|
||||
@@ -202,6 +225,10 @@ public class MTRMGui extends GuiContainer
|
||||
this.buttonList.add(reuse = new GuiCheckBox(ID_OPTION_REUSE, wOffset, hOffset += 10, "Reuse", false));
|
||||
this.buttonList.add(noReturn = new GuiCheckBox(ID_OPTION_NO_RETURN, wOffset, hOffset += 10, "No Return", false));
|
||||
this.buttonList.add(giveBack = new GuiCheckBox(ID_OPTION_GIVE_BACK, wOffset, hOffset += 10, "GiveBack", false));
|
||||
this.buttonList.add(slashBlade = new GuiCheckBox(ID_OPTION_SLASHBLADE, wOffset, hOffset += 10, "SlashBlade data (kills / refines)", false));
|
||||
this.buttonList.add(killCount = new GuiSlider(200, wOffset, hOffset + 50, 220 + this.xSize, 20, "Kill count = ", "", 0, SlashBladeTag.MAX, 0, false, true, iSlider));
|
||||
this.buttonList.add(refineCount = new GuiSlider(201, wOffset, hOffset + 70, 220 + this.xSize, 20, "Refine count = ", "", 0, SlashBladeTag.MAX, 0, false, true, iSlider));
|
||||
slashBlade.visible = killCount.visible = refineCount.visible = false;
|
||||
|
||||
this.buttonList.add(nextOreDict = new GuiButtonExt(ID_OPTION_NEXT_OREDICT, wOffset, hOffset += 40, 110, 20, "Next oredict value"));
|
||||
this.buttonList.add(optionsOk = new GuiButtonExt(ID_OPTION_OK, wOffset, hOffset += 20, 110, 20, "Save changes!"));
|
||||
@@ -350,6 +377,10 @@ public class MTRMGui extends GuiContainer
|
||||
radioBoxToggle((GuiCheckBox) btn, allTransform);
|
||||
tokenTxt.setText(getStackToken(false, inventorySlots.getSlot(editing).getStack()));
|
||||
break;
|
||||
case ID_OPTION_SLASHBLADE:
|
||||
killCount.visible = refineCount.visible = slashBlade.isChecked();
|
||||
tokenTxt.setText(getStackToken(false, inventorySlots.getSlot(editing).getStack()));
|
||||
break;
|
||||
case ID_OPTION_OREDICT:
|
||||
nextOreDict.enabled = oreDict.isChecked();
|
||||
case ID_OPTION_NEXT_OREDICT:
|
||||
@@ -475,6 +506,7 @@ public class MTRMGui extends GuiContainer
|
||||
}
|
||||
|
||||
setOptionsVisible(true);
|
||||
updateSlashBladeOptions(container.getSlot(id).getStack(), token);
|
||||
}
|
||||
|
||||
private void setOptionsVisible(boolean visible)
|
||||
@@ -500,6 +532,7 @@ public class MTRMGui extends GuiContainer
|
||||
}
|
||||
}
|
||||
|
||||
if (!visible) slashBlade.visible = killCount.visible = refineCount.visible = false;
|
||||
tokenTxt.setVisible(visible);
|
||||
|
||||
if (!visible) container.putStackInSlot(RETURN_SLOT_ID, null);
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
package net.doubledoordev.mtrm;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class SlashBladeTagTest
|
||||
{
|
||||
@Test
|
||||
public void buildsTheMineTweakerTag()
|
||||
{
|
||||
assertEquals(".withTag({killCount: 1000, RepairCounter: 7})", SlashBladeTag.build(1000, 7));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clampsToSlashBladesRange()
|
||||
{
|
||||
assertEquals(".withTag({killCount: 0, RepairCounter: 999999999})", SlashBladeTag.build(-5, Integer.MAX_VALUE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parsesWhatItBuilt()
|
||||
{
|
||||
assertArrayEquals(new int[]{1000, 7}, SlashBladeTag.parse("<flammpfeil.slashblade:slashblade>" + SlashBladeTag.build(1000, 7) + " * 1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void parseNeedsBothNumbers()
|
||||
{
|
||||
assertNull(SlashBladeTag.parse("<flammpfeil.slashblade:slashblade>"));
|
||||
assertNull(SlashBladeTag.parse("<a>.withTag({killCount: 3})"));
|
||||
assertNull(SlashBladeTag.parse(null));
|
||||
assertNull(SlashBladeTag.parse("<a>.withTag({killCount: 99999999999, RepairCounter: 1})"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void replaceSwapsOrRemovesTheTag()
|
||||
{
|
||||
String token = "<flammpfeil.slashblade:slashblade>" + SlashBladeTag.build(1, 2) + " * 1";
|
||||
assertEquals("<flammpfeil.slashblade:slashblade>.withTag({killCount: 5, RepairCounter: 6}) * 1", SlashBladeTag.replaceIn(token, SlashBladeTag.build(5, 6)));
|
||||
assertEquals("<flammpfeil.slashblade:slashblade> * 1", SlashBladeTag.replaceIn(token, ""));
|
||||
}
|
||||
|
||||
static class ItemSlashBlade
|
||||
{
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recognisesBladesByClassNameThroughSubclasses()
|
||||
{
|
||||
assertFalse(SlashBladeTag.isSlashBlade(Object.class));
|
||||
assertFalse(SlashBladeTag.isSlashBlade(ItemSlashBlade.class)); // 同名不同包不算
|
||||
assertFalse(SlashBladeTag.isSlashBlade(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void realSlashBladeClassIsRecognisedWithTheNbtKeysItUses() throws Exception
|
||||
{
|
||||
Class<?> blade = Class.forName(SlashBladeTag.BLADE_CLASS);
|
||||
assertTrue(SlashBladeTag.isSlashBlade(blade));
|
||||
Class<?> sub = Class.forName("mods.flammpfeil.slashblade.ItemSlashBladeNamed");
|
||||
assertTrue(SlashBladeTag.isSlashBlade(sub));
|
||||
Object kill = blade.getField("KillCount").get(null);
|
||||
Object refine = blade.getField("RepairCount").get(null);
|
||||
assertEquals(SlashBladeTag.KILL_KEY, nameOf(kill));
|
||||
assertEquals(SlashBladeTag.REFINE_KEY, nameOf(refine));
|
||||
}
|
||||
|
||||
private static String nameOf(Object accessor) throws Exception
|
||||
{
|
||||
for (Class<?> c = accessor.getClass(); c != null; c = c.getSuperclass())
|
||||
{
|
||||
for (java.lang.reflect.Field f : c.getDeclaredFields())
|
||||
{
|
||||
if (f.getType() == String.class)
|
||||
{
|
||||
f.setAccessible(true);
|
||||
return (String) f.get(accessor);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError("no name field");
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user