Finish loottable generation & chest generation

This commit is contained in:
thebigsmileXD 2017-05-14 11:32:58 +02:00
parent 58b4870fee
commit 1f10807c7b
4 changed files with 188 additions and 140 deletions

View file

@ -23,23 +23,23 @@ use Ad5001\BetterGen\structure\SakuraTree;
use Ad5001\BetterGen\structure\Temple;
use Ad5001\BetterGen\structure\Well;
use pocketmine\block\Block;
use pocketmine\block\Chest;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\event\block\BlockBreakEvent;
use pocketmine\event\Listener;
use pocketmine\event\player\PlayerInteractEvent;
use pocketmine\item\Item;
use pocketmine\level\generator\biome\Biome;
use pocketmine\level\generator\Generator;
use pocketmine\level\generator\object\OakTree;
use pocketmine\level\Position;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\StringTag;
use pocketmine\Player;
use pocketmine\plugin\PluginBase;
use pocketmine\tile\Chest as TileChest;
use pocketmine\block\Chest;
use pocketmine\tile\Tile;
use pocketmine\utils\Config;
use pocketmine\utils\Random;
@ -48,6 +48,7 @@ use pocketmine\utils\TextFormat;
class Main extends PluginBase implements Listener {
const PREFIX = "§l§o§b[§r§l§2Better§aGen§o§b]§r§f ";
const SAKURA_FOREST = 100; // Letting some place for future biomes.
private static $instance;
/**
@ -60,21 +61,39 @@ class Main extends PluginBase implements Listener {
BetterNormal::registerBiome($biome);
}
/**
* Places a looting chest block and creates the corresponding tile
* @param Block $block
* @param $lootfile
*/
static public function placeLootChest(Block $block, $lootfile) {
$block->getLevel()->setBlock($block, $block, true);
$nbt = new CompoundTag("", [
new StringTag("id", Tile::CHEST),
new IntTag("x", (int)$block->x),
new IntTag("y", (int)$block->y),
new IntTag("z", (int)$block->z),
new StringTag("generateLoot", $lootfile)
]);
$tile = new TileChest($block->getLevel(), $nbt);
$tile->spawnToAll();
}
/**
* Called when the plugin enables
*/
public function onEnable() {
self::$instance = $this;
$this->getServer()->getPluginManager()->registerEvents($this, $this);
Generator::addGenerator(BetterNormal::class, "betternormal");
if ($this->isOtherNS()) $this->getLogger()->warning("Tesseract detected. Note that Tesseract is not up to date with the generation structure and some generation features may be limited or not working");
@mkdir($this->getDataFolder());
if (!file_exists(LootTable::getPluginFolder() . "processingLoots.json"))
file_put_contents(LootTable::getPluginFolder() . "processingLoots.json", "{}");
if (!in_array($this->getDataFolder() . 'resources\mcpe-default-addon\.', $this->getResources())) $this->getLogger()->alert('The loot files are missing! Make sure you got all files / did git clone --recursive');
}
/**
* Check if it's a Tesseract like namespace
* @return bool
* @return bool
*/
public static function isOtherNS() {
try {
@ -84,14 +103,12 @@ class Main extends PluginBase implements Listener {
}
}
/**
* Called when the plugin disables
*/
public function onDisable() {
}
/**
* Called when one of the defined commands of the plugin has been called
* @param $sender \pocketmine\command\CommandSender
@ -261,8 +278,9 @@ class Main extends PluginBase implements Listener {
* Registers a forest type.
* @param $name string
* @param $treeClass string
* @params $infos Array(temperature, rainfall)
* @param array $infos
* @return bool
* @params $infos Array(temperature, rainfall)
*/
public function registerForest(string $name, string $treeClass, array $infos): bool {
if (!@class_exists($treeClass))
@ -274,16 +292,54 @@ class Main extends PluginBase implements Listener {
return BetterForest::registerForest($name, $treeClass, $infos);
}
/**
* Checks when a player attempts to open a loot chest which is not created yet
* @param PlayerInteractEvent $event
*/
public function onInteract(PlayerInteractEvent $event) {
if (($block = $event->getBlock())->getId() !== Block::CHEST) return;
if (($block = $event->getBlock())->getId() !== Block::CHEST || $event->getAction() !== PlayerInteractEvent::RIGHT_CLICK_BLOCK) return;
$this->generateLootChest($block);
}
/**
* Fills a chest with loot
* @param Block $block
* @param Random|null $random
*/
static public function generateLootChest(Block $block, Random $random = null) {
if (!$block instanceof Chest) return;
$tile = $block->getLevel()->getTile($block);
if (is_null($tile)) {
//TODO new tile, but no loot, because we don't know which type of loot chest this is
$nbt = new CompoundTag("", [
new StringTag("id", Tile::CHEST),
new IntTag("x", (int)$block->x),
new IntTag("y", (int)$block->y),
new IntTag("z", (int)$block->z)
]);
$tile = new TileChest($block->getLevel(), $nbt);
$tile->spawnToAll();
return;
}
if (!$tile instanceof TileChest) return;
//Check if lootchest (or already generated loot)
if (!isset($tile->namedtag->generateLoot)) return;
$table = new LootTable($config = new Config(self::getInstance()->getDataFolder() . '\\resources\\mcpe-default-addon\\' . $tile->namedtag->generateLoot . '.json'));
$size = $tile->getInventory()->getSize();
$loot = $table->getRandomLoot($random);
$items = array_pad($loot, $size, Item::get(0));
shuffle($items);
$tile->getInventory()->setContents($items);
unset($tile->namedtag->generateLoot);
}
/**
* @return Main
*/
static public function getInstance() {
return self::$instance;
}
/**
* Checks when a player breaks a loot chest which is not created yet
* @param BlockBreakEvent $event
@ -292,16 +348,4 @@ class Main extends PluginBase implements Listener {
if (($block = $event->getBlock())->getId() !== Block::CHEST) return;
$this->generateLootChest($block);
}
private function generateLootChest(Block $block) {
//TODO
if (!$block instanceof Chest) return;
if (is_null($block->getLevel()->getTile($block))) {
//TODO new tile, but no loot, because we don't know which type of loot it is
return;
}
if (!($tile = $block->getLevel()->getTile($block)) instanceof TileChest) return;
/** TileChest $tile */
$tile->getInventory()->setContents([]);//TODO
}
}

View file

@ -14,9 +14,9 @@
namespace Ad5001\BetterGen\loot;
use Ad5001\BetterGen\Main;
use pocketmine\item\Item;
use pocketmine\item\Tool;
use pocketmine\Server;
use pocketmine\utils\Config;
use pocketmine\utils\Random;
@ -36,114 +36,105 @@ class LootTable {
}
/**
* Public function to generate loot. A {@link: \pocketmine\utils\Random} can be passed.
* @param Random|null $random
* @return Item[]
*/
public function createLoot(Random $random = null) {
return self::getRandomLoot($random);
}
/**
* Internal function. Serves as actual file reader + sub-table loader
* Public function to generate loot. A {@link: \pocketmine\utils\Random} can be passed. Serves as file reader + sub-table loader
* Do _NOT_ use this in the source, use LootTable::createLoot instead
* @param Random|null $random
* @return Item[]
*/
private function getRandomLoot(Random $random = null) {
if (is_null($random)) $random = new Random(microtime());
$array = [];
public function getRandomLoot(Random $random = null) {
if (is_null($random)) $random = new Random();
$items = [];
foreach ($this->lootFile->get("pools") as $rolls) {
$maxrolls = $rolls["rolls"];//TODO: $rolls["conditions"]
if (isset($rolls["rolls"]["min"]) && isset($rolls["rolls"]["max"])) $maxrolls = $random->nextRange($rolls["rolls"]["min"], $rolls["rolls"]["max"]);
else $maxrolls = $rolls["rolls"];//TODO: $rolls["conditions"] //Example: looting swords
while ($maxrolls > 0) {
$array = [];
$maxrolls--;
foreach ($rolls["entries"] as $index => $entries) {
$array[] = $entries["weight"]??1;
}
}
$val = $rolls["entries"][$this->getRandomWeightedElement($array)];
//typecheck
if ($val["type"] == "loot_table") {
$loottable = new self(new Config(Server::getInstance()->getFilePath() . "src/pocketmine/resources/" . $val["name"] . ".json", Config::JSON, []));
$items = array_merge($items, $loottable->getRandomLoot());
unset($loottable);
} elseif ($val["type"] == "item") {
print $val["name"] . PHP_EOL;
//name fix
$val["name"] = self::fixItemName($val["name"]);
$item = Item::fromString($val["name"]);
if (isset($val["functions"])) {
foreach ($val["functions"] as $function) {
switch ($functionname = $function["function"]) {
case "set_damage": {
if ($item instanceof Tool) $item->setDamage(mt_rand($function["damage"]["min"] * $item->getMaxDurability(), $function["damage"]["max"] * $item->getMaxDurability()));
else $item->setDamage($random->nextRange($function["damage"]["min"], $function["damage"]["max"]));
}
break;
case "set_data": {
//fish fix, blame mojang
if ($item->getId() == Item::RAW_FISH) {
switch ($function["data"]) {
case 1:
$item = Item::get(Item::RAW_SALMON, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
case 2:
$item = Item::get(Item::CLOWN_FISH, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
case 3:
$item = Item::get(Item::PUFFER_FISH, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
default:
break;
$val = $rolls["entries"][$this->getRandomWeightedElement($array)];
//typecheck
if ($val["type"] == "loot_table") {
$loottable = new LootTable(new Config(Main::getInstance()->getDataFolder() . '\\resources\\mcpe-default-addon\\' . $val["name"] . ".json"));
$items = array_merge($items, $loottable->getRandomLoot($random));
unset($loottable);
} elseif ($val["type"] == "item") {
//name fix
$val["name"] = self::fixItemName($val["name"]);
$item = Item::fromString($val["name"]);
if (isset($val["functions"])) {
foreach ($val["functions"] as $function) {
switch ($functionname = $function["function"]) {
case "set_damage": {
if ($item instanceof Tool) $item->setDamage($random->nextRange($function["damage"]["min"] * $item->getMaxDurability(), $function["damage"]["max"] * $item->getMaxDurability()));
else $item->setDamage($random->nextRange($function["damage"]["min"], $function["damage"]["max"]));
}
break;
case "set_data": {
//fish fix, blame mojang
if ($item->getId() == Item::RAW_FISH) {
switch ($function["data"]) {
case 1:
$item = Item::get(Item::RAW_SALMON, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
case 2:
$item = Item::get(Item::CLOWN_FISH, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
case 3:
$item = Item::get(Item::PUFFER_FISH, $item->getDamage(), $item->getCount(), $item->getCompoundTag());
break;
default:
break;
}
} else $item->setDamage($function["data"]);
}
break;
case "set_count": {
$item->setCount($random->nextRange($function["count"]["min"], $function["count"]["max"]));
}
break;
case "furnace_smelt": {
/* TODO
Mostly bound to conditions (burning)
"conditions": [
{
"condition": "entity_properties",
"entity": "this",
"properties": {
"on_fire": true
}
}
} else $item->setDamage($function["data"]);
]
*/
}
break;
case "enchant_randomly": {
//TODO
}
break;
case "enchant_with_levels": {
//TODO
}
break;
case "looting_enchant": {
//TODO
}
break;
default:
assert("Unknown looting table function $functionname, skipping");
}
break;
case "set_count": {
$item->setCount($random->nextRange($function["count"]["min"], $function["count"]["max"]));
}
break;
case "furnace_smelt": {
/* TODO
Mostly bound to conditions (burning)
"conditions": [
{
"condition": "entity_properties",
"entity": "this",
"properties": {
"on_fire": true
}
}
]
*/
}
break;
case "enchant_randomly": {
//TODO
}
break;
case "enchant_with_levels": {
//TODO
}
break;
case "looting_enchant": {
//TODO
}
break;
default:
assert("Unknown looting table function $functionname, skipping");
}
}
$items[] = $item;
}
$items[] = $item;
}
}
return $items;
}
/**
* TODO: Make random actually useful here.
* TODO: Make Random::class actually useful here.
* @param array $weightedValues
* @return mixed
*/
@ -161,7 +152,19 @@ class LootTable {
* @return mixed
*/
private static function fixItemName($name) {
//TODO add a switch-case here
switch ($name) {
case 'minecraft:horsearmoriron':
$name = 'minecraft:iron_horse_armor';
break;
case 'minecraft:horsearmorgold':
$name = 'minecraft:gold_horse_armor';
break;
case 'minecraft:horsearmordiamond':
$name = 'minecraft:diamond_horse_armor';
break;
default: {
}
}
return $name;
}
}

View file

@ -35,11 +35,11 @@ class Igloo extends Object {
/**
* Places an igloo
* @param $level pocketmine\level\ChunkManager
* @param $x int
* @param $y int
* @param $z int
* @param $random pocketmine\utils\Random
* @param ChunkManager $level
* @param $x
* @param $y
* @param $z
* @param Random $random
* @return bool placed
*/
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random) {
@ -347,11 +347,11 @@ class Igloo extends Object {
/**
* Checks if an igloo is placeable
* @param $level pocketmine\level\ChunkManager
* @param $x int
* @param $y int
* @param $z int
* @param $random pocketmine\utils\Random
* @param ChunkManager $level
* @param $x
* @param $y
* @param $z
* @param Random $random
* @return bool
*/
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random) {

View file

@ -14,10 +14,12 @@
namespace Ad5001\BetterGen\structure;
use Ad5001\BetterGen\Main;
use Ad5001\BetterGen\utils\BuildingUtils;
use pocketmine\block\Block;
use pocketmine\level\ChunkManager;
use pocketmine\level\generator\object\Object;
use pocketmine\level\Position;
use pocketmine\math\Vector3;
use pocketmine\utils\Random;
@ -113,14 +115,13 @@ class Temple extends Object {
/**
* Checks if a temple is placeable
* @param $level pocketmine\level\ChunkManager
* @param $x int
* @param $y int
* @param $z int
* @param $random pocketmine\utils\Random
* @param ChunkManager $level
* @param $x
* @param $y
* @param $z
* @param Random $random
* @return bool
*/
public function canPlaceObject(ChunkManager $level, $x, $y, $z, Random $random) {
$this->level = $level;
$this->direction = $random->nextBoundedInt(4);
@ -134,11 +135,11 @@ class Temple extends Object {
/**
* Places a temple
* @param $level pocketmine\level\ChunkManager
* @param $x int
* @param $y int
* @param $z int
* @param $random pocketmine\utils\Random
* @param ChunkManager $level
* @param $x
* @param $y
* @param $z
* @param Random $random
*/
public function placeObject(ChunkManager $level, $x, $y, $z, Random $random) {
// Clearing space...
@ -216,20 +217,19 @@ class Temple extends Object {
$this->placeBlock($xx, $y - 13, $zz, Block::TNT);
$this->placeBlock($x, $y - 11, $z, Block::STONE_PRESSURE_PLATE);
//TODO TILES
$this->placeBlock($x, $y - 11, $z + 2, Block::CHEST, 4);
$this->placeBlock($x, $y - 11, $z - 2, Block::CHEST, 2);
$this->placeBlock($x + 2, $y - 11, $z, Block::CHEST, 5);
$this->placeBlock($x - 2, $y - 11, $z, Block::CHEST, 3);
$this->placeBlock($x, $y - 10, $z + 2, Block::AIR);
$this->placeBlock($x, $y - 10, $z - 2, Block::AIR);
$this->placeBlock($x + 2, $y - 10, $z, Block::AIR);
$this->placeBlock($x - 2, $y - 10, $z, Block::AIR);
// Chests
/*LootTable::buildLootTable(new Vector3($x, $y - 11, $z + 2), LootTable::LOOT_DESERT_TEMPLE, $random);//TODO: Improve using addon
LootTable::buildLootTable(new Vector3($x, $y - 11, $z - 2), LootTable::LOOT_DESERT_TEMPLE, $random);
LootTable::buildLootTable(new Vector3($x + 2, $y - 11, $z), LootTable::LOOT_DESERT_TEMPLE, $random);
LootTable::buildLootTable(new Vector3($x - 2, $y - 11, $z), LootTable::LOOT_DESERT_TEMPLE, $random);*/
#$this->placeBlock($x, $y - 11, $z + 2, Block::CHEST, 4);
#$this->placeBlock($x, $y - 11, $z - 2, Block::CHEST, 2);
#$this->placeBlock($x + 2, $y - 11, $z, Block::CHEST, 5);
#$this->placeBlock($x - 2, $y - 11, $z, Block::CHEST, 3);
Main::placeLootChest(Block::get(Block::CHEST, 2, new Position($x, $y - 11, $z + 2, $this->level)), 'loot_tables\\chests\\desert_pyramid');
Main::placeLootChest(Block::get(Block::CHEST, 3, new Position($x, $y - 11, $z - 2, $this->level)), 'loot_tables\\chests\\desert_pyramid');
Main::placeLootChest(Block::get(Block::CHEST, 4, new Position($x + 2, $y - 11, $z, $this->level)), 'loot_tables\\chests\\desert_pyramid');
Main::placeLootChest(Block::get(Block::CHEST, 5, new Position($x - 2, $y - 11, $z, $this->level)), 'loot_tables\\chests\\desert_pyramid');
// Entrance is a rectangular parallelepiped
switch ($this->direction) {
@ -896,6 +896,7 @@ class Temple extends Object {
* @param $z int
* @param $id int
* @param $meta int
* @param bool $top
* @return void
*/
protected function placeSlab($x, $y, $z, $id = 44, $meta = 1, $top = false) {