From 02b96c0c05e139993dc106a00d7b56978c46be21 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Sat, 24 Aug 2013 07:52:35 -0400 Subject: [PATCH 1/2] 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. --- StevenDimDoors/mod_pocketDim/DDLoot.java | 49 +++++++++++++++++++ .../mod_pocketDim/SchematicLoader.java | 49 +++++++++++-------- .../dungeon/FillContainersOperation.java | 5 +- schematics/ruins/rules.txt | 20 +++++--- 4 files changed, 92 insertions(+), 31 deletions(-) diff --git a/StevenDimDoors/mod_pocketDim/DDLoot.java b/StevenDimDoors/mod_pocketDim/DDLoot.java index 4226697..41d80a9 100644 --- a/StevenDimDoors/mod_pocketDim/DDLoot.java +++ b/StevenDimDoors/mod_pocketDim/DDLoot.java @@ -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); + } + } + } } diff --git a/StevenDimDoors/mod_pocketDim/SchematicLoader.java b/StevenDimDoors/mod_pocketDim/SchematicLoader.java index 630b21f..0cc771c 100644 --- a/StevenDimDoors/mod_pocketDim/SchematicLoader.java +++ b/StevenDimDoors/mod_pocketDim/SchematicLoader.java @@ -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; } } \ No newline at end of file diff --git a/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java b/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java index 7979eaf..c51df0e 100644 --- a/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java +++ b/StevenDimDoors/mod_pocketDim/dungeon/FillContainersOperation.java @@ -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); } } diff --git a/schematics/ruins/rules.txt b/schematics/ruins/rules.txt index ef838c1..a14fe17 100644 --- a/schematics/ruins/rules.txt +++ b/schematics/ruins/rules.txt @@ -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 \ No newline at end of file +? ? ? ? -> 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 From 30b8e98ad8e06b174cd687a696bd17f05ed30a51 Mon Sep 17 00:00:00 2001 From: SenseiKiwi Date: Mon, 26 Aug 2013 01:59:37 -0400 Subject: [PATCH 2/2] Minor Fixes to Old Dungeons Fixed the two blocks sticking out in the multilevel maze that made a staircase unusable. Put a floor in simpleDropHall so it's actually usable rather than killing the player. --- ...e_smallMultilevelMaze_closed_100.schematic | Bin 1764 -> 2024 bytes ...leHall_simpleDropHall_closed_100.schematic | Bin 1011 -> 1248 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/schematics/ruins/maze_smallMultilevelMaze_closed_100.schematic b/schematics/ruins/maze_smallMultilevelMaze_closed_100.schematic index 93fc931c9f963f162cc7ed175efe38d4f983fa5e..333eec84e7c6ad179b2626e419026c1858e5234c 100644 GIT binary patch literal 2024 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 diff --git a/schematics/ruins/simpleHall_simpleDropHall_closed_100.schematic b/schematics/ruins/simpleHall_simpleDropHall_closed_100.schematic index 48c71ab06f2101ba53c4aae42ec1492218fbe2af..d8071e235c9d56209021870aa13a5f6493fe2d6c 100644 GIT binary patch literal 1248 zcmb2|=3sz;w|CCwg``Rx__+OP@~t3~SEpK~&GI_5g|0amswKr_DBYe}c4$sOn%Z2$ zhUt$|XGr89>n`kSaJ4?0{k&$+^Ox_pYx4a6Rc<~<=j4aR-uJwHHSpJyzmM1RuM%h9eDmbilI*ge-sPtsZkpxmZ~q`n z%BQXS`1(~(UrnDM*4;dR`T1j;UoJkmVV2#>W#_~D-<^N<_~m!?{=W3?kAb_UZL5pi zd;j_B?d$#h3%-84`s!`^%Du_&wpN_6&hOuO|Da+1e*0%-yMCtF)NY%zWOwtH-w$5A zc=++nho4GIy0NLl~&81d?saM-^o>$#*upx zH!*>9sc9zw6(x|t2{^-lWU}cjc#M8Id|Irg;>gdL|LeaVj69L@KZW=A^+=Nit=$^W zCab;mAbfxij8%735O=k5qh`hi8q+XzDe1-&T?%nEJKQs@d{@ppxM{V~hNfC|j-G9E9rWi%uH?r=& zx2igQt!0moTDJ1$%^c^>O*+YxS|)bzW~5Q%f-~ooEUdlNxToBVWJymynbHVU|KN?z zOr3zVbip}uOC7iEvWa{?pY`VdlW%mIHgC?VIP_mv-|2V-sT2ElOrE+{Z|nD&TW7z~S;t_VU0e45pN6*g{b#9_?7o%k zvbA%+x6J=F|8HXY&Bzba=7u^?+nO47|I3x17rX8ml}($=dUnI+s##P27|p%Tw)-^L z4Jn%(X3ljjD4XUZra`KQR422Z!e+?2cXL%IpX@sSMyE=<92gFpw5P$N5Sl3+wqN_l zv0~oIJsvZC)NZT1-p;MRNq*P9)>5)W7`7~V-5T7yE?<#B_hr??D+^vnb2xbKinB0% k@k@;9!lZTLe!NEdTW8{yl-^Q=!S&;9lI;>kzXZ)R1RWcu%)w>E9<-u)R2 z46L$mezAhA5&52T<;|&khvPUHqQAZQnLhvf)cBk~SN{seaWnKEX#c;e_LZ<5)Ir2j zM`mwp&cC;A+Ulo|{!CuIp8wXD_M2HBe;1Wi-TM|dci!AH%ijLc@}a%lFIvExLE>XMFvJ+{xF@vB^nQz4?|eSCL_Kjk`H_*B%>LeqKAX z9Z#6+#T3px`S!_=ZQ7pSebztTggswAA$ophTYvR;+42uJSFZC{blzV3cl*}eo2zT0 znSAs9eXaR><81r6S80<2H$N%2DhhM>RIz>DRxl?I#3?WO5FJ15lbFmOb}za2eVaBv zxIS&7=meX4v8j{y>HO5qvD8+m+_UMznuaGIPX%pensi*7`z)7-+)$*0*gSss=BWR(FU`-LlODbM^Uilu z84IRY%l?@-FZb+0_8<9Y?|;4Xyz=$#C$-!E*~!#As9*DWMOoabd(HI~6*mN}elRG% zvRS2XHSgu6>7UQl&zOHcKL6jj>lbgZ-#XZCxYakM-+cEUpSm4}zn}NTPm|u<{dM~N zb*Ja0OMlco;gzc5aZli;+2(V?wY48^|2g_S*(UmvL(1*59uxK`ZpuxYt6p_Sr&3|l z+cc2ENS|*TEgo(?Dd+Ci^n`cf(G