Свалки у дорог: бочки серые/синие/красные и ящики еды как блоки, лут по детерминированным таблицам, синие на кольце дают продвинутые фрагменты
This commit is contained in:
@@ -48,6 +48,23 @@ public final class RsxBlockMap {
|
||||
net.minecraft.item.EnumDyeColor.GRAY);
|
||||
STATES[net.rustnotmc.protocol.Blocks.SNOW] =
|
||||
Blocks.SNOW.getDefaultState();
|
||||
// the junk piles of the roads: grey, blue and red barrels, and the
|
||||
// wooden crate of food. A barrel is drawn as a barrel's colour clang
|
||||
// of lantern hardened clay, the crate as a plank blocked square
|
||||
STATES[net.rustnotmc.protocol.Blocks.BARREL] =
|
||||
Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(
|
||||
net.minecraft.block.BlockColored.COLOR,
|
||||
net.minecraft.item.EnumDyeColor.SILVER);
|
||||
STATES[net.rustnotmc.protocol.Blocks.BARREL_BLUE] =
|
||||
Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(
|
||||
net.minecraft.block.BlockColored.COLOR,
|
||||
net.minecraft.item.EnumDyeColor.BLUE);
|
||||
STATES[net.rustnotmc.protocol.Blocks.BARREL_RED] =
|
||||
Blocks.STAINED_HARDENED_CLAY.getDefaultState().withProperty(
|
||||
net.minecraft.block.BlockColored.COLOR,
|
||||
net.minecraft.item.EnumDyeColor.RED);
|
||||
STATES[net.rustnotmc.protocol.Blocks.CRATE] =
|
||||
Blocks.PLANKS.getDefaultState();
|
||||
}
|
||||
|
||||
private RsxBlockMap() {
|
||||
|
||||
@@ -33,9 +33,17 @@ public final class Blocks {
|
||||
/** The surface of the high and cold places. */
|
||||
public static final byte SNOW = 9;
|
||||
|
||||
/** A barrel that may be broken for what is in it. */
|
||||
public static final byte BARREL = 10;
|
||||
public static final byte BARREL_BLUE = 11;
|
||||
public static final byte BARREL_RED = 12;
|
||||
|
||||
/** A wooden box of food, standing with the barrels. */
|
||||
public static final byte CRATE = 13;
|
||||
|
||||
/** Lowest and highest legal block id. */
|
||||
public static final byte MIN = AIR;
|
||||
public static final byte MAX = SNOW;
|
||||
public static final byte MAX = CRATE;
|
||||
|
||||
private Blocks() {
|
||||
}
|
||||
@@ -52,6 +60,10 @@ public final class Blocks {
|
||||
case LEAVES: return "leaves";
|
||||
case ASPHALT: return "asphalt";
|
||||
case SNOW: return "snow";
|
||||
case BARREL: return "barrel";
|
||||
case BARREL_BLUE: return "barrel_blue";
|
||||
case BARREL_RED: return "barrel_red";
|
||||
case CRATE: return "crate";
|
||||
default: return "unknown(" + id + ")";
|
||||
}
|
||||
}
|
||||
@@ -66,6 +78,18 @@ public final class Blocks {
|
||||
return id == AIR || id == WATER;
|
||||
}
|
||||
|
||||
/**
|
||||
* True when breaking it gives what is inside instead of nothing.
|
||||
*
|
||||
* The barrels and the crates are the loot of the roads: a broken one
|
||||
* empties its content into the hands of whoever broke it, whatever the
|
||||
* game mode says.
|
||||
*/
|
||||
public static boolean isLootable(byte id) {
|
||||
return id == BARREL || id == BARREL_BLUE || id == BARREL_RED
|
||||
|| id == CRATE;
|
||||
}
|
||||
|
||||
public static boolean isValid(byte id) {
|
||||
return id >= MIN && id <= MAX;
|
||||
}
|
||||
|
||||
@@ -178,8 +178,9 @@ public final class ItemRegistry {
|
||||
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.
|
||||
// The fragments of the blueprints. They come out of the blue barrels
|
||||
// of the junk piles on the roads, and the advanced ones out of the
|
||||
// barrels on the ring road.
|
||||
define(ItemType.of(BASIC_FRAGMENT, "Basic Blueprint Fragment"));
|
||||
define(ItemType.of(ADVANCED_FRAGMENT, "Advanced Blueprint Fragment"));
|
||||
define(ItemType.of(METAL_PIPE, "Metal Pipe").rid("metalpipe"));
|
||||
|
||||
@@ -50,6 +50,7 @@ $suites = @(
|
||||
'net.rustnotmc.server.world.WorldTest',
|
||||
'net.rustnotmc.server.world.NodeTest',
|
||||
'net.rustnotmc.server.world.LodTest',
|
||||
'net.rustnotmc.server.world.PilesTest',
|
||||
'net.rustnotmc.server.item.InventoryTest',
|
||||
'net.rustnotmc.server.craft.CraftTest',
|
||||
'net.rustnotmc.server.hitbox.HitboxTest',
|
||||
|
||||
@@ -14,6 +14,7 @@ import net.rustnotmc.protocol.ChunkFormat;
|
||||
import net.rustnotmc.protocol.Frame;
|
||||
import net.rustnotmc.protocol.FrameDecoder;
|
||||
import net.rustnotmc.protocol.ItemRegistry;
|
||||
import net.rustnotmc.protocol.ItemStack;
|
||||
import net.rustnotmc.protocol.ItemType;
|
||||
import net.rustnotmc.protocol.Packet;
|
||||
import net.rustnotmc.protocol.Packets;
|
||||
@@ -797,6 +798,34 @@ public final class ClientSession implements Runnable {
|
||||
send(Packets.BREAK_PROGRESS, new Packets.BreakProgress(dig.x, dig.y, dig.z, -1));
|
||||
return;
|
||||
}
|
||||
byte at;
|
||||
try {
|
||||
at = this.server.world().blockAt(dig.x, dig.y, dig.z);
|
||||
} catch (IOException lost) {
|
||||
at = Blocks.AIR; // the place has no readable ground there
|
||||
}
|
||||
if (Blocks.isLootable(at)) {
|
||||
// a barrel or a crate of the roads gives up what is in it when it
|
||||
// is broken, in the survival mode as well as the creative one
|
||||
if (this.server.world().setBlock(dig.x, dig.y, dig.z, Blocks.AIR)) {
|
||||
this.server.broadcastBlockChange(dig.x, dig.y, dig.z, Blocks.AIR);
|
||||
boolean took = false;
|
||||
for (ItemStack stack : this.server.world().generator()
|
||||
.pileLoot(at, dig.x, dig.y, dig.z)) {
|
||||
if (this.player.inventory().add(stack)) {
|
||||
took = true;
|
||||
}
|
||||
}
|
||||
if (took) {
|
||||
this.server.log("pile: " + this.account + " emptied a "
|
||||
+ (at == Blocks.CRATE ? "crate" : "barrel")
|
||||
+ " at " + dig.x + "," + dig.y + "," + dig.z);
|
||||
send(Packets.INVENTORY, this.player.inventory().toPacket());
|
||||
}
|
||||
}
|
||||
send(Packets.BREAK_PROGRESS, new Packets.BreakProgress(dig.x, dig.y, dig.z, -1));
|
||||
return;
|
||||
}
|
||||
if (this.player.mayBreakBlocks()) {
|
||||
if (this.server.world().setBlock(dig.x, dig.y, dig.z, Blocks.AIR)) {
|
||||
this.server.broadcastBlockChange(dig.x, dig.y, dig.z, Blocks.AIR);
|
||||
|
||||
@@ -156,6 +156,14 @@ public final class LodGenerator {
|
||||
return 0x6E6E6E;
|
||||
case Blocks.ASPHALT:
|
||||
return 0x3A3A3A;
|
||||
case Blocks.BARREL:
|
||||
return 0x53514C;
|
||||
case Blocks.BARREL_BLUE:
|
||||
return 0x3D5C85;
|
||||
case Blocks.BARREL_RED:
|
||||
return 0x7B3535;
|
||||
case Blocks.CRATE:
|
||||
return 0x8A7040;
|
||||
case Blocks.SNOW:
|
||||
return 0xE9EDEF;
|
||||
case Blocks.WATER:
|
||||
|
||||
@@ -2,6 +2,11 @@ package net.rustnotmc.server.world;
|
||||
|
||||
import net.rustnotmc.protocol.Blocks;
|
||||
import net.rustnotmc.protocol.ChunkFormat;
|
||||
import net.rustnotmc.protocol.ItemRegistry;
|
||||
import net.rustnotmc.protocol.ItemStack;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Turns a seed and a position into terrain.
|
||||
@@ -58,6 +63,14 @@ public final class WorldGenerator {
|
||||
/** How many blocks of canopy a tree may reach. */
|
||||
private static final int TREE_REACH = 2;
|
||||
|
||||
/** The salts of the junk piles: usually a point stands empty. */
|
||||
private static final int SALT_SITE = 21;
|
||||
private static final int SALT_COUNT = 22;
|
||||
private static final int SALT_COLOUR = 23;
|
||||
private static final int SALT_CRATE = 24;
|
||||
private static final int SALT_LOOT = 25;
|
||||
private static final int SALT_ITEM = 26;
|
||||
|
||||
private final long seed;
|
||||
private final int size;
|
||||
private final int islandRadius;
|
||||
@@ -579,10 +592,170 @@ public final class WorldGenerator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// the junk piles, put up on the roads where they belong
|
||||
layPiles(chunk, blocks, baseX, baseZ);
|
||||
chunk.markGenerated();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Puts the junk piles of the roads into a chunk.
|
||||
*
|
||||
* The heaps stand where the roads meet the network, at every point that
|
||||
* the road keeps free for them: a quarter of those points carries a
|
||||
* heap, and the rest stands empty so the roads do not read as one long
|
||||
* wall of barrels. A heap is a short row of barrels, two to four of them,
|
||||
* and every third row ends with a crate of food.
|
||||
*
|
||||
* Nothing here depends on a random number, the same way the roads
|
||||
* themselves do not: the same world always has the same heaps, so a
|
||||
* broken one stays broken and an untouched one stays untouched when the
|
||||
* chunk is worked out again.
|
||||
*/
|
||||
private void layPiles(Chunk chunk, byte[] blocks, int baseX, int baseZ) {
|
||||
ChunkPos pos = chunk.pos();
|
||||
for (int[] point : this.roads.spawnPoints()) {
|
||||
int sx = point[0];
|
||||
int sz = point[1];
|
||||
if (sx < baseX - 6 || sx > baseX + ChunkFormat.WIDTH + 6
|
||||
|| sz < baseZ - 6 || sz > baseZ + ChunkFormat.WIDTH + 6) {
|
||||
continue;
|
||||
}
|
||||
if (!isRoad(sx, sz) || (hash(sx, SALT_SITE, sz) & 0xFFFF) % 3 != 0) {
|
||||
continue; // this point stands empty
|
||||
}
|
||||
int count = 2 + (int) ((hash(sx, SALT_COUNT, sz) & 0xFFFF) % 3);
|
||||
int[] dir = pileDirection(sx, sz);
|
||||
for (int j = 0; j < count; j++) {
|
||||
putPileBlock(blocks, baseX, baseZ,
|
||||
sx + dir[0] * j, sz + dir[1] * j, barrelOf(sx, sz, j));
|
||||
}
|
||||
if ((hash(sx, SALT_CRATE, sz) & 0xFFFF) % 3 == 0) {
|
||||
putPileBlock(blocks, baseX, baseZ,
|
||||
sx + dir[0] * count, sz + dir[1] * count, Blocks.CRATE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The way a row of barrels runs at a point of the roads.
|
||||
*
|
||||
* Along the road it stands on, so a heap reads as a little side of the
|
||||
* road rather than a wall across it.
|
||||
*/
|
||||
private int[] pileDirection(int sx, int sz) {
|
||||
int[] ring = this.roads.onRing(sx, sz);
|
||||
if (ring == null) {
|
||||
// the straight roads run along the axes, so a heap laid along the
|
||||
// axis the road runs in lies along the road
|
||||
if (Math.abs(sz - this.roads.centerLine(sz)) <= RoadNetwork.HALF_WIDTH
|
||||
&& Math.abs(sx - this.roads.centerLine(sx)) > RoadNetwork.HALF_WIDTH) {
|
||||
return new int[]{1, 0};
|
||||
}
|
||||
return new int[]{0, 1};
|
||||
}
|
||||
// the ring: a heap lies along the way the ring goes. The tangent is a
|
||||
// quarter turn from the radius, and it is snapped to the axis it
|
||||
// mostly goes in
|
||||
int rx = ring[0] - this.roads.center();
|
||||
int rz = ring[1] - this.roads.center();
|
||||
int tx = -rz;
|
||||
int tz = rx;
|
||||
if (Math.abs(tx) >= Math.abs(tz)) {
|
||||
return new int[]{Integer.signum(tx), 0};
|
||||
}
|
||||
return new int[]{0, Integer.signum(tz)};
|
||||
}
|
||||
|
||||
/** What one barrel of a heap is, grey or blue or red. */
|
||||
private byte barrelOf(int sx, int sz, int index) {
|
||||
int pick = (int) ((hash(sx, SALT_COLOUR + index, sz) & 0xFFFF) % 100);
|
||||
if (pick < 60) {
|
||||
return Blocks.BARREL;
|
||||
}
|
||||
if (pick < 85) {
|
||||
return Blocks.BARREL_BLUE;
|
||||
}
|
||||
return Blocks.BARREL_RED;
|
||||
}
|
||||
|
||||
/** Lays one barrel of a heap into a chunk that holds its square. */
|
||||
private void putPileBlock(byte[] blocks, int baseX, int baseZ,
|
||||
int px, int pz, byte block) {
|
||||
if (!isRoad(px, pz)) {
|
||||
return; // only a road carries the heaps
|
||||
}
|
||||
int height = heightAt(px, pz);
|
||||
int y = height + 1;
|
||||
if (height < SEA_LEVEL + 1 || y <= 0 || y >= Chunk.HEIGHT - 1) {
|
||||
return;
|
||||
}
|
||||
int x = px - baseX;
|
||||
int z = pz - baseZ;
|
||||
if (x < 0 || x >= ChunkFormat.WIDTH || z < 0 || z >= ChunkFormat.WIDTH) {
|
||||
return; // the barrel belongs to the chunk next door
|
||||
}
|
||||
int index = ChunkFormat.index(x, y, z);
|
||||
if (blocks[index] == Blocks.AIR) {
|
||||
blocks[index] = block;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* What comes out of a barrel or a crate of the roads.
|
||||
*
|
||||
* The loot is a pure function of the place as well, so the same barrel
|
||||
* always gives the same content: the looting does not depend on who
|
||||
* counted what when. A grey thing holds scrap with rags, a blue one
|
||||
* parts with a fragment of a blueprint, a red one fuel, and a crate
|
||||
* food and drink. The blue barrels of the ring road sometimes hold the
|
||||
* advanced fragments, the rest of the world gives only the basic ones.
|
||||
*/
|
||||
public List<ItemStack> pileLoot(byte block, int x, int y, int z) {
|
||||
List<ItemStack> out = new ArrayList<ItemStack>();
|
||||
long loot = hash(x, y ^ SALT_LOOT, z);
|
||||
long item = hash(x, SALT_ITEM, z ^ y);
|
||||
switch (block) {
|
||||
case Blocks.BARREL: {
|
||||
out.add(ItemStack.of(ItemRegistry.SCRAP,
|
||||
2 + (int) ((loot & 0xFFFF) % 3)));
|
||||
if ((item & 0xFF) < 64) {
|
||||
out.add(ItemStack.of(ItemRegistry.BANDANA_MASK
|
||||
+ (int) (((item & 0xFF00) >>> 8) % 16), 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blocks.BARREL_BLUE: {
|
||||
out.add(ItemStack.of(ItemRegistry.SCRAP,
|
||||
1 + (int) ((loot & 0xFFFF) % 2)));
|
||||
if ((item & 0xFF) < 115) {
|
||||
out.add(ItemStack.of(ItemRegistry.BASIC_FRAGMENT, 1));
|
||||
}
|
||||
if (this.roads.onRing(x, z) != null && (item & 0xFF00) < 0x2000) {
|
||||
out.add(ItemStack.of(ItemRegistry.ADVANCED_FRAGMENT, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blocks.BARREL_RED: {
|
||||
out.add(ItemStack.of(ItemRegistry.LOW_GRADE_FUEL,
|
||||
1 + (int) ((loot & 0xFFFF) % 2)));
|
||||
if ((item & 0xFF) < 102) {
|
||||
out.add(ItemStack.of(ItemRegistry.CRUDE_OIL, 1));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case Blocks.CRATE: {
|
||||
out.add(ItemStack.of(ItemRegistry.APPLE + (int) ((item & 0xFFFF) % 17),
|
||||
1 + (int) ((loot & 0xFFFF) % 2)));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lays the wood of one tree into a column of a chunk.
|
||||
*
|
||||
|
||||
@@ -0,0 +1,346 @@
|
||||
package net.rustnotmc.server.world;
|
||||
|
||||
import java.util.List;
|
||||
import net.rustnotmc.protocol.Blocks;
|
||||
import net.rustnotmc.protocol.ItemRegistry;
|
||||
import net.rustnotmc.protocol.ItemStack;
|
||||
|
||||
/**
|
||||
* Tests the junk piles of the roads.
|
||||
*
|
||||
* The heaps are what the roads are for: someone put barrels and crates out
|
||||
* there, and a player on foot breaks one and carries off what it kept. The
|
||||
* heaps have to be on the roads, out of the water, and the same in every
|
||||
* world of the same seed, and the blue ones have to pay in the fragments of
|
||||
* the blueprints the workbenches want.
|
||||
*/
|
||||
public final class PilesTest {
|
||||
|
||||
private static int checks;
|
||||
private static int failed;
|
||||
private static final java.util.List<String> failures = new java.util.ArrayList<String>();
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
run("the heaps stand on the roads", new Throwing() {
|
||||
public void go() throws Exception { testPlacement(); }
|
||||
});
|
||||
run("a world has the same heaps again", new Throwing() {
|
||||
public void go() throws Exception { testDeterminism(); }
|
||||
});
|
||||
run("no road keeps a barrel over the sea", new Throwing() {
|
||||
public void go() throws Exception { testNoHeapOverTheSea(); }
|
||||
});
|
||||
run("a grey barrel gives scrap and rags", new Throwing() {
|
||||
public void go() throws Exception { testGrey(); }
|
||||
});
|
||||
run("a blue barrel gives the pieces of a blueprint", new Throwing() {
|
||||
public void go() throws Exception { testFragments(); }
|
||||
});
|
||||
run("a red barrel gives fuel", new Throwing() {
|
||||
public void go() throws Exception { testFuel(); }
|
||||
});
|
||||
run("a crate gives food and drink", new Throwing() {
|
||||
public void go() throws Exception { testCrate(); }
|
||||
});
|
||||
run("a walk over the middle of the island meets heaps", new Throwing() {
|
||||
public void go() throws Exception { testRealWorldHeaps(); }
|
||||
});
|
||||
|
||||
System.out.println();
|
||||
for (int i = 0; i < failures.size(); i++) {
|
||||
System.out.println(" FAIL " + failures.get(i));
|
||||
}
|
||||
System.out.println();
|
||||
System.out.println("checks: " + checks + ", failed: " + failed);
|
||||
if (failed != 0) {
|
||||
System.out.println("FAILED");
|
||||
System.exit(1);
|
||||
}
|
||||
System.out.println("OK");
|
||||
}
|
||||
|
||||
private interface Throwing {
|
||||
void go() throws Exception;
|
||||
}
|
||||
|
||||
private static void run(String name, Throwing body) {
|
||||
checks++;
|
||||
int before = failed;
|
||||
try {
|
||||
body.go();
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
|
||||
private static Chunk chunkOf(WorldGenerator generator, int cx, int cz) {
|
||||
Chunk chunk = new Chunk(new ChunkPos(cx, cz));
|
||||
generator.generate(chunk);
|
||||
return chunk;
|
||||
}
|
||||
|
||||
/**
|
||||
* Walks the heaps of the chunks around the points the road network keeps
|
||||
* for them, and says every barrel stands where a barrel should: on a
|
||||
* road, on the ground, with nothing in the way over it.
|
||||
*/
|
||||
private static void testPlacement() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
RoadNetwork roads = new RoadNetwork(generator.size());
|
||||
int seen = 0;
|
||||
int heaps = 0;
|
||||
java.util.Set<Long> visited = new java.util.HashSet<Long>();
|
||||
for (int[] site : roads.spawnPoints()) {
|
||||
int chunkX = Math.floorDiv(site[0], 16);
|
||||
int chunkZ = Math.floorDiv(site[1], 16);
|
||||
long key = ((long) chunkX << 32) ^ chunkZ;
|
||||
// the same chunk may hold several points, and generation costs
|
||||
// real seconds
|
||||
if (!visited.add(key)) {
|
||||
continue;
|
||||
}
|
||||
Chunk chunk = chunkOf(generator, chunkX, chunkZ);
|
||||
boolean heap = false;
|
||||
for (int y = 0; y < Chunk.HEIGHT; y++) {
|
||||
for (int z = 0; z < Chunk.WIDTH; z++) {
|
||||
for (int x = 0; x < Chunk.WIDTH; x++) {
|
||||
byte id = chunk.block(x, y, z);
|
||||
if (!Blocks.isLootable(id)) {
|
||||
continue;
|
||||
}
|
||||
heap = true;
|
||||
seen++;
|
||||
int worldX = chunkX * 16 + x;
|
||||
int worldZ = chunkZ * 16 + z;
|
||||
yes("a barrel on the road at " + worldX + "," + worldZ,
|
||||
generator.isRoad(worldX, worldZ));
|
||||
eq("the ground under the barrel at " + worldX
|
||||
+ "," + worldZ,
|
||||
Integer.valueOf(y - 1),
|
||||
Integer.valueOf(generator.heightAt(worldX, worldZ)));
|
||||
eq("nothing wedged over the barrel at " + worldX
|
||||
+ "," + worldZ,
|
||||
Byte.valueOf(Blocks.AIR),
|
||||
Byte.valueOf(chunk.block(x, y + 1, z)));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (heap) {
|
||||
heaps++;
|
||||
}
|
||||
}
|
||||
yes("the roads carry heaps (" + seen + " barrels seen)", seen > 0);
|
||||
yes("more heaps of one (" + heaps + " chunks with a heap)",
|
||||
heaps > 3);
|
||||
}
|
||||
|
||||
private static void testDeterminism() throws Exception {
|
||||
Chunk one = chunkOf(new WorldGenerator(1234L), 0, 0);
|
||||
Chunk two = chunkOf(new WorldGenerator(1234L), 0, 0);
|
||||
yes("same seed, same chunk, barrels and all",
|
||||
java.util.Arrays.equals(one.raw(), two.raw()));
|
||||
}
|
||||
|
||||
/** No barrel stands where the sea is, whatever the network plans. */
|
||||
private static void testNoHeapOverTheSea() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
RoadNetwork roads = new RoadNetwork(generator.size());
|
||||
for (int[] point : roads.spawnPoints()) {
|
||||
if (!generator.isRoad(point[0], point[1])) {
|
||||
continue; // the network does not keep a road here
|
||||
}
|
||||
yes("the road at " + point[0] + "," + point[1] + " is dry",
|
||||
generator.heightAt(point[0], point[1]) >= WorldGenerator.SEA_LEVEL + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/** One broken barrel: what comes out of it puts a player on their way. */
|
||||
private static void greyLoot(WorldGenerator generator, int x, int y, int z) {
|
||||
List<ItemStack> loot = generator.pileLoot(Blocks.BARREL, x, y, z);
|
||||
yes("a grey barrel gives something", !loot.isEmpty());
|
||||
int scrap = 0;
|
||||
int others = 0;
|
||||
for (ItemStack stack : loot) {
|
||||
if (stack.item == ItemRegistry.SCRAP) {
|
||||
scrap = stack.count;
|
||||
} else {
|
||||
others++;
|
||||
yes("the rags of a heap are worn things (" + stack.item + ")",
|
||||
stack.item >= ItemRegistry.BANDANA_MASK
|
||||
&& stack.item <= ItemRegistry.ROADSIGN_GLOVES);
|
||||
eq("a rag is one", Integer.valueOf(1),
|
||||
Integer.valueOf(stack.count));
|
||||
}
|
||||
}
|
||||
eq("a grey barrel keeps at most two stacks", Integer.valueOf(1),
|
||||
Integer.valueOf(loot.size() <= 2 ? 1 : 0));
|
||||
yes("the scrap is " + scrap + ", between two and four",
|
||||
scrap >= 2 && scrap <= 4);
|
||||
yes("at most one rag with the scrap", others <= 1);
|
||||
}
|
||||
|
||||
private static void testGrey() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
int rags = 0;
|
||||
for (int w = 0; w < 120; w++) {
|
||||
List<ItemStack> loot = generator.pileLoot(Blocks.BARREL,
|
||||
w * 37, 70, -w * 11);
|
||||
for (ItemStack stack : loot) {
|
||||
if (stack.item != ItemRegistry.SCRAP) {
|
||||
rags++;
|
||||
}
|
||||
}
|
||||
greyLoot(generator, w * 37, 70, -w * 11);
|
||||
}
|
||||
yes("now and then a rag rides with the scrap (" + rags + ")",
|
||||
rags > 0 && rags < 60);
|
||||
}
|
||||
|
||||
/** The blue barrels pay in the fragments of the blueprints. */
|
||||
private static void testFragments() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
RoadNetwork roads = new RoadNetwork(generator.size());
|
||||
int radius = roads.ringRadius();
|
||||
int basic = 0;
|
||||
int advanced = 0;
|
||||
int offRing = 0;
|
||||
// the straight roads of the grid, all the way out, and the ring road
|
||||
// itself: the advanced fragments only belong to the ring
|
||||
for (int w = 0; w < 800; w++) {
|
||||
int x = w * 7 - 2800;
|
||||
int z = (w * 13) % 2200;
|
||||
List<ItemStack> loot = generator.pileLoot(Blocks.BARREL_BLUE, x, 70, z);
|
||||
for (ItemStack stack : loot) {
|
||||
if (stack.item == ItemRegistry.BASIC_FRAGMENT) {
|
||||
basic++;
|
||||
}
|
||||
if (stack.item == ItemRegistry.ADVANCED_FRAGMENT) {
|
||||
advanced++;
|
||||
if (roads.onRing(x, z) == null) {
|
||||
offRing++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (ItemStack stack : generator.pileLoot(Blocks.BARREL_BLUE, x, 70, -z)) {
|
||||
if (stack.item == ItemRegistry.BASIC_FRAGMENT) {
|
||||
basic++;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < 720; i++) {
|
||||
double angle = 2.0D * Math.PI * i / 720;
|
||||
int x = (int) Math.round(Math.cos(angle) * radius);
|
||||
int z = (int) Math.round(Math.sin(angle) * radius);
|
||||
for (ItemStack stack : generator.pileLoot(Blocks.BARREL_BLUE, x, 70, z)) {
|
||||
if (stack.item == ItemRegistry.ADVANCED_FRAGMENT) {
|
||||
advanced++;
|
||||
}
|
||||
}
|
||||
}
|
||||
yes("the blue barrels give the basic fragments (" + basic + ")",
|
||||
basic > 200);
|
||||
yes("the advanced fragments come out now and then (" + advanced + ")",
|
||||
advanced > 0);
|
||||
eq("none of them off the ring", Integer.valueOf(0),
|
||||
Integer.valueOf(offRing));
|
||||
}
|
||||
|
||||
/** The red barrels give fuel, the crude oil with it. */
|
||||
private static void testFuel() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
boolean fuel = false;
|
||||
int oils = 0;
|
||||
for (int w = 0; w < 120; w++) {
|
||||
List<ItemStack> loot = generator.pileLoot(Blocks.BARREL_RED,
|
||||
-w * 19, 70, w * 29);
|
||||
yes("a red barrel gives something", !loot.isEmpty());
|
||||
for (ItemStack stack : loot) {
|
||||
if (stack.item == ItemRegistry.LOW_GRADE_FUEL) {
|
||||
fuel = true;
|
||||
yes("the fuel is " + stack.count + ", one or two",
|
||||
stack.count >= 1 && stack.count <= 2);
|
||||
}
|
||||
if (stack.item == ItemRegistry.CRUDE_OIL) {
|
||||
oils++;
|
||||
eq("a jug of oil is one", Integer.valueOf(1),
|
||||
Integer.valueOf(stack.count));
|
||||
}
|
||||
}
|
||||
yes("a red barrel keeps at most two stacks at " + w,
|
||||
loot.size() <= 2);
|
||||
}
|
||||
yes("the red barrels give fuel", fuel);
|
||||
yes("some of them give crude oil (" + oils + ")",
|
||||
oils > 0 && oils < 60);
|
||||
}
|
||||
|
||||
/** A crate feeds a player: the thing inside is something to eat. */
|
||||
private static void testCrate() throws Exception {
|
||||
WorldGenerator generator = new WorldGenerator(1234L);
|
||||
int foods = 0;
|
||||
boolean drink = false;
|
||||
for (int w = 0; w < 170; w++) {
|
||||
List<ItemStack> loot = generator.pileLoot(Blocks.CRATE,
|
||||
w * 23, 70, -w * 31);
|
||||
eq("a crate gives one stack", Integer.valueOf(1),
|
||||
Integer.valueOf(loot.size()));
|
||||
int item = loot.get(0).item;
|
||||
yes("a crate keeps food or drink (" + item + ")",
|
||||
item >= ItemRegistry.APPLE && item <= ItemRegistry.MUSHROOM);
|
||||
foods++;
|
||||
if (item >= ItemRegistry.SMALL_WATER_BOTTLE
|
||||
&& item <= ItemRegistry.WATER_JUG) {
|
||||
drink = true;
|
||||
}
|
||||
}
|
||||
yes("the crates are counted (" + foods + ")", foods > 0);
|
||||
yes("now and then a crate keeps drink", drink);
|
||||
}
|
||||
|
||||
/**
|
||||
* The heaps a player meets on an actual walk: the chunks over the middle
|
||||
* of the island carry roads, the roads carry heaps, and a heap carries
|
||||
* barrels in a row of two to four.
|
||||
*/
|
||||
private static void testRealWorldHeaps() throws Exception {
|
||||
int heaps = 0;
|
||||
for (int cx = -2; cx <= 2; cx++) {
|
||||
for (int cz = -2; cz <= 2; cz++) {
|
||||
Chunk chunk = chunkOf(new WorldGenerator(1234L), cx, cz);
|
||||
for (int y = 0; y < Chunk.HEIGHT; y++) {
|
||||
for (int z = 0; z < Chunk.WIDTH; z++) {
|
||||
for (int x = 0; x < Chunk.WIDTH; x++) {
|
||||
if (Blocks.isLootable(chunk.block(x, y, z))) {
|
||||
heaps++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
yes("the roads about the middle carry heaps (" + heaps + " barrels)",
|
||||
heaps >= 2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user