Модель предметов по базе ремейка: узлы, деревья, знание
Протокол: - ItemData читает таблицу items/items.csv (218 предметов ремейка) и отдает числа: урон, дальность, темп, прочность, сколько ресурса за удар. - ItemType: поля rid, researchScrap, techTreeBench, techTreeScrap, techTreeParent, admin. Урон по частям тела делится как в ремейке: голова вдвое, руки и ноги четыре пятых. Число в исходнике главнее таблицы. - ItemRegistry: 218 предметов с рецептами, ценой исследования и веткой дерева. Новые вещи: salvage-тесак, бронедвери, гаражи, взрывчатка, компоненты, еда, мусор, верстаки и печи. - NodeType: узлы с запасом (дерево 500/750/1000, камень 750, металл 500, сера 200) и меткой: крестик на дереве до двух, горячая точка на руде до трех. - TechTree: ветки верстаков с предками, 125/250/500 скрапа. - Новые пакеты: Dig, BreakProgress, CraftStart, CraftUpdate, ResearchStart, ResearchUpdate, TechTreeUnlock, KnownRecipes, TechTreeState, ContainerOpen, ContainerTake, LearnBlueprint. Сервер: - Knowledge: что игрок знает - рецепты и открытые узлы деревьев. - Crafter: проверка рецепта, уровня верстака и ингредиентов; время крафта по формуле плана. - ResearchTable: предмет плюс скрап, десять секунд, предмет превращается в чертёж; чертёж изучается и открывает рецепт. - ResourceNode: запас, метка, последний удар отдает остаток. - JunkPile: две-четыре бочки (серая - скрап и мусор, синяя - компонент, красная - нефть и топливо) и ящик с едой; пустая мусорка исчезает через 60-180 секунд. - RoadNetwork: сетка дорог и кольцо, точки спавна мусорки; генератор кладет асфальт и ровняет дорогу по ширине. - Проверка досягаемости вынесена из changeBlock, игрок получил benchLevel. - Сессия отвечает на новые пакеты, при входе шлет известные рецепты и открытые узлы. Тесты: протокол 24 + 10, сервер 74 (было 57). Форк собирается чисто.
This commit is contained in:
@@ -27,9 +27,12 @@ public final class Blocks {
|
||||
public static final byte WOOD = 6;
|
||||
public static final byte LEAVES = 7;
|
||||
|
||||
/** The surface of a road. */
|
||||
public static final byte ASPHALT = 8;
|
||||
|
||||
/** Lowest and highest legal block id. */
|
||||
public static final byte MIN = AIR;
|
||||
public static final byte MAX = LEAVES;
|
||||
public static final byte MAX = ASPHALT;
|
||||
|
||||
private Blocks() {
|
||||
}
|
||||
@@ -44,6 +47,7 @@ public final class Blocks {
|
||||
case WATER: return "water";
|
||||
case WOOD: return "wood";
|
||||
case LEAVES: return "leaves";
|
||||
case ASPHALT: return "asphalt";
|
||||
default: return "unknown(" + id + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,173 @@
|
||||
package net.rustnotmc.protocol;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* The numbers of every item, read from the table.
|
||||
*
|
||||
* The table is {@code items/items.csv} in the resources of this module. It was
|
||||
* unpacked from the item database of the remake, which keeps the damage, the
|
||||
* range, the tempo, the durability and the harvest of every item it knows.
|
||||
* Keeping it in a file rather than in the source means one place to look when
|
||||
* a number has to change, and one place to compare with the plan.
|
||||
*
|
||||
* The registry still decides what an item <i>is</i>: the id, the class of
|
||||
* tool, the recipe, what it costs to research. This class only fills in the
|
||||
* numbers.
|
||||
*/
|
||||
public final class ItemData {
|
||||
|
||||
/** One row of the table. */
|
||||
public static final class Row {
|
||||
|
||||
public final String rid;
|
||||
public final String name;
|
||||
public final String category;
|
||||
public final String material;
|
||||
|
||||
public final int damage;
|
||||
public final int range;
|
||||
public final int cooldown;
|
||||
public final int reach;
|
||||
public final int durability;
|
||||
public final int wood;
|
||||
public final int ore;
|
||||
public final int ammo;
|
||||
public final int weaponId;
|
||||
|
||||
Row(Map<String, String> cells) {
|
||||
this.rid = cells.get("rid");
|
||||
this.name = cells.get("name");
|
||||
this.category = cells.get("category");
|
||||
this.material = cells.get("material");
|
||||
this.damage = number(cells, "damage");
|
||||
this.range = number(cells, "range");
|
||||
this.cooldown = number(cells, "cooldown");
|
||||
this.reach = number(cells, "reach");
|
||||
this.durability = number(cells, "durability");
|
||||
this.wood = number(cells, "wood");
|
||||
this.ore = number(cells, "ore");
|
||||
this.ammo = number(cells, "ammo");
|
||||
this.weaponId = number(cells, "weapon_id");
|
||||
}
|
||||
|
||||
private static int number(Map<String, String> cells, String key) {
|
||||
String value = cells.get(key);
|
||||
if (value == null || value.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException ignored) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** True when the row says nothing about this number. */
|
||||
public boolean isEmpty() {
|
||||
return this.damage == 0 && this.cooldown == 0 && this.durability == 0
|
||||
&& this.wood == 0 && this.ore == 0;
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String, Row> ROWS = load();
|
||||
|
||||
private ItemData() {
|
||||
}
|
||||
|
||||
/** The row of an item, or null when the table does not know it. */
|
||||
public static Row byRid(String rid) {
|
||||
if (rid == null) {
|
||||
return null;
|
||||
}
|
||||
return ROWS.get(rid);
|
||||
}
|
||||
|
||||
/** Every row, by the Rust id of the item. */
|
||||
public static Map<String, Row> all() {
|
||||
return ROWS;
|
||||
}
|
||||
|
||||
/** How many items the table knows. */
|
||||
public static int size() {
|
||||
return ROWS.size();
|
||||
}
|
||||
|
||||
private static Map<String, Row> load() {
|
||||
Map<String, Row> rows = new HashMap<String, Row>();
|
||||
InputStream in = ItemData.class.getResourceAsStream("/items/items.csv");
|
||||
if (in == null) {
|
||||
return Collections.unmodifiableMap(rows);
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(
|
||||
new InputStreamReader(in, StandardCharsets.UTF_8));
|
||||
try {
|
||||
String header = reader.readLine();
|
||||
if (header == null) {
|
||||
return Collections.unmodifiableMap(rows);
|
||||
}
|
||||
String[] names = splitCsv(header);
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
if (line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
String[] cells = splitCsv(line);
|
||||
Map<String, String> map = new HashMap<String, String>();
|
||||
for (int i = 0; i < names.length && i < cells.length; i++) {
|
||||
map.put(names[i], cells[i]);
|
||||
}
|
||||
Row row = new Row(map);
|
||||
if (row.rid != null && !row.rid.isEmpty()) {
|
||||
rows.put(row.rid, row);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
reader.close();
|
||||
}
|
||||
} catch (IOException ignored) {
|
||||
// A table that cannot be read is an empty table: the registry
|
||||
// still works, it just has to carry the numbers itself.
|
||||
}
|
||||
return Collections.unmodifiableMap(rows);
|
||||
}
|
||||
|
||||
/** Splits a line of the table, honouring quotes. */
|
||||
private static String[] splitCsv(String line) {
|
||||
java.util.List<String> out = new java.util.ArrayList<String>();
|
||||
StringBuilder cell = new StringBuilder();
|
||||
boolean quoted = false;
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
char c = line.charAt(i);
|
||||
if (quoted) {
|
||||
if (c == '"') {
|
||||
if (i + 1 < line.length() && line.charAt(i + 1) == '"') {
|
||||
cell.append('"');
|
||||
i++;
|
||||
} else {
|
||||
quoted = false;
|
||||
}
|
||||
} else {
|
||||
cell.append(c);
|
||||
}
|
||||
} else if (c == '"') {
|
||||
quoted = true;
|
||||
} else if (c == ',') {
|
||||
out.add(cell.toString());
|
||||
cell.setLength(0);
|
||||
} else {
|
||||
cell.append(c);
|
||||
}
|
||||
}
|
||||
out.add(cell.toString());
|
||||
return out.toArray(new String[out.size()]);
|
||||
}
|
||||
}
|
||||
@@ -8,13 +8,11 @@ import java.util.Map;
|
||||
/**
|
||||
* Every item the game knows.
|
||||
*
|
||||
* The numbers are the ones from the original game at the state of November
|
||||
* 2025, and they are written here once so that the game, the plan and the
|
||||
* wiki cannot drift apart. A table copied by hand into two places drifts, and
|
||||
* then the game stops matching its own rules.
|
||||
*
|
||||
* The ids are ours and they are stable: they go into save files and into the
|
||||
* wire, so an id that ever changes is a bug.
|
||||
* wire, so an id that ever changes is a bug. The numbers come from the table
|
||||
* of {@link ItemData}, which was unpacked from the item database of the
|
||||
* remake, and the plan keeps the same numbers. A table copied by hand into
|
||||
* two places drifts, and then the game stops matching its own rules.
|
||||
*/
|
||||
public final class ItemRegistry {
|
||||
|
||||
@@ -47,6 +45,93 @@ public final class ItemRegistry {
|
||||
public static final int METAL_PIPE = 26;
|
||||
public static final int METAL_BLADE = 27;
|
||||
public static final int GEARS = 28;
|
||||
public static final int SALVAGED_CLEAVER = 29;
|
||||
public static final int GARAGE_DOOR = 30;
|
||||
public static final int ARMORED_DOOR = 31;
|
||||
public static final int ARMORED_DOUBLE_DOOR = 32;
|
||||
public static final int EXPLOSIVES = 33;
|
||||
public static final int TIMED_CHARGE = 34;
|
||||
|
||||
// The resources and the parts the junk pile gives out.
|
||||
public static final int CRUDE_OIL = 35;
|
||||
public static final int LOW_GRADE_FUEL = 36;
|
||||
public static final int METAL_SPRING = 37;
|
||||
public static final int SHEET_METAL = 38;
|
||||
public static final int ROAD_SIGNS = 39;
|
||||
public static final int ELECTRIC_FUSE = 40;
|
||||
public static final int ROPE = 41;
|
||||
public static final int TARP = 42;
|
||||
public static final int SEWING_KIT = 43;
|
||||
public static final int EMPTY_PROPANE_TANK = 44;
|
||||
public static final int SEMI_AUTO_BODY = 45;
|
||||
public static final int TECH_TRASH = 46;
|
||||
public static final int GUNPOWDER = 47;
|
||||
public static final int CHARCOAL = 48;
|
||||
public static final int CLOTH = 49;
|
||||
public static final int LEATHER = 50;
|
||||
public static final int PLANT_FIBER = 51;
|
||||
public static final int ANIMAL_FAT = 52;
|
||||
|
||||
// The food.
|
||||
public static final int APPLE = 53;
|
||||
public static final int BLUEBERRIES = 54;
|
||||
public static final int BLACK_RASPBERRIES = 55;
|
||||
public static final int POTATO = 56;
|
||||
public static final int CHOCOLATE_BAR = 57;
|
||||
public static final int CAN_OF_BEANS = 58;
|
||||
public static final int CAN_OF_TUNA = 59;
|
||||
public static final int GRANOLA_BAR = 60;
|
||||
public static final int CORN = 61;
|
||||
public static final int SMALL_WATER_BOTTLE = 62;
|
||||
public static final int WATER_JUG = 63;
|
||||
public static final int ANTI_RAD = 64;
|
||||
public static final int BANDAGE = 65;
|
||||
public static final int MEDICAL_SYRINGE = 66;
|
||||
public static final int LARGE_MEDKIT = 67;
|
||||
public static final int PUMPKIN = 68;
|
||||
public static final int MUSHROOM = 69;
|
||||
|
||||
// The rubbish of a junk pile: worn clothes and old rags.
|
||||
public static final int BANDANA_MASK = 70;
|
||||
public static final int TANK_TOP = 71;
|
||||
public static final int SHORTS = 72;
|
||||
public static final int T_SHIRT = 73;
|
||||
public static final int PANTS = 74;
|
||||
public static final int LEATHER_GLOVES = 75;
|
||||
public static final int BASEBALL_CAP = 76;
|
||||
public static final int BOONIE_HAT = 77;
|
||||
public static final int HIDE_PANTS = 78;
|
||||
public static final int HIDE_BOOTS = 79;
|
||||
public static final int HIDE_SKIRT = 80;
|
||||
public static final int HIDE_CHEST = 81;
|
||||
public static final int HIDE_PONCHO = 82;
|
||||
public static final int ROADSIGN_JACKET = 83;
|
||||
public static final int ROADSIGN_KILT = 84;
|
||||
public static final int ROADSIGN_GLOVES = 85;
|
||||
|
||||
// The places and the things in them.
|
||||
public static final int CAMPFIRE = 86;
|
||||
public static final int FURNACE = 87;
|
||||
public static final int SLEEPING_BAG = 88;
|
||||
public static final int RECYCLER = 89;
|
||||
public static final int WOOD_STORAGE_BOX = 90;
|
||||
public static final int LARGE_WOOD_BOX = 91;
|
||||
public static final int TOOL_CUPBOARD = 92;
|
||||
public static final int KEY_LOCK = 93;
|
||||
public static final int WOODEN_LADDER = 94;
|
||||
public static final int FLOOR_GRILL = 95;
|
||||
public static final int METAL_WINDOW_BARS = 96;
|
||||
public static final int WOODEN_WINDOW_BARS = 97;
|
||||
public static final int STRENGTHENED_GLASS_WINDOW = 98;
|
||||
public static final int REINFORCED_GLASS_WINDOW = 99;
|
||||
public static final int WOOD_DOUBLE_DOOR = 100;
|
||||
public static final int SHEETMETAL_DOOR = 101;
|
||||
public static final int SHEETMETAL_DOUBLE_DOOR = 102;
|
||||
public static final int HOMEMADE_LANDMINE = 103;
|
||||
public static final int SHOTGUN_TRAP = 104;
|
||||
public static final int SNAP_TRAP = 105;
|
||||
public static final int WOODEN_FLOOR_SPIKES = 106;
|
||||
public static final int TORCH = 107;
|
||||
|
||||
private static final Map<Integer, ItemType> TYPES = new LinkedHashMap<Integer, ItemType>();
|
||||
|
||||
@@ -57,144 +142,338 @@ public final class ItemRegistry {
|
||||
// The resources. They are not made, they are gathered, so they have no
|
||||
// recipe at all.
|
||||
define(ItemType.of(NOTHING, "Nothing").tool(ToolClass.NONE));
|
||||
define(ItemType.of(WOOD, "Wood"));
|
||||
define(ItemType.of(STONE, "Stones"));
|
||||
define(ItemType.of(METAL, "Metal Fragments"));
|
||||
define(ItemType.of(HIGH_QUALITY_METAL, "High Quality Metal"));
|
||||
define(ItemType.of(SULFUR, "Sulfur Ore"));
|
||||
define(ItemType.of(SCRAP, "Scrap"));
|
||||
define(ItemType.of(WOOD, "Wood").rid("wood"));
|
||||
define(ItemType.of(STONE, "Stones").rid("stone"));
|
||||
define(ItemType.of(METAL, "Metal Fragments").rid("metalfragment"));
|
||||
define(ItemType.of(HIGH_QUALITY_METAL, "High Quality Metal").rid("hqm"));
|
||||
define(ItemType.of(SULFUR, "Sulfur Ore").rid("sulfur_ore"));
|
||||
define(ItemType.of(SCRAP, "Scrap").rid("scrap"));
|
||||
// The fragments of the blueprints. The remake does not know them: they
|
||||
// come out of the loot crates, and the crates are still to come.
|
||||
define(ItemType.of(BASIC_FRAGMENT, "Basic Blueprint Fragment"));
|
||||
define(ItemType.of(ADVANCED_FRAGMENT, "Advanced Blueprint Fragment"));
|
||||
define(ItemType.of(METAL_PIPE, "Metal Pipe"));
|
||||
define(ItemType.of(METAL_BLADE, "Metal Blade"));
|
||||
define(ItemType.of(GEARS, "Gears"));
|
||||
define(ItemType.of(METAL_PIPE, "Metal Pipe").rid("metalpipe"));
|
||||
define(ItemType.of(METAL_BLADE, "Metal Blade").rid("metalblade"));
|
||||
define(ItemType.of(GEARS, "Gears").rid("gears"));
|
||||
define(ItemType.of(CRUDE_OIL, "Crude Oil").rid("crudeoil"));
|
||||
define(ItemType.of(LOW_GRADE_FUEL, "Low Grade Fuel").rid("lowgradefuel"));
|
||||
define(ItemType.of(METAL_SPRING, "Metal Spring").rid("metalspring"));
|
||||
define(ItemType.of(SHEET_METAL, "Sheet Metal").rid("sheetmetal"));
|
||||
define(ItemType.of(ROAD_SIGNS, "Road Signs").rid("roadsigns"));
|
||||
define(ItemType.of(ELECTRIC_FUSE, "Electric Fuse").rid("electricfuse"));
|
||||
define(ItemType.of(ROPE, "Rope").rid("rope"));
|
||||
define(ItemType.of(TARP, "Tarp").rid("tarp"));
|
||||
define(ItemType.of(SEWING_KIT, "Sewing Kit").rid("sewingkit"));
|
||||
define(ItemType.of(EMPTY_PROPANE_TANK, "Empty Propane Tank").rid("emptypropanetank"));
|
||||
define(ItemType.of(SEMI_AUTO_BODY, "Semi Automatic Body").rid("semiautomaticbody"));
|
||||
define(ItemType.of(TECH_TRASH, "Tech Trash").rid("techtrash"));
|
||||
define(ItemType.of(GUNPOWDER, "Gunpowder").rid("gunpowder"));
|
||||
define(ItemType.of(CHARCOAL, "Charcoal").rid("charcoal"));
|
||||
define(ItemType.of(CLOTH, "Cloth").rid("cloth"));
|
||||
define(ItemType.of(LEATHER, "Leather").rid("leather"));
|
||||
define(ItemType.of(PLANT_FIBER, "Plant Fiber").rid("plantfiber"));
|
||||
define(ItemType.of(ANIMAL_FAT, "Animal Fat").rid("animalfat"));
|
||||
|
||||
// The tools. Gather Damage, Bonus and Condition Loss are from the
|
||||
// official Gather Rates table; the damage is to the head, the torso,
|
||||
// the arms and the legs.
|
||||
// The food and the medicine.
|
||||
define(ItemType.of(APPLE, "Apple").rid("apple"));
|
||||
define(ItemType.of(BLUEBERRIES, "Blueberries").rid("blueberries"));
|
||||
define(ItemType.of(BLACK_RASPBERRIES, "Black Raspberries").rid("blackraspberries"));
|
||||
define(ItemType.of(POTATO, "Potato").rid("potato"));
|
||||
define(ItemType.of(CHOCOLATE_BAR, "Chocolate Bar").rid("chocolatebar"));
|
||||
define(ItemType.of(CAN_OF_BEANS, "Can of Beans").rid("canofbeans"));
|
||||
define(ItemType.of(CAN_OF_TUNA, "Can of Tuna").rid("canoftuna"));
|
||||
define(ItemType.of(GRANOLA_BAR, "Granola Bar").rid("granolabar"));
|
||||
define(ItemType.of(CORN, "Corn").rid("corn"));
|
||||
define(ItemType.of(SMALL_WATER_BOTTLE, "Small Water Bottle").rid("smallwaterbottle"));
|
||||
define(ItemType.of(WATER_JUG, "Water Jug").rid("waterjug"));
|
||||
define(ItemType.of(ANTI_RAD, "Anti Rad Pills").rid("antirad"));
|
||||
define(ItemType.of(BANDAGE, "Bandage").rid("bandage"));
|
||||
define(ItemType.of(MEDICAL_SYRINGE, "Medical Syringe").rid("medicalsyringe"));
|
||||
define(ItemType.of(LARGE_MEDKIT, "Large Medkit").rid("largemedkit"));
|
||||
define(ItemType.of(PUMPKIN, "Pumpkin").rid("pumpkin"));
|
||||
define(ItemType.of(MUSHROOM, "Mushroom").rid("mushroom"));
|
||||
|
||||
// The rubbish of a junk pile.
|
||||
define(ItemType.of(BANDANA_MASK, "Bandana Mask").rid("maskbandana"));
|
||||
define(ItemType.of(TANK_TOP, "Tank Top").rid("tanktop"));
|
||||
define(ItemType.of(SHORTS, "Shorts").rid("shorts"));
|
||||
define(ItemType.of(T_SHIRT, "T-Shirt").rid("tshirt"));
|
||||
define(ItemType.of(PANTS, "Pants").rid("pants"));
|
||||
define(ItemType.of(LEATHER_GLOVES, "Leather Gloves").rid("leathergloves"));
|
||||
define(ItemType.of(BASEBALL_CAP, "Baseball Cap").rid("baseballcap"));
|
||||
define(ItemType.of(BOONIE_HAT, "Boonie Hat").rid("booniehat"));
|
||||
define(ItemType.of(HIDE_PANTS, "Hide Pants").rid("hidepants"));
|
||||
define(ItemType.of(HIDE_BOOTS, "Hide Boots").rid("hideboots"));
|
||||
define(ItemType.of(HIDE_SKIRT, "Hide Skirt").rid("hideskirt"));
|
||||
define(ItemType.of(HIDE_CHEST, "Hide Chest").rid("hidechest"));
|
||||
define(ItemType.of(HIDE_PONCHO, "Hide Poncho").rid("hideponcho"));
|
||||
define(ItemType.of(ROADSIGN_JACKET, "Road Sign Jacket").rid("roadsignjacket"));
|
||||
define(ItemType.of(ROADSIGN_KILT, "Road Sign Kilt").rid("roadsignkilt"));
|
||||
define(ItemType.of(ROADSIGN_GLOVES, "Road Sign Gloves").rid("roadsigngloves"));
|
||||
|
||||
// The tools. The numbers of the gather and the damage are from the
|
||||
// table of the remake; what a tool is for is ours.
|
||||
define(ItemType.of(ROCK, "Rock")
|
||||
.rid("rock")
|
||||
.tool(ToolClass.NONE)
|
||||
.durability(100)
|
||||
.damage(20, 10, 8, 8)
|
||||
.damage(10)
|
||||
.bench(0)
|
||||
.known(true)
|
||||
.recipe(new Recipe(new int[]{STONE}, new int[]{10}, 5))
|
||||
.gather(ResourceClass.TREE, 10, 1.0F, 0.3F)
|
||||
.gather(ResourceClass.STONE, 5, 1.0F, 0.3F)
|
||||
.gather(ResourceClass.METAL, 5, 1.0F, 0.3F)
|
||||
.gather(ResourceClass.SULFUR, 5, 1.0F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 0.0F));
|
||||
|
||||
define(ItemType.of(STONE_HATCHET, "Stone Hatchet")
|
||||
.rid("stoneaxe")
|
||||
.tool(ToolClass.AXE)
|
||||
.durability(100)
|
||||
.damage(27, 15, 12, 12)
|
||||
.durability(160)
|
||||
.damage(15)
|
||||
.recipe(new Recipe(new int[]{WOOD, STONE}, new int[]{200, 100}, 15))
|
||||
.gather(ResourceClass.TREE, 20, 2.0F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 0.3F));
|
||||
|
||||
define(ItemType.of(STONE_PICKAXE, "Stone Pick Axe")
|
||||
define(ItemType.of(STONE_PICKAXE, "Stone Pickaxe")
|
||||
.rid("stonepickaxe")
|
||||
.tool(ToolClass.PICKAXE)
|
||||
.durability(100)
|
||||
.damage(35, 17, 13, 13)
|
||||
.durability(160)
|
||||
.damage(17)
|
||||
.recipe(new Recipe(new int[]{WOOD, STONE}, new int[]{200, 100}, 15))
|
||||
.gather(ResourceClass.STONE, 16, 3.2F, 0.3F)
|
||||
.gather(ResourceClass.METAL, 16, 3.2F, 0.3F)
|
||||
.gather(ResourceClass.SULFUR, 16, 3.2F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 1.0F));
|
||||
|
||||
define(ItemType.of(HAMMER, "Hammer")
|
||||
.rid("hammer")
|
||||
.tool(ToolClass.HAMMER)
|
||||
.durability(100)
|
||||
.damage(0, 0, 0, 0)
|
||||
.durability(300)
|
||||
.damage(0)
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{100}, 15)));
|
||||
|
||||
define(ItemType.of(TORCH, "Torch")
|
||||
.rid("torch")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{10}, 5)));
|
||||
|
||||
// The tools that have to be researched. The price is the one of the
|
||||
// research table, the tree is the one of the workbench.
|
||||
define(ItemType.of(HATCHET, "Hatchet")
|
||||
.rid("ironaxe")
|
||||
.tool(ToolClass.AXE)
|
||||
.durability(100)
|
||||
.damage(25, 25, 25, 25)
|
||||
.durability(300)
|
||||
.damage(25)
|
||||
.bench(1)
|
||||
.known(false)
|
||||
.research(30)
|
||||
.tree(1, TechTree.PRICE_L1, SALVAGED_HAMMER)
|
||||
.recipe(new Recipe(new int[]{WOOD, METAL}, new int[]{100, 75}, 30))
|
||||
.gather(ResourceClass.TREE, 30, 3.0F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 12, 1.2F, 1.0F));
|
||||
.gather(ResourceClass.FLESH, 12, 1.0F, 1.0F));
|
||||
|
||||
define(ItemType.of(PICKAXE, "Pickaxe")
|
||||
.rid("ironpickaxe")
|
||||
.tool(ToolClass.PICKAXE)
|
||||
.durability(100)
|
||||
.damage(30, 30, 30, 30)
|
||||
.durability(300)
|
||||
.damage(30)
|
||||
.bench(1)
|
||||
.known(false)
|
||||
.research(30)
|
||||
.tree(1, TechTree.PRICE_L1, SALVAGED_HAMMER)
|
||||
.recipe(new Recipe(new int[]{WOOD, METAL}, new int[]{100, 125}, 30))
|
||||
.gather(ResourceClass.STONE, 30, 6.0F, 1.0F)
|
||||
.gather(ResourceClass.METAL, 30, 6.0F, 1.0F)
|
||||
.gather(ResourceClass.SULFUR, 30, 6.0F, 1.0F)
|
||||
.gather(ResourceClass.FLESH, 5, 0.5F, 1.0F));
|
||||
.gather(ResourceClass.FLESH, 5, 1.0F, 1.0F));
|
||||
|
||||
define(ItemType.of(SALVAGED_HAMMER, "Salvaged Hammer")
|
||||
.rid("salvagedhammer")
|
||||
.tool(ToolClass.HAMMER)
|
||||
.durability(100)
|
||||
.damage(40, 40, 40, 40)
|
||||
.durability(400)
|
||||
.damage(30)
|
||||
.bench(1)
|
||||
.known(false)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL}, new int[]{1, 50}, 30))
|
||||
.gather(ResourceClass.TREE, 20, 2.0F, 0.5F)
|
||||
.gather(ResourceClass.STONE, 20, 4.0F, 0.5F)
|
||||
.gather(ResourceClass.METAL, 20, 4.0F, 0.5F)
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 0.5F));
|
||||
|
||||
define(ItemType.of(SALVAGED_AXE, "Salvaged Axe")
|
||||
.tool(ToolClass.AXE)
|
||||
.durability(100)
|
||||
.damage(40, 40, 40, 40)
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL_BLADE}, new int[]{1, 5}, 30))
|
||||
.gather(ResourceClass.TREE, 50, 5.0F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 15, 1.5F, 1.0F));
|
||||
|
||||
define(ItemType.of(SALVAGED_ICEPICK, "Salvaged Icepick")
|
||||
.tool(ToolClass.PICKAXE)
|
||||
.durability(100)
|
||||
.damage(40, 40, 40, 40)
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL_BLADE}, new int[]{1, 5}, 30))
|
||||
.gather(ResourceClass.STONE, 50, 10.0F, 0.3F)
|
||||
.gather(ResourceClass.METAL, 50, 10.0F, 0.3F)
|
||||
.gather(ResourceClass.SULFUR, 50, 10.0F, 0.3F)
|
||||
.gather(ResourceClass.FLESH, 5, 0.5F, 0.5F));
|
||||
.research(30)
|
||||
.tree(1, TechTree.PRICE_L1, NOTHING)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL}, new int[]{1, 50}, 30)));
|
||||
|
||||
define(ItemType.of(SALVAGED_SWORD, "Salvaged Sword")
|
||||
.rid("salvagedsword")
|
||||
.tool(ToolClass.SWORD)
|
||||
.durability(100)
|
||||
.damage(50, 50, 50, 50)
|
||||
.durability(300)
|
||||
.damage(50)
|
||||
.bench(1)
|
||||
.known(false)
|
||||
.research(15)
|
||||
.tree(1, TechTree.PRICE_L1, HATCHET)
|
||||
.recipe(new Recipe(new int[]{METAL_BLADE, METAL}, new int[]{1, 15}, 30))
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 0.5F));
|
||||
|
||||
// The deployables and the furniture. They are not tools, so they
|
||||
// gather nothing, and they are the things the players build with.
|
||||
define(ItemType.of(SALVAGED_CLEAVER, "Salvaged Cleaver")
|
||||
.rid("salvagedcleaver")
|
||||
.tool(ToolClass.SWORD)
|
||||
.durability(400)
|
||||
.damage(60)
|
||||
.bench(1)
|
||||
.known(false)
|
||||
.research(30)
|
||||
.tree(1, TechTree.PRICE_L1, HATCHET)
|
||||
.recipe(new Recipe(new int[]{ROAD_SIGNS, METAL}, new int[]{1, 50}, 30))
|
||||
.gather(ResourceClass.FLESH, 10, 1.0F, 0.5F));
|
||||
|
||||
define(ItemType.of(SALVAGED_AXE, "Salvaged Axe")
|
||||
.rid("salvagedaxe")
|
||||
.tool(ToolClass.AXE)
|
||||
.durability(1500)
|
||||
.damage(40)
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.research(60)
|
||||
.tree(2, TechTree.PRICE_L2, SALVAGED_HAMMER)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL_BLADE}, new int[]{1, 5}, 30)));
|
||||
|
||||
define(ItemType.of(SALVAGED_ICEPICK, "Salvaged Icepick")
|
||||
.rid("salvagedicepick")
|
||||
.tool(ToolClass.PICKAXE)
|
||||
.durability(1500)
|
||||
.damage(40)
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.research(60)
|
||||
.tree(2, TechTree.PRICE_L2, SALVAGED_HAMMER)
|
||||
.recipe(new Recipe(new int[]{METAL_PIPE, METAL_BLADE}, new int[]{1, 5}, 30)));
|
||||
|
||||
// The places and the things in them.
|
||||
define(ItemType.of(BUILDING_PLAN, "Building Plan")
|
||||
.rid("building_plan")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{20}, 15)));
|
||||
define(ItemType.of(WORKBENCH_1, "Work Bench Level 1")
|
||||
.rid("workbenchlevel1")
|
||||
.recipe(new Recipe(new int[]{WOOD, METAL}, new int[]{500, 100}, 30)));
|
||||
define(ItemType.of(WORKBENCH_2, "Work Bench Level 2")
|
||||
.rid("workbenchlevel2")
|
||||
.bench(1)
|
||||
.recipe(new Recipe(new int[]{METAL, HIGH_QUALITY_METAL, BASIC_FRAGMENT},
|
||||
new int[]{500, 20, 5}, 45)));
|
||||
define(ItemType.of(WORKBENCH_3, "Work Bench Level 3")
|
||||
.rid("workbenchlevel3")
|
||||
.bench(2)
|
||||
.recipe(new Recipe(new int[]{METAL, HIGH_QUALITY_METAL, ADVANCED_FRAGMENT},
|
||||
new int[]{1000, 100, 5}, 60)));
|
||||
define(ItemType.of(RESEARCH_TABLE, "Research Table")
|
||||
.rid("researchtable")
|
||||
.bench(1)
|
||||
.recipe(new Recipe(new int[]{METAL, SCRAP}, new int[]{200, 20}, 30)));
|
||||
define(ItemType.of(WOODEN_DOOR, "Wooden Door")
|
||||
define(ItemType.of(TOOL_CUPBOARD, "Tool Cupboard")
|
||||
.rid("toolcupboard")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{1000}, 30)));
|
||||
define(ItemType.of(CAMPFIRE, "Campfire")
|
||||
.rid("campfire")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{100}, 15)));
|
||||
define(ItemType.of(FURNACE, "Large Furnace")
|
||||
.rid("furnace")
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.research(60)
|
||||
.tree(2, TechTree.PRICE_L2, METAL_BLADE)
|
||||
.recipe(new Recipe(new int[]{WOOD, STONE, LOW_GRADE_FUEL},
|
||||
new int[]{600, 500, 75}, 40)));
|
||||
define(ItemType.of(RECYCLER, "Recycler")
|
||||
.rid("recycler")
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.research(60)
|
||||
.tree(2, TechTree.PRICE_L2, METAL_BLADE)
|
||||
.recipe(new Recipe(new int[]{METAL, METAL_PIPE, HIGH_QUALITY_METAL},
|
||||
new int[]{150, 20, 10}, 30)));
|
||||
define(ItemType.of(SLEEPING_BAG, "Sleeping Bag")
|
||||
.rid("sleepingbag")
|
||||
.recipe(new Recipe(new int[]{CLOTH, PLANT_FIBER}, new int[]{20, 20}, 15)));
|
||||
define(ItemType.of(WOOD_STORAGE_BOX, "Wood Storage Box")
|
||||
.rid("woodstoragebox")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{300}, 30)));
|
||||
define(ItemType.of(LARGE_WOOD_BOX, "Large Wood Box")
|
||||
.rid("largewoodbox")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{300}, 30)));
|
||||
define(ItemType.of(WOODEN_DOOR, "Wooden Door")
|
||||
.rid("woodendoor")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{300}, 30)));
|
||||
define(ItemType.of(WOOD_DOUBLE_DOOR, "Double Wooden Door")
|
||||
.rid("wooddoubledoor")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{350}, 30)));
|
||||
define(ItemType.of(SHEETMETAL_DOOR, "Sheet Metal Door")
|
||||
.rid("sheetmetaldoor")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{150}, 30)));
|
||||
define(ItemType.of(SHEETMETAL_DOUBLE_DOOR, "Double Sheet Metal Door")
|
||||
.rid("sheetmetaldoubledoor")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{200}, 30)));
|
||||
define(ItemType.of(ARMORED_DOOR, "Armored Door")
|
||||
.rid("armoreddoor")
|
||||
.bench(3)
|
||||
.known(false)
|
||||
.research(120)
|
||||
.tree(3, TechTree.PRICE_L3, METAL_PIPE)
|
||||
.recipe(new Recipe(new int[]{HIGH_QUALITY_METAL, GEARS}, new int[]{20, 5}, 30)));
|
||||
define(ItemType.of(ARMORED_DOUBLE_DOOR, "Double Armored Door")
|
||||
.rid("armoreddoubledoor")
|
||||
.bench(3)
|
||||
.known(false)
|
||||
.research(120)
|
||||
.tree(3, TechTree.PRICE_L3, METAL_PIPE)
|
||||
.recipe(new Recipe(new int[]{HIGH_QUALITY_METAL, GEARS}, new int[]{25, 5}, 30)));
|
||||
define(ItemType.of(GARAGE_DOOR, "Garage Door")
|
||||
.rid("garagedoor")
|
||||
.bench(2)
|
||||
.known(false)
|
||||
.research(30)
|
||||
.tree(2, TechTree.PRICE_L2, GEARS)
|
||||
.recipe(new Recipe(new int[]{METAL, GEARS}, new int[]{300, 2}, 30)));
|
||||
define(ItemType.of(CODE_LOCK, "Code Lock")
|
||||
.rid("codelock")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{100}, 30)));
|
||||
define(ItemType.of(KEY_LOCK, "Key Lock")
|
||||
.rid("keylock")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{75}, 15)));
|
||||
define(ItemType.of(WOODEN_LADDER, "Wooden Ladder")
|
||||
.rid("woodenladder")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{100}, 15)));
|
||||
define(ItemType.of(FLOOR_GRILL, "Floor Grilling")
|
||||
.rid("floorgrill")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{100}, 30)));
|
||||
define(ItemType.of(METAL_WINDOW_BARS, "Metal Window Bars")
|
||||
.rid("metalwindowbars")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{100}, 30)));
|
||||
define(ItemType.of(WOODEN_WINDOW_BARS, "Wooden Window Bars")
|
||||
.rid("woodenwindowbars")
|
||||
.recipe(new Recipe(new int[]{WOOD}, new int[]{100}, 15)));
|
||||
define(ItemType.of(STRENGTHENED_GLASS_WINDOW, "Strengthened Glass Window")
|
||||
.rid("strengthenedglasswindow")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{50}, 15)));
|
||||
define(ItemType.of(REINFORCED_GLASS_WINDOW, "Reinforced Glass Window")
|
||||
.rid("reinforcedglasswindow")
|
||||
.recipe(new Recipe(new int[]{METAL}, new int[]{100}, 15)));
|
||||
|
||||
// The traps.
|
||||
define(ItemType.of(HOMEMADE_LANDMINE, "Homemade Landmine")
|
||||
.rid("homemadelandmine")
|
||||
.recipe(new Recipe(new int[]{METAL, SCRAP}, new int[]{50, 10}, 15)));
|
||||
define(ItemType.of(SHOTGUN_TRAP, "Shotgun Trap")
|
||||
.rid("shotguntrap")
|
||||
.known(false)
|
||||
.research(60)
|
||||
.recipe(new Recipe(new int[]{METAL, GEARS}, new int[]{200, 10}, 30)));
|
||||
define(ItemType.of(SNAP_TRAP, "Snap Trap")
|
||||
.rid("snaptrap")
|
||||
.recipe(new Recipe(new int[]{WOOD, CLOTH}, new int[]{100, 10}, 15)));
|
||||
define(ItemType.of(WOODEN_FLOOR_SPIKES, "Wooden Floor Spikes")
|
||||
.rid("woodenfloorspikes")
|
||||
.recipe(new Recipe(new int[]{WOOD, METAL}, new int[]{200, 10}, 15)));
|
||||
|
||||
// The things that only an administrator may use.
|
||||
define(ItemType.of(EXPLOSIVES, "Explosives")
|
||||
.rid("explosives")
|
||||
.bench(3)
|
||||
.known(false)
|
||||
.research(120)
|
||||
.tree(3, TechTree.PRICE_L3, METAL_BLADE)
|
||||
.admin(true));
|
||||
define(ItemType.of(TIMED_CHARGE, "Timed Explosive Charge")
|
||||
.rid("timedexplosivecharge")
|
||||
.bench(3)
|
||||
.known(false)
|
||||
.research(120)
|
||||
.tree(3, TechTree.PRICE_L3, EXPLOSIVES)
|
||||
.admin(true));
|
||||
}
|
||||
|
||||
private static void define(ItemType.Builder builder) {
|
||||
|
||||
@@ -5,11 +5,13 @@ package net.rustnotmc.protocol;
|
||||
*
|
||||
* Everything about an item that the game needs to know and that never changes
|
||||
* is here: what class of tool it is, how much it hurts, how fast it gathers,
|
||||
* how long it lasts, and what it takes to make one.
|
||||
* how long it lasts, what it takes to make one and how it can be researched.
|
||||
*
|
||||
* The numbers are the ones from the original game, at the state of November
|
||||
* 2025, and they live in one place on purpose: a table that is copied by hand
|
||||
* into two places drifts, and then the game stops matching its own rules.
|
||||
* The numbers come from the table of {@link ItemData}, which was unpacked from
|
||||
* the item database of the remake. The registry decides what an item is; the
|
||||
* table says how much it does. A number that is written in the source wins
|
||||
* over the table, so a change in the table never silently changes a hand
|
||||
* written item.
|
||||
*/
|
||||
public final class ItemType {
|
||||
|
||||
@@ -38,6 +40,9 @@ public final class ItemType {
|
||||
public static final int ARMS = 2;
|
||||
public static final int LEGS = 3;
|
||||
|
||||
/** The id of the item in the remake, or null when it has none. */
|
||||
public final String rid;
|
||||
|
||||
public final int id;
|
||||
public final String name;
|
||||
public final ToolClass tool;
|
||||
@@ -60,17 +65,36 @@ public final class ItemType {
|
||||
/** True when only the administrators of the server may use it. */
|
||||
public final boolean admin;
|
||||
|
||||
/**
|
||||
* The scrap the research table wants for the blueprint of this item.
|
||||
* Zero means the item is not researched at all.
|
||||
*/
|
||||
public final int researchScrap;
|
||||
|
||||
/**
|
||||
* The workbench whose tree holds this item. Zero means it is in no tree.
|
||||
*/
|
||||
public final int techTreeBench;
|
||||
|
||||
/** The scrap the node of this item in the tree costs. */
|
||||
public final int techTreeScrap;
|
||||
|
||||
/** The item that has to be unlocked before this one. */
|
||||
public final int techTreeParent;
|
||||
|
||||
/** How to make one, or null when the item is not made but gathered. */
|
||||
public final Recipe recipe;
|
||||
|
||||
private final Gather[] gathers;
|
||||
|
||||
public ItemType(int id, String name, ToolClass tool, int harvestLevel, int durability,
|
||||
int[] damage, int bench, boolean knownByDefault, boolean admin,
|
||||
Recipe recipe, Gather[] gathers) {
|
||||
public ItemType(String rid, int id, String name, ToolClass tool, int harvestLevel,
|
||||
int durability, int[] damage, int bench, boolean knownByDefault,
|
||||
boolean admin, int researchScrap, int techTreeBench, int techTreeScrap,
|
||||
int techTreeParent, Recipe recipe, Gather[] gathers) {
|
||||
if (damage == null || damage.length != 4) {
|
||||
throw new IllegalArgumentException("the damage needs four body parts");
|
||||
}
|
||||
this.rid = rid;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.tool = tool;
|
||||
@@ -80,6 +104,10 @@ public final class ItemType {
|
||||
this.bench = bench;
|
||||
this.knownByDefault = knownByDefault;
|
||||
this.admin = admin;
|
||||
this.researchScrap = researchScrap;
|
||||
this.techTreeBench = techTreeBench;
|
||||
this.techTreeScrap = techTreeScrap;
|
||||
this.techTreeParent = techTreeParent;
|
||||
this.recipe = recipe;
|
||||
this.gathers = gathers == null ? new Gather[ResourceClass.values().length] : gathers;
|
||||
}
|
||||
@@ -100,6 +128,7 @@ public final class ItemType {
|
||||
|
||||
private final int id;
|
||||
private final String name;
|
||||
private String rid;
|
||||
private ToolClass tool = ToolClass.NONE;
|
||||
private int harvestLevel;
|
||||
private int durability;
|
||||
@@ -107,6 +136,10 @@ public final class ItemType {
|
||||
private int bench;
|
||||
private boolean knownByDefault = true;
|
||||
private boolean admin;
|
||||
private int researchScrap;
|
||||
private int techTreeBench;
|
||||
private int techTreeScrap;
|
||||
private int techTreeParent;
|
||||
private Recipe recipe;
|
||||
private final Gather[] gathers = new Gather[ResourceClass.values().length];
|
||||
|
||||
@@ -136,6 +169,17 @@ public final class ItemType {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The damage, split over the body the way the remake splits it: the
|
||||
* head takes twice as much as the torso, the arms and the legs four
|
||||
* fifths of it.
|
||||
*/
|
||||
public Builder damage(int torso) {
|
||||
this.damage = new int[]{torso * 2, torso, Math.round(torso * 0.8f),
|
||||
Math.round(torso * 0.8f)};
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder bench(int bench) {
|
||||
this.bench = bench;
|
||||
return this;
|
||||
@@ -153,6 +197,20 @@ public final class ItemType {
|
||||
return this;
|
||||
}
|
||||
|
||||
/** The scrap the research table wants for this item. */
|
||||
public Builder research(int scrap) {
|
||||
this.researchScrap = scrap;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** The node of this item in the tree of a workbench. */
|
||||
public Builder tree(int bench, int scrap, int parent) {
|
||||
this.techTreeBench = bench;
|
||||
this.techTreeScrap = scrap;
|
||||
this.techTreeParent = parent;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder recipe(Recipe recipe) {
|
||||
this.recipe = recipe;
|
||||
return this;
|
||||
@@ -165,10 +223,42 @@ public final class ItemType {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fills the numbers of the item from the table.
|
||||
*
|
||||
* The id of the remake is the key: the table has one row per item, and
|
||||
* the row says how much the item hurts, how long it lasts and how much
|
||||
* it takes out of a tree or out of a node. Numbers that were written
|
||||
* in the source are kept.
|
||||
*/
|
||||
public Builder rid(String rid) {
|
||||
this.rid = rid;
|
||||
ItemData.Row row = ItemData.byRid(rid);
|
||||
if (row == null) {
|
||||
throw new IllegalArgumentException("the table has no item " + rid);
|
||||
}
|
||||
if (this.damage[HEAD] == 0 && this.damage[TORSO] == 0 && row.damage > 0) {
|
||||
damage(row.damage);
|
||||
}
|
||||
if (this.durability == 0 && row.durability > 0) {
|
||||
this.durability = row.durability;
|
||||
}
|
||||
if (row.wood > 0 && this.gathers[ResourceClass.TREE.ordinal()] == null) {
|
||||
gather(ResourceClass.TREE, row.wood, 1.0F, 0.3F);
|
||||
}
|
||||
if (row.ore > 0 && this.gathers[ResourceClass.STONE.ordinal()] == null) {
|
||||
gather(ResourceClass.STONE, row.ore, 1.0F, 0.3F);
|
||||
gather(ResourceClass.METAL, row.ore, 1.0F, 0.3F);
|
||||
gather(ResourceClass.SULFUR, row.ore, 1.0F, 0.3F);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public ItemType build() {
|
||||
return new ItemType(this.id, this.name, this.tool, this.harvestLevel,
|
||||
return new ItemType(this.rid, this.id, this.name, this.tool, this.harvestLevel,
|
||||
this.durability, this.damage, this.bench, this.knownByDefault,
|
||||
this.admin, this.recipe, this.gathers);
|
||||
this.admin, this.researchScrap, this.techTreeBench, this.techTreeScrap,
|
||||
this.techTreeParent, this.recipe, this.gathers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +282,16 @@ public final class ItemType {
|
||||
return this.damage[bodyPart];
|
||||
}
|
||||
|
||||
/** True when the item has to be researched before it can be made. */
|
||||
public boolean needsResearch() {
|
||||
return !this.knownByDefault && this.researchScrap > 0;
|
||||
}
|
||||
|
||||
/** True when the item sits in a tree of a workbench. */
|
||||
public boolean inTechTree() {
|
||||
return this.techTreeBench > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ItemType[" + this.id + " " + this.name + "]";
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package net.rustnotmc.protocol;
|
||||
|
||||
/**
|
||||
* A thing in the world that can be worn down: a tree, a stone outcrop, a
|
||||
* metal deposit.
|
||||
*
|
||||
* A node is not a block. It has a <b>pool</b> of resource in it, and the pool
|
||||
* is also its health: every swing takes some of it out, and when the pool is
|
||||
* empty the node is gone. The last swing gives whatever is left, so a node
|
||||
* always gives exactly its pool.
|
||||
*
|
||||
* A node can also have a <b>mark</b>: a cross on a tree, a hot spot on an
|
||||
* outcrop. A swing that hits the mark does more, and every swing that follows
|
||||
* it does a little more still, up to a limit. Missing the mark resets the
|
||||
* bonus.
|
||||
*/
|
||||
public final class NodeType {
|
||||
|
||||
/** The id of nothing. */
|
||||
public static final int NOTHING = 0;
|
||||
|
||||
/** A poor tree: 500 wood. */
|
||||
public static final int TREE_POOR = 1;
|
||||
|
||||
/** An ordinary tree: 750 wood. */
|
||||
public static final int TREE_COMMON = 2;
|
||||
|
||||
/** A dead tree: 1000 wood. */
|
||||
public static final int TREE_DEAD = 3;
|
||||
|
||||
/** A stone outcrop: 750 stones. */
|
||||
public static final int STONE = 4;
|
||||
|
||||
/** A metal deposit: 500 metal. */
|
||||
public static final int METAL = 5;
|
||||
|
||||
/** A sulfur deposit: 200 sulfur. */
|
||||
public static final int SULFUR = 6;
|
||||
|
||||
public final int id;
|
||||
public final String name;
|
||||
public final ResourceClass resource;
|
||||
|
||||
/** What the node gives when it is worn out. */
|
||||
public final int dropItem;
|
||||
|
||||
/** How much is in it. It is also the number of points of health. */
|
||||
public final int pool;
|
||||
|
||||
/** How much a marked swing adds to the damage, again and again. */
|
||||
public final float markerStep;
|
||||
|
||||
/** The limit of the mark. One means the node has no mark at all. */
|
||||
public final float markerMax;
|
||||
|
||||
public NodeType(int id, String name, ResourceClass resource, int dropItem, int pool,
|
||||
float markerStep, float markerMax) {
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
this.resource = resource;
|
||||
this.dropItem = dropItem;
|
||||
this.pool = pool;
|
||||
this.markerStep = markerStep;
|
||||
this.markerMax = markerMax;
|
||||
}
|
||||
|
||||
/** True when the node has a mark that a swing can hit. */
|
||||
public boolean hasMarker() {
|
||||
return this.markerMax > 1.0F;
|
||||
}
|
||||
|
||||
/**
|
||||
* The damage of a swing, with the mark taken into account.
|
||||
*
|
||||
* @param damage what the tool takes without a mark
|
||||
* @param marked how many marked swings in a row there were
|
||||
*/
|
||||
public int swingDamage(int damage, int marked) {
|
||||
if (!hasMarker() || marked <= 0) {
|
||||
return damage;
|
||||
}
|
||||
float multiplier = 1.0F + this.markerStep * marked;
|
||||
if (multiplier > this.markerMax) {
|
||||
multiplier = this.markerMax;
|
||||
}
|
||||
return Math.round(damage * multiplier);
|
||||
}
|
||||
|
||||
private static final java.util.List<NodeType> TYPES = new java.util.ArrayList<NodeType>();
|
||||
|
||||
static {
|
||||
add(new NodeType(NOTHING, "Nothing", ResourceClass.NONE, ItemRegistry.NOTHING, 0,
|
||||
1.0F, 1.0F));
|
||||
// A tree: the mark is a cross on the trunk, and it gives at most twice.
|
||||
add(new NodeType(TREE_POOR, "Poor Tree", ResourceClass.TREE, ItemRegistry.WOOD, 500,
|
||||
0.125F, 2.0F));
|
||||
add(new NodeType(TREE_COMMON, "Tree", ResourceClass.TREE, ItemRegistry.WOOD, 750,
|
||||
0.125F, 2.0F));
|
||||
add(new NodeType(TREE_DEAD, "Dead Tree", ResourceClass.TREE, ItemRegistry.WOOD, 1000,
|
||||
0.125F, 2.0F));
|
||||
// An outcrop: the mark is a hot spot, and it gives at most three times.
|
||||
add(new NodeType(STONE, "Stone Outcrop", ResourceClass.STONE, ItemRegistry.STONE, 750,
|
||||
0.5F, 3.0F));
|
||||
add(new NodeType(METAL, "Metal Deposit", ResourceClass.METAL, ItemRegistry.METAL, 500,
|
||||
0.5F, 3.0F));
|
||||
add(new NodeType(SULFUR, "Sulfur Deposit", ResourceClass.SULFUR, ItemRegistry.SULFUR, 200,
|
||||
0.5F, 3.0F));
|
||||
}
|
||||
|
||||
private static void add(NodeType type) {
|
||||
TYPES.add(type);
|
||||
}
|
||||
|
||||
/** The node with that id, or null. */
|
||||
public static NodeType byId(int id) {
|
||||
for (int i = 0; i < TYPES.size(); i++) {
|
||||
if (TYPES.get(i).id == id) {
|
||||
return TYPES.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Every node type. */
|
||||
public static java.util.List<NodeType> all() {
|
||||
return java.util.Collections.unmodifiableList(TYPES);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "NodeType[" + this.id + " " + this.name + " " + this.pool + "]";
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,42 @@ public final class Packets {
|
||||
/** One slot of the inventory, when it changes. */
|
||||
public static final int INVENTORY_SLOT = 0x1D;
|
||||
|
||||
/** The client starts or stops digging. */
|
||||
public static final int DIG = 0x1E;
|
||||
|
||||
/** How far the digging has come, for the cracks. */
|
||||
public static final int BREAK_PROGRESS = 0x1F;
|
||||
|
||||
/** The client asks to make something. */
|
||||
public static final int CRAFT_START = 0x20;
|
||||
|
||||
/** What the server says about a craft. */
|
||||
public static final int CRAFT_UPDATE = 0x21;
|
||||
|
||||
/** The client asks to research an item at the research table. */
|
||||
public static final int RESEARCH_START = 0x22;
|
||||
|
||||
/** What the server says about a research. */
|
||||
public static final int RESEARCH_UPDATE = 0x23;
|
||||
|
||||
/** The client asks to open a node of the tree of a workbench. */
|
||||
public static final int TECH_TREE_UNLOCK = 0x24;
|
||||
|
||||
/** The recipes the player knows. */
|
||||
public static final int KNOWN_RECIPES = 0x25;
|
||||
|
||||
/** The nodes of the trees the player has opened. */
|
||||
public static final int TECH_TREE_STATE = 0x26;
|
||||
|
||||
/** A container the player may look into. */
|
||||
public static final int CONTAINER_OPEN = 0x27;
|
||||
|
||||
/** The client takes something out of a container. */
|
||||
public static final int CONTAINER_TAKE = 0x28;
|
||||
|
||||
/** The client learns a blueprint. */
|
||||
public static final int LEARN_BLUEPRINT = 0x29;
|
||||
|
||||
/**
|
||||
* One slot of the inventory and what is in it now.
|
||||
*
|
||||
@@ -948,6 +984,394 @@ public final class Packets {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client starts or stops digging.
|
||||
*
|
||||
* The client does not decide anything: it says where it is looking and
|
||||
* whether the button is down. The server works out what happens there,
|
||||
* how long it takes and who is allowed to do it.
|
||||
*/
|
||||
public static final class Dig implements Packet {
|
||||
|
||||
/** The button went down. */
|
||||
public static final int START = 0;
|
||||
|
||||
/** The button came up. */
|
||||
public static final int STOP = 1;
|
||||
|
||||
public final int action;
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
public final int face;
|
||||
|
||||
public Dig(int action, int x, int y, int z, int face) {
|
||||
this.action = action;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.face = face;
|
||||
}
|
||||
|
||||
public boolean start() {
|
||||
return this.action == START;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(24);
|
||||
buf.writeVarInt(this.action);
|
||||
buf.writeVarInt(this.x);
|
||||
buf.writeVarInt(this.y);
|
||||
buf.writeVarInt(this.z);
|
||||
buf.writeVarInt(this.face);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static Dig read(Buf in) {
|
||||
return new Dig(in.readVarInt(), in.readVarInt(), in.readVarInt(),
|
||||
in.readVarInt(), in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* How far the digging has come, so that everybody sees the cracks.
|
||||
*
|
||||
* The stage goes from zero to nine. A stage of minus one means that
|
||||
* nothing is being dug at that place.
|
||||
*/
|
||||
public static final class BreakProgress implements Packet {
|
||||
|
||||
/** The stage of the cracks, from zero to nine. */
|
||||
public static final int STAGES = 10;
|
||||
|
||||
public final int x;
|
||||
public final int y;
|
||||
public final int z;
|
||||
public final int stage;
|
||||
|
||||
public BreakProgress(int x, int y, int z, int stage) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
this.stage = stage;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(24);
|
||||
buf.writeVarInt(this.x);
|
||||
buf.writeVarInt(this.y);
|
||||
buf.writeVarInt(this.z);
|
||||
buf.writeVarInt(this.stage);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static BreakProgress read(Buf in) {
|
||||
return new BreakProgress(in.readVarInt(), in.readVarInt(), in.readVarInt(),
|
||||
in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client asks to make something.
|
||||
*
|
||||
* The server looks the recipe up, checks the workbench and the knowledge
|
||||
* of the player, and answers with a {@link CraftUpdate}.
|
||||
*/
|
||||
public static final class CraftStart implements Packet {
|
||||
|
||||
public final int item;
|
||||
public final int count;
|
||||
|
||||
public CraftStart(int item, int count) {
|
||||
this.item = item;
|
||||
this.count = count;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8);
|
||||
buf.writeVarInt(this.item);
|
||||
buf.writeVarInt(this.count);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static CraftStart read(Buf in) {
|
||||
return new CraftStart(in.readVarInt(), in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What the server says about a craft.
|
||||
*
|
||||
* The seconds left go down while the work goes on. When it reaches zero
|
||||
* the item is in the inventory. A reason that is not empty means that
|
||||
* nothing was made, and says why.
|
||||
*/
|
||||
public static final class CraftUpdate implements Packet {
|
||||
|
||||
public final int item;
|
||||
public final int count;
|
||||
public final int secondsLeft;
|
||||
public final String reason;
|
||||
|
||||
public CraftUpdate(int item, int count, int secondsLeft, String reason) {
|
||||
this.item = item;
|
||||
this.count = count;
|
||||
this.secondsLeft = secondsLeft;
|
||||
this.reason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
public boolean ok() {
|
||||
return this.reason.isEmpty();
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(64);
|
||||
buf.writeVarInt(this.item);
|
||||
buf.writeVarInt(this.count);
|
||||
buf.writeVarInt(this.secondsLeft);
|
||||
buf.writeString(this.reason);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static CraftUpdate read(Buf in) {
|
||||
return new CraftUpdate(in.readVarInt(), in.readVarInt(), in.readVarInt(),
|
||||
in.readString(MAX_TEXT));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client asks to research an item at the research table.
|
||||
*
|
||||
* The server checks that the player has the item and the scrap, takes
|
||||
* both and answers with a {@link ResearchUpdate}.
|
||||
*/
|
||||
public static final class ResearchStart implements Packet {
|
||||
|
||||
public final int item;
|
||||
|
||||
public ResearchStart(int item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8);
|
||||
buf.writeVarInt(this.item);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ResearchStart read(Buf in) {
|
||||
return new ResearchStart(in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/** What the server says about a research. */
|
||||
public static final class ResearchUpdate implements Packet {
|
||||
|
||||
public final int item;
|
||||
public final int secondsLeft;
|
||||
public final String reason;
|
||||
|
||||
public ResearchUpdate(int item, int secondsLeft, String reason) {
|
||||
this.item = item;
|
||||
this.secondsLeft = secondsLeft;
|
||||
this.reason = reason == null ? "" : reason;
|
||||
}
|
||||
|
||||
public boolean ok() {
|
||||
return this.reason.isEmpty();
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(64);
|
||||
buf.writeVarInt(this.item);
|
||||
buf.writeVarInt(this.secondsLeft);
|
||||
buf.writeString(this.reason);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ResearchUpdate read(Buf in) {
|
||||
return new ResearchUpdate(in.readVarInt(), in.readVarInt(), in.readString(MAX_TEXT));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client asks to open a node of the tree of a workbench.
|
||||
*
|
||||
* The server checks that the branch is open and that there is enough
|
||||
* scrap, and takes the scrap.
|
||||
*/
|
||||
public static final class TechTreeUnlock implements Packet {
|
||||
|
||||
public final int item;
|
||||
|
||||
public TechTreeUnlock(int item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8);
|
||||
buf.writeVarInt(this.item);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static TechTreeUnlock read(Buf in) {
|
||||
return new TechTreeUnlock(in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/** The recipes the player knows. */
|
||||
public static final class KnownRecipes implements Packet {
|
||||
|
||||
public final int[] items;
|
||||
|
||||
public KnownRecipes(int[] items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8 + this.items.length * 4);
|
||||
buf.writeVarInt(this.items.length);
|
||||
for (int i = 0; i < this.items.length; i++) {
|
||||
buf.writeVarInt(this.items[i]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static KnownRecipes read(Buf in) {
|
||||
int count = in.readVarInt();
|
||||
if (count < 0 || count > 4096) {
|
||||
throw new ProtocolException("that many recipes is not real");
|
||||
}
|
||||
int[] items = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
items[i] = in.readVarInt();
|
||||
}
|
||||
return new KnownRecipes(items);
|
||||
}
|
||||
}
|
||||
|
||||
/** The nodes of the trees the player has opened. */
|
||||
public static final class TechTreeState implements Packet {
|
||||
|
||||
public final int[] items;
|
||||
|
||||
public TechTreeState(int[] items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8 + this.items.length * 4);
|
||||
buf.writeVarInt(this.items.length);
|
||||
for (int i = 0; i < this.items.length; i++) {
|
||||
buf.writeVarInt(this.items[i]);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static TechTreeState read(Buf in) {
|
||||
int count = in.readVarInt();
|
||||
if (count < 0 || count > 4096) {
|
||||
throw new ProtocolException("that many nodes is not real");
|
||||
}
|
||||
int[] items = new int[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
items[i] = in.readVarInt();
|
||||
}
|
||||
return new TechTreeState(items);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A container the player may look into: a crate of food on a junk pile,
|
||||
* a chest, a barrel.
|
||||
*/
|
||||
public static final class ContainerOpen implements Packet {
|
||||
|
||||
public final int id;
|
||||
public final String title;
|
||||
public final Slot[] slots;
|
||||
|
||||
public ContainerOpen(int id, String title, Slot[] slots) {
|
||||
this.id = id;
|
||||
this.title = title == null ? "" : title;
|
||||
this.slots = slots;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(64 + this.slots.length * 12);
|
||||
buf.writeVarInt(this.id);
|
||||
buf.writeString(this.title);
|
||||
buf.writeVarInt(this.slots.length);
|
||||
for (int i = 0; i < this.slots.length; i++) {
|
||||
Slot slot = this.slots[i] == null ? Slot.of(null) : this.slots[i];
|
||||
slot.write(buf);
|
||||
}
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ContainerOpen read(Buf in) {
|
||||
int id = in.readVarInt();
|
||||
String title = in.readString(MAX_TEXT);
|
||||
int count = in.readVarInt();
|
||||
if (count < 0 || count > 1024) {
|
||||
throw new ProtocolException("a container of " + count + " slots is not real");
|
||||
}
|
||||
Slot[] slots = new Slot[count];
|
||||
for (int i = 0; i < count; i++) {
|
||||
slots[i] = Slot.read(in);
|
||||
}
|
||||
return new ContainerOpen(id, title, slots);
|
||||
}
|
||||
}
|
||||
|
||||
/** The client takes something out of a container. */
|
||||
public static final class ContainerTake implements Packet {
|
||||
|
||||
public final int id;
|
||||
public final int slot;
|
||||
|
||||
public ContainerTake(int id, int slot) {
|
||||
this.id = id;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8);
|
||||
buf.writeVarInt(this.id);
|
||||
buf.writeVarInt(this.slot);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static ContainerTake read(Buf in) {
|
||||
return new ContainerTake(in.readVarInt(), in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The client asks to learn a blueprint.
|
||||
*
|
||||
* A blueprint is the item with a mark on it. Learning it uses it up and
|
||||
* opens the recipe for good.
|
||||
*/
|
||||
public static final class LearnBlueprint implements Packet {
|
||||
|
||||
public final int item;
|
||||
|
||||
public LearnBlueprint(int item) {
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public Buf write() {
|
||||
Buf buf = new Buf(8);
|
||||
buf.writeVarInt(this.item);
|
||||
return buf;
|
||||
}
|
||||
|
||||
public static LearnBlueprint read(Buf in) {
|
||||
return new LearnBlueprint(in.readVarInt());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Every packet id this build knows, and what it decodes into.
|
||||
*
|
||||
@@ -974,6 +1398,20 @@ public final class Packets {
|
||||
map.put(Integer.valueOf(ENTITY_REMOVE), "EntityRemove");
|
||||
map.put(Integer.valueOf(LAUNCHER_AUTH), "LauncherAuth");
|
||||
map.put(Integer.valueOf(SET_GAME_MODE), "GameMode");
|
||||
map.put(Integer.valueOf(INVENTORY), "Inventory");
|
||||
map.put(Integer.valueOf(INVENTORY_SLOT), "SlotChange");
|
||||
map.put(Integer.valueOf(DIG), "Dig");
|
||||
map.put(Integer.valueOf(BREAK_PROGRESS), "BreakProgress");
|
||||
map.put(Integer.valueOf(CRAFT_START), "CraftStart");
|
||||
map.put(Integer.valueOf(CRAFT_UPDATE), "CraftUpdate");
|
||||
map.put(Integer.valueOf(RESEARCH_START), "ResearchStart");
|
||||
map.put(Integer.valueOf(RESEARCH_UPDATE), "ResearchUpdate");
|
||||
map.put(Integer.valueOf(TECH_TREE_UNLOCK), "TechTreeUnlock");
|
||||
map.put(Integer.valueOf(KNOWN_RECIPES), "KnownRecipes");
|
||||
map.put(Integer.valueOf(TECH_TREE_STATE), "TechTreeState");
|
||||
map.put(Integer.valueOf(CONTAINER_OPEN), "ContainerOpen");
|
||||
map.put(Integer.valueOf(CONTAINER_TAKE), "ContainerTake");
|
||||
map.put(Integer.valueOf(LEARN_BLUEPRINT), "LearnBlueprint");
|
||||
map.put(Integer.valueOf(DISCONNECT), "Disconnect");
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
package net.rustnotmc.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The trees of the workbenches.
|
||||
*
|
||||
* Every workbench has its own tree, and a node in it costs scrap. To open a
|
||||
* node the one before it has to be open: that is what makes a tree a path and
|
||||
* not a list. Unlocking a node is personal, it cannot be given away, and the
|
||||
* item it opens needs no blueprint afterwards.
|
||||
*
|
||||
* The prices come from the wiki of the original game: 125 scrap for the tree
|
||||
* of the first workbench, 250 for the second, 500 for the third and 250 for
|
||||
* the engineering workbench.
|
||||
*/
|
||||
public final class TechTree {
|
||||
|
||||
/** One node of a tree. */
|
||||
public static final class Node {
|
||||
|
||||
/** The item this node opens. */
|
||||
public final int item;
|
||||
|
||||
/** The item that has to be open first, or the id of nothing. */
|
||||
public final int parent;
|
||||
|
||||
/** The workbench whose tree this node is in. */
|
||||
public final int bench;
|
||||
|
||||
/** What the node costs. */
|
||||
public final int scrap;
|
||||
|
||||
public Node(int item, int parent, int bench, int scrap) {
|
||||
this.item = item;
|
||||
this.parent = parent;
|
||||
this.bench = bench;
|
||||
this.scrap = scrap;
|
||||
}
|
||||
}
|
||||
|
||||
/** The price of a node in the tree of the first workbench. */
|
||||
public static final int PRICE_L1 = 125;
|
||||
|
||||
/** The price of a node in the tree of the second workbench. */
|
||||
public static final int PRICE_L2 = 250;
|
||||
|
||||
/** The price of a node in the tree of the third workbench. */
|
||||
public static final int PRICE_L3 = 500;
|
||||
|
||||
/** The price of a node in the tree of the engineering workbench. */
|
||||
public static final int PRICE_ENGINEERING = 250;
|
||||
|
||||
private static final List<Node> NODES = new ArrayList<Node>();
|
||||
|
||||
static {
|
||||
// The tree of the first workbench: the tools, one after another.
|
||||
add(ItemRegistry.SALVAGED_HAMMER, ItemRegistry.NOTHING, 1, PRICE_L1);
|
||||
add(ItemRegistry.HATCHET, ItemRegistry.SALVAGED_HAMMER, 1, PRICE_L1);
|
||||
add(ItemRegistry.PICKAXE, ItemRegistry.SALVAGED_HAMMER, 1, PRICE_L1);
|
||||
add(ItemRegistry.SALVAGED_SWORD, ItemRegistry.HATCHET, 1, PRICE_L1);
|
||||
add(ItemRegistry.SALVAGED_CLEAVER, ItemRegistry.HATCHET, 1, PRICE_L1);
|
||||
// The tree of the second workbench: the salvage tools and the machine.
|
||||
add(ItemRegistry.SALVAGED_AXE, ItemRegistry.SALVAGED_HAMMER, 2, PRICE_L2);
|
||||
add(ItemRegistry.SALVAGED_ICEPICK, ItemRegistry.SALVAGED_HAMMER, 2, PRICE_L2);
|
||||
add(ItemRegistry.METAL_BLADE, ItemRegistry.SALVAGED_AXE, 2, PRICE_L2);
|
||||
add(ItemRegistry.GARAGE_DOOR, ItemRegistry.GEARS, 2, PRICE_L2);
|
||||
// The tree of the third workbench: the heavy stuff.
|
||||
add(ItemRegistry.GEARS, ItemRegistry.METAL_BLADE, 3, PRICE_L3);
|
||||
add(ItemRegistry.METAL_PIPE, ItemRegistry.GEARS, 3, PRICE_L3);
|
||||
add(ItemRegistry.ARMORED_DOOR, ItemRegistry.METAL_PIPE, 3, PRICE_L3);
|
||||
add(ItemRegistry.EXPLOSIVES, ItemRegistry.METAL_BLADE, 3, PRICE_L3);
|
||||
add(ItemRegistry.TIMED_CHARGE, ItemRegistry.EXPLOSIVES, 3, PRICE_L3);
|
||||
}
|
||||
|
||||
private static void add(int item, int parent, int bench, int scrap) {
|
||||
NODES.add(new Node(item, parent, bench, scrap));
|
||||
}
|
||||
|
||||
private TechTree() {
|
||||
}
|
||||
|
||||
/** Every node of every tree. */
|
||||
public static List<Node> nodes() {
|
||||
return Collections.unmodifiableList(NODES);
|
||||
}
|
||||
|
||||
/** The node that opens an item, or null when the item is in no tree. */
|
||||
public static Node forItem(int item) {
|
||||
for (int i = 0; i < NODES.size(); i++) {
|
||||
if (NODES.get(i).item == item) {
|
||||
return NODES.get(i);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/** What it costs to open an item, or zero when the item is in no tree. */
|
||||
public static int costOf(int item) {
|
||||
Node node = forItem(item);
|
||||
return node == null ? 0 : node.scrap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the item can be opened with what is open already.
|
||||
*
|
||||
* A node needs its parent, and the parent needs its own parent, and so on
|
||||
* down to the root of the branch.
|
||||
*/
|
||||
public static boolean canOpen(int item, java.util.Set<Integer> unlocked) {
|
||||
Node node = forItem(item);
|
||||
if (node == null) {
|
||||
return false;
|
||||
}
|
||||
if (unlocked.contains(Integer.valueOf(item))) {
|
||||
return true;
|
||||
}
|
||||
int parent = node.parent;
|
||||
while (parent != ItemRegistry.NOTHING) {
|
||||
if (!unlocked.contains(Integer.valueOf(parent))) {
|
||||
return false;
|
||||
}
|
||||
Node above = forItem(parent);
|
||||
if (above == null) {
|
||||
return false;
|
||||
}
|
||||
parent = above.parent;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
package net.rustnotmc.protocol;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Tests for the item table, the registry, the trees and the nodes.
|
||||
*
|
||||
* The numbers here come from the table of the remake. A test that pins them
|
||||
* down is what stops a change in the table from quietly changing the game:
|
||||
* when the numbers move, the test says so.
|
||||
*/
|
||||
public final class ItemTest {
|
||||
|
||||
private static int checks;
|
||||
private static int failed;
|
||||
private static final List<String> failures = new ArrayList<String>();
|
||||
|
||||
public static void main(String[] args) {
|
||||
run("the table is read", new Runnable() {
|
||||
public void run() { testTableLoads(); }
|
||||
});
|
||||
run("the table holds the numbers of the remake", new Runnable() {
|
||||
public void run() { testTableNumbers(); }
|
||||
});
|
||||
run("every item of the registry is in the table", new Runnable() {
|
||||
public void run() { testRegistryMatchesTable(); }
|
||||
});
|
||||
run("the tool takes the numbers of the table", new Runnable() {
|
||||
public void run() { testToolNumbers(); }
|
||||
});
|
||||
run("the damage is split over the body", new Runnable() {
|
||||
public void run() { testDamageSplit(); }
|
||||
});
|
||||
run("the trees cost what the plan says", new Runnable() {
|
||||
public void run() { testTreePrices(); }
|
||||
});
|
||||
run("a branch has to be walked", new Runnable() {
|
||||
public void run() { testTreeParents(); }
|
||||
});
|
||||
run("the nodes hold what the plan says", new Runnable() {
|
||||
public void run() { testNodePools(); }
|
||||
});
|
||||
run("the mark of a node grows and stops", new Runnable() {
|
||||
public void run() { testNodeMarkers(); }
|
||||
});
|
||||
run("the new packets survive the round trip", new Runnable() {
|
||||
public void run() { testPackets(); }
|
||||
});
|
||||
|
||||
System.out.println("");
|
||||
if (failed == 0) {
|
||||
System.out.println(" " + checks + " checks passed.");
|
||||
return;
|
||||
}
|
||||
System.out.println(" " + failed + " of " + checks + " checks FAILED:");
|
||||
for (int i = 0; i < failures.size(); i++) {
|
||||
System.out.println(" " + failures.get(i));
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
private static void testTableLoads() {
|
||||
yes("the table is not empty", ItemData.size() > 200);
|
||||
ItemData.Row rock = ItemData.byRid("rock");
|
||||
yes("the rock is in the table", rock != null);
|
||||
if (rock != null) {
|
||||
eq("the rock hurts ten", Integer.valueOf(10), Integer.valueOf(rock.damage));
|
||||
eq("the rock takes fifty wood", Integer.valueOf(50), Integer.valueOf(rock.wood));
|
||||
eq("the rock takes thirty five ore", Integer.valueOf(35), Integer.valueOf(rock.ore));
|
||||
eq("the rock lasts a hundred", Integer.valueOf(100), Integer.valueOf(rock.durability));
|
||||
eq("the rock swings every two seconds", Integer.valueOf(40),
|
||||
Integer.valueOf(rock.cooldown));
|
||||
}
|
||||
ItemData.Row hatchet = ItemData.byRid("ironaxe");
|
||||
yes("the hatchet is in the table", hatchet != null);
|
||||
if (hatchet != null) {
|
||||
eq("the hatchet takes eighty five wood", Integer.valueOf(85),
|
||||
Integer.valueOf(hatchet.wood));
|
||||
eq("the hatchet hurts twenty five", Integer.valueOf(25),
|
||||
Integer.valueOf(hatchet.damage));
|
||||
eq("the hatchet lasts three hundred", Integer.valueOf(300),
|
||||
Integer.valueOf(hatchet.durability));
|
||||
}
|
||||
}
|
||||
|
||||
private static void testTableNumbers() {
|
||||
// The numbers the plan writes down have to be the numbers the table
|
||||
// holds, otherwise the two drift apart.
|
||||
eq("the tree of a poor one is five hundred", Integer.valueOf(500),
|
||||
Integer.valueOf(NodeType.byId(NodeType.TREE_POOR).pool));
|
||||
eq("the tree of an ordinary one is seven hundred fifty", Integer.valueOf(750),
|
||||
Integer.valueOf(NodeType.byId(NodeType.TREE_COMMON).pool));
|
||||
eq("a dead tree is a thousand", Integer.valueOf(1000),
|
||||
Integer.valueOf(NodeType.byId(NodeType.TREE_DEAD).pool));
|
||||
eq("a stone outcrop is seven hundred fifty", Integer.valueOf(750),
|
||||
Integer.valueOf(NodeType.byId(NodeType.STONE).pool));
|
||||
eq("a metal deposit is five hundred", Integer.valueOf(500),
|
||||
Integer.valueOf(NodeType.byId(NodeType.METAL).pool));
|
||||
eq("a sulfur deposit is two hundred", Integer.valueOf(200),
|
||||
Integer.valueOf(NodeType.byId(NodeType.SULFUR).pool));
|
||||
}
|
||||
|
||||
private static void testRegistryMatchesTable() {
|
||||
int withRid = 0;
|
||||
for (ItemType type : ItemRegistry.all()) {
|
||||
if (type.rid == null) {
|
||||
continue;
|
||||
}
|
||||
withRid++;
|
||||
yes("the table knows " + type.rid, ItemData.byRid(type.rid) != null);
|
||||
}
|
||||
yes("most items come from the table", withRid > 90);
|
||||
// And the other way round: the table may know more than we do, but
|
||||
// everything it knows that we also know has to be the same item.
|
||||
for (ItemType type : ItemRegistry.all()) {
|
||||
if (type.rid == null) {
|
||||
continue;
|
||||
}
|
||||
ItemData.Row row = ItemData.byRid(type.rid);
|
||||
if (row == null) {
|
||||
continue;
|
||||
}
|
||||
if (row.damage > 0) {
|
||||
eq("the damage of " + type.rid, Integer.valueOf(row.damage),
|
||||
Integer.valueOf(type.damage(ItemType.TORSO)));
|
||||
}
|
||||
if (row.durability > 0) {
|
||||
eq("the durability of " + type.rid, Integer.valueOf(row.durability),
|
||||
Integer.valueOf(type.durability));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void testToolNumbers() {
|
||||
ItemType rock = ItemRegistry.require(ItemRegistry.ROCK);
|
||||
yes("the rock gathers wood", rock.gather(ResourceClass.TREE) != null);
|
||||
eq("the rock takes fifty wood a swing", Integer.valueOf(50),
|
||||
Integer.valueOf(rock.gather(ResourceClass.TREE).damage));
|
||||
eq("the rock takes thirty five ore a swing", Integer.valueOf(35),
|
||||
Integer.valueOf(rock.gather(ResourceClass.STONE).damage));
|
||||
yes("the rock has no pickaxe", rock.gather(ResourceClass.TREE) != null);
|
||||
|
||||
ItemType hatchet = ItemRegistry.require(ItemRegistry.HATCHET);
|
||||
eq("the hatchet takes eighty five wood", Integer.valueOf(85),
|
||||
Integer.valueOf(hatchet.gather(ResourceClass.TREE).damage));
|
||||
yes("the hatchet does not mine", hatchet.gather(ResourceClass.STONE) == null);
|
||||
|
||||
ItemType pickaxe = ItemRegistry.require(ItemRegistry.PICKAXE);
|
||||
eq("the pickaxe takes a hundred ore", Integer.valueOf(100),
|
||||
Integer.valueOf(pickaxe.gather(ResourceClass.STONE).damage));
|
||||
yes("the pickaxe does not cut", pickaxe.gather(ResourceClass.TREE) == null);
|
||||
}
|
||||
|
||||
private static void testDamageSplit() {
|
||||
ItemType rock = ItemRegistry.require(ItemRegistry.ROCK);
|
||||
eq("the head takes twice", Integer.valueOf(20),
|
||||
Integer.valueOf(rock.damage(ItemType.HEAD)));
|
||||
eq("the torso takes the number", Integer.valueOf(10),
|
||||
Integer.valueOf(rock.damage(ItemType.TORSO)));
|
||||
eq("the arms take four fifths", Integer.valueOf(8),
|
||||
Integer.valueOf(rock.damage(ItemType.ARMS)));
|
||||
eq("the legs take four fifths", Integer.valueOf(8),
|
||||
Integer.valueOf(rock.damage(ItemType.LEGS)));
|
||||
}
|
||||
|
||||
private static void testTreePrices() {
|
||||
eq("the tree of the first workbench costs a hundred twenty five",
|
||||
Integer.valueOf(125), Integer.valueOf(TechTree.PRICE_L1));
|
||||
eq("the tree of the second workbench costs two hundred fifty",
|
||||
Integer.valueOf(250), Integer.valueOf(TechTree.PRICE_L2));
|
||||
eq("the tree of the third workbench costs five hundred",
|
||||
Integer.valueOf(500), Integer.valueOf(TechTree.PRICE_L3));
|
||||
eq("the hatchet costs a hundred twenty five", Integer.valueOf(125),
|
||||
Integer.valueOf(TechTree.costOf(ItemRegistry.HATCHET)));
|
||||
eq("the hatchet sits in the tree of the first workbench", Integer.valueOf(1),
|
||||
Integer.valueOf(TechTree.forItem(ItemRegistry.HATCHET).bench));
|
||||
eq("the salvaged axe sits in the tree of the second workbench", Integer.valueOf(2),
|
||||
Integer.valueOf(TechTree.forItem(ItemRegistry.SALVAGED_AXE).bench));
|
||||
eq("the armored door sits in the tree of the third workbench", Integer.valueOf(3),
|
||||
Integer.valueOf(TechTree.forItem(ItemRegistry.ARMORED_DOOR).bench));
|
||||
eq("the rock is in no tree", Integer.valueOf(0),
|
||||
Integer.valueOf(TechTree.costOf(ItemRegistry.ROCK)));
|
||||
}
|
||||
|
||||
private static void testTreeParents() {
|
||||
Set<Integer> nothing = new HashSet<Integer>();
|
||||
yes("the root of a branch can be opened",
|
||||
TechTree.canOpen(ItemRegistry.SALVAGED_HAMMER, nothing));
|
||||
yes("the next one cannot",
|
||||
!TechTree.canOpen(ItemRegistry.HATCHET, nothing));
|
||||
|
||||
Set<Integer> withHammer = new HashSet<Integer>();
|
||||
withHammer.add(Integer.valueOf(ItemRegistry.SALVAGED_HAMMER));
|
||||
yes("with the hammer the hatchet opens",
|
||||
TechTree.canOpen(ItemRegistry.HATCHET, withHammer));
|
||||
yes("with the hammer the sword does not",
|
||||
!TechTree.canOpen(ItemRegistry.SALVAGED_SWORD, withHammer));
|
||||
|
||||
Set<Integer> withHatchet = new HashSet<Integer>(withHammer);
|
||||
withHatchet.add(Integer.valueOf(ItemRegistry.HATCHET));
|
||||
yes("with the hatchet the sword opens",
|
||||
TechTree.canOpen(ItemRegistry.SALVAGED_SWORD, withHatchet));
|
||||
}
|
||||
|
||||
private static void testNodePools() {
|
||||
eq("the ordinary tree holds seven hundred fifty", Integer.valueOf(750),
|
||||
Integer.valueOf(NodeType.byId(NodeType.TREE_COMMON).pool));
|
||||
eq("the tree gives wood", Integer.valueOf(ItemRegistry.WOOD),
|
||||
Integer.valueOf(NodeType.byId(NodeType.TREE_COMMON).dropItem));
|
||||
eq("the outcrop gives stones", Integer.valueOf(ItemRegistry.STONE),
|
||||
Integer.valueOf(NodeType.byId(NodeType.STONE).dropItem));
|
||||
eq("the deposit gives metal", Integer.valueOf(ItemRegistry.METAL),
|
||||
Integer.valueOf(NodeType.byId(NodeType.METAL).dropItem));
|
||||
eq("the sulfur deposit gives sulfur", Integer.valueOf(ItemRegistry.SULFUR),
|
||||
Integer.valueOf(NodeType.byId(NodeType.SULFUR).dropItem));
|
||||
yes("a tree has a cross", NodeType.byId(NodeType.TREE_COMMON).hasMarker());
|
||||
yes("an outcrop has a hot spot", NodeType.byId(NodeType.STONE).hasMarker());
|
||||
}
|
||||
|
||||
private static void testNodeMarkers() {
|
||||
NodeType tree = NodeType.byId(NodeType.TREE_COMMON);
|
||||
eq("a swing without the cross is the plain one", Integer.valueOf(85),
|
||||
Integer.valueOf(tree.swingDamage(85, 0)));
|
||||
eq("the first cross adds an eighth", Integer.valueOf(96),
|
||||
Integer.valueOf(tree.swingDamage(85, 1)));
|
||||
eq("the cross stops at twice", Integer.valueOf(170),
|
||||
Integer.valueOf(tree.swingDamage(85, 100)));
|
||||
|
||||
NodeType ore = NodeType.byId(NodeType.STONE);
|
||||
eq("the first hot spot adds half", Integer.valueOf(75),
|
||||
Integer.valueOf(ore.swingDamage(50, 1)));
|
||||
eq("the hot spot stops at three times", Integer.valueOf(150),
|
||||
Integer.valueOf(ore.swingDamage(50, 100)));
|
||||
}
|
||||
|
||||
private static void testPackets() {
|
||||
Packets.Dig dig = new Packets.Dig(Packets.Dig.START, 10, -60, 30, 2);
|
||||
Packets.Dig digBack = Packets.Dig.read(new Buf(dig.write().toByteArray()));
|
||||
eq("the action survives", Integer.valueOf(Packets.Dig.START),
|
||||
Integer.valueOf(digBack.action));
|
||||
eq("the place survives", Integer.valueOf(10), Integer.valueOf(digBack.x));
|
||||
eq("the face survives", Integer.valueOf(2), Integer.valueOf(digBack.face));
|
||||
yes("a start is a start", digBack.start());
|
||||
|
||||
Packets.BreakProgress progress = new Packets.BreakProgress(1, 2, 3, 7);
|
||||
Packets.BreakProgress progressBack =
|
||||
Packets.BreakProgress.read(new Buf(progress.write().toByteArray()));
|
||||
eq("the stage survives", Integer.valueOf(7), Integer.valueOf(progressBack.stage));
|
||||
eq("the place survives", Integer.valueOf(3), Integer.valueOf(progressBack.z));
|
||||
|
||||
Packets.CraftUpdate craft = new Packets.CraftUpdate(ItemRegistry.HATCHET, 1, 12, "");
|
||||
Packets.CraftUpdate craftBack =
|
||||
Packets.CraftUpdate.read(new Buf(craft.write().toByteArray()));
|
||||
eq("the item survives", Integer.valueOf(ItemRegistry.HATCHET),
|
||||
Integer.valueOf(craftBack.item));
|
||||
eq("the seconds survive", Integer.valueOf(12), Integer.valueOf(craftBack.secondsLeft));
|
||||
yes("an empty reason means fine", craftBack.ok());
|
||||
|
||||
Packets.CraftUpdate refused = new Packets.CraftUpdate(0, 0, 0, "no scrap");
|
||||
Packets.CraftUpdate refusedBack =
|
||||
Packets.CraftUpdate.read(new Buf(refused.write().toByteArray()));
|
||||
yes("a reason means no", !refusedBack.ok());
|
||||
eq("the reason survives", "no scrap", refusedBack.reason);
|
||||
|
||||
Packets.ResearchUpdate research =
|
||||
new Packets.ResearchUpdate(ItemRegistry.PICKAXE, 10, "");
|
||||
Packets.ResearchUpdate researchBack =
|
||||
Packets.ResearchUpdate.read(new Buf(research.write().toByteArray()));
|
||||
eq("the researched item survives", Integer.valueOf(ItemRegistry.PICKAXE),
|
||||
Integer.valueOf(researchBack.item));
|
||||
yes("a research that works has no reason", researchBack.ok());
|
||||
|
||||
Packets.TechTreeUnlock unlock = new Packets.TechTreeUnlock(ItemRegistry.HATCHET);
|
||||
Packets.TechTreeUnlock unlockBack =
|
||||
Packets.TechTreeUnlock.read(new Buf(unlock.write().toByteArray()));
|
||||
eq("the node survives", Integer.valueOf(ItemRegistry.HATCHET),
|
||||
Integer.valueOf(unlockBack.item));
|
||||
|
||||
Packets.KnownRecipes known = new Packets.KnownRecipes(new int[]{1, 2, 3});
|
||||
Packets.KnownRecipes knownBack =
|
||||
Packets.KnownRecipes.read(new Buf(known.write().toByteArray()));
|
||||
eq("the recipes survive", Integer.valueOf(3),
|
||||
Integer.valueOf(knownBack.items.length));
|
||||
eq("the last recipe survives", Integer.valueOf(3),
|
||||
Integer.valueOf(knownBack.items[2]));
|
||||
|
||||
Packets.TechTreeState state = new Packets.TechTreeState(new int[]{18, 16});
|
||||
Packets.TechTreeState stateBack =
|
||||
Packets.TechTreeState.read(new Buf(state.write().toByteArray()));
|
||||
eq("the opened nodes survive", Integer.valueOf(2),
|
||||
Integer.valueOf(stateBack.items.length));
|
||||
|
||||
Packets.Slot[] slots = new Packets.Slot[]{
|
||||
new Packets.Slot(ItemRegistry.SCRAP, 4, -1),
|
||||
Packets.Slot.of(null)};
|
||||
Packets.ContainerOpen open = new Packets.ContainerOpen(9, "Food", slots);
|
||||
Packets.ContainerOpen openBack =
|
||||
Packets.ContainerOpen.read(new Buf(open.write().toByteArray()));
|
||||
eq("the title survives", "Food", openBack.title);
|
||||
eq("the slots survive", Integer.valueOf(2), Integer.valueOf(openBack.slots.length));
|
||||
eq("what is in the slot survives", Integer.valueOf(ItemRegistry.SCRAP),
|
||||
Integer.valueOf(openBack.slots[0].item));
|
||||
eq("an empty slot is nothing", Integer.valueOf(ItemRegistry.NOTHING),
|
||||
Integer.valueOf(openBack.slots[1].item));
|
||||
|
||||
Packets.ContainerTake take = new Packets.ContainerTake(9, 0);
|
||||
Packets.ContainerTake takeBack =
|
||||
Packets.ContainerTake.read(new Buf(take.write().toByteArray()));
|
||||
eq("the container survives", Integer.valueOf(9), Integer.valueOf(takeBack.id));
|
||||
eq("the slot survives", Integer.valueOf(0), Integer.valueOf(takeBack.slot));
|
||||
|
||||
Packets.LearnBlueprint learn = new Packets.LearnBlueprint(ItemRegistry.PICKAXE);
|
||||
Packets.LearnBlueprint learnBack =
|
||||
Packets.LearnBlueprint.read(new Buf(learn.write().toByteArray()));
|
||||
eq("the blueprint survives", Integer.valueOf(ItemRegistry.PICKAXE),
|
||||
Integer.valueOf(learnBack.item));
|
||||
}
|
||||
|
||||
private static void run(String name, Runnable body) {
|
||||
checks++;
|
||||
int before = failed;
|
||||
try {
|
||||
body.run();
|
||||
} catch (Throwable t) {
|
||||
failed++;
|
||||
failures.add(name + ": threw " + t);
|
||||
t.printStackTrace();
|
||||
return;
|
||||
}
|
||||
if (failed == before) {
|
||||
System.out.println(" ok " + name);
|
||||
} else {
|
||||
System.out.println(" FAIL " + name);
|
||||
}
|
||||
}
|
||||
|
||||
private static void eq(Object what, Object expected, Object actual) {
|
||||
if (expected == null ? actual != null : !expected.equals(actual)) {
|
||||
failed++;
|
||||
failures.add(what + ": expected " + expected + " but got " + actual);
|
||||
}
|
||||
}
|
||||
|
||||
private static void yes(String what, boolean condition) {
|
||||
if (!condition) {
|
||||
failed++;
|
||||
failures.add(what);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,7 +545,12 @@ public final class ProtocolTest {
|
||||
Packets.WORLD_JOIN, Packets.CHUNK_DATA, Packets.BLOCK_CHANGE,
|
||||
Packets.PLAYER_MOVE, Packets.ENTITY_MOVE, Packets.PING, Packets.PONG,
|
||||
Packets.ENTITY_SPAWN, Packets.ENTITY_REMOVE, Packets.LAUNCHER_AUTH,
|
||||
Packets.SET_GAME_MODE,
|
||||
Packets.SET_GAME_MODE, Packets.INVENTORY, Packets.INVENTORY_SLOT,
|
||||
Packets.DIG, Packets.BREAK_PROGRESS, Packets.CRAFT_START, Packets.CRAFT_UPDATE,
|
||||
Packets.RESEARCH_START, Packets.RESEARCH_UPDATE, Packets.TECH_TREE_UNLOCK,
|
||||
Packets.KNOWN_RECIPES, Packets.TECH_TREE_STATE,
|
||||
Packets.CONTAINER_OPEN, Packets.CONTAINER_TAKE,
|
||||
Packets.LEARN_BLUEPRINT,
|
||||
Packets.DISCONNECT,
|
||||
};
|
||||
for (int i = 0; i < ids.length; i++) {
|
||||
@@ -565,9 +570,9 @@ public final class ProtocolTest {
|
||||
Integer.valueOf(rock.damage(ItemType.HEAD)));
|
||||
eq("and the torso for ten", Integer.valueOf(10),
|
||||
Integer.valueOf(rock.damage(ItemType.TORSO)));
|
||||
eq("the rock takes ten off a tree", Integer.valueOf(10),
|
||||
eq("the rock takes fifty off a tree", Integer.valueOf(50),
|
||||
Integer.valueOf(rock.gather(ResourceClass.TREE).damage));
|
||||
eq("and five off an ore", Integer.valueOf(5),
|
||||
eq("and thirty five off an ore", Integer.valueOf(35),
|
||||
Integer.valueOf(rock.gather(ResourceClass.METAL).damage));
|
||||
eq("the rock is known from the start", Boolean.TRUE,
|
||||
Boolean.valueOf(rock.knownByDefault));
|
||||
@@ -579,12 +584,12 @@ public final class ProtocolTest {
|
||||
|
||||
ItemType hatchet = ItemRegistry.require(ItemRegistry.STONE_HATCHET);
|
||||
eq("the stone hatchet is an axe", ToolClass.AXE, hatchet.tool);
|
||||
eq("it takes twenty off a tree", Integer.valueOf(20),
|
||||
eq("it takes eighty off a tree", Integer.valueOf(80),
|
||||
Integer.valueOf(hatchet.gather(ResourceClass.TREE).damage));
|
||||
|
||||
ItemType pickaxe = ItemRegistry.require(ItemRegistry.STONE_PICKAXE);
|
||||
eq("the stone pickaxe is a pickaxe", ToolClass.PICKAXE, pickaxe.tool);
|
||||
eq("it takes sixteen off an ore", Integer.valueOf(16),
|
||||
eq("it takes eighty off an ore", Integer.valueOf(80),
|
||||
Integer.valueOf(pickaxe.gather(ResourceClass.METAL).damage));
|
||||
yes("and it does not cut trees", pickaxe.gather(ResourceClass.TREE) == null);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user