Improved Dungeon Loot and Selection
DDLoot: Implemented a custom version of MC's generateChestContents() for our own chests. It avoids two notable bugs that affect MC's version. FillContainersOperation: Changed code to use DDLoot.generateChestContents() SchematicLoader: Fixed a bug in the way we calculated a seed for selecting our dungeons that would cause certain seeds to dominate all the others. Under certain circumstances, the function would only return -1. That would make our dungeon selection severely biased. That was resolved and the code was specifically tuned for seeding Java's Random even for doors with nearly identical positions. The result was an apparent major improvement in the randomness of dungeons. ruins\rules.txt: Changed the dungeon generation rules to precisely match the complicated scheme we had before. We're still using simple rules to choose dungeons - I used a program to derive the effective distribution of dungeon types that the old code would produce and converted it into the current rule system.
This commit is contained in:
parent
a1a9e39caa
commit
02b96c0c05
4 changed files with 92 additions and 31 deletions
|
@ -4,7 +4,10 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.WeightedRandom;
|
||||
import net.minecraft.util.WeightedRandomChestContent;
|
||||
import net.minecraftforge.common.ChestGenHooks;
|
||||
|
||||
|
@ -33,6 +36,8 @@ public class DDLoot {
|
|||
private static final int RARE_LOOT_WEIGHT = 1; //Same weight as music discs, golden apple
|
||||
private static final int DUNGEON_CHEST_WEIGHT_INFLATION = 10; // (weight of iron ingots in dungeon) / (weight of iron ingots in other chests)
|
||||
|
||||
private DDLoot() { }
|
||||
|
||||
public static void registerInfo()
|
||||
{
|
||||
DDProperties properties = DDProperties.instance();
|
||||
|
@ -156,4 +161,48 @@ public class DDLoot {
|
|||
//System.out.println(item.theItemId.getDisplayName() + "\t" + item.itemWeight);
|
||||
}
|
||||
}
|
||||
|
||||
public static void generateChestContents(ChestGenHooks chestInfo, IInventory inventory, Random random)
|
||||
{
|
||||
//This is a custom version of net.minecraft.util.WeightedRandomChestContent.generateChestContents()
|
||||
//It's designed to avoid the following bugs in MC 1.5:
|
||||
//1. The randomized filling algorithm will sometimes overwrite item stacks with other stacks
|
||||
//2. If multiple enchanted books appear, then they will have the same enchantment
|
||||
|
||||
//The prime number below is used for choosing chest slots in a seemingly-random pattern. Its value
|
||||
//was selected specifically to achieve a spread-out distribution for chests with up to 104 slots.
|
||||
//Choosing a prime number ensures that our increments are relatively-prime to the chest size, which
|
||||
//means we'll cover all the slots before repeating any. This is mathematically guaranteed.
|
||||
final int primeOffset = 239333;
|
||||
|
||||
int count = chestInfo.getCount(random);
|
||||
int size = inventory.getSizeInventory();
|
||||
WeightedRandomChestContent[] content = chestInfo.getItems(random);
|
||||
|
||||
for (int k = 0; k < count; k++)
|
||||
{
|
||||
WeightedRandomChestContent selection = (WeightedRandomChestContent)WeightedRandom.getRandomItem(random, content);
|
||||
|
||||
//Call getChestGenBase() to make sure we generate a different enchantment for books.
|
||||
//Don't just use a condition to check if the item is an instance of ItemEnchantedBook because
|
||||
//we don't know if other mods might add items that also need to be regenerated.
|
||||
selection = selection.theItemId.getItem().getChestGenBase(chestInfo, random, selection);
|
||||
|
||||
ItemStack[] stacks = ChestGenHooks.generateStacks(random, selection.theItemId, selection.theMinimumChanceToGenerateItem, selection.theMaximumChanceToGenerateItem);
|
||||
|
||||
for (ItemStack item : stacks)
|
||||
{
|
||||
int limit = size;
|
||||
int index = random.nextInt(size);
|
||||
|
||||
while (limit > 0 && inventory.getStackInSlot(index) != null)
|
||||
{
|
||||
limit--;
|
||||
index = (index + primeOffset) % size;
|
||||
}
|
||||
|
||||
inventory.setInventorySlotContents(index, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ public class SchematicLoader
|
|||
if (dimList.get(destDimID).dungeonGenerator == null)
|
||||
{
|
||||
//TODO: We should centralize RNG initialization and world-seed modifiers for each specific application.
|
||||
final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L ^ computeDestinationHash(link);
|
||||
final long localSeed = world.getSeed() ^ 0x2F50DB9B4A8057E4L ^ calculateDestinationSeed(link);
|
||||
final Random random = new Random(localSeed);
|
||||
|
||||
dungeonHelper.generateDungeonLink(link, dungeonHelper.getDimDungeonPack(originDimID), random);
|
||||
|
@ -155,43 +155,50 @@ public class SchematicLoader
|
|||
return dungeon;
|
||||
}
|
||||
|
||||
private static long computeDestinationHash(LinkData link)
|
||||
private static long calculateDestinationSeed(LinkData link)
|
||||
{
|
||||
//Time for some witchcraft.
|
||||
//The code here is inspired by a discussion on Stack Overflow regarding hash codes for 3D.
|
||||
//Source: http://stackoverflow.com/questions/9858376/hashcode-for-3d-integer-coordinates-with-high-spatial-coherence
|
||||
|
||||
//Use 8 bits from Y and 24 bits from X and Z. Mix in 8 bits from the destination dim ID too - that means
|
||||
//Use 8 bits from Y and 16 bits from X and Z. Mix in 8 bits from the destination dim ID too - that means
|
||||
//even if you aligned two doors perfectly between two pockets, it's unlikely they would lead to the same dungeon.
|
||||
//We map bits in reverse order to produce more varied RNG output for nearly-identical points. The reason is
|
||||
//that Java's Random outputs the 32 MSBs of its internal state to produce its output. If the differences
|
||||
//between two seeds are small (i.e. in the LSBs), then they will tend to produce similar random outputs anyway!
|
||||
|
||||
//Only bother to assign the 48 least-significant bits since Random only takes those bits from its seed.
|
||||
//NOTE: The casts to long are necessary to get the right results from the bit shifts!!!
|
||||
|
||||
int bit;
|
||||
int index;
|
||||
long hash;
|
||||
int w = link.destDimID;
|
||||
int x = link.destXCoord;
|
||||
int y = link.destYCoord;
|
||||
int z = link.destZCoord;
|
||||
final int w = link.destDimID;
|
||||
final int x = link.destXCoord;
|
||||
final int y = link.destYCoord;
|
||||
final int z = link.destZCoord;
|
||||
|
||||
hash = 0;
|
||||
index = 0;
|
||||
index = 48;
|
||||
for (bit = 0; bit < 8; bit++)
|
||||
{
|
||||
hash |= ((w >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= ((x >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= ((y >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= ((z >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= (long) ((w >> bit) & 1) << index;
|
||||
index--;
|
||||
hash |= (long) ((x >> bit) & 1) << index;
|
||||
index--;
|
||||
hash |= (long) ((y >> bit) & 1) << index;
|
||||
index--;
|
||||
hash |= (long) ((z >> bit) & 1) << index;
|
||||
index--;
|
||||
}
|
||||
for (; bit < 24; bit++)
|
||||
for (; bit < 16; bit++)
|
||||
{
|
||||
hash |= ((x >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= ((z >> bit) & 1) << index;
|
||||
index++;
|
||||
hash |= (long) ((x >> bit) & 1) << index;
|
||||
index--;
|
||||
hash |= (long) ((z >> bit) & 1) << index;
|
||||
index--;
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
}
|
|
@ -10,9 +10,7 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.tileentity.TileEntityChest;
|
||||
import net.minecraft.tileentity.TileEntityDispenser;
|
||||
import net.minecraft.util.WeightedRandomChestContent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ChestGenHooks;
|
||||
import StevenDimDoors.mod_pocketDim.DDLoot;
|
||||
import StevenDimDoors.mod_pocketDim.schematic.WorldOperation;
|
||||
|
||||
|
@ -42,8 +40,7 @@ public class FillContainersOperation extends WorldOperation
|
|||
TileEntityChest chest = (TileEntityChest) tileEntity;
|
||||
if (isInventoryEmpty(chest))
|
||||
{
|
||||
ChestGenHooks info = DDLoot.DungeonChestInfo;
|
||||
WeightedRandomChestContent.generateChestContents(random, info.getItems(random), chest, info.getCount(random));
|
||||
DDLoot.generateChestContents(DDLoot.DungeonChestInfo, chest, random);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -22,14 +22,22 @@ Exit -> DeadEnd Exit
|
|||
|
||||
DeadEnd -> DeadEnd Exit
|
||||
|
||||
? ? ? ? ? ? ? ? -> Trap#20 SimpleHall#40 ComplexHall#10 Exit#20 DeadEnd#10
|
||||
? ? ? ? ? ? ? ? ? -> DeadEnd#10 Exit#11 SimpleHall#24 ComplexHall#16 Trap#23 Maze#11
|
||||
|
||||
? ? ? ? -> Trap#18 SimpleHall#40 ComplexHall#10 Exit#18 DeadEnd#10 Hub#4
|
||||
? ? ? ? ? ? ? ? -> DeadEnd#9 Exit#10 SimpleHall#24 ComplexHall#16 Trap#23 Maze#11 Hub#6
|
||||
|
||||
? ? ? -> ComplexHall Hub Trap SimpleHall Maze
|
||||
? ? ? ? ? ? ? -> DeadEnd#8 Exit#9 SimpleHall#25 ComplexHall#17 Trap#23 Maze#11 Hub#7
|
||||
|
||||
? ? -> ComplexHall Hub Trap SimpleHall Maze
|
||||
? ? ? ? ? ? -> DeadEnd#7 Exit#8 SimpleHall#26 ComplexHall#17 Trap#23 Maze#11 Hub#8
|
||||
|
||||
? -> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10
|
||||
? ? ? ? ? -> DeadEnd#6 Exit#6 SimpleHall#27 ComplexHall#18 Trap#23 Maze#11 Hub#9
|
||||
|
||||
-> ComplexHall#40 Hub#30 Trap#10 SimpleHall#10 Maze#10
|
||||
? ? ? ? -> DeadEnd#5 Exit#5 SimpleHall#28 ComplexHall#18 Trap#23 Maze#11 Hub#10
|
||||
|
||||
? ? ? -> DeadEnd#3 Exit#3 SimpleHall#29 ComplexHall#19 Trap#23 Maze#11 Hub#11
|
||||
|
||||
? ? -> SimpleHall#9 ComplexHall#8 Trap#17 Maze#23 Hub#44
|
||||
|
||||
? -> SimpleHall#9 ComplexHall#8 Trap#17 Maze#23 Hub#44
|
||||
|
||||
-> SimpleHall#2 ComplexHall#54 Trap#2 Maze#2 Hub#41
|
||||
|
|
Loading…
Reference in a new issue